source: trunk/src/network/access/qnetworkrequest.cpp@ 440

Last change on this file since 440 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 24.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
51QT_BEGIN_NAMESPACE
52
53/*!
54 \class QNetworkRequest
55 \brief The QNetworkRequest class holds one request to be sent with the Network Access API.
56 \since 4.4
57
58 \ingroup io
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). Note that the default QNetworkAccessManager
157 implementation does not support caching, so it will ignore
158 this attribute.
159
160 \value SourceIsFromCacheAttribute
161 Replies only, type: QVariant::Bool (default: false)
162 Indicates whether the data was obtained from cache
163 or not.
164
165 \value User
166 Special type. Additional information can be passed in
167 QVariants with types ranging from User to UserMax. The default
168 implementation of Network Access will ignore any request
169 attributes in this range and it will not produce any
170 attributes in this range in replies. The range is reserved for
171 extensions of QNetworkAccessManager.
172
173 \value UserMax
174 Special type. See User.
175*/
176
177/*!
178 \enum QNetworkRequest::CacheLoadControl
179
180 Controls the caching mechanism of QNetworkAccessManager.
181
182 \value AlwaysNetwork always load from network and do not
183 check if the cache has a valid entry (similar to the
184 "Reload" feature in browsers)
185
186 \value PreferNetwork default value; load from the network
187 if the cached entry is older than the network entry
188
189 \value PreferCache load from cache if available,
190 otherwise load from network. Note that this can return possibly
191 stale (but not expired) items from cache.
192
193 \value AlwaysCache only load from cache, indicating error
194 if the item was not cached (i.e., off-line mode)
195*/
196
197class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
198{
199public:
200 inline QNetworkRequestPrivate()
201#ifndef QT_NO_OPENSSL
202 : sslConfiguration(0)
203#endif
204 { qRegisterMetaType<QNetworkRequest>(); }
205 ~QNetworkRequestPrivate()
206 {
207#ifndef QT_NO_OPENSSL
208 delete sslConfiguration;
209#endif
210 }
211
212
213 QNetworkRequestPrivate(const QNetworkRequestPrivate &other)
214 : QSharedData(other), QNetworkHeadersPrivate(other)
215 {
216 url = other.url;
217
218#ifndef QT_NO_OPENSSL
219 if (other.sslConfiguration)
220 sslConfiguration = new QSslConfiguration(*other.sslConfiguration);
221 else
222 sslConfiguration = 0;
223#endif
224 }
225
226 inline bool operator==(const QNetworkRequestPrivate &other) const
227 {
228 return url == other.url &&
229 rawHeaders == other.rawHeaders &&
230 attributes == other.attributes;
231 // don't compare cookedHeaders
232 }
233
234 QUrl url;
235#ifndef QT_NO_OPENSSL
236 mutable QSslConfiguration *sslConfiguration;
237#endif
238};
239
240/*!
241 Constructs a QNetworkRequest object with \a url as the URL to be
242 requested.
243
244 \sa url(), setUrl()
245*/
246QNetworkRequest::QNetworkRequest(const QUrl &url)
247 : d(new QNetworkRequestPrivate)
248{
249 d->url = url;
250}
251
252/*!
253 Creates a copy of \a other.
254*/
255QNetworkRequest::QNetworkRequest(const QNetworkRequest &other)
256 : d(other.d)
257{
258}
259
260/*!
261 Disposes of the QNetworkRequest object.
262*/
263QNetworkRequest::~QNetworkRequest()
264{
265 // QSharedDataPointer auto deletes
266 d = 0;
267}
268
269/*!
270 Returns true if this object is the same as \a other (i.e., if they
271 have the same URL, same headers and same meta-data settings).
272
273 \sa operator!=()
274*/
275bool QNetworkRequest::operator==(const QNetworkRequest &other) const
276{
277 return d == other.d || *d == *other.d;
278}
279
280/*!
281 \fn bool QNetworkRequest::operator!=(const QNetworkRequest &other) const
282
283 Returns false if this object is not the same as \a other.
284
285 \sa operator==()
286*/
287
288/*!
289 Creates a copy of \a other
290*/
291QNetworkRequest &QNetworkRequest::operator=(const QNetworkRequest &other)
292{
293 d = other.d;
294 return *this;
295}
296
297/*!
298 Returns the URL this network request is referring to.
299
300 \sa setUrl()
301*/
302QUrl QNetworkRequest::url() const
303{
304 return d->url;
305}
306
307/*!
308 Sets the URL this network request is referring to to be \a url.
309
310 \sa url()
311*/
312void QNetworkRequest::setUrl(const QUrl &url)
313{
314 d->url = url;
315}
316
317/*!
318 Returns the value of the known network header \a header if it is
319 present in this request. If it is not present, returns QVariant()
320 (i.e., an invalid variant).
321
322 \sa KnownHeaders, rawHeader(), setHeader()
323*/
324QVariant QNetworkRequest::header(KnownHeaders header) const
325{
326 return d->cookedHeaders.value(header);
327}
328
329/*!
330 Sets the value of the known header \a header to be \a value,
331 overriding any previously set headers. This operation also sets
332 the equivalent raw HTTP header.
333
334 \sa KnownHeaders, setRawHeader(), header()
335*/
336void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
337{
338 d->setCookedHeader(header, value);
339}
340
341/*!
342 Returns true if the raw header \a headerName is present in this
343 network request.
344
345 \sa rawHeader(), setRawHeader()
346*/
347bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
348{
349 return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
350}
351
352/*!
353 Returns the raw form of header \a headerName. If no such header is
354 present, an empty QByteArray is returned, which may be
355 indistinguishable from a header that is present but has no content
356 (use hasRawHeader() to find out if the header exists or not).
357
358 Raw headers can be set with setRawHeader() or with setHeader().
359
360 \sa header(), setRawHeader()
361*/
362QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
363{
364 QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
365 d->findRawHeader(headerName);
366 if (it != d->rawHeaders.constEnd())
367 return it->second;
368 return QByteArray();
369}
370
371/*!
372 Returns a list of all raw headers that are set in this network
373 request. The list is in the order that the headers were set.
374
375 \sa hasRawHeader(), rawHeader()
376*/
377QList<QByteArray> QNetworkRequest::rawHeaderList() const
378{
379 return d->rawHeadersKeys();
380}
381
382/*!
383 Sets the header \a headerName to be of value \a headerValue. If \a
384 headerName corresponds to a known header (see
385 QNetworkRequest::KnownHeaders), the raw format will be parsed and
386 the corresponding "cooked" header will be set as well.
387
388 For example:
389 \snippet doc/src/snippets/code/src_network_access_qnetworkrequest.cpp 0
390
391 will also set the known header LastModifiedHeader to be the
392 QDateTime object of the parsed date.
393
394 Note: setting the same header twice overrides the previous
395 setting. To accomplish the behaviour of multiple HTTP headers of
396 the same name, you should concatenate the two values, separating
397 them with a comma (",") and set one single raw header.
398
399 \sa KnownHeaders, setHeader(), hasRawHeader(), rawHeader()
400*/
401void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
402{
403 d->setRawHeader(headerName, headerValue);
404}
405
406/*!
407 Returns the attribute associated with the code \a code. If the
408 attribute has not been set, it returns \a defaultValue.
409
410 Note: this function does not apply the defaults listed in
411 QNetworkRequest::Attribute.
412
413 \sa setAttribute(), QNetworkRequest::Attribute
414*/
415QVariant QNetworkRequest::attribute(Attribute code, const QVariant &defaultValue) const
416{
417 return d->attributes.value(code, defaultValue);
418}
419
420/*!
421 Sets the attribute associated with code \a code to be value \a
422 value. If the attribute is already set, the previous value is
423 discarded. In special, if \a value is an invalid QVariant, the
424 attribute is unset.
425
426 \sa attribute(), QNetworkRequest::Attribute
427*/
428void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
429{
430 if (value.isValid())
431 d->attributes.insert(code, value);
432 else
433 d->attributes.remove(code);
434}
435
436#ifndef QT_NO_OPENSSL
437/*!
438 Returns this network request's SSL configuration. By default, no
439 SSL settings are specified.
440
441 \sa setSslConfiguration()
442*/
443QSslConfiguration QNetworkRequest::sslConfiguration() const
444{
445 if (!d->sslConfiguration)
446 d->sslConfiguration = new QSslConfiguration;
447 return *d->sslConfiguration;
448}
449
450/*!
451 Sets this network request's SSL configuration to be \a config. The
452 settings that apply are the private key, the local certificate,
453 the SSL protocol (SSLv2, SSLv3, TLSv1 where applicable) and the
454 ciphers that the SSL backend is allowed to use.
455
456 By default, no SSL configuration is set, which allows the backends
457 to choose freely what configuration is best for them.
458
459 \sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
460*/
461void QNetworkRequest::setSslConfiguration(const QSslConfiguration &config)
462{
463 if (!d->sslConfiguration)
464 d->sslConfiguration = new QSslConfiguration(config);
465 else
466 *d->sslConfiguration = config;
467}
468#endif
469
470static QByteArray headerName(QNetworkRequest::KnownHeaders header)
471{
472 switch (header) {
473 case QNetworkRequest::ContentTypeHeader:
474 return "Content-Type";
475
476 case QNetworkRequest::ContentLengthHeader:
477 return "Content-Length";
478
479 case QNetworkRequest::LocationHeader:
480 return "Location";
481
482 case QNetworkRequest::LastModifiedHeader:
483 return "Last-Modified";
484
485 case QNetworkRequest::CookieHeader:
486 return "Cookie";
487
488 case QNetworkRequest::SetCookieHeader:
489 return "Set-Cookie";
490
491 // no default:
492 // if new values are added, this will generate a compiler warning
493 }
494
495 return QByteArray();
496}
497
498static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
499{
500 switch (header) {
501 case QNetworkRequest::ContentTypeHeader:
502 case QNetworkRequest::ContentLengthHeader:
503 return value.toByteArray();
504
505 case QNetworkRequest::LocationHeader:
506 switch (value.type()) {
507 case QVariant::Url:
508 return value.toUrl().toEncoded();
509
510 default:
511 return value.toByteArray();
512 }
513
514 case QNetworkRequest::LastModifiedHeader:
515 switch (value.type()) {
516 case QVariant::Date:
517 case QVariant::DateTime:
518 // generate RFC 1123/822 dates:
519 return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
520
521 default:
522 return value.toByteArray();
523 }
524
525 case QNetworkRequest::CookieHeader: {
526 QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
527 if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
528 cookies << qvariant_cast<QNetworkCookie>(value);
529
530 QByteArray result;
531 bool first = true;
532 foreach (const QNetworkCookie &cookie, cookies) {
533 if (!first)
534 result += "; ";
535 first = false;
536 result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
537 }
538 return result;
539 }
540
541 case QNetworkRequest::SetCookieHeader: {
542 QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
543 if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
544 cookies << qvariant_cast<QNetworkCookie>(value);
545
546 QByteArray result;
547 bool first = true;
548 foreach (const QNetworkCookie &cookie, cookies) {
549 if (!first)
550 result += ", ";
551 first = false;
552 result += cookie.toRawForm(QNetworkCookie::Full);
553 }
554 return result;
555 }
556 }
557
558 return QByteArray();
559}
560
561static QNetworkRequest::KnownHeaders parseHeaderName(const QByteArray &headerName)
562{
563 // headerName is not empty here
564
565 QByteArray lower = headerName.toLower();
566 switch (lower.at(0)) {
567 case 'c':
568 if (lower == "content-type")
569 return QNetworkRequest::ContentTypeHeader;
570 else if (lower == "content-length")
571 return QNetworkRequest::ContentLengthHeader;
572 else if (lower == "cookie")
573 return QNetworkRequest::CookieHeader;
574 break;
575
576 case 'l':
577 if (lower == "location")
578 return QNetworkRequest::LocationHeader;
579 else if (lower == "last-modified")
580 return QNetworkRequest::LastModifiedHeader;
581 break;
582
583 case 's':
584 if (lower == "set-cookie")
585 return QNetworkRequest::SetCookieHeader;
586 break;
587 }
588
589 return QNetworkRequest::KnownHeaders(-1); // nothing found
590}
591
592static QVariant parseHttpDate(const QByteArray &raw)
593{
594 QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
595 if (dt.isValid())
596 return dt;
597 return QVariant(); // transform an invalid QDateTime into a null QVariant
598}
599
600static QVariant parseCookieHeader(const QByteArray &raw)
601{
602 QList<QNetworkCookie> result;
603 QList<QByteArray> cookieList = raw.split(';');
604 foreach (QByteArray cookie, cookieList) {
605 QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
606 if (parsed.count() != 1)
607 return QVariant(); // invalid Cookie: header
608
609 result += parsed;
610 }
611
612 return qVariantFromValue(result);
613}
614
615static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
616{
617 // header is always a valid value
618 switch (header) {
619 case QNetworkRequest::ContentTypeHeader:
620 // copy exactly, convert to QString
621 return QString::fromLatin1(value);
622
623 case QNetworkRequest::ContentLengthHeader: {
624 bool ok;
625 qint64 result = value.trimmed().toLongLong(&ok);
626 if (ok)
627 return result;
628 return QVariant();
629 }
630
631 case QNetworkRequest::LocationHeader: {
632 QUrl result = QUrl::fromEncoded(value, QUrl::StrictMode);
633 if (result.isValid() && !result.scheme().isEmpty())
634 return result;
635 return QVariant();
636 }
637
638 case QNetworkRequest::LastModifiedHeader:
639 return parseHttpDate(value);
640
641 case QNetworkRequest::CookieHeader:
642 return parseCookieHeader(value);
643
644 case QNetworkRequest::SetCookieHeader:
645 return qVariantFromValue(QNetworkCookie::parseCookies(value));
646
647 default:
648 Q_ASSERT(0);
649 }
650 return QVariant();
651}
652
653QNetworkHeadersPrivate::RawHeadersList::ConstIterator
654QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
655{
656 QByteArray lowerKey = key.toLower();
657 RawHeadersList::ConstIterator it = rawHeaders.constBegin();
658 RawHeadersList::ConstIterator end = rawHeaders.constEnd();
659 for ( ; it != end; ++it)
660 if (it->first.toLower() == lowerKey)
661 return it;
662
663 return end; // not found
664}
665
666QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
667{
668 QList<QByteArray> result;
669 RawHeadersList::ConstIterator it = rawHeaders.constBegin(),
670 end = rawHeaders.constEnd();
671 for ( ; it != end; ++it)
672 result << it->first;
673
674 return result;
675}
676
677void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value)
678{
679 if (key.isEmpty())
680 // refuse to accept an empty raw header
681 return;
682
683 setRawHeaderInternal(key, value);
684 parseAndSetHeader(key, value);
685}
686
687/*!
688 \internal
689 Sets the internal raw headers list to match \a list. The cooked headers
690 will also be updated.
691
692 If \a list contains duplicates, they will be stored, but only the first one
693 is usually accessed.
694*/
695void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
696{
697 cookedHeaders.clear();
698 rawHeaders = list;
699
700 RawHeadersList::ConstIterator it = rawHeaders.constBegin();
701 RawHeadersList::ConstIterator end = rawHeaders.constEnd();
702 for ( ; it != end; ++it)
703 parseAndSetHeader(it->first, it->second);
704}
705
706void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
707 const QVariant &value)
708{
709 QByteArray name = headerName(header);
710 if (name.isEmpty()) {
711 // headerName verifies that \a header is a known value
712 qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
713 return;
714 }
715
716 if (value.isNull()) {
717 setRawHeaderInternal(name, QByteArray());
718 cookedHeaders.remove(header);
719 } else {
720 QByteArray rawValue = headerValue(header, value);
721 if (rawValue.isEmpty()) {
722 qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
723 value.typeName(), name.constData());
724 return;
725 }
726
727 setRawHeaderInternal(name, rawValue);
728 cookedHeaders.insert(header, value);
729 }
730}
731
732void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value)
733{
734 QByteArray lowerKey = key.toLower();
735 RawHeadersList::Iterator it = rawHeaders.begin();
736 while (it != rawHeaders.end()) {
737 if (it->first.toLower() == lowerKey)
738 it = rawHeaders.erase(it);
739 else
740 ++it;
741 }
742
743 if (value.isNull())
744 return; // only wanted to erase key
745
746 RawHeaderPair pair;
747 pair.first = key;
748 pair.second = value;
749 rawHeaders.append(pair);
750}
751
752void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByteArray &value)
753{
754 // is it a known header?
755 QNetworkRequest::KnownHeaders parsedKey = parseHeaderName(key);
756 if (parsedKey != QNetworkRequest::KnownHeaders(-1)) {
757 if (value.isNull())
758 cookedHeaders.remove(parsedKey);
759 else
760 cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value));
761 }
762}
763
764QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
765{
766 // HTTP dates have three possible formats:
767 // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
768 // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT"
769 // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy
770 // We only handle them exactly. If they deviate, we bail out.
771
772 int pos = value.indexOf(',');
773 QDateTime dt;
774#ifndef QT_NO_DATESTRING
775 if (pos == -1) {
776 // no comma -> asctime(3) format
777 dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
778 } else {
779 // eat the weekday, the comma and the space following it
780 QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
781
782 QLocale c = QLocale::c();
783 if (pos == 3)
784 // must be RFC 1123 date
785 dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT"));
786 else
787 // must be RFC 850 date
788 dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
789 }
790#endif // QT_NO_DATESTRING
791
792 if (dt.isValid())
793 dt.setTimeSpec(Qt::UTC);
794 return dt;
795}
796
797QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
798{
799 return QLocale::c().toString(dt, QLatin1String("ddd, dd MMM yyyy hh:mm:ss 'GMT'"))
800 .toLatin1();
801}
802
803QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.