/src/wt/src/web/WebRequest.h
Line | Count | Source |
1 | | // This may look like C code, but it's really -*- C++ -*- |
2 | | /* |
3 | | * Copyright (C) 2008 Emweb bv, Herent, Belgium. |
4 | | * |
5 | | * See the LICENSE file for terms of use. |
6 | | */ |
7 | | #ifndef WEB_REQUEST_H_ |
8 | | #define WEB_REQUEST_H_ |
9 | | |
10 | | #include <chrono> |
11 | | #include <cstdint> |
12 | | #include <iostream> |
13 | | #include <Wt/WDllDefs.h> |
14 | | #include <Wt/WGlobal.h> |
15 | | #include <Wt/Http/Request.h> |
16 | | |
17 | | #include <boost/utility/string_view.hpp> |
18 | | |
19 | | #include <cstdint> |
20 | | #include <functional> |
21 | | |
22 | | namespace Wt { |
23 | | |
24 | | class Configuration; |
25 | | class EntryPoint; |
26 | | class WebSocketConnection; |
27 | | class WSslInfo; |
28 | | |
29 | | /* |
30 | | * A single, raw, HTTP request/response, which conveys all of the http-related |
31 | | * information to the application and gathers the response. |
32 | | */ |
33 | | class WT_API WebRequest |
34 | | { |
35 | | public: |
36 | | WebRequest(); |
37 | | |
38 | | void log(); |
39 | | |
40 | | enum class ResponseState { |
41 | | ResponseDone, |
42 | | ResponseFlush |
43 | | }; |
44 | | |
45 | | enum class ResponseType { |
46 | | Page, |
47 | | Script, |
48 | | Update |
49 | | }; |
50 | | |
51 | | typedef std::function<void(WebWriteEvent)> WriteCallback; |
52 | | typedef std::function<void(WebReadEvent)> ReadCallback; |
53 | | typedef std::function<void(void)> DisconnectCallback; |
54 | | typedef std::function<void(const Http::Request &request, std::shared_ptr<WebSocketConnection>)> WebSocketResourceTransferCallback; |
55 | | |
56 | | /* |
57 | | * Signal that the response should be flushed. |
58 | | * |
59 | | * ResponseDone: flush & close |
60 | | * |
61 | | * ResponseFlush: flush what we have so far, do not close |
62 | | * - callback must be specified for ResponseFlush, and is called |
63 | | * if more data can be written. Until then, you cannot do new |
64 | | * writes. |
65 | | */ |
66 | | virtual void flush(ResponseState state = ResponseState::ResponseDone, |
67 | | const WriteCallback& callback = WriteCallback()) = 0; |
68 | | #ifdef WT_TARGET_JAVA |
69 | | virtual void flushBuffer(); |
70 | | #endif |
71 | | /* |
72 | | * For a web socket request (isWebSocketRequest()), read a message |
73 | | * and call the given callback function when done. |
74 | | * |
75 | | * The new message is available in in() and has length contentLength() |
76 | | */ |
77 | | virtual void readWebSocketMessage(const ReadCallback& callback); |
78 | | |
79 | | /* |
80 | | * For a web socket request (isWebSocketRequest()), returns whether |
81 | | * more data is available. This is used to defer a response but wait |
82 | | * for more incoming events. |
83 | | */ |
84 | | virtual bool webSocketMessagePending() const; |
85 | | |
86 | | /* |
87 | | * When a connection to a WWebSocketResource is set up, the socket is |
88 | | * transferred from the http server to the resource using this callback. |
89 | | * |
90 | | * Possibly not supported by all front-ends. |
91 | | */ |
92 | | virtual bool supportsTransferWebSocketResourceSocket() = 0; |
93 | | void setTransferWebSocketResourceSocketCallBack(WebSocketResourceTransferCallback cb); |
94 | | bool hasTransferWebSocketResourceSocketCallBack(); |
95 | | void transferWebSocketResourceSocket(const std::shared_ptr<WebSocketConnection> &socket); |
96 | | |
97 | | /* |
98 | | * Indicate that we're deferring to write a response, but in the mean-time |
99 | | * we do want to know if there's a disconnect event (by reading from |
100 | | * the socket). |
101 | | */ |
102 | | virtual bool detectDisconnect(const DisconnectCallback& callback); |
103 | | |
104 | | /* |
105 | | * Access the stream that contains the request body (HTTP) or a |
106 | | * single message (WS) |
107 | | */ |
108 | | virtual std::istream& in() = 0; |
109 | | |
110 | | /* |
111 | | * Access the stream to submit the response. |
112 | | * |
113 | | * This is either the entire response body (HTTP), or a single response |
114 | | * message (WS) |
115 | | */ |
116 | | virtual std::ostream& out() = 0; |
117 | | |
118 | 0 | WT_BOSTREAM& bout() { return out(); } |
119 | | |
120 | | /* |
121 | | * (Not used) |
122 | | */ |
123 | | virtual std::ostream& err() = 0; |
124 | | |
125 | | /* |
126 | | * Sets the redirect (instead of anything else). |
127 | | */ |
128 | | virtual void setRedirect(const std::string& url) = 0; |
129 | | |
130 | | /* |
131 | | * Sets the status |
132 | | */ |
133 | | virtual void setStatus(int status) = 0; |
134 | | |
135 | | /* |
136 | | * returns current status value |
137 | | */ |
138 | | virtual int status() = 0; |
139 | | |
140 | | /* |
141 | | * Sets the content-type for a normal response. |
142 | | */ |
143 | | virtual void setContentType(const std::string& value) = 0; |
144 | | |
145 | | /* |
146 | | * Sets the content-length for a normal response. |
147 | | */ |
148 | | virtual void setContentLength(std::int64_t length) = 0; |
149 | | |
150 | | /* |
151 | | * Adds a header for a normal response. |
152 | | */ |
153 | | virtual void addHeader(const std::string& name, const std::string& value) = 0; |
154 | | |
155 | | /* |
156 | | * Inserts a header for a normal response. |
157 | | * |
158 | | * Inserting will not always add the header, but can replace an |
159 | | * existing header value if one with the same name is already created. |
160 | | */ |
161 | | virtual void insertHeader(const std::string& name, const std::string& value) = 0; |
162 | | |
163 | | /* |
164 | | * Returns request information, which are not http headers. |
165 | | */ |
166 | | virtual const char *envValue(const char *name) const = 0; |
167 | | |
168 | | virtual const std::string& serverName() const = 0; |
169 | | virtual const std::string& serverPort() const = 0; |
170 | | /*! \internal |
171 | | * \brief Returns the SCRIPT_NAME |
172 | | * |
173 | | * This is equivalent to the SCRIPT_NAME CGI parameter, see: |
174 | | * https://www.rfc-editor.org/rfc/rfc3875#section-4.1.13 |
175 | | * |
176 | | * - for wthttp this will be the empty string |
177 | | * - for wtisapi this will be the path up until the DLL file |
178 | | * - for wtfcgi this should be set properly in the server configuration, |
179 | | * if `^/path(/.*)?$` is routed to the wtfcgi instance, then this should be `/path`. |
180 | | * SCRIPT_NAME should be empty or start with a forward slash (`/`). |
181 | | */ |
182 | | virtual const std::string& scriptName() const = 0; |
183 | | virtual const char *requestMethod() const = 0; |
184 | | virtual const std::string& queryString() const = 0; |
185 | | /*! \internal |
186 | | * \brief Returns the PATH_INFO |
187 | | * |
188 | | * This is equivalent to the PATH_INFO CGI parameter, see: |
189 | | * https://www.rfc-editor.org/rfc/rfc3875#section-4.1.5 |
190 | | * |
191 | | * - for wthttp this is the full request path |
192 | | * - for wtisapi this is the part of the request path after the DLL file |
193 | | * - for wtfcgi this should be set properly in the server configuration, |
194 | | * if `^/path(/.*)?$` is routed to the wtfcgi instance, and the user |
195 | | * requests `/path/name` then pathInfo() is `/name`. |
196 | | * PATH_INFO should be empty or start with a forward slash (`/`). |
197 | | */ |
198 | | virtual const std::string& pathInfo() const = 0; |
199 | | /*! \internal |
200 | | * \brief Returns the part of pathInfo() that corresponds to the entrypoint |
201 | | * |
202 | | * e.g. if `/entry/point` is the request path, and the request matches the entrypoint `/entry`, |
203 | | * then this will return `/entry`. |
204 | | * |
205 | | * If the request did not match an entrypoint (yet), then the result will be the empty string. |
206 | | */ |
207 | | boost::string_view entryPointPath() const; |
208 | | /*! \internal |
209 | | * \brief Returns the full path up to and including the entrypoint |
210 | | * |
211 | | * This differs from entryPointPath() in that the scriptName() is prepended. |
212 | | * |
213 | | * - for wthttp this is the same as entryPointPath() |
214 | | * - for wtisapi, if the DLL is deployed at `/hello.dll` and the matched entrypoint is `/entrypoint`, |
215 | | * then this returns `/hello.dll/entrypoint` |
216 | | * - for wtfcgi, if the scriptName is `/hello.wt` and the matched entrypoint is `/entrypoint`, |
217 | | * then this returns `/hello.wt/entrypoint` |
218 | | * |
219 | | * For JWt, calls to this function are translated to WebRequest#getScriptName(), yielding the full path |
220 | | * that the servlet is deployed at (context path + servlet path), since JWt has no concept of an "entrypoint". |
221 | | */ |
222 | | std::string fullEntryPointPath() const; |
223 | | /*! \internal |
224 | | * \brief Returns the part of pathInfo() that corresponds to the part after the entrypoint |
225 | | * |
226 | | * e.g. if `/entry/point` is the request path, and the request matches the entrypoint `/entry`, |
227 | | * then this will return `/point`. |
228 | | * |
229 | | * If the request did not match an entrypoint (yet), then the result will be the full pathInfo(). |
230 | | * |
231 | | * For JWt, calls to this function are translated to WebRequest#getPathInfo(), yielding the part of |
232 | | * the request path that comes after the servlet path, since JWt has no concept of an "entrypoint". |
233 | | */ |
234 | | boost::string_view extraPathInfo() const; |
235 | | virtual const std::string& remoteAddr() const = 0; |
236 | | |
237 | | virtual const char *urlScheme() const = 0; |
238 | | |
239 | 0 | virtual bool isWebSocketMessage() const { return false; } |
240 | | |
241 | 0 | bool isWebSocketRequest() const { return webSocketRequest_; } |
242 | 0 | void setWebSocketRequest(bool ws) { webSocketRequest_ = ws; } |
243 | | |
244 | | /* |
245 | | * Accesses to cgi environment variables and headers -- rfc2616 name |
246 | | */ |
247 | | virtual const char *headerValue(const char *name) const = 0; |
248 | | |
249 | | /* |
250 | | * Accesses to specific header fields (calls headerValue()). |
251 | | */ |
252 | | const char *userAgent() const; |
253 | | const char *referer() const; |
254 | | |
255 | | #ifndef WT_TARGET_JAVA |
256 | | virtual std::vector<Wt::Http::Message::Header> headers() const = 0; |
257 | | #endif |
258 | | |
259 | | virtual const char *contentType() const; |
260 | | virtual ::int64_t contentLength() const; |
261 | | |
262 | | #ifdef WT_TARGET_JAVA |
263 | | /* |
264 | | * In JavaEE, the servlet determines how session tracking is encoded in |
265 | | * the URL. |
266 | | */ |
267 | | std::string encodeURL(const std::string& url) const; |
268 | | #endif // WT_TARGET_JAVA |
269 | | |
270 | | const std::string *getParameter(const std::string& name) const; |
271 | | const Http::ParameterValues& getParameterValues(const std::string& name) |
272 | | const; |
273 | 0 | const Http::ParameterMap& getParameterMap() const { return parameters_; } |
274 | 0 | const Http::UploadedFileMap& uploadedFiles() const { return files_; } |
275 | 0 | ::int64_t postDataExceeded() const { return postDataExceeded_; } |
276 | | |
277 | | WLocale parseLocale() const; |
278 | | |
279 | | void setResponseType(ResponseType responseType); |
280 | 0 | ResponseType responseType() const { return responseType_; } |
281 | | |
282 | | /* |
283 | | * Returns \c nullptr if the request does not have SSL client certificate |
284 | | * information. |
285 | | */ |
286 | | virtual std::unique_ptr<WSslInfo> sslInfo(const Configuration & conf) const = 0; |
287 | | |
288 | | virtual const std::vector<std::pair<std::string, std::string> >& urlParams() const; |
289 | | |
290 | | std::string clientAddress(const Configuration & conf) const; |
291 | | |
292 | | std::string hostName(const Configuration & conf) const; |
293 | | |
294 | | std::string urlScheme(const Configuration & conf) const; |
295 | | |
296 | 0 | const std::string& nonce() const { return nonce_; } |
297 | | protected: |
298 | | const EntryPoint *entryPoint_; |
299 | | std::size_t extraStartIndex_; |
300 | | |
301 | | virtual ~WebRequest(); |
302 | | void reset(); |
303 | | |
304 | | #ifndef WT_CNOR |
305 | | struct AsyncEmulation; |
306 | | AsyncEmulation *async_; |
307 | | |
308 | | void emulateAsync(ResponseState state); |
309 | | void setAsyncCallback(const WriteCallback& cb); |
310 | | const WriteCallback& getAsyncCallback(); |
311 | | #endif // WT_CNOR |
312 | | |
313 | | private: |
314 | | std::string parsePreferredAcceptValue(const char *value) const; |
315 | | void addNonce(); |
316 | | |
317 | | ::int64_t postDataExceeded_; |
318 | | Http::ParameterMap parameters_; |
319 | | Http::UploadedFileMap files_; |
320 | | ResponseType responseType_; |
321 | | bool webSocketRequest_; |
322 | | WebSocketResourceTransferCallback wsResourceTransferCb_; |
323 | | std::chrono::high_resolution_clock::time_point start_; |
324 | | std::vector<std::pair<std::string, std::string> > urlParams_; |
325 | | std::string nonce_; |
326 | | |
327 | | static Http::ParameterValues emptyValues_; |
328 | | |
329 | | #ifndef WT_CNOR |
330 | | WriteCallback asyncCallback_; |
331 | | #endif // WT_CNOR |
332 | | |
333 | | friend class CgiParser; |
334 | | friend class Http::Request; |
335 | | friend class WEnvironment; |
336 | | friend class WebController; |
337 | | }; |
338 | | |
339 | | class WebResponse : public WebRequest |
340 | | { |
341 | | #ifdef WT_TARGET_JAVA |
342 | | public: |
343 | | void addCookie(const Http::Cookie& cookie); |
344 | | #endif |
345 | | }; |
346 | | |
347 | | } |
348 | | |
349 | | #endif // WEB_REQUEST_H_ |