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