|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtNetwork module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qplatformdefs.h" |
|
43 #include "qnetworkrequest.h" |
|
44 #include "qnetworkcookie.h" |
|
45 #include "qnetworkrequest_p.h" |
|
46 #include "qsslconfiguration.h" |
|
47 #include "QtCore/qshareddata.h" |
|
48 #include "QtCore/qlocale.h" |
|
49 #include "QtCore/qdatetime.h" |
|
50 |
|
51 QT_BEGIN_NAMESPACE |
|
52 |
|
53 /*! |
|
54 \class QNetworkRequest |
|
55 \brief The QNetworkRequest class holds a request to be sent with QNetworkAccessManager. |
|
56 \since 4.4 |
|
57 |
|
58 \ingroup network |
|
59 \inmodule QtNetwork |
|
60 |
|
61 QNetworkRequest is part of the Network Access API and is the class |
|
62 holding the information necessary to send a request over the |
|
63 network. It contains a URL and some ancillary information that can |
|
64 be used to modify the request. |
|
65 |
|
66 \sa QNetworkReply, QNetworkAccessManager |
|
67 */ |
|
68 |
|
69 /*! |
|
70 \enum QNetworkRequest::KnownHeaders |
|
71 |
|
72 List of known header types that QNetworkRequest parses. Each known |
|
73 header is also represented in raw form with its full HTTP name. |
|
74 |
|
75 \value ContentTypeHeader corresponds to the HTTP Content-Type |
|
76 header and contains a string containing the media (MIME) type and |
|
77 any auxiliary data (for instance, charset) |
|
78 |
|
79 \value ContentLengthHeader corresponds to the HTTP Content-Length |
|
80 header and contains the length in bytes of the data transmitted. |
|
81 |
|
82 \value LocationHeader corresponds to the HTTP Location |
|
83 header and contains a URL representing the actual location of the |
|
84 data, including the destination URL in case of redirections. |
|
85 |
|
86 \value LastModifiedHeader corresponds to the HTTP Last-Modified |
|
87 header and contains a QDateTime representing the last modification |
|
88 date of the contents |
|
89 |
|
90 \value CookieHeader corresponds to the HTTP Cookie header |
|
91 and contains a QList<QNetworkCookie> representing the cookies to |
|
92 be sent back to the server |
|
93 |
|
94 \value SetCookieHeader corresponds to the HTTP Set-Cookie |
|
95 header and contains a QList<QNetworkCookie> representing the |
|
96 cookies sent by the server to be stored locally |
|
97 |
|
98 \sa header(), setHeader(), rawHeader(), setRawHeader() |
|
99 */ |
|
100 |
|
101 /*! |
|
102 \enum QNetworkRequest::Attribute |
|
103 |
|
104 Attribute codes for the QNetworkRequest and QNetworkReply. |
|
105 |
|
106 Attributes are extra meta-data that are used to control the |
|
107 behavior of the request and to pass further information from the |
|
108 reply back to the application. Attributes are also extensible, |
|
109 allowing custom implementations to pass custom values. |
|
110 |
|
111 The following table explains what the default attribute codes are, |
|
112 the QVariant types associated, the default value if said attribute |
|
113 is missing and whether it's used in requests or replies. |
|
114 |
|
115 \value HttpStatusCodeAttribute |
|
116 Replies only, type: QVariant::Int (no default) |
|
117 Indicates the HTTP status code received from the HTTP server |
|
118 (like 200, 304, 404, 401, etc.). If the connection was not |
|
119 HTTP-based, this attribute will not be present. |
|
120 |
|
121 \value HttpReasonPhraseAttribute |
|
122 Replies only, type: QVariant::ByteArray (no default) |
|
123 Indicates the HTTP reason phrase as received from the HTTP |
|
124 server (like "Ok", "Found", "Not Found", "Access Denied", |
|
125 etc.) This is the human-readable representation of the status |
|
126 code (see above). If the connection was not HTTP-based, this |
|
127 attribute will not be present. |
|
128 |
|
129 \value RedirectionTargetAttribute |
|
130 Replies only, type: QVariant::Url (no default) |
|
131 If present, it indicates that the server is redirecting the |
|
132 request to a different URL. The Network Access API does not by |
|
133 default follow redirections: it's up to the application to |
|
134 determine if the requested redirection should be allowed, |
|
135 according to its security policies. |
|
136 |
|
137 \value ConnectionEncryptedAttribute |
|
138 Replies only, type: QVariant::Bool (default: false) |
|
139 Indicates whether the data was obtained through an encrypted |
|
140 (secure) connection. |
|
141 |
|
142 \value CacheLoadControlAttribute |
|
143 Requests only, type: QVariant::Int (default: QNetworkRequest::PreferNetwork) |
|
144 Controls how the cache should be accessed. The possible values |
|
145 are those of QNetworkRequest::CacheLoadControl. Note that the |
|
146 default QNetworkAccessManager implementation does not support |
|
147 caching. However, this attribute may be used by certain |
|
148 backends to modify their requests (for example, for caching proxies). |
|
149 |
|
150 \value CacheSaveControlAttribute |
|
151 Requests only, type: QVariant::Bool (default: true) |
|
152 Controls if the data obtained should be saved to cache for |
|
153 future uses. If the value is false, the data obtained will not |
|
154 be automatically cached. If true, data may be cached, provided |
|
155 it is cacheable (what is cacheable depends on the protocol |
|
156 being used). |
|
157 |
|
158 \value SourceIsFromCacheAttribute |
|
159 Replies only, type: QVariant::Bool (default: false) |
|
160 Indicates whether the data was obtained from cache |
|
161 or not. |
|
162 |
|
163 \value DoNotBufferUploadDataAttribute |
|
164 Requests only, type: QVariant::Bool (default: false) |
|
165 Indicates whether the QNetworkAccessManager code is |
|
166 allowed to buffer the upload data, e.g. when doing a HTTP POST. |
|
167 When using this flag with sequential upload data, the ContentLengthHeader |
|
168 header must be set. |
|
169 |
|
170 \value HttpPipeliningAllowedAttribute |
|
171 Requests only, type: QVariant::Bool (default: false) |
|
172 Indicates whether the QNetworkAccessManager code is |
|
173 allowed to use HTTP pipelining with this request. |
|
174 |
|
175 \value HttpPipeliningWasUsedAttribute |
|
176 Replies only, type: QVariant::Bool |
|
177 Indicates whether the HTTP pipelining was used for receiving |
|
178 this reply. |
|
179 |
|
180 \value User |
|
181 Special type. Additional information can be passed in |
|
182 QVariants with types ranging from User to UserMax. The default |
|
183 implementation of Network Access will ignore any request |
|
184 attributes in this range and it will not produce any |
|
185 attributes in this range in replies. The range is reserved for |
|
186 extensions of QNetworkAccessManager. |
|
187 |
|
188 \value UserMax |
|
189 Special type. See User. |
|
190 */ |
|
191 |
|
192 /*! |
|
193 \enum QNetworkRequest::CacheLoadControl |
|
194 |
|
195 Controls the caching mechanism of QNetworkAccessManager. |
|
196 |
|
197 \value AlwaysNetwork always load from network and do not |
|
198 check if the cache has a valid entry (similar to the |
|
199 "Reload" feature in browsers) |
|
200 |
|
201 \value PreferNetwork default value; load from the network |
|
202 if the cached entry is older than the network entry |
|
203 |
|
204 \value PreferCache load from cache if available, |
|
205 otherwise load from network. Note that this can return possibly |
|
206 stale (but not expired) items from cache. |
|
207 |
|
208 \value AlwaysCache only load from cache, indicating error |
|
209 if the item was not cached (i.e., off-line mode) |
|
210 */ |
|
211 |
|
212 class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate |
|
213 { |
|
214 public: |
|
215 inline QNetworkRequestPrivate() |
|
216 #ifndef QT_NO_OPENSSL |
|
217 : sslConfiguration(0) |
|
218 #endif |
|
219 { qRegisterMetaType<QNetworkRequest>(); } |
|
220 ~QNetworkRequestPrivate() |
|
221 { |
|
222 #ifndef QT_NO_OPENSSL |
|
223 delete sslConfiguration; |
|
224 #endif |
|
225 } |
|
226 |
|
227 |
|
228 QNetworkRequestPrivate(const QNetworkRequestPrivate &other) |
|
229 : QSharedData(other), QNetworkHeadersPrivate(other) |
|
230 { |
|
231 url = other.url; |
|
232 |
|
233 #ifndef QT_NO_OPENSSL |
|
234 sslConfiguration = 0; |
|
235 if (other.sslConfiguration) |
|
236 sslConfiguration = new QSslConfiguration(*other.sslConfiguration); |
|
237 #endif |
|
238 } |
|
239 |
|
240 inline bool operator==(const QNetworkRequestPrivate &other) const |
|
241 { |
|
242 return url == other.url && |
|
243 rawHeaders == other.rawHeaders && |
|
244 attributes == other.attributes; |
|
245 // don't compare cookedHeaders |
|
246 } |
|
247 |
|
248 QUrl url; |
|
249 #ifndef QT_NO_OPENSSL |
|
250 mutable QSslConfiguration *sslConfiguration; |
|
251 #endif |
|
252 }; |
|
253 |
|
254 /*! |
|
255 Constructs a QNetworkRequest object with \a url as the URL to be |
|
256 requested. |
|
257 |
|
258 \sa url(), setUrl() |
|
259 */ |
|
260 QNetworkRequest::QNetworkRequest(const QUrl &url) |
|
261 : d(new QNetworkRequestPrivate) |
|
262 { |
|
263 d->url = url; |
|
264 } |
|
265 |
|
266 /*! |
|
267 Creates a copy of \a other. |
|
268 */ |
|
269 QNetworkRequest::QNetworkRequest(const QNetworkRequest &other) |
|
270 : d(other.d) |
|
271 { |
|
272 } |
|
273 |
|
274 /*! |
|
275 Disposes of the QNetworkRequest object. |
|
276 */ |
|
277 QNetworkRequest::~QNetworkRequest() |
|
278 { |
|
279 // QSharedDataPointer auto deletes |
|
280 d = 0; |
|
281 } |
|
282 |
|
283 /*! |
|
284 Returns true if this object is the same as \a other (i.e., if they |
|
285 have the same URL, same headers and same meta-data settings). |
|
286 |
|
287 \sa operator!=() |
|
288 */ |
|
289 bool QNetworkRequest::operator==(const QNetworkRequest &other) const |
|
290 { |
|
291 return d == other.d || *d == *other.d; |
|
292 } |
|
293 |
|
294 /*! |
|
295 \fn bool QNetworkRequest::operator!=(const QNetworkRequest &other) const |
|
296 |
|
297 Returns false if this object is not the same as \a other. |
|
298 |
|
299 \sa operator==() |
|
300 */ |
|
301 |
|
302 /*! |
|
303 Creates a copy of \a other |
|
304 */ |
|
305 QNetworkRequest &QNetworkRequest::operator=(const QNetworkRequest &other) |
|
306 { |
|
307 d = other.d; |
|
308 return *this; |
|
309 } |
|
310 |
|
311 /*! |
|
312 Returns the URL this network request is referring to. |
|
313 |
|
314 \sa setUrl() |
|
315 */ |
|
316 QUrl QNetworkRequest::url() const |
|
317 { |
|
318 return d->url; |
|
319 } |
|
320 |
|
321 /*! |
|
322 Sets the URL this network request is referring to to be \a url. |
|
323 |
|
324 \sa url() |
|
325 */ |
|
326 void QNetworkRequest::setUrl(const QUrl &url) |
|
327 { |
|
328 d->url = url; |
|
329 } |
|
330 |
|
331 /*! |
|
332 Returns the value of the known network header \a header if it is |
|
333 present in this request. If it is not present, returns QVariant() |
|
334 (i.e., an invalid variant). |
|
335 |
|
336 \sa KnownHeaders, rawHeader(), setHeader() |
|
337 */ |
|
338 QVariant QNetworkRequest::header(KnownHeaders header) const |
|
339 { |
|
340 return d->cookedHeaders.value(header); |
|
341 } |
|
342 |
|
343 /*! |
|
344 Sets the value of the known header \a header to be \a value, |
|
345 overriding any previously set headers. This operation also sets |
|
346 the equivalent raw HTTP header. |
|
347 |
|
348 \sa KnownHeaders, setRawHeader(), header() |
|
349 */ |
|
350 void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value) |
|
351 { |
|
352 d->setCookedHeader(header, value); |
|
353 } |
|
354 |
|
355 /*! |
|
356 Returns true if the raw header \a headerName is present in this |
|
357 network request. |
|
358 |
|
359 \sa rawHeader(), setRawHeader() |
|
360 */ |
|
361 bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const |
|
362 { |
|
363 return d->findRawHeader(headerName) != d->rawHeaders.constEnd(); |
|
364 } |
|
365 |
|
366 /*! |
|
367 Returns the raw form of header \a headerName. If no such header is |
|
368 present, an empty QByteArray is returned, which may be |
|
369 indistinguishable from a header that is present but has no content |
|
370 (use hasRawHeader() to find out if the header exists or not). |
|
371 |
|
372 Raw headers can be set with setRawHeader() or with setHeader(). |
|
373 |
|
374 \sa header(), setRawHeader() |
|
375 */ |
|
376 QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const |
|
377 { |
|
378 QNetworkHeadersPrivate::RawHeadersList::ConstIterator it = |
|
379 d->findRawHeader(headerName); |
|
380 if (it != d->rawHeaders.constEnd()) |
|
381 return it->second; |
|
382 return QByteArray(); |
|
383 } |
|
384 |
|
385 /*! |
|
386 Returns a list of all raw headers that are set in this network |
|
387 request. The list is in the order that the headers were set. |
|
388 |
|
389 \sa hasRawHeader(), rawHeader() |
|
390 */ |
|
391 QList<QByteArray> QNetworkRequest::rawHeaderList() const |
|
392 { |
|
393 return d->rawHeadersKeys(); |
|
394 } |
|
395 |
|
396 /*! |
|
397 Sets the header \a headerName to be of value \a headerValue. If \a |
|
398 headerName corresponds to a known header (see |
|
399 QNetworkRequest::KnownHeaders), the raw format will be parsed and |
|
400 the corresponding "cooked" header will be set as well. |
|
401 |
|
402 For example: |
|
403 \snippet doc/src/snippets/code/src_network_access_qnetworkrequest.cpp 0 |
|
404 |
|
405 will also set the known header LastModifiedHeader to be the |
|
406 QDateTime object of the parsed date. |
|
407 |
|
408 Note: setting the same header twice overrides the previous |
|
409 setting. To accomplish the behaviour of multiple HTTP headers of |
|
410 the same name, you should concatenate the two values, separating |
|
411 them with a comma (",") and set one single raw header. |
|
412 |
|
413 \sa KnownHeaders, setHeader(), hasRawHeader(), rawHeader() |
|
414 */ |
|
415 void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue) |
|
416 { |
|
417 d->setRawHeader(headerName, headerValue); |
|
418 } |
|
419 |
|
420 /*! |
|
421 Returns the attribute associated with the code \a code. If the |
|
422 attribute has not been set, it returns \a defaultValue. |
|
423 |
|
424 Note: this function does not apply the defaults listed in |
|
425 QNetworkRequest::Attribute. |
|
426 |
|
427 \sa setAttribute(), QNetworkRequest::Attribute |
|
428 */ |
|
429 QVariant QNetworkRequest::attribute(Attribute code, const QVariant &defaultValue) const |
|
430 { |
|
431 return d->attributes.value(code, defaultValue); |
|
432 } |
|
433 |
|
434 /*! |
|
435 Sets the attribute associated with code \a code to be value \a |
|
436 value. If the attribute is already set, the previous value is |
|
437 discarded. In special, if \a value is an invalid QVariant, the |
|
438 attribute is unset. |
|
439 |
|
440 \sa attribute(), QNetworkRequest::Attribute |
|
441 */ |
|
442 void QNetworkRequest::setAttribute(Attribute code, const QVariant &value) |
|
443 { |
|
444 if (value.isValid()) |
|
445 d->attributes.insert(code, value); |
|
446 else |
|
447 d->attributes.remove(code); |
|
448 } |
|
449 |
|
450 #ifndef QT_NO_OPENSSL |
|
451 /*! |
|
452 Returns this network request's SSL configuration. By default, no |
|
453 SSL settings are specified. |
|
454 |
|
455 \sa setSslConfiguration() |
|
456 */ |
|
457 QSslConfiguration QNetworkRequest::sslConfiguration() const |
|
458 { |
|
459 if (!d->sslConfiguration) |
|
460 d->sslConfiguration = new QSslConfiguration; |
|
461 return *d->sslConfiguration; |
|
462 } |
|
463 |
|
464 /*! |
|
465 Sets this network request's SSL configuration to be \a config. The |
|
466 settings that apply are the private key, the local certificate, |
|
467 the SSL protocol (SSLv2, SSLv3, TLSv1 where applicable) and the |
|
468 ciphers that the SSL backend is allowed to use. |
|
469 |
|
470 By default, no SSL configuration is set, which allows the backends |
|
471 to choose freely what configuration is best for them. |
|
472 |
|
473 \sa sslConfiguration(), QSslConfiguration::defaultConfiguration() |
|
474 */ |
|
475 void QNetworkRequest::setSslConfiguration(const QSslConfiguration &config) |
|
476 { |
|
477 if (!d->sslConfiguration) |
|
478 d->sslConfiguration = new QSslConfiguration(config); |
|
479 else |
|
480 *d->sslConfiguration = config; |
|
481 } |
|
482 #endif |
|
483 |
|
484 /*! |
|
485 \since 4.6 |
|
486 |
|
487 Allows setting a reference to the \a object initiating |
|
488 the request. |
|
489 |
|
490 For example QtWebKit sets the originating object to the |
|
491 QWebFrame that initiated the request. |
|
492 |
|
493 \sa originatingObject() |
|
494 */ |
|
495 void QNetworkRequest::setOriginatingObject(QObject *object) |
|
496 { |
|
497 d->originatingObject = object; |
|
498 } |
|
499 |
|
500 /*! |
|
501 \since 4.6 |
|
502 |
|
503 Returns a reference to the object that initiated this |
|
504 network request; returns 0 if not set or the object has |
|
505 been destroyed. |
|
506 |
|
507 \sa setOriginatingObject() |
|
508 */ |
|
509 QObject *QNetworkRequest::originatingObject() const |
|
510 { |
|
511 return d->originatingObject.data(); |
|
512 } |
|
513 |
|
514 static QByteArray headerName(QNetworkRequest::KnownHeaders header) |
|
515 { |
|
516 switch (header) { |
|
517 case QNetworkRequest::ContentTypeHeader: |
|
518 return "Content-Type"; |
|
519 |
|
520 case QNetworkRequest::ContentLengthHeader: |
|
521 return "Content-Length"; |
|
522 |
|
523 case QNetworkRequest::LocationHeader: |
|
524 return "Location"; |
|
525 |
|
526 case QNetworkRequest::LastModifiedHeader: |
|
527 return "Last-Modified"; |
|
528 |
|
529 case QNetworkRequest::CookieHeader: |
|
530 return "Cookie"; |
|
531 |
|
532 case QNetworkRequest::SetCookieHeader: |
|
533 return "Set-Cookie"; |
|
534 |
|
535 // no default: |
|
536 // if new values are added, this will generate a compiler warning |
|
537 } |
|
538 |
|
539 return QByteArray(); |
|
540 } |
|
541 |
|
542 static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value) |
|
543 { |
|
544 switch (header) { |
|
545 case QNetworkRequest::ContentTypeHeader: |
|
546 case QNetworkRequest::ContentLengthHeader: |
|
547 return value.toByteArray(); |
|
548 |
|
549 case QNetworkRequest::LocationHeader: |
|
550 switch (value.type()) { |
|
551 case QVariant::Url: |
|
552 return value.toUrl().toEncoded(); |
|
553 |
|
554 default: |
|
555 return value.toByteArray(); |
|
556 } |
|
557 |
|
558 case QNetworkRequest::LastModifiedHeader: |
|
559 switch (value.type()) { |
|
560 case QVariant::Date: |
|
561 case QVariant::DateTime: |
|
562 // generate RFC 1123/822 dates: |
|
563 return QNetworkHeadersPrivate::toHttpDate(value.toDateTime()); |
|
564 |
|
565 default: |
|
566 return value.toByteArray(); |
|
567 } |
|
568 |
|
569 case QNetworkRequest::CookieHeader: { |
|
570 QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value); |
|
571 if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>()) |
|
572 cookies << qvariant_cast<QNetworkCookie>(value); |
|
573 |
|
574 QByteArray result; |
|
575 bool first = true; |
|
576 foreach (const QNetworkCookie &cookie, cookies) { |
|
577 if (!first) |
|
578 result += "; "; |
|
579 first = false; |
|
580 result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly); |
|
581 } |
|
582 return result; |
|
583 } |
|
584 |
|
585 case QNetworkRequest::SetCookieHeader: { |
|
586 QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value); |
|
587 if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>()) |
|
588 cookies << qvariant_cast<QNetworkCookie>(value); |
|
589 |
|
590 QByteArray result; |
|
591 bool first = true; |
|
592 foreach (const QNetworkCookie &cookie, cookies) { |
|
593 if (!first) |
|
594 result += ", "; |
|
595 first = false; |
|
596 result += cookie.toRawForm(QNetworkCookie::Full); |
|
597 } |
|
598 return result; |
|
599 } |
|
600 } |
|
601 |
|
602 return QByteArray(); |
|
603 } |
|
604 |
|
605 static QNetworkRequest::KnownHeaders parseHeaderName(const QByteArray &headerName) |
|
606 { |
|
607 // headerName is not empty here |
|
608 |
|
609 QByteArray lower = headerName.toLower(); |
|
610 switch (lower.at(0)) { |
|
611 case 'c': |
|
612 if (lower == "content-type") |
|
613 return QNetworkRequest::ContentTypeHeader; |
|
614 else if (lower == "content-length") |
|
615 return QNetworkRequest::ContentLengthHeader; |
|
616 else if (lower == "cookie") |
|
617 return QNetworkRequest::CookieHeader; |
|
618 break; |
|
619 |
|
620 case 'l': |
|
621 if (lower == "location") |
|
622 return QNetworkRequest::LocationHeader; |
|
623 else if (lower == "last-modified") |
|
624 return QNetworkRequest::LastModifiedHeader; |
|
625 break; |
|
626 |
|
627 case 's': |
|
628 if (lower == "set-cookie") |
|
629 return QNetworkRequest::SetCookieHeader; |
|
630 break; |
|
631 } |
|
632 |
|
633 return QNetworkRequest::KnownHeaders(-1); // nothing found |
|
634 } |
|
635 |
|
636 static QVariant parseHttpDate(const QByteArray &raw) |
|
637 { |
|
638 QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw); |
|
639 if (dt.isValid()) |
|
640 return dt; |
|
641 return QVariant(); // transform an invalid QDateTime into a null QVariant |
|
642 } |
|
643 |
|
644 static QVariant parseCookieHeader(const QByteArray &raw) |
|
645 { |
|
646 QList<QNetworkCookie> result; |
|
647 QList<QByteArray> cookieList = raw.split(';'); |
|
648 foreach (QByteArray cookie, cookieList) { |
|
649 QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed()); |
|
650 if (parsed.count() != 1) |
|
651 return QVariant(); // invalid Cookie: header |
|
652 |
|
653 result += parsed; |
|
654 } |
|
655 |
|
656 return qVariantFromValue(result); |
|
657 } |
|
658 |
|
659 static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value) |
|
660 { |
|
661 // header is always a valid value |
|
662 switch (header) { |
|
663 case QNetworkRequest::ContentTypeHeader: |
|
664 // copy exactly, convert to QString |
|
665 return QString::fromLatin1(value); |
|
666 |
|
667 case QNetworkRequest::ContentLengthHeader: { |
|
668 bool ok; |
|
669 qint64 result = value.trimmed().toLongLong(&ok); |
|
670 if (ok) |
|
671 return result; |
|
672 return QVariant(); |
|
673 } |
|
674 |
|
675 case QNetworkRequest::LocationHeader: { |
|
676 QUrl result = QUrl::fromEncoded(value, QUrl::StrictMode); |
|
677 if (result.isValid() && !result.scheme().isEmpty()) |
|
678 return result; |
|
679 return QVariant(); |
|
680 } |
|
681 |
|
682 case QNetworkRequest::LastModifiedHeader: |
|
683 return parseHttpDate(value); |
|
684 |
|
685 case QNetworkRequest::CookieHeader: |
|
686 return parseCookieHeader(value); |
|
687 |
|
688 case QNetworkRequest::SetCookieHeader: |
|
689 return qVariantFromValue(QNetworkCookie::parseCookies(value)); |
|
690 |
|
691 default: |
|
692 Q_ASSERT(0); |
|
693 } |
|
694 return QVariant(); |
|
695 } |
|
696 |
|
697 QNetworkHeadersPrivate::RawHeadersList::ConstIterator |
|
698 QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const |
|
699 { |
|
700 QByteArray lowerKey = key.toLower(); |
|
701 RawHeadersList::ConstIterator it = rawHeaders.constBegin(); |
|
702 RawHeadersList::ConstIterator end = rawHeaders.constEnd(); |
|
703 for ( ; it != end; ++it) |
|
704 if (it->first.toLower() == lowerKey) |
|
705 return it; |
|
706 |
|
707 return end; // not found |
|
708 } |
|
709 |
|
710 QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const |
|
711 { |
|
712 QList<QByteArray> result; |
|
713 RawHeadersList::ConstIterator it = rawHeaders.constBegin(), |
|
714 end = rawHeaders.constEnd(); |
|
715 for ( ; it != end; ++it) |
|
716 result << it->first; |
|
717 |
|
718 return result; |
|
719 } |
|
720 |
|
721 void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value) |
|
722 { |
|
723 if (key.isEmpty()) |
|
724 // refuse to accept an empty raw header |
|
725 return; |
|
726 |
|
727 setRawHeaderInternal(key, value); |
|
728 parseAndSetHeader(key, value); |
|
729 } |
|
730 |
|
731 /*! |
|
732 \internal |
|
733 Sets the internal raw headers list to match \a list. The cooked headers |
|
734 will also be updated. |
|
735 |
|
736 If \a list contains duplicates, they will be stored, but only the first one |
|
737 is usually accessed. |
|
738 */ |
|
739 void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list) |
|
740 { |
|
741 cookedHeaders.clear(); |
|
742 rawHeaders = list; |
|
743 |
|
744 RawHeadersList::ConstIterator it = rawHeaders.constBegin(); |
|
745 RawHeadersList::ConstIterator end = rawHeaders.constEnd(); |
|
746 for ( ; it != end; ++it) |
|
747 parseAndSetHeader(it->first, it->second); |
|
748 } |
|
749 |
|
750 void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header, |
|
751 const QVariant &value) |
|
752 { |
|
753 QByteArray name = headerName(header); |
|
754 if (name.isEmpty()) { |
|
755 // headerName verifies that \a header is a known value |
|
756 qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header); |
|
757 return; |
|
758 } |
|
759 |
|
760 if (value.isNull()) { |
|
761 setRawHeaderInternal(name, QByteArray()); |
|
762 cookedHeaders.remove(header); |
|
763 } else { |
|
764 QByteArray rawValue = headerValue(header, value); |
|
765 if (rawValue.isEmpty()) { |
|
766 qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s", |
|
767 value.typeName(), name.constData()); |
|
768 return; |
|
769 } |
|
770 |
|
771 setRawHeaderInternal(name, rawValue); |
|
772 cookedHeaders.insert(header, value); |
|
773 } |
|
774 } |
|
775 |
|
776 void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value) |
|
777 { |
|
778 QByteArray lowerKey = key.toLower(); |
|
779 RawHeadersList::Iterator it = rawHeaders.begin(); |
|
780 while (it != rawHeaders.end()) { |
|
781 if (it->first.toLower() == lowerKey) |
|
782 it = rawHeaders.erase(it); |
|
783 else |
|
784 ++it; |
|
785 } |
|
786 |
|
787 if (value.isNull()) |
|
788 return; // only wanted to erase key |
|
789 |
|
790 RawHeaderPair pair; |
|
791 pair.first = key; |
|
792 pair.second = value; |
|
793 rawHeaders.append(pair); |
|
794 } |
|
795 |
|
796 void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByteArray &value) |
|
797 { |
|
798 // is it a known header? |
|
799 QNetworkRequest::KnownHeaders parsedKey = parseHeaderName(key); |
|
800 if (parsedKey != QNetworkRequest::KnownHeaders(-1)) { |
|
801 if (value.isNull()) |
|
802 cookedHeaders.remove(parsedKey); |
|
803 else |
|
804 cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value)); |
|
805 } |
|
806 } |
|
807 |
|
808 QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value) |
|
809 { |
|
810 // HTTP dates have three possible formats: |
|
811 // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT" |
|
812 // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT" |
|
813 // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy |
|
814 // We only handle them exactly. If they deviate, we bail out. |
|
815 |
|
816 int pos = value.indexOf(','); |
|
817 QDateTime dt; |
|
818 #ifndef QT_NO_DATESTRING |
|
819 if (pos == -1) { |
|
820 // no comma -> asctime(3) format |
|
821 dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate); |
|
822 } else { |
|
823 // eat the weekday, the comma and the space following it |
|
824 QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2); |
|
825 |
|
826 QLocale c = QLocale::c(); |
|
827 if (pos == 3) |
|
828 // must be RFC 1123 date |
|
829 dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT")); |
|
830 else |
|
831 // must be RFC 850 date |
|
832 dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'")); |
|
833 } |
|
834 #endif // QT_NO_DATESTRING |
|
835 |
|
836 if (dt.isValid()) |
|
837 dt.setTimeSpec(Qt::UTC); |
|
838 return dt; |
|
839 } |
|
840 |
|
841 QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt) |
|
842 { |
|
843 return QLocale::c().toString(dt, QLatin1String("ddd, dd MMM yyyy hh:mm:ss 'GMT'")) |
|
844 .toLatin1(); |
|
845 } |
|
846 |
|
847 QT_END_NAMESPACE |