1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qhttpserverrequest_p.h"
5
6#include <QtHttpServer/qhttpserverrequest.h>
7
8#include <QtCore/qdebug.h>
9#include <QtCore/qloggingcategory.h>
10#include <QtNetwork/qtcpsocket.h>
11#if QT_CONFIG(ssl)
12#include <QtNetwork/qsslsocket.h>
13#endif
14
15Q_LOGGING_CATEGORY(lc, "qt.httpserver.request")
16
17QT_BEGIN_NAMESPACE
18
19using namespace Qt::StringLiterals;
20
21#if !defined(QT_NO_DEBUG_STREAM)
22
23/*!
24 \fn QDebug QHttpServerRequest::operator<<(QDebug debug, const QHttpServerRequest &request)
25
26 Writes information about \a request to the \a debug stream.
27
28 \sa QDebug
29 */
30Q_HTTPSERVER_EXPORT QDebug operator<<(QDebug debug, const QHttpServerRequest &request)
31{
32 QDebugStateSaver saver(debug);
33 debug.nospace() << "QHttpServerRequest(";
34 debug << "(Url: " << request.url() << ")";
35 debug << "(Headers: ";
36 auto headers = request.headers();
37 bool firstHeader = true;
38 for (auto i = headers.begin(); i != headers.end(); ++i) {
39 if (firstHeader)
40 firstHeader = false;
41 else
42 debug << ", ";
43 debug << "(" << i->first << ": " << i->second << ")";
44 }
45 debug << ")";
46 debug << "(RemoteHost: " << request.remoteAddress() << ")";
47 debug << "(BodySize: " << request.body().size() << ")";
48 debug << ')';
49 return debug;
50}
51
52#endif
53
54/*!
55 \internal
56*/
57bool QHttpServerRequestPrivate::parseRequestLine(QByteArrayView line)
58{
59 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
60 auto i = line.indexOf(ch: ' ');
61 if (i == -1)
62 return false;
63 const auto requestMethod = line.first(n: i);
64 i++;
65
66 while (i < line.size() && line[i] == ' ')
67 i++;
68
69 auto j = line.indexOf(ch: ' ', from: i);
70 if (j == -1)
71 return false;
72
73 const auto requestUrl = line.sliced(pos: i, n: j - i);
74 i = j + 1;
75
76 while (i < line.size() && line[i] == ' ')
77 i++;
78
79 if (i >= line.size())
80 return false;
81
82 j = line.indexOf(ch: ' ', from: i);
83
84 const auto protocol = j == -1 ? line.sliced(pos: i) : line.sliced(pos: i, n: j - i);
85 if (protocol.size() != 8 || !protocol.startsWith(other: "HTTP"))
86 return false;
87
88 parser.setMajorVersion(protocol[5] - '0');
89 parser.setMinorVersion(protocol[7] - '0');
90
91 if (requestMethod == "GET")
92 method = QHttpServerRequest::Method::Get;
93 else if (requestMethod == "PUT")
94 method = QHttpServerRequest::Method::Put;
95 else if (requestMethod == "DELETE")
96 method = QHttpServerRequest::Method::Delete;
97 else if (requestMethod == "POST")
98 method = QHttpServerRequest::Method::Post;
99 else if (requestMethod == "HEAD")
100 method = QHttpServerRequest::Method::Head;
101 else if (requestMethod == "OPTIONS")
102 method = QHttpServerRequest::Method::Options;
103 else if (requestMethod == "PATCH")
104 method = QHttpServerRequest::Method::Patch;
105 else if (requestMethod == "CONNECT")
106 method = QHttpServerRequest::Method::Connect;
107 else
108 method = QHttpServerRequest::Method::Unknown;
109
110 url = QUrl::fromEncoded(url: requestUrl.toByteArray());
111 return true;
112}
113
114/*!
115 \internal
116*/
117qsizetype QHttpServerRequestPrivate::readRequestLine(QAbstractSocket *socket)
118{
119 if (fragment.isEmpty()) {
120 // reserve bytes for the request line. This is better than always append() which reallocs
121 // the byte array
122 fragment.reserve(asize: 32);
123 }
124
125 qsizetype bytes = 0;
126 char c;
127 qsizetype haveRead = 0;
128
129 do {
130 haveRead = socket->read(data: &c, maxlen: 1);
131 if (haveRead == -1)
132 return -1; // unexpected EOF
133 else if (haveRead == 0)
134 break; // read more later
135 else if (haveRead == 1 && fragment.size() == 0
136 && (c == '\v' || c == '\n' || c == '\r' || c == ' ' || c == '\t'))
137 continue; // Ignore all whitespace that was trailing from a previous request on that
138 // socket
139
140 bytes++;
141
142 // allow both CRLF & LF (only) line endings
143 if (c == '\n') {
144 // remove the CR at the end
145 if (fragment.endsWith(c: '\r')) {
146 fragment.truncate(pos: fragment.size() - 1);
147 }
148 bool ok = parseRequestLine(line: fragment);
149 state = State::ReadingHeader;
150 fragment.clear();
151 if (!ok) {
152 return -1;
153 }
154 break;
155 } else {
156 fragment.append(c);
157 }
158 } while (haveRead == 1);
159
160 return bytes;
161}
162
163/*!
164 \internal
165*/
166qint64 QHttpServerRequestPrivate::contentLength() const
167{
168 bool ok = false;
169 QByteArray value = parser.firstHeaderField(name: "content-length");
170 qint64 length = value.toULongLong(ok: &ok);
171 if (ok)
172 return length;
173 return -1; // the header field is not set
174}
175
176/*!
177 \internal
178*/
179qsizetype QHttpServerRequestPrivate::readHeader(QAbstractSocket *socket)
180{
181 if (fragment.isEmpty()) {
182 // according to
183 // https://maqentaer.com/devopera-static-backup/http/dev.opera.com/articles/view/mama-http-headers/index.html
184 // the average size of the header block is 381 bytes. reserve bytes. This is better than
185 // always append() which reallocs the byte array.
186 fragment.reserve(asize: 512);
187 }
188
189 qint64 bytes = 0;
190 char c = 0;
191 bool allHeaders = false;
192 qint64 haveRead = 0;
193 do {
194 haveRead = socket->read(data: &c, maxlen: 1);
195 if (haveRead == 0) {
196 // read more later
197 break;
198 } else if (haveRead == -1) {
199 // connection broke down
200 return -1;
201 } else {
202 fragment.append(c);
203 bytes++;
204
205 if (c == '\n') {
206 // check for possible header endings. As per HTTP rfc,
207 // the header endings will be marked by CRLFCRLF. But
208 // we will allow CRLFCRLF, CRLFLF, LFCRLF, LFLF
209 if (fragment.endsWith(bv: "\n\r\n") || fragment.endsWith(bv: "\n\n"))
210 allHeaders = true;
211
212 // there is another case: We have no headers. Then the fragment equals just the line
213 // ending
214 if ((fragment.size() == 2 && fragment.endsWith(bv: "\r\n"))
215 || (fragment.size() == 1 && fragment.endsWith(bv: "\n")))
216 allHeaders = true;
217 }
218 }
219 } while (!allHeaders && haveRead > 0);
220
221 // we received all headers now parse them
222 if (allHeaders) {
223 parser.parseHeaders(headers: fragment);
224 fragment.clear(); // next fragment
225
226 auto hostUrl = QString::fromUtf8(ba: headerField(name: "host"));
227 if (!hostUrl.isEmpty())
228 url.setAuthority(authority: hostUrl);
229
230 if (url.scheme().isEmpty()) {
231#if QT_CONFIG(ssl)
232 auto sslSocket = qobject_cast<QSslSocket *>(object: socket);
233 url.setScheme(sslSocket && sslSocket->isEncrypted() ? u"https"_s : u"http"_s);
234#else
235 url.setScheme(u"http"_s);
236#endif
237 }
238
239 if (url.host().isEmpty())
240 url.setHost(host: u"127.0.0.1"_s);
241
242 if (url.port() == -1)
243 url.setPort(port);
244
245 bodyLength = contentLength(); // cache the length
246
247 // cache isChunked() since it is called often
248 // FIXME: the RFC says that anything but "identity" should be interpreted as chunked (4.4
249 // [2])
250 chunkedTransferEncoding = headerField(name: "transfer-encoding").toLower().contains(bv: "chunked");
251
252 QByteArray connectionHeaderField = headerField(name: "connection");
253 upgrade = connectionHeaderField.toLower().contains(bv: "upgrade");
254
255 if (chunkedTransferEncoding || bodyLength > 0) {
256 if (headerField(name: "expect").compare(a: "100-continue", cs: Qt::CaseInsensitive) == 0)
257 state = State::ExpectContinue;
258 else
259 state = State::ReadingData;
260 } else {
261 state = State::AllDone;
262 }
263 }
264 return bytes;
265}
266
267/*!
268 \internal
269*/
270qsizetype QHttpServerRequestPrivate::sendContinue(QAbstractSocket *socket)
271{
272 qsizetype ret = socket->write(data: "HTTP/1.1 100 Continue\r\n\r\n");
273 state = State::ReadingData;
274 return ret;
275}
276
277/*!
278 \internal
279*/
280QHttpServerRequestPrivate::QHttpServerRequestPrivate(const QHostAddress &remoteAddress,
281 quint16 remotePort,
282 const QHostAddress &localAddress,
283 quint16 localPort)
284 : remoteAddress(remoteAddress),
285 remotePort(remotePort),
286 localAddress(localAddress),
287 localPort(localPort)
288{
289 clear();
290}
291
292/*!
293 \internal
294*/
295bool QHttpServerRequestPrivate::parse(QAbstractSocket *socket)
296{
297 qsizetype read;
298
299 do {
300 switch (state) {
301 case State::AllDone:
302 clear();
303 [[fallthrough]];
304 case State::NothingDone:
305 state = State::ReadingRequestLine;
306 [[fallthrough]];
307 case State::ReadingRequestLine:
308 read = readRequestLine(socket);
309 continue;
310 case State::ReadingHeader:
311 read = readHeader(socket);
312 continue;
313 case State::ExpectContinue:
314 read = sendContinue(socket);
315 continue;
316 case State::ReadingData:
317 if (chunkedTransferEncoding)
318 read = readRequestBodyChunked(socket);
319 else
320 read = readBodyFast(socket);
321
322 if (state == State::AllDone) {
323 body = bodyBuffer.readAll();
324 bodyBuffer.clear();
325 }
326
327 continue;
328 }
329 Q_UNREACHABLE(); // fixes GCC -Wmaybe-uninitialized warning on `read`
330 } while (state != State::AllDone && read > 0);
331
332 return read != -1;
333}
334
335/*!
336 \internal
337*/
338void QHttpServerRequestPrivate::clear()
339{
340 parser.clear();
341 bodyLength = -1;
342 contentRead = 0;
343 chunkedTransferEncoding = false;
344 lastChunkRead = false;
345 currentChunkRead = 0;
346 currentChunkSize = 0;
347 upgrade = false;
348
349 fragment.clear();
350 bodyBuffer.clear();
351 body.clear();
352}
353
354// The body reading functions were mostly copied from QHttpNetworkReplyPrivate
355
356/*!
357 \internal
358*/
359// note this function can only be used for non-chunked, non-compressed with
360// known content length
361qsizetype QHttpServerRequestPrivate::readBodyFast(QAbstractSocket *socket)
362{
363
364 qsizetype toBeRead = qMin(a: socket->bytesAvailable(), b: bodyLength - contentRead);
365 if (!toBeRead)
366 return 0;
367
368 QByteArray bd;
369 bd.resize(size: toBeRead);
370 qsizetype haveRead = socket->read(data: bd.data(), maxlen: toBeRead);
371 if (haveRead == -1) {
372 bd.clear();
373 return 0; // ### error checking here;
374 }
375 bd.resize(size: haveRead);
376
377 bodyBuffer.append(bd);
378
379 contentRead += haveRead;
380
381 if (contentRead == bodyLength)
382 state = State::AllDone;
383
384 return haveRead;
385}
386
387/*!
388 \internal
389*/
390qsizetype QHttpServerRequestPrivate::readRequestBodyRaw(QAbstractSocket *socket, qsizetype size)
391{
392 // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
393 qsizetype bytes = 0;
394 Q_ASSERT(socket);
395
396 int toBeRead = qMin<qsizetype>(a: 128 * 1024, b: qMin<qint64>(a: size, b: socket->bytesAvailable()));
397
398 while (toBeRead > 0) {
399 QByteArray byteData;
400 byteData.resize(size: toBeRead);
401 qsizetype haveRead = socket->read(data: byteData.data(), maxlen: byteData.size());
402 if (haveRead <= 0) {
403 // ### error checking here
404 byteData.clear();
405 return bytes;
406 }
407
408 byteData.resize(size: haveRead);
409 bodyBuffer.append(bd: byteData);
410 bytes += haveRead;
411 size -= haveRead;
412
413 toBeRead = qMin<qsizetype>(a: 128 * 1024, b: qMin<qsizetype>(a: size, b: socket->bytesAvailable()));
414 }
415 return bytes;
416}
417
418/*!
419 \internal
420*/
421qsizetype QHttpServerRequestPrivate::readRequestBodyChunked(QAbstractSocket *socket)
422{
423 qsizetype bytes = 0;
424 while (socket->bytesAvailable()) {
425 if (!lastChunkRead && currentChunkRead >= currentChunkSize) {
426 // For the first chunk and when we're done with a chunk
427 currentChunkSize = 0;
428 currentChunkRead = 0;
429 if (bytes) {
430 // After a chunk
431 char crlf[2];
432 // read the "\r\n" after the chunk
433 qsizetype haveRead = socket->read(data: crlf, maxlen: 2);
434 // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not
435 // available yet?! For nice reasons (the toLong in getChunkSize accepting \n at the
436 // beginning it right now still works, but we should definitely fix this.
437
438 if (haveRead != 2)
439 return bytes;
440 bytes += haveRead;
441 }
442 // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes
443 // read
444 bytes += getChunkSize(socket, chunkSize: &currentChunkSize);
445 if (currentChunkSize == -1)
446 break;
447 }
448 // if the chunk size is 0, end of the stream
449 if (currentChunkSize == 0 || lastChunkRead) {
450 lastChunkRead = true;
451 // try to read the "\r\n" after the chunk
452 char crlf[2];
453 qsizetype haveRead = socket->read(data: crlf, maxlen: 2);
454 if (haveRead > 0)
455 bytes += haveRead;
456
457 if ((haveRead == 2 && crlf[0] == '\r' && crlf[1] == '\n')
458 || (haveRead == 1 && crlf[0] == '\n')) {
459 state = State::AllDone;
460 } else if (haveRead == 1 && crlf[0] == '\r') {
461 break; // Still waiting for the last \n
462 } else if (haveRead > 0) {
463 // If we read something else then CRLF, we need to close the channel.
464 // FIXME forceConnectionCloseEnabled = true;
465 state = State::AllDone;
466 }
467 break;
468 }
469
470 // otherwise, try to begin reading this chunk / to read what is missing for this chunk
471 qsizetype haveRead = readRequestBodyRaw(socket, size: currentChunkSize - currentChunkRead);
472 currentChunkRead += haveRead;
473 bytes += haveRead;
474
475 // ### error checking here
476 }
477 return bytes;
478}
479
480/*!
481 \internal
482*/
483qsizetype QHttpServerRequestPrivate::getChunkSize(QAbstractSocket *socket, qsizetype *chunkSize)
484{
485 qsizetype bytes = 0;
486 char crlf[2];
487 *chunkSize = -1;
488
489 int bytesAvailable = socket->bytesAvailable();
490 // FIXME rewrite to permanent loop without bytesAvailable
491 while (bytesAvailable > bytes) {
492 qsizetype sniffedBytes = socket->peek(data: crlf, maxlen: 2);
493 int fragmentSize = fragment.size();
494
495 // check the next two bytes for a "\r\n", skip blank lines
496 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
497 || (fragmentSize > 1 && fragment.endsWith(c: '\r') && crlf[0] == '\n')) {
498 bytes += socket->read(data: crlf, maxlen: 1); // read the \r or \n
499 if (crlf[0] == '\r')
500 bytes += socket->read(data: crlf, maxlen: 1); // read the \n
501 bool ok = false;
502 // ignore the chunk-extension
503 fragment = fragment.mid(index: 0, len: fragment.indexOf(c: ';')).trimmed();
504 *chunkSize = fragment.toLong(ok: &ok, base: 16);
505 fragment.clear();
506 break; // size done
507 } else {
508 // read the fragment to the buffer
509 char c = 0;
510 qsizetype haveRead = socket->read(data: &c, maxlen: 1);
511 if (haveRead < 0)
512 return -1;
513
514 bytes += haveRead;
515 fragment.append(c);
516 }
517 }
518
519 return bytes;
520}
521
522/*!
523 \class QHttpServerRequest
524 \since 6.4
525 \inmodule QtHttpServer
526 \brief Encapsulates an HTTP request.
527
528 API for accessing the different parameters of an incoming request.
529*/
530
531/*!
532 \enum QHttpServerRequest::Method
533
534 This enum type specifies an HTTP request method:
535
536 \value Unknown
537 An unknown method.
538 \value Get
539 HTTP GET method.
540 \value Put
541 HTTP PUT method.
542 \value Delete
543 HTTP DELETE method.
544 \value Post
545 HTTP POST method.
546 \value Head
547 HTTP HEAD method.
548 \value Options
549 HTTP OPTIONS method.
550 \value Patch
551 HTTP PATCH method (\l {https://www.rfc-editor.org/rfc/rfc5789}{RFC 5789}).
552 \value Connect
553 HTTP CONNECT method.
554 \value Trace
555 HTTP TRACE method.
556 \value AnyKnown
557 Combination of all known methods.
558*/
559
560/*!
561 \internal
562*/
563QHttpServerRequest::QHttpServerRequest(const QHostAddress &remoteAddress, quint16 remotePort,
564 const QHostAddress &localAddress, quint16 localPort)
565 : d(new QHttpServerRequestPrivate(remoteAddress, remotePort, localAddress, localPort))
566{}
567
568/*!
569 Destroys a QHttpServerRequest
570*/
571QHttpServerRequest::~QHttpServerRequest()
572{}
573
574/*!
575 Returns the combined value of all headers with the named \a key.
576*/
577QByteArray QHttpServerRequest::value(const QByteArray &key) const
578{
579 return d->parser.combinedHeaderValue(name: key);
580}
581
582/*!
583 Returns the URL the request asked for.
584*/
585QUrl QHttpServerRequest::url() const
586{
587 return d->url;
588}
589
590/*!
591 Returns the query in the request.
592*/
593QUrlQuery QHttpServerRequest::query() const
594{
595 return QUrlQuery(d->url.query());
596}
597
598/*!
599 Returns the method of the request.
600*/
601QHttpServerRequest::Method QHttpServerRequest::method() const
602{
603 return d->method;
604}
605
606/*!
607 Returns all the request headers.
608*/
609QList<QPair<QByteArray, QByteArray>> QHttpServerRequest::headers() const
610{
611 return d->parser.headers();
612}
613
614/*!
615 Returns the body of the request.
616*/
617QByteArray QHttpServerRequest::body() const
618{
619 return d->body;
620}
621
622/*!
623 Returns the address of the origin host of the request.
624*/
625QHostAddress QHttpServerRequest::remoteAddress() const
626{
627 return d->remoteAddress;
628}
629
630/*!
631 Returns the port of the origin host of the request.
632
633 \since 6.5
634*/
635quint16 QHttpServerRequest::remotePort() const
636{
637 return d->remotePort;
638}
639
640/*!
641 Returns the host address of the local socket which received the request.
642
643 \since 6.5
644*/
645QHostAddress QHttpServerRequest::localAddress() const
646{
647 return d->localAddress;
648}
649
650/*!
651 Returns the port of the local socket which received the request.
652
653 \since 6.5
654*/
655quint16 QHttpServerRequest::localPort() const
656{
657 return d->localPort;
658}
659
660QT_END_NAMESPACE
661
662#include "moc_qhttpserverrequest.cpp"
663

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