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

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

trunk: Merged in qt 4.6.1 sources.

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