[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/BlueTailCat/esp32_https_server/master/src/HTTPConnection.cpp [Back]  [Original]

#include "HTTPConnection.hpp"

namespace httpsserver {

HTTPConnection::HTTPConnection(ResourceResolver * resResolver):
  _resResolver(resResolver) {
  _socket = -1;
  _addrLen = 0;

  _bufferProcessed = 0;
  _bufferUnusedIdx = 0;

  _connectionState = STATE_UNDEFINED;
  _clientState = CSTATE_UNDEFINED;
  _httpHeaders = NULL;
  _defaultHeaders = NULL;
  _isKeepAlive = false;
  _lastTransmissionTS = millis();
  _shutdownTS = 0;
  _wsHandler = nullptr;
}

HTTPConnection::~HTTPConnection() {
  // Close the socket
  closeConnection();
}

/**
 * Initializes the connection from a server socket.
 *
 * The call WILL BLOCK if accept(serverSocketID) blocks. So use select() to check for that in advance.
 */
int HTTPConnection::initialize(int serverSocketID, HTTPHeaders *defaultHeaders) {
  if (_connectionState == STATE_UNDEFINED) {
    _defaultHeaders = defaultHeaders;
    _socket = accept(serverSocketID, (struct sockaddr * )&_sockAddr, &_addrLen);

    // Build up SSL Connection context if the socket has been created successfully
    if (_socket >= 0) {
      HTTPS_DLOGHEX("[-->] New connection. Socket fid is: ", _socket);
      _connectionState = STATE_INITIAL;
      _httpHeaders = new HTTPHeaders();
      refreshTimeout();
      return _socket;

    }
     
    HTTPS_DLOG("[ERR] Could not accept() new connection");
   
    _connectionState = STATE_ERROR;
    _clientState = CSTATE_ACTIVE;

    // This will only be called if the connection could not be established and cleanup
    // variables  etc.
    closeConnection();
  }
  // Error: The connection has already been established or could not be established
  return -1;
}


/**
 * True if the connection is timed out.
 *
 * (Should be checkd in the loop and transition should go to CONNECTION_CLOSE if exceeded)
 */
bool HTTPConnection::isTimeoutExceeded() {
  return _lastTransmissionTS + HTTPS_CONNECTION_TIMEOUT < millis();
}

/**
 * Resets the timeout to allow again the full HTTPS_CONNECTION_TIMEOUT milliseconds
 */
void HTTPConnection::refreshTimeout() {
  _lastTransmissionTS = millis();
}

/**
 * Returns true, if the connection has been closed.
 */
bool HTTPConnection::isClosed() {
  return (_connectionState == STATE_ERROR || _connectionState == STATE_CLOSED);
}

/**
 * Returns true, if the connection has been closed due to error
 */
bool HTTPConnection::isError() {
  return (_connectionState == STATE_ERROR);
}

bool HTTPConnection::isSecure() {
  return false;
}

void HTTPConnection::closeConnection() {
  // TODO: Call an event handler here, maybe?

  if (_connectionState != STATE_ERROR && _connectionState != STATE_CLOSED) {

    // First call to closeConnection - set the timestamp to calculate the timeout later on
    if (_connectionState != STATE_CLOSING) {
      _shutdownTS = millis();
    }

    // Set the connection state to closing. We stay in closing as long as SSL has not been shutdown
    // correctly
    _connectionState = STATE_CLOSING;
  }

  // Tear down the socket
  if (_socket >= 0) {
    HTTPS_DLOGHEX("[] Websocket detected");
      return true;
  } else
      return false;
}

/**
 * Middleware function that handles the validation of parameters
 */
void validationMiddleware(HTTPRequest * req, HTTPResponse * res, std::function next) {
  bool valid = true;
  // Get the matched node
  HTTPNode * node = req->getResolvedNode();
  // Get the parameters
  ResourceParameters * params = req->getParams();

  // Iterate over the validators and run them
  std::vector * validators = node->getValidators();
  for(std::vector::iterator validator = validators->begin(); valid && validator != validators->end(); ++validator) {
    std::string param = params->getUrlParameter((*validator)->_idx);
    valid = ((*validator)->_validatorFunction)(param);
  }

  if (valid) {
    next();
  } else {
    res->setStatusCode(400);
    res->setStatusText("Bad Request");
    res->print("400 Bad Request");
  }
}

/**
 * Handler function for the websocket handshake. Will be used by HTTPConnection if a websocket is detected
 */
void handleWebsocketHandshake(HTTPRequest * req, HTTPResponse * res) {
  res->setStatusCode(101);
  res->setStatusText("Switching Protocols");
  res->setHeader("Upgrade", "websocket");
  res->setHeader("Connection", "Upgrade");
  res->setHeader("Sec-WebSocket-Accept", websocketKeyResponseHash(req->getHeader("Sec-WebSocket-Key")));
  res->print("");
}

/**
 * Function used to compute the value of the Sec-WebSocket-Accept during Websocket handshake
 */
std::string websocketKeyResponseHash(std::string const &key) {
  std::string newKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  uint8_t shaData[HTTPS_SHA1_LENGTH];
  esp_sha(SHA1, (uint8_t*)newKey.data(), newKey.length(), shaData);

  // Get output size required for base64 representation
  size_t b64BufferSize = 0;
  mbedtls_base64_encode(nullptr, 0, &b64BufferSize, (const unsigned char*)shaData, HTTPS_SHA1_LENGTH);

  // Do the real encoding
  unsigned char bufferOut[b64BufferSize];
  size_t bytesEncoded = 0;
  int res = mbedtls_base64_encode(
    bufferOut,
    b64BufferSize,
    &bytesEncoded,
    (const unsigned char*)shaData,
    HTTPS_SHA1_LENGTH
  );
  
  // Check result and return the encoded string
  if (res != 0) {
    return std::string();
  }
  return std::string((char*)bufferOut, bytesEncoded);
} // WebsocketKeyResponseHash

} /* namespace httpsserver */

Web Proxy Viewer  |  New URL  |  Original Page