void HttpResponseSocketWriter::write( int socketFD, HttpResponse& response ) { std::string header("HTTP/1.1 "); write( socketFD, header.c_str(), header.length() ); write( socketFD, response.status().c_str(), response.status().length() ); write( socketFD, "\r\n\r\n", 4 ); write( socketFD, response.body(), response.bodyLength() ); socketApi_.close( socketFD ); }
TEST_F( HttpEchoRequestHandlerTests, EchoseBody ) { const char* body = "abcdef"; setRequestBody( body ); HttpResponse* response = getResponse(); int diff = memcmp( response->body(), body, strlen(body) ); ASSERT_EQ( 0, diff ); delete response; }
int main(int argc, char *argv[]) { if(argc != 2) { std::cerr << "Usage: " << argv[0] << " <URL>" << std::endl; return 1; } Uri uri(argv[1]); HttpRequest request("GET", uri); HttpResponse response = HttpClient::request(request, uri.hostname, uri.port); if(response.isChunked()) { try { while(1) { std::cout << response.readChunk(); } } catch(HttpMessage::NoChunksLeftException&) {} } else { std::cout << response.body(); } }
Handler::status_e RestBatchHandler::execute() { // extract the request type const HttpRequest::HttpRequestType type = _request->requestType(); if (type != HttpRequest::HTTP_REQUEST_POST && type != HttpRequest::HTTP_REQUEST_PUT) { generateError(HttpResponse::METHOD_NOT_ALLOWED, TRI_ERROR_HTTP_METHOD_NOT_ALLOWED); return Handler::HANDLER_DONE; } string boundary; if (! getBoundary(&boundary)) { // invalid content-type or boundary sent generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid content-type or boundary received"); return Handler::HANDLER_FAILED; } size_t errors = 0; // get authorization header. we will inject this into the subparts string authorization = _request->header("authorization"); // create the response _response = createResponse(HttpResponse::OK); _response->setContentType(_request->header("content-type")); // setup some auxiliary structures to parse the multipart message MultipartMessage message(boundary.c_str(), boundary.size(), _request->body(), _request->body() + _request->bodySize()); SearchHelper helper; helper.message = &message; helper.searchStart = (char*) message.messageStart; // iterate over all parts of the multipart message while (true) { // get the next part from the multipart message if (! extractPart(&helper)) { // error generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid multipart message received"); LOGGER_WARNING << "received a corrupted multipart message"; return Handler::HANDLER_FAILED; } // split part into header & body const char* partStart = helper.foundStart; const char* partEnd = partStart + helper.foundLength; const size_t partLength = helper.foundLength; const char* headerStart = partStart; char* bodyStart = NULL; size_t headerLength = 0; size_t bodyLength = 0; // assume Windows linebreak \r\n\r\n as delimiter char* p = strstr((char*) headerStart, "\r\n\r\n"); if (p != NULL) { headerLength = p - partStart; bodyStart = p + 4; bodyLength = partEnd - bodyStart; } else { // test Unix linebreak p = strstr((char*) headerStart, "\n\n"); if (p != NULL) { headerLength = p - partStart; bodyStart = p + 2; bodyLength = partEnd - bodyStart; } else { // no delimiter found, assume we have only a header headerLength = partLength; } } // set up request object for the part LOGGER_TRACE << "part header is " << string(headerStart, headerLength); HttpRequest* request = new HttpRequest(headerStart, headerLength); if (bodyLength > 0) { LOGGER_TRACE << "part body is " << string(bodyStart, bodyLength); request->setBody(bodyStart, bodyLength); } if (authorization.size()) { // inject Authorization header of multipart message into part message request->setHeader("authorization", 13, authorization.c_str()); } HttpHandler* handler = _server->createHandler(request); if (! handler) { delete request; generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create handler for batch part processing"); return Handler::HANDLER_FAILED; } Handler::status_e status = Handler::HANDLER_FAILED; do { try { status = handler->execute(); } catch (triagens::basics::TriagensError const& ex) { handler->handleError(ex); } catch (std::exception const& ex) { triagens::basics::InternalError err(ex, __FILE__, __LINE__); handler->handleError(err); } catch (...) { triagens::basics::InternalError err("executeDirectHandler", __FILE__, __LINE__); handler->handleError(err); } } while (status == Handler::HANDLER_REQUEUE); if (status == Handler::HANDLER_FAILED) { // one of the handlers failed, we must exit now delete handler; generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "executing a handler for batch part failed"); return Handler::HANDLER_FAILED; } HttpResponse* partResponse = handler->getResponse(); if (partResponse == 0) { delete handler; generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create a response for batch part request"); return Handler::HANDLER_FAILED; } const HttpResponse::HttpResponseCode code = partResponse->responseCode(); if (code >= 400) { // error ++errors; } // append the boundary for this subpart _response->body().appendText(boundary + "\r\nContent-Type: "); _response->body().appendText(_partContentType); if (helper.contentId != 0) { // append content-id _response->body().appendText("\r\nContent-Id: " + string(helper.contentId, helper.contentIdLength)); } _response->body().appendText("\r\n\r\n", 4); // remove some headers we don't need partResponse->setHeader("connection", 10, ""); partResponse->setHeader("server", 6, ""); // append the part response header partResponse->writeHeader(&_response->body()); // append the part response body _response->body().appendText(partResponse->body()); _response->body().appendText("\r\n", 2); delete handler; if (! helper.containsMore) { // we've read the last part break; } } // next part // append final boundary + "--" _response->body().appendText(boundary + "--"); if (errors > 0) { _response->setHeader(HttpResponse::getBatchErrorHeader(), StringUtils::itoa(errors)); } // success return Handler::HANDLER_DONE; }
static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, mrb_state* mrb, TRI_action_t const* action, mrb_value callback, HttpRequest* request) { MR_state_t* mrs; mrb_sym id; mrb_sym bodyId; mrb_value key; mrb_value val; mrs = (MR_state_t*) mrb->ud; // setup the request mrb_value req = mrb_class_new_instance(mrb, 0, 0, mrs->_arangoRequest); // setup the response mrb_value res = mrb_class_new_instance(mrb, 0, 0, mrs->_arangoResponse); // copy suffixes vector<string> const& suffix = request->suffix(); mrb_value suffixArray = mrb_ary_new_capa(mrb, suffix.size()); uint32_t index = 0; for (size_t s = action->_urlParts; s < suffix.size(); ++s, ++index) { string const& str = suffix[s]; val = mrb_str_new(mrb, str.c_str(), str.size()); mrb_ary_set(mrb, suffixArray, index, val); } id = mrb_intern(mrb, "@suffix"); mrb_iv_set(mrb, req, id, suffixArray); // copy header fields map<string, string> const& headers = request->headers(); map<string, string>::const_iterator iter = headers.begin(); mrb_value headerFields = mrb_hash_new_capa(mrb, headers.size()); for (; iter != headers.end(); ++iter) { string const& f = iter->first; string const& s = iter->second; key = mrb_str_new(mrb, f.c_str(), f.size()); val = mrb_str_new(mrb, s.c_str(), s.size()); mrb_hash_set(mrb, headerFields, key, val); } id = mrb_intern(mrb, "@headers"); mrb_iv_set(mrb, req, id, headerFields); // copy request type id = mrb_intern(mrb, "@request_type"); bodyId = mrb_intern(mrb, "@body"); switch (request->requestType()) { case HttpRequest::HTTP_REQUEST_POST: mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "POST")); mrb_iv_set(mrb, req, bodyId, mrb_str_new(mrb, request->body(), request->bodySize())); break; case HttpRequest::HTTP_REQUEST_PUT: mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "PUT")); mrb_iv_set(mrb, req, bodyId, mrb_str_new(mrb, request->body(), request->bodySize())); break; case HttpRequest::HTTP_REQUEST_DELETE: mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "DELETE")); break; case HttpRequest::HTTP_REQUEST_HEAD: mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "DELETE")); break; default: mrb_iv_set(mrb, req, id, mrb_str_new_cstr(mrb, "GET")); break; } // copy request parameter map<string, string> values = request->values(); mrb_value parametersArray = mrb_hash_new_capa(mrb, values.size()); for (map<string, string>::iterator i = values.begin(); i != values.end(); ++i) { string const& k = i->first; string const& v = i->second; map<string, TRI_action_parameter_type_e>::const_iterator p = action->_parameters.find(k); if (p == action->_parameters.end()) { key = mrb_str_new(mrb, k.c_str(), k.size()); val = mrb_str_new(mrb, v.c_str(), v.size()); mrb_hash_set(mrb, parametersArray, key, val); } else { TRI_action_parameter_type_e const& ap = p->second; switch (ap) { case TRI_ACT_COLLECTION: case TRI_ACT_COLLECTION_NAME: case TRI_ACT_COLLECTION_ID: case TRI_ACT_STRING: { key = mrb_str_new(mrb, k.c_str(), k.size()); val = mrb_str_new(mrb, v.c_str(), v.size()); mrb_hash_set(mrb, parametersArray, key, val); break; } case TRI_ACT_NUMBER: { key = mrb_str_new(mrb, k.c_str(), k.size()); val = mrb_str_new(mrb, v.c_str(), v.size()); val = mrb_float_value(TRI_DoubleString(v.c_str())); mrb_hash_set(mrb, parametersArray, key, val); break; } } } } id = mrb_intern(mrb, "@parameters"); mrb_iv_set(mrb, req, id, parametersArray); // execute the callback mrb_value args[2]; args[0] = req; args[1] = res; mrb_funcall_argv(mrb, callback, "service", 2, args); if (mrb->exc) { TRI_LogRubyException(mrb, mrb->exc); mrb->exc = 0; return new HttpResponse(HttpResponse::SERVER_ERROR); } // set status code id = mrb_intern(mrb, "@status"); val = mrb_iv_get(mrb, res, id); HttpResponse::HttpResponseCode code = HttpResponse::OK; if (! mrb_nil_p(val)) { code = (HttpResponse::HttpResponseCode) MR_float(mrb, val); } // generate response HttpResponse* response = new HttpResponse(code); // set content type id = mrb_intern(mrb, "@content_type"); val = mrb_iv_get(mrb, res, id); if (! mrb_nil_p(val)) { response->setContentType(MR_string(mrb, val)); } id = mrb_intern(mrb, "@body"); val = mrb_iv_get(mrb, res, id); if (! mrb_nil_p(val)) { response->body().appendText(MR_string(mrb, val)); } return response; }
HttpHandler::status_t RestBatchHandler::execute () { // extract the request type const HttpRequest::HttpRequestType type = _request->requestType(); if (type != HttpRequest::HTTP_REQUEST_POST && type != HttpRequest::HTTP_REQUEST_PUT) { generateError(HttpResponse::METHOD_NOT_ALLOWED, TRI_ERROR_HTTP_METHOD_NOT_ALLOWED); return status_t(HttpHandler::HANDLER_DONE); } string boundary; // invalid content-type or boundary sent if (! getBoundary(&boundary)) { generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid content-type or boundary received"); return status_t(HttpHandler::HANDLER_FAILED); } LOG_TRACE("boundary of multipart-message is '%s'", boundary.c_str()); size_t errors = 0; // get authorization header. we will inject this into the subparts string authorization = _request->header("authorization"); // create the response _response = createResponse(HttpResponse::OK); _response->setContentType(_request->header("content-type")); // setup some auxiliary structures to parse the multipart message MultipartMessage message(boundary.c_str(), boundary.size(), _request->body(), _request->body() + _request->bodySize()); SearchHelper helper; helper.message = &message; helper.searchStart = (char*) message.messageStart; // iterate over all parts of the multipart message while (true) { // get the next part from the multipart message if (! extractPart(&helper)) { // error generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid multipart message received"); LOG_WARNING("received a corrupted multipart message"); return status_t(HttpHandler::HANDLER_FAILED); } // split part into header & body const char* partStart = helper.foundStart; const char* partEnd = partStart + helper.foundLength; const size_t partLength = helper.foundLength; const char* headerStart = partStart; char* bodyStart = nullptr; size_t headerLength = 0; size_t bodyLength = 0; // assume Windows linebreak \r\n\r\n as delimiter char* p = strstr((char*) headerStart, "\r\n\r\n"); if (p != nullptr && p + 4 <= partEnd) { headerLength = p - partStart; bodyStart = p + 4; bodyLength = partEnd - bodyStart; } else { // test Unix linebreak p = strstr((char*) headerStart, "\n\n"); if (p != nullptr && p + 2 <= partEnd) { headerLength = p - partStart; bodyStart = p + 2; bodyLength = partEnd - bodyStart; } else { // no delimiter found, assume we have only a header headerLength = partLength; } } // set up request object for the part LOG_TRACE("part header is: %s", string(headerStart, headerLength).c_str()); HttpRequest* request = new HttpRequest(_request->connectionInfo(), headerStart, headerLength, _request->compatibility(), false); if (request == nullptr) { generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_OUT_OF_MEMORY); return status_t(HttpHandler::HANDLER_FAILED); } // we do not have a client task id here request->setClientTaskId(0); // inject the request context from the framing (batch) request // the "false" means the context is not responsible for resource handling request->setRequestContext(_request->getRequestContext(), false); request->setDatabaseName(_request->databaseName()); if (bodyLength > 0) { LOG_TRACE("part body is '%s'", string(bodyStart, bodyLength).c_str()); request->setBody(bodyStart, bodyLength); } if (authorization.size()) { // inject Authorization header of multipart message into part message request->setHeader("authorization", 13, authorization.c_str()); } HttpHandler* handler = _server->createHandler(request); if (! handler) { delete request; generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create handler for batch part processing"); return status_t(HttpHandler::HANDLER_FAILED); } HttpHandler::status_t status(HttpHandler::HANDLER_FAILED); do { handler->prepareExecute(); try { status = handler->execute(); } catch (triagens::basics::Exception const& ex) { handler->handleError(ex); } catch (std::exception const& ex) { triagens::basics::Exception err(TRI_ERROR_INTERNAL, ex.what(), __FILE__, __LINE__); handler->handleError(err); } catch (...) { triagens::basics::Exception err(TRI_ERROR_INTERNAL, __FILE__, __LINE__); handler->handleError(err); } handler->finalizeExecute(); } while (status.status == HttpHandler::HANDLER_REQUEUE); if (status.status == HttpHandler::HANDLER_FAILED) { // one of the handlers failed, we must exit now delete handler; generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "executing a handler for batch part failed"); return status_t(HttpHandler::HANDLER_FAILED); } HttpResponse* partResponse = handler->getResponse(); if (partResponse == nullptr) { delete handler; generateError(HttpResponse::BAD, TRI_ERROR_INTERNAL, "could not create a response for batch part request"); return status_t(HttpHandler::HANDLER_FAILED); } const HttpResponse::HttpResponseCode code = partResponse->responseCode(); if (code >= 400) { // error ++errors; } // append the boundary for this subpart _response->body().appendText(boundary + "\r\nContent-Type: "); _response->body().appendText(triagens::rest::HttpRequest::BatchContentType); if (helper.contentId != 0) { // append content-id _response->body().appendText("\r\nContent-Id: " + string(helper.contentId, helper.contentIdLength)); } _response->body().appendText(TRI_CHAR_LENGTH_PAIR("\r\n\r\n")); // remove some headers we don't need partResponse->setHeader(TRI_CHAR_LENGTH_PAIR("connection"), ""); partResponse->setHeader(TRI_CHAR_LENGTH_PAIR("server"), ""); // append the part response header partResponse->writeHeader(&_response->body()); // append the part response body _response->body().appendText(partResponse->body()); _response->body().appendText(TRI_CHAR_LENGTH_PAIR("\r\n")); delete handler; if (! helper.containsMore) { // we've read the last part break; } } // next part // append final boundary + "--" _response->body().appendText(boundary + "--"); if (errors > 0) { _response->setHeader(HttpResponse::BatchErrorHeader, StringUtils::itoa(errors)); } // success return status_t(HttpHandler::HANDLER_DONE); }