Mercurial > hg > orthanc
changeset 908:e078ea944089 plugins
refactoring HttpOutput
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 19 Jun 2014 17:47:39 +0200 |
parents | 9b8298234254 |
children | ef71057d8b26 |
files | CMakeLists.txt Core/HttpServer/BufferHttpSender.h Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/FilesystemHttpSender.cpp Core/HttpServer/HttpOutput.cpp Core/HttpServer/HttpOutput.h Core/HttpServer/HttpOutputStream.cpp Core/HttpServer/HttpOutputStream.h Core/HttpServer/MongooseServer.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/main.cpp Plugins/Engine/PluginsHttpHandler.cpp Plugins/Engine/PluginsHttpHandler.h Plugins/OrthancCPlugin/OrthancCPlugin.h |
diffstat | 14 files changed, 316 insertions(+), 107 deletions(-) [+] |
line wrap: on
line diff
--- 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
--- 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; }
--- 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("<html>"); - output.SendString(" <body>"); - output.SendString(" <h1>Subdirectories</h1>"); - output.SendString(" <ul>"); + output.SendBodyString("<html>"); + output.SendBodyString(" <body>"); + output.SendBodyString(" <h1>Subdirectories</h1>"); + output.SendBodyString(" <ul>"); if (uri.size() > 0) { std::string h = Toolbox::FlattenUri(uri) + "/.."; - output.SendString("<li><a href=\"" + h + "\">..</a></li>"); + output.SendBodyString("<li><a href=\"" + h + "\">..</a></li>"); } fs::directory_iterator end; @@ -78,12 +78,12 @@ std::string h = Toolbox::FlattenUri(uri) + "/" + f; if (fs::is_directory(it->status())) - output.SendString("<li><a href=\"" + h + "\">" + f + "</a></li>"); + output.SendBodyString("<li><a href=\"" + h + "\">" + f + "</a></li>"); } - output.SendString(" </ul>"); - output.SendString(" <h1>Files</h1>"); - output.SendString(" <ul>"); + output.SendBodyString(" </ul>"); + output.SendBodyString(" <h1>Files</h1>"); + output.SendBodyString(" <ul>"); for (fs::directory_iterator it(p) ; it != end; ++it) { @@ -95,12 +95,12 @@ std::string h = Toolbox::FlattenUri(uri) + "/" + f; if (fs::is_regular_file(it->status())) - output.SendString("<li><a href=\"" + h + "\">" + f + "</a></li>"); + output.SendBodyString("<li><a href=\"" + h + "\">" + f + "</a></li>"); } - output.SendString(" </ul>"); - output.SendString(" </body>"); - output.SendString("</html>"); + output.SendBodyString(" </ul>"); + output.SendBodyString(" </body>"); + output.SendBodyString("</html>"); }
--- 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); } }
--- 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<std::string>(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"); + } + }
--- 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 <string> #include <stdint.h> #include "../Enumerations.h" +#include "HttpOutputStream.h" #include "HttpHandler.h" namespace Orthanc @@ -45,8 +46,6 @@ private: typedef std::list< std::pair<std::string, std::string> > 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,
--- /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 <http://www.gnu.org/licenses/>. + **/ + + +#include "HttpOutputStream.h" + +#include "../OrthancException.h" + +#include <boost/lexical_cast.hpp> + +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<std::string>(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()); + } + } +}
--- /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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include <string> +#include <boost/noncopyable.hpp> + +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); + }; +}
--- 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<MongooseServer*>(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*) ""; }
--- 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
--- 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))
--- 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<boost::regex*, OrthancPluginRestCallback> 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; + } + }
--- 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 <list> #include <boost/shared_ptr.hpp> @@ -62,5 +63,7 @@ virtual bool InvokeService(_OrthancPluginService service, const void* parameters); + + void SetOrthancRestApi(OrthancRestApi& restApi); }; }
--- 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,