bool SimpleHttpClient::readHeader () { char* pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length()); while (pos) { // size_t is unsigned, should never get < 0 assert(pos >= _readBuffer.c_str()); size_t len = pos - _readBuffer.c_str(); string line(_readBuffer.c_str(), len); _readBuffer.erase_front(len + 1); //printf("found header line %s\n", line.c_str()); if (line == "\r" || line == "") { // end of header found if (_result->isChunked()) { _state = IN_READ_CHUNKED_HEADER; return readChunkedHeader(); } else if (! _result->hasContentLength()) { // no content-length header in response _state = IN_READ_BODY; return readBody(); } else if (_result->hasContentLength() && _result->getContentLength() > 0) { // found content-length header in response if (_result->getContentLength() > _maxPacketSize) { setErrorMessage("Content-Length > max packet size found", true); // reset connection this->close(); _state = DEAD; return false; } _state = IN_READ_BODY; return readBody(); } else { _result->setResultType(SimpleHttpResult::COMPLETE); _state = FINISHED; if (! _keepAlive) { _connection->disconnect(); } } break; } else { _result->addHeaderField(line); } pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length()); } return true; }
bool SimpleHttpClient::readChunkedBody () { if (_readBuffer.length() >= _nextChunkedSize) { _result->getBody().write(_readBuffer.c_str(), (size_t) _nextChunkedSize); _readBuffer.erase_front((size_t) _nextChunkedSize); _state = IN_READ_CHUNKED_HEADER; return readChunkedHeader(); } return true; }
bool SimpleHttpClient::readHeader () { char* pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length()); while (pos) { size_t len = pos - _readBuffer.c_str(); string line(_readBuffer.c_str(), len); _readBuffer.erase_front(len + 1); //printf("found header line %s\n", line.c_str()); if (line == "\r" || line == "") { // end of header found if (_result->isChunked()) { _state = IN_READ_CHUNKED_HEADER; return readChunkedHeader(); } else if (_result->getContentLength()) { if (_result->getContentLength() > 5000000) { _errorMessage = "Content length > 5000000 bytes found!"; LOGGER_ERROR << "Content length > 5000000 bytes found! Closing connection."; // reset connection close(); return false; } _state = IN_READ_BODY; return readBody(); } else { _result->setResultType(SimpleHttpResult::COMPLETE); _state = FINISHED; } break; } else { _result->addHeaderField(line); } pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length()); } return true; }
bool SimpleHttpClient::read () { if (!checkSocket()) { return false; } do { char buffer[READBUFFER_SIZE]; int len_read = ::read(_socket, buffer, READBUFFER_SIZE - 1); if (len_read <= 0) { // error: stop reading break; } _readBuffer.appendText(buffer, len_read); } while(readable()); switch (_state) { case (IN_READ_HEADER): readHeader(); break; case (IN_READ_BODY): readBody(); break; case (IN_READ_CHUNKED_HEADER): readChunkedHeader(); break; case (IN_READ_CHUNKED_BODY): readChunkedBody(); break; default: break; } return true; }
bool SimpleHttpClient::readChunkedBody () { if (_method == HttpRequest::HTTP_REQUEST_HEAD) { // HEAD requests may be responded to without a body... _result->setResultType(SimpleHttpResult::COMPLETE); _state = FINISHED; if (! _keepAlive) { _connection->disconnect(); } return true; } if (_readBuffer.length() >= _nextChunkedSize) { _result->getBody().write(_readBuffer.c_str(), (size_t) _nextChunkedSize); _readBuffer.erase_front((size_t) _nextChunkedSize); _state = IN_READ_CHUNKED_HEADER; return readChunkedHeader(); } return true; }
SimpleHttpResult* SimpleHttpClient::request (rest::HttpRequest::HttpRequestType method, const string& location, const char* body, size_t bodyLength, const map<string, string>& headerFields) { assert(_result == 0); _result = new SimpleHttpResult; _errorMessage = ""; // set body to all connections setRequest(method, location, body, bodyLength, headerFields); double endTime = now() + _requestTimeout; double remainingTime = _requestTimeout; while (isWorking() && remainingTime > 0.0) { switch (_state) { case (IN_CONNECT): { handleConnect(); break; } case (IN_WRITE): { size_t bytesWritten = 0; TRI_set_errno(TRI_ERROR_NO_ERROR); if (! _connection->handleWrite(remainingTime, (void*) (_writeBuffer.c_str() + _written), _writeBuffer.length() - _written, &bytesWritten)) { setErrorMessage(TRI_last_error(), false); this->close(); } else { _written += bytesWritten; if (_written == _writeBuffer.length()) { _state = IN_READ_HEADER; } } break; } case (IN_READ_HEADER): case (IN_READ_BODY): case (IN_READ_CHUNKED_HEADER): case (IN_READ_CHUNKED_BODY): { TRI_set_errno(TRI_ERROR_NO_ERROR); if (_connection->handleRead(remainingTime, _readBuffer)) { switch (_state) { case (IN_READ_HEADER): readHeader(); break; case (IN_READ_BODY): readBody(); break; case (IN_READ_CHUNKED_HEADER): readChunkedHeader(); break; case (IN_READ_CHUNKED_BODY): readChunkedBody(); break; default: break; } } else { setErrorMessage(TRI_last_error(), false); this->close(); } break; } default: break; } remainingTime = endTime - now(); } if (isWorking() && _errorMessage == "" ) { setErrorMessage("Request timeout reached"); } // set result type in getResult() SimpleHttpResult* result = getResult(); _result = 0; return result; }