# HG changeset patch # User Sebastien Jodogne # Date 1409665880 -7200 # Node ID ba5c0908600c75a38242f8961865ee914f064da6 # Parent a119f9ae3640c8d433b77d415914bef0f3a4de62 Refactoring of HttpOutput ("Content-Length" header is now always sent) diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/BufferHttpSender.h --- a/Core/HttpServer/BufferHttpSender.h Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/BufferHttpSender.h Tue Sep 02 15:51:20 2014 +0200 @@ -50,7 +50,7 @@ { if (buffer_.size()) { - output.SendBodyData(&buffer_[0], buffer_.size()); + output.SendBody(&buffer_[0], buffer_.size()); } return true; diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/EmbeddedResourceHttpHandler.cpp --- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -78,12 +78,14 @@ { const void* buffer = EmbeddedResources::GetDirectoryResourceBuffer(resourceId_, resourcePath.c_str()); size_t size = EmbeddedResources::GetDirectoryResourceSize(resourceId_, resourcePath.c_str()); - output.AnswerBufferWithContentType(buffer, size, contentType); + + output.SetContentType(contentType.c_str()); + output.SendBody(buffer, size); } catch (OrthancException&) { LOG(WARNING) << "Unable to find HTTP resource: " << resourcePath; - output.SendHeader(HttpStatus_404_NotFound); + output.SendStatus(HttpStatus_404_NotFound); } return true; diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/FilesystemHttpHandler.cpp --- a/Core/HttpServer/FilesystemHttpHandler.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/FilesystemHttpHandler.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -55,16 +55,18 @@ { namespace fs = boost::filesystem; - output.SendOkHeader("text/html", false, 0, NULL); - output.SendBodyString(""); - output.SendBodyString(" "); - output.SendBodyString("

Subdirectories

