1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtHttpServer/qhttpserverresponder.h> |
5 | #include <QtHttpServer/qhttpserverrequest.h> |
6 | #include <QtHttpServer/qhttpserverresponse.h> |
7 | #include <private/qhttpserverresponder_p.h> |
8 | #include <private/qhttpserverliterals_p.h> |
9 | #include <private/qhttpserverrequest_p.h> |
10 | #include <private/qhttpserverresponse_p.h> |
11 | #include <private/qhttpserverstream_p.h> |
12 | #include <QtCore/qjsondocument.h> |
13 | #include <QtCore/qloggingcategory.h> |
14 | #include <memory> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | /*! |
19 | \class QHttpServerResponder |
20 | \since 6.4 |
21 | \inmodule QtHttpServer |
22 | \brief API for sending replies from an HTTP server. |
23 | |
24 | Provides functions for writing back to an HTTP client with overloads for |
25 | serializing JSON objects. It also has support for writing HTTP headers and |
26 | status code. |
27 | */ |
28 | |
29 | /*! |
30 | \enum QHttpServerResponder::StatusCode |
31 | |
32 | HTTP status codes |
33 | |
34 | \value Continue |
35 | \value SwitchingProtocols |
36 | \value Processing |
37 | |
38 | \value Ok |
39 | \value Created |
40 | \value Accepted |
41 | \value NonAuthoritativeInformation |
42 | \value NoContent |
43 | \value ResetContent |
44 | \value PartialContent |
45 | \value MultiStatus |
46 | \value AlreadyReported |
47 | \value IMUsed |
48 | |
49 | \value MultipleChoices |
50 | \value MovedPermanently |
51 | \value Found |
52 | \value SeeOther |
53 | \value NotModified |
54 | \value UseProxy |
55 | |
56 | \value TemporaryRedirect |
57 | \value PermanentRedirect |
58 | |
59 | \value BadRequest |
60 | \value Unauthorized |
61 | \value PaymentRequired |
62 | \value Forbidden |
63 | \value NotFound |
64 | \value MethodNotAllowed |
65 | \value NotAcceptable |
66 | \value ProxyAuthenticationRequired |
67 | \value RequestTimeout |
68 | \value Conflict |
69 | \value Gone |
70 | \value LengthRequired |
71 | \value PreconditionFailed |
72 | \value PayloadTooLarge |
73 | \value UriTooLong |
74 | \value UnsupportedMediaType |
75 | \value RequestRangeNotSatisfiable |
76 | \value ExpectationFailed |
77 | \value ImATeapot |
78 | \value MisdirectedRequest |
79 | \value UnprocessableEntity |
80 | \value Locked |
81 | \value FailedDependency |
82 | \value UpgradeRequired |
83 | \value PreconditionRequired |
84 | \value TooManyRequests |
85 | \value RequestHeaderFieldsTooLarge |
86 | \value UnavailableForLegalReasons |
87 | |
88 | \value InternalServerError |
89 | \value NotImplemented |
90 | \value BadGateway |
91 | \value ServiceUnavailable |
92 | \value GatewayTimeout |
93 | \value HttpVersionNotSupported |
94 | \value VariantAlsoNegotiates |
95 | \value InsufficientStorage |
96 | \value LoopDetected |
97 | \value NotExtended |
98 | \value NetworkAuthenticationRequired |
99 | \value NetworkConnectTimeoutError |
100 | */ |
101 | |
102 | /*! |
103 | \internal |
104 | */ |
105 | QHttpServerResponderPrivate::QHttpServerResponderPrivate(QHttpServerStream *stream) : stream(stream) |
106 | { |
107 | Q_ASSERT(stream); |
108 | stream->startHandlingRequest(); |
109 | } |
110 | |
111 | /*! |
112 | \internal |
113 | */ |
114 | QHttpServerResponderPrivate::~QHttpServerResponderPrivate() |
115 | { |
116 | Q_ASSERT(stream); |
117 | stream->responderDestroyed(); |
118 | } |
119 | |
120 | /*! |
121 | \internal |
122 | */ |
123 | void QHttpServerResponderPrivate::write(QHttpServerResponder::StatusCode status) |
124 | { |
125 | Q_ASSERT(stream); |
126 | stream->write(status, streamId: m_streamId); |
127 | } |
128 | |
129 | /*! |
130 | \internal |
131 | */ |
132 | void QHttpServerResponderPrivate::(const QByteArray &body, const QHttpHeaders &, |
133 | QHttpServerResponder::StatusCode status) |
134 | { |
135 | Q_ASSERT(stream); |
136 | stream->write(body, headers, status, streamId: m_streamId); |
137 | } |
138 | |
139 | /*! |
140 | \internal |
141 | */ |
142 | void QHttpServerResponderPrivate::(QIODevice *data, const QHttpHeaders &, |
143 | QHttpServerResponder::StatusCode status) |
144 | { |
145 | Q_ASSERT(stream); |
146 | stream->write(data, headers, status, streamId: m_streamId); |
147 | } |
148 | |
149 | /*! |
150 | \internal |
151 | */ |
152 | void QHttpServerResponderPrivate::(const QHttpHeaders &, |
153 | QHttpServerResponder::StatusCode status) |
154 | { |
155 | Q_ASSERT(stream); |
156 | stream->writeBeginChunked(headers, status, streamId: m_streamId); |
157 | } |
158 | |
159 | /*! |
160 | \internal |
161 | */ |
162 | void QHttpServerResponderPrivate::writeChunk(const QByteArray &data) |
163 | { |
164 | Q_ASSERT(stream); |
165 | stream->writeChunk(body: data, streamId: m_streamId); |
166 | } |
167 | |
168 | /*! |
169 | \internal |
170 | */ |
171 | void QHttpServerResponderPrivate::(const QByteArray &data, |
172 | const QHttpHeaders &trailers) |
173 | { |
174 | Q_ASSERT(stream); |
175 | stream->writeEndChunked(data, trailers, streamId: m_streamId); |
176 | } |
177 | |
178 | /*! |
179 | Constructs a QHttpServerResponder instance using a \a stream |
180 | to output the response to. |
181 | */ |
182 | QHttpServerResponder::QHttpServerResponder(QHttpServerStream *stream) |
183 | : d_ptr(new QHttpServerResponderPrivate(stream)) |
184 | { |
185 | Q_ASSERT(stream); |
186 | } |
187 | |
188 | /*! |
189 | \fn QHttpServerResponder::QHttpServerResponder(QHttpServerResponder &&other) |
190 | |
191 | Move-constructs a QHttpServerResponder instance, making it point |
192 | at the same object that \a other was pointing to. |
193 | */ |
194 | |
195 | /*! |
196 | Destroys a QHttpServerResponder. |
197 | */ |
198 | QHttpServerResponder::~QHttpServerResponder() |
199 | { |
200 | delete d_ptr; |
201 | }; |
202 | |
203 | /*! |
204 | \fn void QHttpServerResponder::swap(QHttpServerResponder &other) noexcept |
205 | |
206 | Swaps QHttpServerResponder \a other with this QHttpServerResponder. |
207 | This operation is very fast and never fails. |
208 | |
209 | \since 6.8 |
210 | */ |
211 | |
212 | /*! |
213 | Answers a request with an HTTP status code \a status and |
214 | HTTP headers \a headers. The I/O device \a data provides the body |
215 | of the response. If \a data is sequential, the body of the |
216 | message is sent in chunks: otherwise, the function assumes all |
217 | the content is available and sends it all at once but the read |
218 | is done in chunks. |
219 | |
220 | \note This function takes the ownership of \a data. |
221 | */ |
222 | void QHttpServerResponder::(QIODevice *data, |
223 | const QHttpHeaders &, |
224 | StatusCode status) |
225 | { |
226 | Q_D(QHttpServerResponder); |
227 | d->write(data, headers, status); |
228 | } |
229 | |
230 | /*! |
231 | Answers a request with an HTTP status code \a status and a |
232 | MIME type \a mimeType. The I/O device \a data provides the body |
233 | of the response. If \a data is sequential, the body of the |
234 | message is sent in chunks: otherwise, the function assumes all |
235 | the content is available and sends it all at once but the read |
236 | is done in chunks. |
237 | |
238 | \note This function takes the ownership of \a data. |
239 | */ |
240 | void QHttpServerResponder::write(QIODevice *data, |
241 | const QByteArray &mimeType, |
242 | StatusCode status) |
243 | { |
244 | QHttpHeaders ; |
245 | headers.append(name: QHttpHeaders::WellKnownHeader::ContentType, value: mimeType); |
246 | write(data, headers, status); |
247 | } |
248 | |
249 | /*! |
250 | Answers a request with an HTTP status code \a status, JSON |
251 | document \a document and HTTP headers \a headers. |
252 | |
253 | Note: This function sets HTTP Content-Type header as "application/json". |
254 | */ |
255 | void QHttpServerResponder::(const QJsonDocument &document, |
256 | const QHttpHeaders &, |
257 | StatusCode status) |
258 | { |
259 | Q_D(QHttpServerResponder); |
260 | const QByteArray &json = document.toJson(); |
261 | QHttpHeaders (headers); |
262 | allHeaders.append(name: QHttpHeaders::WellKnownHeader::ContentType, |
263 | value: QHttpServerLiterals::contentTypeJson()); |
264 | allHeaders.append(name: QHttpHeaders::WellKnownHeader::ContentLength, |
265 | value: QByteArray::number(json.size())); |
266 | d->write(body: document.toJson(), headers: allHeaders, status); |
267 | } |
268 | |
269 | /*! |
270 | Answers a request with an HTTP status code \a status, and JSON |
271 | document \a document. |
272 | |
273 | Note: This function sets HTTP Content-Type header as "application/json". |
274 | */ |
275 | void QHttpServerResponder::write(const QJsonDocument &document, |
276 | StatusCode status) |
277 | { |
278 | write(document, headers: {}, status); |
279 | } |
280 | |
281 | /*! |
282 | Answers a request with an HTTP status code \a status, |
283 | HTTP Headers \a headers and a body \a data. |
284 | |
285 | Note: This function sets HTTP Content-Length header. |
286 | */ |
287 | void QHttpServerResponder::(const QByteArray &data, |
288 | const QHttpHeaders &, |
289 | StatusCode status) |
290 | { |
291 | Q_D(QHttpServerResponder); |
292 | QHttpHeaders (headers); |
293 | allHeaders.append(name: QHttpHeaders::WellKnownHeader::ContentLength, |
294 | value: QByteArray::number(data.size())); |
295 | d->write(body: data, headers: allHeaders, status); |
296 | } |
297 | |
298 | /*! |
299 | Answers a request with an HTTP status code \a status, a |
300 | MIME type \a mimeType and a body \a data. |
301 | */ |
302 | void QHttpServerResponder::write(const QByteArray &data, |
303 | const QByteArray &mimeType, |
304 | StatusCode status) |
305 | { |
306 | QHttpHeaders ; |
307 | headers.append(name: QHttpHeaders::WellKnownHeader::ContentType, value: mimeType); |
308 | write(data, headers, status); |
309 | } |
310 | |
311 | /*! |
312 | Answers a request with an HTTP status code \a status. |
313 | |
314 | Note: This function sets HTTP Content-Type header as "application/x-empty". |
315 | */ |
316 | void QHttpServerResponder::write(StatusCode status) |
317 | { |
318 | write(data: QByteArray(), mimeType: QHttpServerLiterals::contentTypeXEmpty(), status); |
319 | } |
320 | |
321 | /*! |
322 | Answers a request with an HTTP status code \a status and |
323 | HTTP Headers \a headers. |
324 | */ |
325 | void QHttpServerResponder::(const QHttpHeaders &, StatusCode status) |
326 | { |
327 | write(data: QByteArray(), headers, status); |
328 | } |
329 | |
330 | /*! |
331 | Sends a HTTP \a response to the client. |
332 | |
333 | \since 6.5 |
334 | */ |
335 | void QHttpServerResponder::sendResponse(const QHttpServerResponse &response) |
336 | { |
337 | Q_D(QHttpServerResponder); |
338 | const auto &r = response.d_ptr; |
339 | QHttpHeaders (r->headers); |
340 | allHeaders.append(name: QHttpHeaders::WellKnownHeader::ContentLength, |
341 | value: QByteArray::number(r->data.size())); |
342 | |
343 | d->write(body: r->data, headers: allHeaders, status: r->statusCode); |
344 | } |
345 | |
346 | /*! |
347 | Start sending chunks of data with \a headers and and the status |
348 | code \a status. This call must be followed up with an arbitrary |
349 | number of repeated \c writeChunk calls and and a single call to |
350 | \c writeEndChunked. |
351 | |
352 | \since 6.8 |
353 | \sa writeChunk, writeEndChunked |
354 | */ |
355 | void QHttpServerResponder::(const QHttpHeaders &, StatusCode status) |
356 | { |
357 | Q_D(QHttpServerResponder); |
358 | d->writeBeginChunked(headers, status); |
359 | } |
360 | |
361 | /*! |
362 | Start sending chunks of data with the mime type \a mimeType and |
363 | and the given status code \a status. This call must be followed |
364 | up with an arbitrary number of repeated \c writeChunk calls and |
365 | and a single call to \c writeEndChunked. |
366 | |
367 | \since 6.8 |
368 | \sa writeChunk, writeEndChunked |
369 | */ |
370 | void QHttpServerResponder::writeBeginChunked(const QByteArray &mimeType, StatusCode status) |
371 | { |
372 | QHttpHeaders ; |
373 | headers.append(name: QHttpHeaders::WellKnownHeader::ContentType, value: mimeType); |
374 | writeBeginChunked(headers, status); |
375 | } |
376 | |
377 | /*! |
378 | Start sending chunks of data with \a headers and and the given |
379 | status code \a status. This call must be followed up with an |
380 | arbitrary number of repeated \c writeChunk calls and and a single |
381 | call to \c writeEndChunked with the same trailers given in |
382 | \a trailers. |
383 | |
384 | \since 6.8 |
385 | \sa writeChunk, writeEndChunked |
386 | */ |
387 | void QHttpServerResponder::(const QHttpHeaders &, |
388 | QList<QHttpHeaders::WellKnownHeader> trailers, |
389 | StatusCode status) |
390 | { |
391 | QHttpHeaders (headers); |
392 | QByteArray trailerList; |
393 | for (qsizetype i = 0; i < trailers.size(); ++i) { |
394 | if (i != 0) |
395 | trailerList.append(s: ", " ); |
396 | |
397 | trailerList.append(a: QHttpHeaders::wellKnownHeaderName(name: trailers[i]).toByteArray()); |
398 | } |
399 | allHeaders.append(name: QHttpHeaders::WellKnownHeader::Trailer, value: trailerList); |
400 | writeBeginChunked(headers: allHeaders, status); |
401 | } |
402 | |
403 | /*! |
404 | Write \a data back to the client. To be called when data is |
405 | available to write. This can be called multiple times, but before |
406 | calling this \c writeBeginChunked must called, and afterwards |
407 | \c writeEndChunked must be called. |
408 | |
409 | \sa writeBeginChunked, writeEndChunked |
410 | \since 6.8 |
411 | */ |
412 | void QHttpServerResponder::writeChunk(const QByteArray &data) |
413 | { |
414 | Q_D(QHttpServerResponder); |
415 | d->writeChunk(data); |
416 | } |
417 | |
418 | /*! |
419 | Write \a data back to the client with the \a trailers |
420 | announced in \c writeBeginChunked. |
421 | |
422 | \since 6.8 |
423 | \sa writeBeginChunked, writeChunk |
424 | */ |
425 | void QHttpServerResponder::(const QByteArray &data, const QHttpHeaders &trailers) |
426 | { |
427 | Q_D(QHttpServerResponder); |
428 | d->writeEndChunked(data, trailers); |
429 | } |
430 | |
431 | /*! |
432 | Write \a data back to the client. Must be preceded |
433 | by a call to \c writeBeginChunked. |
434 | |
435 | \since 6.8 |
436 | \sa writeBeginChunked, writeChunk |
437 | */ |
438 | void QHttpServerResponder::writeEndChunked(const QByteArray &data) |
439 | { |
440 | writeEndChunked(data, trailers: {}); |
441 | } |
442 | |
443 | QT_END_NAMESPACE |
444 | |