# HG changeset patch # User Sebastien Jodogne # Date 1403192859 -7200 # Node ID e078ea944089294a6b0bfb71adf55060f85c29fc # Parent 9b829823425411c2628657ed61f5ce642f3e0deb refactoring HttpOutput diff -r 9b8298234254 -r e078ea944089 CMakeLists.txt --- a/CMakeLists.txt Thu Jun 19 14:28:43 2014 +0200 +++ b/CMakeLists.txt Thu Jun 19 17:47:39 2014 +0200 @@ -81,6 +81,7 @@ Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/HttpHandler.cpp Core/HttpServer/HttpOutput.cpp + Core/HttpServer/HttpOutputStream.cpp Core/HttpServer/MongooseServer.cpp Core/HttpServer/HttpFileSender.cpp Core/HttpServer/FilesystemHttpSender.cpp diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/BufferHttpSender.h --- a/Core/HttpServer/BufferHttpSender.h Thu Jun 19 14:28:43 2014 +0200 +++ b/Core/HttpServer/BufferHttpSender.h Thu Jun 19 17:47:39 2014 +0200 @@ -49,7 +49,9 @@ virtual bool SendData(HttpOutput& output) { if (buffer_.size()) - output.Send(&buffer_[0], buffer_.size()); + { + output.SendBodyData(&buffer_[0], buffer_.size()); + } return true; } diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/FilesystemHttpHandler.cpp --- a/Core/HttpServer/FilesystemHttpHandler.cpp Thu Jun 19 14:28:43 2014 +0200 +++ b/Core/HttpServer/FilesystemHttpHandler.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -56,15 +56,15 @@ namespace fs = boost::filesystem; output.SendOkHeader("text/html", false, 0, NULL); - output.SendString(""); - output.SendString(" "); - output.SendString("

Subdirectories

