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       };
52 
53     private:
54       IHttpOutputStream& stream_;
55       State state_;
56 
57       HttpStatus status_;
58       bool hasContentLength_;
59       uint64_t contentLength_;
60       uint64_t contentPosition_;
61       bool keepAlive_;
62       std::list<std::string> headers_;
63 
64       std::string multipartBoundary_;
65       std::string multipartContentType_;
66 
67     public:
68       StateMachine(IHttpOutputStream& stream,
69                    bool isKeepAlive);
70 
71       ~StateMachine();
72 
73       void SetHttpStatus(HttpStatus status);
74 
75       void SetContentLength(uint64_t length);
76 
77       void SetContentType(const char* contentType);
78 
79       void SetContentFilename(const char* filename);
80 
81       void SetCookie(const std::string& cookie,
82                      const std::string& value);
83 
84       void AddHeader(const std::string& header,
85                      const std::string& value);
86 
87       void ClearHeaders();
88 
89       void SendBody(const void* buffer, size_t length);
90 
91       void StartMultipart(const std::string& subType,
92                           const std::string& contentType);
93 
94       void SendMultipartItem(const void* item,
95                              size_t length,
96                              const std::map<std::string, std::string>& headers);
97 
98       void CloseMultipart();
99 
100       void CloseBody();
101 
GetState()102       State GetState() const
103       {
104         return state_;
105       }
106 
107       void CheckHeadersCompatibilityWithMultipart() const;
108     };
109 
110     StateMachine stateMachine_;
111     bool         isDeflateAllowed_;
112     bool         isGzipAllowed_;
113 
114     HttpCompression GetPreferredCompression(size_t bodySize) const;
115 
116   public:
117     HttpOutput(IHttpOutputStream& stream,
118                bool isKeepAlive);
119 
120     void SetDeflateAllowed(bool allowed);
121 
122     bool IsDeflateAllowed() const;
123 
124     void SetGzipAllowed(bool allowed);
125 
126     bool IsGzipAllowed() const;
127 
128     void SendStatus(HttpStatus status,
129 		    const char* message,
130 		    size_t messageSize);
131 
132     void SendStatus(HttpStatus status);
133 
134     void SendStatus(HttpStatus status,
135                     const std::string& message);
136 
137     void SetContentType(MimeType contentType);
138 
139     void SetContentType(const std::string& contentType);
140 
141     void SetContentFilename(const char* filename);
142 
143     void SetCookie(const std::string& cookie,
144                    const std::string& value);
145 
146     void AddHeader(const std::string& key,
147                    const std::string& value);
148 
149     void Answer(const void* buffer,
150                 size_t length);
151 
152     void Answer(const std::string& str);
153 
154     void AnswerEmpty();
155 
156     void SendMethodNotAllowed(const std::string& allowed);
157 
158     void Redirect(const std::string& path);
159 
160     void SendUnauthorized(const std::string& realm);
161 
162     void StartMultipart(const std::string& subType,
163                         const std::string& contentType);
164 
165     void SendMultipartItem(const void* item,
166                            size_t size,
167                            const std::map<std::string, std::string>& headers);
168 
169     void CloseMultipart();
170 
171     bool IsWritingMultipart() const;
172 
173     void Answer(IHttpStreamAnswer& stream);
174 
175     /**
176      * This method is a replacement to the combination
177      * "StartMultipart()" + "SendMultipartItem()". It generates the
178      * same answer, but it gives a chance to compress the body if
179      * "Accept-Encoding: gzip" is provided by the client, which is not
180      * possible in chunked transfers.
181      **/
182     void AnswerMultipartWithoutChunkedTransfer(
183       const std::string& subType,
184       const std::string& contentType,
185       const std::vector<const void*>& parts,
186       const std::vector<size_t>& sizes,
187       const std::vector<const std::map<std::string, std::string>*>& headers);
188   };
189 }
190