"); - output.SendBodyString(" "; + s += " "; + s += ""; + + output.SendBody(s); } @@ -162,7 +166,7 @@ } else { - output.SendHeader(HttpStatus_404_NotFound); + output.SendStatus(HttpStatus_404_NotFound); } return true; diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/FilesystemHttpSender.cpp --- a/Core/HttpServer/FilesystemHttpSender.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/FilesystemHttpSender.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -73,7 +73,7 @@ } else { - output.SendBodyData(&buffer[0], nbytes); + output.SendBody(&buffer[0], nbytes); } } diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/HttpFileSender.cpp --- a/Core/HttpServer/HttpFileSender.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/HttpFileSender.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -33,13 +33,25 @@ #include "../PrecompiledHeaders.h" #include "HttpFileSender.h" +#include "../OrthancException.h" + #include namespace Orthanc { void HttpFileSender::SendHeader(HttpOutput& output) { - output.SendOkHeader(contentType_.c_str(), true, GetFileSize(), downloadFilename_.c_str()); + if (contentType_.size() > 0) + { + output.SetContentType(contentType_.c_str()); + } + + if (downloadFilename_.size() > 0) + { + output.SetContentFilename(downloadFilename_.c_str()); + } + + output.SetContentLength(GetFileSize()); } void HttpFileSender::Send(HttpOutput& output) @@ -48,7 +60,8 @@ if (!SendData(output)) { - output.SendHeader(HttpStatus_500_InternalServerError); + throw OrthancException(ErrorCode_InternalError); + //output.SendHeader(HttpStatus_500_InternalServerError); } } } diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/HttpOutput.cpp --- a/Core/HttpServer/HttpOutput.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/HttpOutput.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -43,181 +43,224 @@ namespace Orthanc { - void HttpOutput::StateMachine::SendHttpStatus(HttpStatus status) + HttpOutput::StateMachine::StateMachine(IHttpOutputStream& stream) : + stream_(stream), + state_(State_WritingHeader), + status_(HttpStatus_200_Ok), + hasContentLength_(false), + contentPosition_(0) + { + } + + HttpOutput::StateMachine::~StateMachine() { - if (state_ != State_WaitingHttpStatus) + if (state_ != State_Done) + { + //asm volatile ("int3;"); + LOG(ERROR) << "This HTTP answer does not contain any body"; + } + + if (hasContentLength_ && contentPosition_ != contentLength_) { - LOG(ERROR) << "Sending twice an HTTP status"; - return; + LOG(ERROR) << "This HTTP answer has not sent the proper number of bytes in its body"; + } + } + + + void HttpOutput::StateMachine::SetHttpStatus(HttpStatus status) + { + if (state_ != State_WritingHeader) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); } - stream_.OnHttpStatusReceived(status); - state_ = State_WritingHeader; + status_ = status; + } + + + void HttpOutput::StateMachine::SetContentLength(uint64_t length) + { + if (state_ != State_WritingHeader) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } - std::string s = "HTTP/1.1 " + - boost::lexical_cast(status) + - " " + std::string(EnumerationToString(status)) + - "\r\n"; + hasContentLength_ = true; + contentLength_ = length; + } - stream_.Send(true, &s[0], s.size()); + void HttpOutput::StateMachine::SetContentType(const char* contentType) + { + AddHeader("Content-Type", contentType); + } + + void HttpOutput::StateMachine::SetContentFilename(const char* filename) + { + // TODO Escape double quotes + AddHeader("Content-Disposition", "filename=\"" + std::string(filename) + "\""); } - void HttpOutput::StateMachine::SendHeaderData(const void* buffer, size_t length) + void HttpOutput::StateMachine::SetCookie(const std::string& cookie, + const std::string& value) + { + if (state_ != State_WritingHeader) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + // TODO Escape "=" characters + AddHeader("Set-Cookie", cookie + "=" + value); + } + + + void HttpOutput::StateMachine::AddHeader(const std::string& header, + const std::string& value) + { + if (state_ != State_WritingHeader) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + headers_.push_back(header + ": " + value + "\r\n"); + } + + void HttpOutput::StateMachine::ClearHeaders() { if (state_ != State_WritingHeader) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } - stream_.Send(true, buffer, length); + headers_.clear(); } - void HttpOutput::StateMachine::SendHeaderString(const std::string& str) + void HttpOutput::StateMachine::SendBody(const void* buffer, size_t length) { - if (str.size() > 0) + if (state_ == State_Done) { - SendHeaderData(&str[0], str.size()); - } - } - - void HttpOutput::StateMachine::SendBodyData(const void* buffer, size_t length) - { - if (state_ == State_WaitingHttpStatus) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); + if (length == 0) + { + return; + } + else + { + LOG(ERROR) << "Because of keep-alive connections, the entire body must be sent at once or Content-Length must be given"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } } if (state_ == State_WritingHeader) { - // Close the HTTP header before writing the body - stream_.Send(true, "\r\n", 2); + // Send the HTTP header before writing the body + + stream_.OnHttpStatusReceived(status_); + + std::string s = "HTTP/1.1 " + + boost::lexical_cast(status_) + + " " + std::string(EnumerationToString(status_)) + + "\r\n"; + + if (status_ != HttpStatus_200_Ok) + { + hasContentLength_ = false; + } + + for (std::list::const_iterator + it = headers_.begin(); it != headers_.end(); ++it) + { + s += *it; + } + + uint64_t contentLength = (hasContentLength_ ? contentLength_ : length); + s += "Content-Length: " + boost::lexical_cast(contentLength) + "\r\n\r\n"; + + stream_.Send(true, s.c_str(), s.size()); state_ = State_WritingBody; } + if (hasContentLength_ && + contentPosition_ + length > contentLength_) + { + LOG(ERROR) << "The body size exceeds what was declared with SetContentSize()"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + if (length > 0) { stream_.Send(false, buffer, length); - } - } - - void HttpOutput::StateMachine::SendBodyString(const std::string& str) - { - if (str.size() > 0) - { - SendBodyData(&str[0], str.size()); - } - } - - - void HttpOutput::PrepareOkHeader(Header& header, - const char* contentType, - bool hasContentLength, - uint64_t contentLength, - const char* contentFilename) - { - header.clear(); - - if (contentType && contentType[0] != '\0') - { - header.push_back(std::make_pair("Content-Type", std::string(contentType))); - } - - if (hasContentLength) - { - header.push_back(std::make_pair("Content-Length", boost::lexical_cast(contentLength))); + contentPosition_ += length; } - if (contentFilename && contentFilename[0] != '\0') + if (!hasContentLength_ || + contentPosition_ == contentLength_) { - std::string attachment = "attachment; filename=\"" + std::string(contentFilename) + "\""; - header.push_back(std::make_pair("Content-Disposition", attachment)); + state_ = State_Done; } } - void HttpOutput::SendOkHeader(const char* contentType, - bool hasContentLength, - uint64_t contentLength, - const char* contentFilename) - { - Header header; - PrepareOkHeader(header, contentType, hasContentLength, contentLength, contentFilename); - SendOkHeader(header); - } - - void HttpOutput::SendOkHeader(const Header& header) - { - stateMachine_.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"; - } - - for (HttpHandler::Arguments::const_iterator - it = cookies_.begin(); it != cookies_.end(); ++it) - { - s += "Set-Cookie: " + it->first + "=" + it->second + "\r\n"; - } - - stateMachine_.SendHeaderString(s); - } - void HttpOutput::SendMethodNotAllowed(const std::string& allowed) { - stateMachine_.SendHttpStatus(HttpStatus_405_MethodNotAllowed); - stateMachine_.SendHeaderString("Allow: " + allowed + "\r\n"); + stateMachine_.ClearHeaders(); + stateMachine_.SetHttpStatus(HttpStatus_405_MethodNotAllowed); + stateMachine_.AddHeader("Allow", allowed); + stateMachine_.SendBody(NULL, 0); } - void HttpOutput::SendHeader(HttpStatus status) + void HttpOutput::SendStatus(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"); + LOG(ERROR) << "Please use the dedicated methods to this HTTP status code in HttpOutput"; + throw OrthancException(ErrorCode_ParameterOutOfRange); } - stateMachine_.SendHttpStatus(status); - } - - - void HttpOutput::AnswerBufferWithContentType(const std::string& buffer, - const std::string& contentType) - { - Header header; - PrepareOkHeader(header, contentType.c_str(), true, buffer.size(), NULL); - SendOkHeader(header); - SendBodyString(buffer); - } - - - void HttpOutput::AnswerBufferWithContentType(const void* buffer, - size_t size, - const std::string& contentType) - { - Header header; - PrepareOkHeader(header, contentType.c_str(), true, size, NULL); - SendOkHeader(header); - SendBodyData(buffer, size); + stateMachine_.ClearHeaders(); + stateMachine_.SetHttpStatus(status); + stateMachine_.SendBody(NULL, 0); } void HttpOutput::Redirect(const std::string& path) { - stateMachine_.SendHttpStatus(HttpStatus_301_MovedPermanently); - stateMachine_.SendHeaderString("Location: " + path + "\r\n"); + stateMachine_.ClearHeaders(); + stateMachine_.SetHttpStatus(HttpStatus_301_MovedPermanently); + stateMachine_.AddHeader("Location", path); + stateMachine_.SendBody(NULL, 0); } void HttpOutput::SendUnauthorized(const std::string& realm) { - stateMachine_.SendHttpStatus(HttpStatus_401_Unauthorized); - stateMachine_.SendHeaderString("WWW-Authenticate: Basic realm=\"" + realm + "\"\r\n"); + stateMachine_.ClearHeaders(); + stateMachine_.SetHttpStatus(HttpStatus_401_Unauthorized); + stateMachine_.AddHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\""); + stateMachine_.SendBody(NULL, 0); + } + + void HttpOutput::SendBody(const void* buffer, size_t length) + { + stateMachine_.SendBody(buffer, length); } + void HttpOutput::SendBody(const std::string& str) + { + if (str.size() == 0) + { + stateMachine_.SendBody(NULL, 0); + } + else + { + stateMachine_.SendBody(str.c_str(), str.size()); + } + } + + void HttpOutput::SendBody() + { + stateMachine_.SendBody(NULL, 0); + } } diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/HttpOutput.h --- a/Core/HttpServer/HttpOutput.h Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/HttpOutput.h Tue Sep 02 15:51:20 2014 +0200 @@ -51,84 +51,84 @@ private: enum State { - State_WaitingHttpStatus, State_WritingHeader, - State_WritingBody + State_WritingBody, + State_Done }; IHttpOutputStream& stream_; State state_; + HttpStatus status_; + bool hasContentLength_; + uint64_t contentLength_; + uint64_t contentPosition_; + std::list headers_; + public: - StateMachine(IHttpOutputStream& stream) : - stream_(stream), - state_(State_WaitingHttpStatus) - { - } + StateMachine(IHttpOutputStream& stream); + + ~StateMachine(); + + void SetHttpStatus(HttpStatus status); - void SendHttpStatus(HttpStatus status); + void SetContentLength(uint64_t length); - void SendHeaderData(const void* buffer, size_t length); + void SetContentType(const char* contentType); + + void SetContentFilename(const char* filename); - void SendHeaderString(const std::string& str); + void SetCookie(const std::string& cookie, + const std::string& value); - void SendBodyData(const void* buffer, size_t length); + void AddHeader(const std::string& header, + const std::string& value); - void SendBodyString(const std::string& str); + void ClearHeaders(); + + void SendBody(const void* buffer, size_t length); }; - void PrepareOkHeader(Header& header, - const char* contentType, - bool hasContentLength, - uint64_t contentLength, - const char* contentFilename); - - void SendOkHeader(const Header& header); - StateMachine stateMachine_; - HttpHandler::Arguments cookies_; public: HttpOutput(IHttpOutputStream& stream) : stateMachine_(stream) { } - void SendOkHeader(const char* contentType, - bool hasContentLength, - uint64_t contentLength, - const char* contentFilename); + void SendStatus(HttpStatus status); - void SendBodyData(const void* buffer, size_t length) + void SetContentType(const char* contentType) { - stateMachine_.SendBodyData(buffer, length); + stateMachine_.SetContentType(contentType); + } + + void SetContentFilename(const char* filename) + { + stateMachine_.SetContentFilename(filename); } - void SendBodyString(const std::string& str) + void SetContentLength(uint64_t length) { - stateMachine_.SendBodyString(str); + stateMachine_.SetContentLength(length); } - void SendMethodNotAllowed(const std::string& allowed); + void SetCookie(const std::string& cookie, + const std::string& value) + { + stateMachine_.SetCookie(cookie, value); + } - void SendHeader(HttpStatus status); + void SendBody(const void* buffer, size_t length); + + void SendBody(const std::string& str); + + void SendBody(); + + void SendMethodNotAllowed(const std::string& allowed); void Redirect(const std::string& path); void SendUnauthorized(const std::string& realm); - - void SetCookie(const std::string& cookie, - const std::string& value) - { - cookies_[cookie] = value; - } - - // Higher-level constructs to send entire buffers ---------------------------- - - void AnswerBufferWithContentType(const std::string& buffer, - const std::string& contentType); - - void AnswerBufferWithContentType(const void* buffer, - size_t size, - const std::string& contentType); }; } diff -r a119f9ae3640 -r ba5c0908600c Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/HttpServer/MongooseServer.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -585,7 +585,7 @@ HttpMethod method; if (!ExtractMethod(method, request, headers, argumentsGET)) { - output.SendHeader(HttpStatus_400_BadRequest); + output.SendStatus(HttpStatus_400_BadRequest); return; } @@ -594,6 +594,7 @@ if (that->IsAuthenticationEnabled() && !Authorize(*that, headers, output)) { + output.SendUnauthorized(ORTHANC_REALM); return; } @@ -649,15 +650,15 @@ switch (status) { case PostDataStatus_NoLength: - output.SendHeader(HttpStatus_411_LengthRequired); + output.SendStatus(HttpStatus_411_LengthRequired); return; case PostDataStatus_Failure: - output.SendHeader(HttpStatus_400_BadRequest); + output.SendStatus(HttpStatus_400_BadRequest); return; case PostDataStatus_Pending: - output.AnswerBufferWithContentType(NULL, 0, ""); + output.SendBody(); return; default: @@ -674,7 +675,7 @@ } catch (OrthancException) { - output.SendHeader(HttpStatus_400_BadRequest); + output.SendStatus(HttpStatus_400_BadRequest); return; } @@ -694,29 +695,43 @@ { // Using this candidate handler results in an exception LOG(ERROR) << "Exception in the HTTP handler: " << e.What(); + + switch (e.GetErrorCode()) + { + case ErrorCode_InexistentFile: + case ErrorCode_InexistentItem: + case ErrorCode_UnknownResource: + output.SendStatus(HttpStatus_404_NotFound); + break; + + case ErrorCode_BadRequest: + case ErrorCode_UriSyntax: + output.SendStatus(HttpStatus_400_BadRequest); + break; + + default: + output.SendStatus(HttpStatus_500_InternalServerError); + } + return; } catch (boost::bad_lexical_cast&) { LOG(ERROR) << "Exception in the HTTP handler: Bad lexical cast"; + output.SendStatus(HttpStatus_400_BadRequest); return; } catch (std::runtime_error&) { LOG(ERROR) << "Exception in the HTTP handler: Presumably a bad JSON request"; + output.SendStatus(HttpStatus_400_BadRequest); return; } } if (!found) { - try - { - output.SendHeader(HttpStatus_404_NotFound); - } - catch (OrthancException&) - { - } + output.SendStatus(HttpStatus_404_NotFound); } } diff -r a119f9ae3640 -r ba5c0908600c Core/RestApi/RestApi.cpp --- a/Core/RestApi/RestApi.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/RestApi/RestApi.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -192,6 +192,7 @@ if (root_.LookupResource(uri, visitor)) { + wrappedOutput.Finalize(); return true; } diff -r a119f9ae3640 -r ba5c0908600c Core/RestApi/RestApiOutput.cpp --- a/Core/RestApi/RestApiOutput.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/RestApi/RestApiOutput.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -50,6 +50,14 @@ RestApiOutput::~RestApiOutput() { } + + void RestApiOutput::Finalize() + { + if (!alreadySent_) + { + output_.SendStatus(HttpStatus_404_NotFound); + } + } void RestApiOutput::CheckStatus() { @@ -75,7 +83,8 @@ #if ORTHANC_PUGIXML_ENABLED == 1 std::string s; Toolbox::JsonToXml(s, value); - output_.AnswerBufferWithContentType(s, "application/xml"); + output_.SetContentType("application/xml"); + output_.SendBody(s); #else LOG(ERROR) << "Orthanc was compiled without XML support"; throw OrthancException(ErrorCode_InternalError); @@ -84,8 +93,8 @@ else { Json::StyledWriter writer; - std::string s = writer.write(value); - output_.AnswerBufferWithContentType(s, "application/json"); + output_.SetContentType("application/json"); + output_.SendBody(writer.write(value)); } alreadySent_ = true; @@ -95,7 +104,8 @@ const std::string& contentType) { CheckStatus(); - output_.AnswerBufferWithContentType(buffer, contentType); + output_.SetContentType(contentType.c_str()); + output_.SendBody(buffer); alreadySent_ = true; } @@ -104,7 +114,8 @@ const std::string& contentType) { CheckStatus(); - output_.AnswerBufferWithContentType(buffer, length, contentType); + output_.SetContentType(contentType.c_str()); + output_.SendBody(buffer, length); alreadySent_ = true; } @@ -124,7 +135,7 @@ } CheckStatus(); - output_.SendHeader(status); + output_.SendStatus(status); alreadySent_ = true; } diff -r a119f9ae3640 -r ba5c0908600c Core/RestApi/RestApiOutput.h --- a/Core/RestApi/RestApiOutput.h Mon Sep 01 12:20:26 2014 +0200 +++ b/Core/RestApi/RestApiOutput.h Tue Sep 02 15:51:20 2014 +0200 @@ -93,5 +93,7 @@ unsigned int maxAge = 0); void ResetCookie(const std::string& name); + + void Finalize(); }; } diff -r a119f9ae3640 -r ba5c0908600c NEWS --- a/NEWS Mon Sep 01 12:20:26 2014 +0200 +++ b/NEWS Tue Sep 02 15:51:20 2014 +0200 @@ -1,7 +1,7 @@ Pending changes in the mainline =============================== -* Upgrade to Mongoose 3.8 (the last release under the MIT license) +* Refactoring of HttpOutput ("Content-Length" header is now always sent) * Fixes for Visual Studio 2013 and Visual Studio 64bit diff -r a119f9ae3640 -r ba5c0908600c OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -264,7 +264,8 @@ Uint32 length = element.getLength(transferSyntax); Uint32 offset = 0; - output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL); + output.GetLowLevelOutput().SetContentType(CONTENT_TYPE_OCTET_STREAM); + output.GetLowLevelOutput().SetContentLength(length); while (offset < length) { @@ -282,7 +283,7 @@ if (cond.good()) { - output.GetLowLevelOutput().SendBodyData(&buffer[0], nbytes); + output.GetLowLevelOutput().SendBody(&buffer[0], nbytes); offset += nbytes; } else diff -r a119f9ae3640 -r ba5c0908600c OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/OrthancServer/ServerIndex.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -866,7 +866,7 @@ ResourceType type; if (!db_->LookupResource(instanceUuid, id, type)) { - throw OrthancException(ErrorCode_InternalError); + throw OrthancException(ErrorCode_UnknownResource); } if (db_->LookupAttachment(attachment, id, contentType)) diff -r a119f9ae3640 -r ba5c0908600c Plugins/Engine/PluginsHttpHandler.cpp --- a/Plugins/Engine/PluginsHttpHandler.cpp Mon Sep 01 12:20:26 2014 +0200 +++ b/Plugins/Engine/PluginsHttpHandler.cpp Tue Sep 02 15:51:20 2014 +0200 @@ -353,7 +353,8 @@ *reinterpret_cast(parameters); HttpOutput* translatedOutput = reinterpret_cast(p.output); - translatedOutput->AnswerBufferWithContentType(p.answer, p.answerSize, p.mimeType); + translatedOutput->SetContentType(p.mimeType); + translatedOutput->SendBody(p.answer, p.answerSize); } @@ -373,7 +374,7 @@ *reinterpret_cast(parameters); HttpOutput* translatedOutput = reinterpret_cast(p.output); - translatedOutput->SendHeader(static_cast(p.status)); + translatedOutput->SendStatus(static_cast(p.status)); } @@ -448,7 +449,8 @@ std::string png; writer.WriteToMemory(png, accessor); - translatedOutput->AnswerBufferWithContentType(png, "image/png"); + translatedOutput->SetContentType("image/png"); + translatedOutput->SendBody(png); }