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

source code of qthttpserver/src/httpserver/qhttpserverresponder.cpp