Mercurial > hg > orthanc
diff Core/HttpServer/HttpServer.cpp @ 3395:0ce9b4f5fdf5
new abstraction: IHttpHandler::CreateStreamHandler()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 06 Jun 2019 18:54:27 +0200 |
parents | c0aa5f1cf2f5 |
children | e280ced38a4c |
line wrap: on
line diff
--- a/Core/HttpServer/HttpServer.cpp Thu Jun 06 16:42:41 2019 +0200 +++ b/Core/HttpServer/HttpServer.cpp Thu Jun 06 18:54:27 2019 +0200 @@ -79,8 +79,8 @@ namespace Orthanc { - static const char multipart[] = "multipart/form-data; boundary="; - static unsigned int multipartLength = sizeof(multipart) / sizeof(char) - 1; + static const char MULTIPART_FORM[] = "multipart/form-data; boundary="; + static unsigned int MULTIPART_FORM_LENGTH = sizeof(MULTIPART_FORM) / sizeof(char) - 1; namespace @@ -302,16 +302,60 @@ } - - static PostDataStatus ReadBody(std::string& postData, - struct mg_connection *connection, - const IHttpHandler::Arguments& headers) + static PostDataStatus ReadBodyWithContentLength(std::string& body, + struct mg_connection *connection, + const std::string& contentLength) { - IHttpHandler::Arguments::const_iterator cs = headers.find("content-length"); - if (cs == headers.end()) + int length; + try + { + length = boost::lexical_cast<int>(contentLength); + } + catch (boost::bad_lexical_cast&) + { + return PostDataStatus_NoLength; + } + + if (length < 0) + { + length = 0; + } + + body.resize(length); + + size_t pos = 0; + while (length > 0) { - // Store all the individual chunks within a temporary file, then - // read it back into the memory buffer "postData" + int r = mg_read(connection, &body[pos], length); + if (r <= 0) + { + return PostDataStatus_Failure; + } + + assert(r <= length); + length -= r; + pos += r; + } + + return PostDataStatus_Success; + } + + + static PostDataStatus ReadBodyToString(std::string& body, + struct mg_connection *connection, + const IHttpHandler::Arguments& headers) + { + IHttpHandler::Arguments::const_iterator contentLength = headers.find("content-length"); + + if (contentLength != headers.end()) + { + // "Content-Length" is available + return ReadBodyWithContentLength(body, connection, contentLength->second); + } + else + { + // No Content-Length. Store the individual chunks in a temporary + // file, then read it back into the memory buffer "body" FileBuffer buffer; std::string tmp(1024 * 1024, 0); @@ -333,42 +377,54 @@ } } - buffer.Read(postData); + buffer.Read(body); return PostDataStatus_Success; } + } + + + static PostDataStatus ReadBodyToStream(IHttpHandler::IStream& stream, + struct mg_connection *connection, + const IHttpHandler::Arguments& headers) + { + IHttpHandler::Arguments::const_iterator contentLength = headers.find("content-length"); + + if (contentLength != headers.end()) + { + // "Content-Length" is available + + std::string body; + PostDataStatus status = ReadBodyWithContentLength(body, connection, contentLength->second); + + if (status == PostDataStatus_Success && + !body.empty()) + { + stream.AddBodyChunk(body.c_str(), body.size()); + } + + return status; + } else { - // "Content-Length" is available - int length; - try - { - length = boost::lexical_cast<int>(cs->second); - } - catch (boost::bad_lexical_cast&) + // No Content-Length. Stream the HTTP connection. + std::string tmp(1024 * 1024, 0); + + for (;;) { - return PostDataStatus_NoLength; - } - - if (length < 0) - { - length = 0; - } - - postData.resize(length); - - size_t pos = 0; - while (length > 0) - { - int r = mg_read(connection, &postData[pos], length); - if (r <= 0) + int r = mg_read(connection, &tmp[0], tmp.size()); + if (r < 0) { return PostDataStatus_Failure; } - - assert(r <= length); - length -= r; - pos += r; + else if (r == 0) + { + break; + } + else + { + stream.AddBodyChunk(tmp.c_str(), r); + } } return PostDataStatus_Success; @@ -376,17 +432,16 @@ } - - static PostDataStatus ParseMultipartPost(std::string &completedFile, + static PostDataStatus ParseMultipartForm(std::string &completedFile, struct mg_connection *connection, const IHttpHandler::Arguments& headers, const std::string& contentType, ChunkStore& chunkStore) { - std::string boundary = "--" + contentType.substr(multipartLength); + std::string boundary = "--" + contentType.substr(MULTIPART_FORM_LENGTH); - std::string postData; - PostDataStatus status = ReadBody(postData, connection, headers); + std::string body; + PostDataStatus status = ReadBodyToString(body, connection, headers); if (status != PostDataStatus_Success) { @@ -433,7 +488,7 @@ { FindIterator last; for (FindIterator it = - make_find_iterator(postData, boost::first_finder(boundary)); + make_find_iterator(body, boost::first_finder(boundary)); it!=FindIterator(); ++it) { @@ -752,7 +807,25 @@ } - // Extract the body of the request for PUT and POST + // Decompose the URI into its components + UriComponents uri; + try + { + Toolbox::SplitUriComponents(uri, requestUri); + } + catch (OrthancException&) + { + output.SendStatus(HttpStatus_400_BadRequest); + return; + } + + LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri); + + + bool found = false; + + // Extract the body of the request for PUT and POST, or process + // the body as a stream // TODO Avoid unneccessary memcopy of the body @@ -762,23 +835,39 @@ { PostDataStatus status; + bool isMultipartForm = false; + IHttpHandler::Arguments::const_iterator ct = headers.find("content-type"); - if (ct == headers.end()) + if (ct != headers.end() && + ct->second.size() >= MULTIPART_FORM_LENGTH && + !memcmp(ct->second.c_str(), MULTIPART_FORM, MULTIPART_FORM_LENGTH)) { - // No content-type specified. Assume no multi-part content occurs at this point. - status = ReadBody(body, connection, headers); + status = ParseMultipartForm(body, connection, headers, ct->second, server.GetChunkStore()); + isMultipartForm = true; } - else + + if (!isMultipartForm) { - std::string contentType = ct->second; - if (contentType.size() >= multipartLength && - !memcmp(contentType.c_str(), multipart, multipartLength)) + std::auto_ptr<IHttpHandler::IStream> stream; + + if (server.HasHandler()) { - status = ParseMultipartPost(body, connection, headers, contentType, server.GetChunkStore()); + stream.reset(server.GetHandler().CreateStreamHandler + (RequestOrigin_RestApi, remoteIp, username.c_str(), method, uri, headers)); + } + + if (stream.get() != NULL) + { + status = ReadBodyToStream(*stream, connection, headers); + + if (status == PostDataStatus_Success) + { + stream->Execute(output); + } } else { - status = ReadBody(body, connection, headers); + status = ReadBodyToString(body, connection, headers); } } @@ -796,30 +885,17 @@ output.AnswerEmpty(); return; + case PostDataStatus_Success: + break; + default: - break; + throw OrthancException(ErrorCode_InternalError); } } - // Decompose the URI into its components - UriComponents uri; - try - { - Toolbox::SplitUriComponents(uri, requestUri); - } - catch (OrthancException&) - { - output.SendStatus(HttpStatus_400_BadRequest); - return; - } - - - LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri); - - bool found = false; - - if (server.HasHandler()) + if (!found && + server.HasHandler()) { found = server.GetHandler().Handle(output, RequestOrigin_RestApi, remoteIp, username.c_str(), method, uri, headers, argumentsGET, body.c_str(), body.size());