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