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
16QT_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*/
105QHttpServerResponderPrivate::QHttpServerResponderPrivate(QHttpServerStream *stream) : stream(stream)
106{
107 Q_ASSERT(stream);
108 stream->startHandlingRequest();
109}
110
111/*!
112 \internal
113*/
114QHttpServerResponderPrivate::~QHttpServerResponderPrivate()
115{
116 Q_ASSERT(stream);
117 stream->responderDestroyed();
118}
119
120/*!
121 \internal
122*/
123void QHttpServerResponderPrivate::write(QHttpServerResponder::StatusCode status)
124{
125 Q_ASSERT(stream);
126 stream->write(status, streamId: m_streamId);
127}
128
129/*!
130 \internal
131*/
132void QHttpServerResponderPrivate::write(const QByteArray &body, const QHttpHeaders &headers,
133 QHttpServerResponder::StatusCode status)
134{
135 Q_ASSERT(stream);
136 stream->write(body, headers, status, streamId: m_streamId);
137}
138
139/*!
140 \internal
141*/
142void QHttpServerResponderPrivate::write(QIODevice *data, const QHttpHeaders &headers,
143 QHttpServerResponder::StatusCode status)
144{
145 Q_ASSERT(stream);
146 stream->write(data, headers, status, streamId: m_streamId);
147}
148
149/*!
150 \internal
151*/
152void QHttpServerResponderPrivate::writeBeginChunked(const QHttpHeaders &headers,
153 QHttpServerResponder::StatusCode status)
154{
155 Q_ASSERT(stream);
156 stream->writeBeginChunked(headers, status, streamId: m_streamId);
157}
158
159/*!
160 \internal
161*/
162void QHttpServerResponderPrivate::writeChunk(const QByteArray &data)
163{
164 Q_ASSERT(stream);
165 stream->writeChunk(body: data, streamId: m_streamId);
166}
167
168/*!
169 \internal
170*/
171void QHttpServerResponderPrivate::writeEndChunked(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*/
182QHttpServerResponder::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*/
198QHttpServerResponder::~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*/
222void QHttpServerResponder::write(QIODevice *data,
223 const QHttpHeaders &headers,
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*/
240void QHttpServerResponder::write(QIODevice *data,
241 const QByteArray &mimeType,
242 StatusCode status)
243{
244 QHttpHeaders headers;
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*/
255void QHttpServerResponder::write(const QJsonDocument &document,
256 const QHttpHeaders &headers,
257 StatusCode status)
258{
259 Q_D(QHttpServerResponder);
260 const QByteArray &json = document.toJson();
261 QHttpHeaders allHeaders(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*/
275void 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*/
287void QHttpServerResponder::write(const QByteArray &data,
288 const QHttpHeaders &headers,
289 StatusCode status)
290{
291 Q_D(QHttpServerResponder);
292 QHttpHeaders allHeaders(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*/
302void QHttpServerResponder::write(const QByteArray &data,
303 const QByteArray &mimeType,
304 StatusCode status)
305{
306 QHttpHeaders headers;
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*/
316void 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*/
325void QHttpServerResponder::write(const QHttpHeaders &headers, 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*/
335void QHttpServerResponder::sendResponse(const QHttpServerResponse &response)
336{
337 Q_D(QHttpServerResponder);
338 const auto &r = response.d_ptr;
339 QHttpHeaders allHeaders(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*/
355void QHttpServerResponder::writeBeginChunked(const QHttpHeaders &headers, 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*/
370void QHttpServerResponder::writeBeginChunked(const QByteArray &mimeType, StatusCode status)
371{
372 QHttpHeaders headers;
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*/
387void QHttpServerResponder::writeBeginChunked(const QHttpHeaders &headers,
388 QList<QHttpHeaders::WellKnownHeader> trailers,
389 StatusCode status)
390{
391 QHttpHeaders allHeaders(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*/
412void 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*/
425void QHttpServerResponder::writeEndChunked(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*/
438void QHttpServerResponder::writeEndChunked(const QByteArray &data)
439{
440 writeEndChunked(data, trailers: {});
441}
442
443QT_END_NAMESPACE
444

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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