"); - output.SendString(" "); + output.SendBodyString(" "); + output.SendBodyString(""); } diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/FilesystemHttpSender.cpp --- a/Core/HttpServer/FilesystemHttpSender.cpp Thu Jun 19 14:28:43 2014 +0200 +++ b/Core/HttpServer/FilesystemHttpSender.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -73,7 +73,7 @@ } else { - output.Send(&buffer[0], nbytes); + output.SendBodyData(&buffer[0], nbytes); } } diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/HttpOutput.cpp --- a/Core/HttpServer/HttpOutput.cpp Thu Jun 19 14:28:43 2014 +0200 +++ b/Core/HttpServer/HttpOutput.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -42,14 +42,6 @@ namespace Orthanc { - void HttpOutput::SendString(const std::string& s) - { - if (s.size() > 0) - { - Send(&s[0], s.size()); - } - } - void HttpOutput::PrepareOkHeader(Header& header, const char* contentType, bool hasContentLength, @@ -87,49 +79,37 @@ void HttpOutput::SendOkHeader(const Header& header) { - std::string s = "HTTP/1.1 200 OK\r\n"; + stream_.SendHttpStatus(HttpStatus_200_Ok); + std::string s; for (Header::const_iterator it = header.begin(); it != header.end(); ++it) { s += it->first + ": " + it->second + "\r\n"; } - s += "\r\n"; - - Send(&s[0], s.size()); + stream_.SendHeaderString(s); } void HttpOutput::SendMethodNotAllowedError(const std::string& allowed) { - std::string s = - "HTTP/1.1 405 " + std::string(EnumerationToString(HttpStatus_405_MethodNotAllowed)) + - "\r\nAllow: " + allowed + - "\r\n\r\n"; - Send(&s[0], s.size()); + stream_.SendHttpStatus(HttpStatus_405_MethodNotAllowed); + stream_.SendHeaderString("Allow: " + allowed + "\r\n"); } void HttpOutput::SendHeader(HttpStatus status) { if (status == HttpStatus_200_Ok || + status == HttpStatus_301_MovedPermanently || + status == HttpStatus_401_Unauthorized || status == HttpStatus_405_MethodNotAllowed) { throw OrthancException("Please use the dedicated methods to this HTTP status code in HttpOutput"); } - SendHeaderInternal(status); - } - - - void HttpOutput::SendHeaderInternal(HttpStatus status) - { - std::string s = "HTTP/1.1 " + - boost::lexical_cast(status) + - " " + std::string(EnumerationToString(status)) + - "\r\n\r\n"; - Send(&s[0], s.size()); + stream_.SendHttpStatus(status); } @@ -137,7 +117,7 @@ const std::string& contentType) { SendOkHeader(contentType.c_str(), true, buffer.size(), NULL); - SendString(buffer); + SendBodyString(buffer); } @@ -160,7 +140,7 @@ PrepareOkHeader(header, contentType.c_str(), true, buffer.size(), NULL); PrepareCookies(header, cookies); SendOkHeader(header); - SendString(buffer); + SendBodyString(buffer); } @@ -169,7 +149,7 @@ const std::string& contentType) { SendOkHeader(contentType.c_str(), true, size, NULL); - Send(buffer, size); + SendBodyData(buffer, size); } @@ -182,17 +162,21 @@ PrepareOkHeader(header, contentType.c_str(), true, size, NULL); PrepareCookies(header, cookies); SendOkHeader(header); - Send(buffer, size); + SendBodyData(buffer, size); } - void HttpOutput::Redirect(const std::string& path) { - std::string s = - "HTTP/1.1 301 " + std::string(EnumerationToString(HttpStatus_301_MovedPermanently)) + - "\r\nLocation: " + path + - "\r\n\r\n"; - Send(&s[0], s.size()); + stream_.SendHttpStatus(HttpStatus_301_MovedPermanently); + stream_.SendHeaderString("Location: " + path + "\r\n"); } + + + void HttpOutput::SendUnauthorized(const std::string& realm) + { + stream_.SendHttpStatus(HttpStatus_401_Unauthorized); + stream_.SendHeaderString("WWW-Authenticate: Basic realm=\"" + realm + "\"\r\n"); + } + } diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/HttpOutput.h --- a/Core/HttpServer/HttpOutput.h Thu Jun 19 14:28:43 2014 +0200 +++ b/Core/HttpServer/HttpOutput.h Thu Jun 19 17:47:39 2014 +0200 @@ -36,6 +36,7 @@ #include #include #include "../Enumerations.h" +#include "HttpOutputStream.h" #include "HttpHandler.h" namespace Orthanc @@ -45,8 +46,6 @@ private: typedef std::list< std::pair > Header; - void SendHeaderInternal(HttpStatus status); - void PrepareOkHeader(Header& header, const char* contentType, bool hasContentLength, @@ -58,19 +57,27 @@ void PrepareCookies(Header& header, const HttpHandler::Arguments& cookies); + HttpOutputStream& stream_; + public: - virtual ~HttpOutput() + HttpOutput(HttpOutputStream& stream) : stream_(stream) { } - virtual void Send(const void* buffer, size_t length) = 0; - void SendOkHeader(const char* contentType, bool hasContentLength, uint64_t contentLength, const char* contentFilename); - void SendString(const std::string& s); + void SendBodyData(const void* buffer, size_t length) + { + stream_.SendBodyData(buffer, length); + } + + void SendBodyString(const std::string& str) + { + stream_.SendBodyString(str); + } void SendMethodNotAllowedError(const std::string& allowed); @@ -78,6 +85,8 @@ void Redirect(const std::string& path); + void SendUnauthorized(const std::string& realm); + // Higher-level constructs to send entire buffers ---------------------------- void AnswerBufferWithContentType(const std::string& buffer, diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/HttpOutputStream.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpOutputStream.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -0,0 +1,104 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "HttpOutputStream.h" + +#include "../OrthancException.h" + +#include + +namespace Orthanc +{ + void HttpOutputStream::SendHttpStatus(HttpStatus status) + { + if (state_ != State_WaitingHttpStatus) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + OnHttpStatusReceived(status); + state_ = State_WritingHeader; + + std::string s = "HTTP/1.1 " + + boost::lexical_cast(status) + + " " + std::string(EnumerationToString(status)) + + "\r\n"; + + SendHeader(&s[0], s.size()); + } + + void HttpOutputStream::SendHeaderData(const void* buffer, size_t length) + { + if (state_ != State_WritingHeader) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + SendHeader(buffer, length); + } + + void HttpOutputStream::SendHeaderString(const std::string& str) + { + if (str.size() > 0) + { + SendHeaderData(&str[0], str.size()); + } + } + + void HttpOutputStream::SendBodyData(const void* buffer, size_t length) + { + if (state_ == State_WaitingHttpStatus) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (state_ == State_WritingHeader) + { + // Close the HTTP header before writing the body + SendHeader("\r\n", 2); + state_ = State_WritingBody; + } + + if (length > 0) + { + SendBody(buffer, length); + } + } + + void HttpOutputStream::SendBodyString(const std::string& str) + { + if (str.size() > 0) + { + SendBodyData(&str[0], str.size()); + } + } +} diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/HttpOutputStream.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpOutputStream.h Thu Jun 19 17:47:39 2014 +0200 @@ -0,0 +1,83 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include +#include + +namespace Orthanc +{ + class HttpOutputStream : public boost::noncopyable + { + protected: + enum State + { + State_WaitingHttpStatus, + State_WritingHeader, + State_WritingBody + }; + + private: + State state_; + + protected: + virtual void OnHttpStatusReceived(HttpStatus status) + { + } + + virtual void SendHeader(const void* buffer, size_t length) = 0; + + virtual void SendBody(const void* buffer, size_t length) = 0; + + public: + HttpOutputStream() : state_(State_WaitingHttpStatus) + { + } + + virtual ~HttpOutputStream() + { + } + + void SendHttpStatus(HttpStatus status); + + void SendHeaderData(const void* buffer, size_t length); + + void SendHeaderString(const std::string& str); + + void SendBodyData(const void* buffer, size_t length); + + void SendBodyString(const std::string& str); + }; +} diff -r 9b8298234254 -r e078ea944089 Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Thu Jun 19 14:28:43 2014 +0200 +++ b/Core/HttpServer/MongooseServer.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -68,23 +68,29 @@ namespace { // Anonymous namespace to avoid clashes between compilation modules - class MongooseOutput : public HttpOutput + class MongooseOutputStream : public HttpOutputStream { private: struct mg_connection* connection_; - public: - MongooseOutput(struct mg_connection* connection) : connection_(connection) - { - } - - virtual void Send(const void* buffer, size_t length) + protected: + virtual void SendBody(const void* buffer, size_t length) { if (length > 0) { mg_write(connection_, buffer, length); } } + + virtual void SendHeader(const void* buffer, size_t length) + { + SendBody(buffer, length); + } + + public: + MongooseOutputStream(struct mg_connection* connection) : connection_(connection) + { + } }; @@ -404,15 +410,6 @@ } - static void SendUnauthorized(HttpOutput& output) - { - std::string s = "HTTP/1.1 401 Unauthorized\r\n" - "WWW-Authenticate: Basic realm=\"" ORTHANC_REALM "\"" - "\r\n\r\n"; - output.Send(&s[0], s.size()); - } - - static bool Authorize(const MongooseServer& that, const HttpHandler::Arguments& headers, HttpOutput& output) @@ -432,7 +429,7 @@ if (!granted) { - SendUnauthorized(output); + output.SendUnauthorized(ORTHANC_REALM); return false; } else @@ -559,13 +556,14 @@ if (event == MG_NEW_REQUEST) { MongooseServer* that = reinterpret_cast(request->user_data); - MongooseOutput output(connection); + MongooseOutputStream stream(connection); + HttpOutput output(stream); // Check remote calls if (!that->IsRemoteAccessAllowed() && request->remote_ip != LOCALHOST) { - SendUnauthorized(output); + output.SendUnauthorized(ORTHANC_REALM); return (void*) ""; } @@ -620,7 +618,7 @@ if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str())) { - SendUnauthorized(output); + output.SendUnauthorized(ORTHANC_REALM); return (void*) ""; } } @@ -689,12 +687,9 @@ // Loop over the candidate handlers for this URI LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri); bool found = false; - bool isError = false; - HttpStatus errorStatus; - std::string errorDescription; for (MongooseServer::Handlers::const_iterator it = - that->GetHandlers().begin(); it != that->GetHandlers().end(); ++it) + that->GetHandlers().begin(); it != that->GetHandlers().end() && !found; ++it) { try { @@ -702,40 +697,27 @@ } catch (OrthancException& e) { - // Using this candidate handler results in an exception, try - // another handler before failing - isError = true; - errorStatus = HttpStatus_500_InternalServerError; - errorDescription = e.What(); + // Using this candidate handler results in an exception + LOG(ERROR) << "Exception in the HTTP handler: " << e.What(); + return (void*) ""; } catch (boost::bad_lexical_cast&) { - isError = true; - errorStatus = HttpStatus_400_BadRequest; - errorDescription = "Bad lexical cast"; + LOG(ERROR) << "Exception in the HTTP handler: Bad lexical cast"; + return (void*) ""; } catch (std::runtime_error&) { - isError = true; - errorStatus = HttpStatus_400_BadRequest; - errorDescription = "Presumably a bad JSON request"; + LOG(ERROR) << "Exception in the HTTP handler: Presumably a bad JSON request"; + return (void*) ""; } } if (!found) { - if (isError) - { - LOG(ERROR) << "Exception in the HTTP handler: " << errorDescription; - output.SendHeader(errorStatus); - } - else - { - output.SendHeader(HttpStatus_404_NotFound); - } + output.SendHeader(HttpStatus_404_NotFound); } - // Mark as processed return (void*) ""; } diff -r 9b8298234254 -r e078ea944089 OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Thu Jun 19 14:28:43 2014 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -279,7 +279,7 @@ if (cond.good()) { - output.GetLowLevelOutput().Send(&buffer[0], nbytes); + output.GetLowLevelOutput().SendBodyData(&buffer[0], nbytes); offset += nbytes; } else diff -r 9b8298234254 -r e078ea944089 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Thu Jun 19 14:28:43 2014 +0200 +++ b/OrthancServer/main.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -446,6 +446,7 @@ httpServer.RegisterHandler(httpPlugins); httpServer.RegisterHandler(staticResources); httpServer.RegisterHandler(restApi); + httpPlugins.SetOrthancRestApi(restApi); // GO !!! Start the requested servers if (Configuration::GetGlobalBoolParameter("HttpServerEnabled", true)) diff -r 9b8298234254 -r e078ea944089 Plugins/Engine/PluginsHttpHandler.cpp --- a/Plugins/Engine/PluginsHttpHandler.cpp Thu Jun 19 14:28:43 2014 +0200 +++ b/Plugins/Engine/PluginsHttpHandler.cpp Thu Jun 19 17:47:39 2014 +0200 @@ -42,6 +42,39 @@ namespace Orthanc { + namespace + { + // Anonymous namespace to avoid clashes between compilation modules + class StringHttpOutput : public HttpOutput + { + private: + std::string target_; + + public: + const std::string& GetOutput() const + { + return target_; + } + + virtual void SendHeaderData(const void* buffer, size_t length) + { + } + + virtual void SendBodyData(const void* buffer, size_t length) + { + size_t pos = target_.size(); + target_.resize(pos + length); + + if (length > 0) + { + memcpy(&target_[pos], buffer, length); + } + } + }; + } + + + struct PluginsHttpHandler::PImpl { typedef std::pair Callback; @@ -49,8 +82,9 @@ ServerContext& context_; Callbacks callbacks_; + OrthancRestApi* restApi_; - PImpl(ServerContext& context) : context_(context) + PImpl(ServerContext& context) : context_(context), restApi_(NULL) { } }; @@ -298,4 +332,10 @@ } } + + void PluginsHttpHandler::SetOrthancRestApi(OrthancRestApi& restApi) + { + pimpl_->restApi_ = &restApi; + } + } diff -r 9b8298234254 -r e078ea944089 Plugins/Engine/PluginsHttpHandler.h --- a/Plugins/Engine/PluginsHttpHandler.h Thu Jun 19 14:28:43 2014 +0200 +++ b/Plugins/Engine/PluginsHttpHandler.h Thu Jun 19 17:47:39 2014 +0200 @@ -35,6 +35,7 @@ #include "PluginsManager.h" #include "../../Core/HttpServer/HttpHandler.h" #include "../../OrthancServer/ServerContext.h" +#include "../../OrthancServer/OrthancRestApi/OrthancRestApi.h" #include #include @@ -62,5 +63,7 @@ virtual bool InvokeService(_OrthancPluginService service, const void* parameters); + + void SetOrthancRestApi(OrthancRestApi& restApi); }; } diff -r 9b8298234254 -r e078ea944089 Plugins/OrthancCPlugin/OrthancCPlugin.h --- a/Plugins/OrthancCPlugin/OrthancCPlugin.h Thu Jun 19 14:28:43 2014 +0200 +++ b/Plugins/OrthancCPlugin/OrthancCPlugin.h Thu Jun 19 17:47:39 2014 +0200 @@ -297,7 +297,7 @@ /** - * @brief Signature of a function that answers to a REST request. + * @brief Signature of a callback function that answers to a REST request. **/ typedef int32_t (*OrthancPluginRestCallback) ( OrthancPluginRestOutput* output,