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

Last change on this file since 788 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

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