1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 **/ 21 22 23 #pragma once 24 25 #include "../Enumerations.h" 26 #include "IHttpOutputStream.h" 27 #include "IHttpStreamAnswer.h" 28 29 #include <list> 30 #include <string> 31 #include <stdint.h> 32 #include <map> 33 #include <vector> 34 35 namespace Orthanc 36 { 37 class ORTHANC_PUBLIC HttpOutput : public boost::noncopyable 38 { 39 private: 40 typedef std::list< std::pair<std::string, std::string> > Header; 41 42 class StateMachine : public boost::noncopyable 43 { 44 public: 45 enum State 46 { 47 State_WritingHeader, 48 State_WritingBody, 49 State_WritingMultipart, 50 State_Done, 51 State_WritingStream 52 }; 53 54 private: 55 IHttpOutputStream& stream_; 56 State state_; 57 58 HttpStatus status_; 59 bool hasContentLength_; 60 uint64_t contentLength_; 61 uint64_t contentPosition_; 62 bool keepAlive_; 63 std::list<std::string> headers_; 64 65 std::string multipartBoundary_; 66 std::string multipartContentType_; 67 68 void StartStreamInternal(const std::string& contentType); 69 70 public: 71 StateMachine(IHttpOutputStream& stream, 72 bool isKeepAlive); 73 74 ~StateMachine(); 75 76 void SetHttpStatus(HttpStatus status); 77 78 void SetContentLength(uint64_t length); 79 80 void SetContentType(const char* contentType); 81 82 void SetContentFilename(const char* filename); 83 84 void SetCookie(const std::string& cookie, 85 const std::string& value); 86 87 void AddHeader(const std::string& header, 88 const std::string& value); 89 90 void ClearHeaders(); 91 92 void SendBody(const void* buffer, size_t length); 93 94 void StartMultipart(const std::string& subType, 95 const std::string& contentType); 96 97 void SendMultipartItem(const void* item, 98 size_t length, 99 const std::map<std::string, std::string>& headers); 100 101 void CloseMultipart(); 102 103 void CloseBody(); 104 GetState()105 State GetState() const 106 { 107 return state_; 108 } 109 110 void CheckHeadersCompatibilityWithMultipart() const; 111 112 void StartStream(const std::string& contentType); 113 114 void SendStreamItem(const void* data, 115 size_t size); 116 117 void CloseStream(); 118 }; 119 120 StateMachine stateMachine_; 121 bool isDeflateAllowed_; 122 bool isGzipAllowed_; 123 124 HttpCompression GetPreferredCompression(size_t bodySize) const; 125 126 public: 127 HttpOutput(IHttpOutputStream& stream, 128 bool isKeepAlive); 129 130 void SetDeflateAllowed(bool allowed); 131 132 bool IsDeflateAllowed() const; 133 134 void SetGzipAllowed(bool allowed); 135 136 bool IsGzipAllowed() const; 137 138 void SendStatus(HttpStatus status, 139 const char* message, 140 size_t messageSize); 141 142 void SendStatus(HttpStatus status); 143 144 void SendStatus(HttpStatus status, 145 const std::string& message); 146 147 void SetContentType(MimeType contentType); 148 149 void SetContentType(const std::string& contentType); 150 151 void SetContentFilename(const char* filename); 152 153 void SetCookie(const std::string& cookie, 154 const std::string& value); 155 156 void AddHeader(const std::string& key, 157 const std::string& value); 158 159 void Answer(const void* buffer, 160 size_t length); 161 162 void Answer(const std::string& str); 163 164 void AnswerEmpty(); 165 166 void SendMethodNotAllowed(const std::string& allowed); 167 168 void Redirect(const std::string& path); 169 170 void SendUnauthorized(const std::string& realm); 171 172 void StartMultipart(const std::string& subType, 173 const std::string& contentType); 174 175 void SendMultipartItem(const void* item, 176 size_t size, 177 const std::map<std::string, std::string>& headers); 178 179 void CloseMultipart(); 180 181 bool IsWritingMultipart() const; 182 183 void Answer(IHttpStreamAnswer& stream); 184 185 /** 186 * This method is a replacement to the combination 187 * "StartMultipart()" + "SendMultipartItem()". It generates the 188 * same answer, but it gives a chance to compress the body if 189 * "Accept-Encoding: gzip" is provided by the client, which is not 190 * possible in chunked transfers. 191 **/ 192 void AnswerMultipartWithoutChunkedTransfer( 193 const std::string& subType, 194 const std::string& contentType, 195 const std::vector<const void*>& parts, 196 const std::vector<size_t>& sizes, 197 const std::vector<const std::map<std::string, std::string>*>& headers); 198 199 /** 200 * Contrarily to "Answer()", this method doesn't bufferizes the 201 * stream before sending it, which reduces memory but cannot be 202 * used to handle compression using "Content-Encoding". 203 **/ 204 void AnswerWithoutBuffering(IHttpStreamAnswer& stream); 205 }; 206 } 207