source: trunk/src/network/qhttp.cpp@ 8

Last change on this file since 8 was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 65.6 KB
Line 
1/****************************************************************************
2** $Id: qhttp.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QHttp and related classes.
5**
6** Created : 970521
7**
8** Copyright (C) 1997-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the network module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition licenses may use this
22** file in accordance with the Qt Commercial License Agreement provided
23** with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qhttp.h"
39
40#ifndef QT_NO_NETWORKPROTOCOL_HTTP
41
42#include "qsocket.h"
43#include "qtextstream.h"
44#include "qmap.h"
45#include "qstring.h"
46#include "qstringlist.h"
47#include "qcstring.h"
48#include "qbuffer.h"
49#include "qurloperator.h"
50#include "qtimer.h"
51#include "private/qinternal_p.h"
52
53//#define QHTTP_DEBUG
54
55class QHttpPrivate
56{
57public:
58 QHttpPrivate() :
59 state( QHttp::Unconnected ),
60 error( QHttp::NoError ),
61 hostname( QString::null ),
62 port( 0 ),
63 toDevice( 0 ),
64 postDevice( 0 ),
65 bytesDone( 0 ),
66 chunkedSize( -1 ),
67 idleTimer( 0 )
68 {
69 pending.setAutoDelete( TRUE );
70 }
71
72 QSocket socket;
73 QPtrList<QHttpRequest> pending;
74
75 QHttp::State state;
76 QHttp::Error error;
77 QString errorString;
78
79 QString hostname;
80 Q_UINT16 port;
81
82 QByteArray buffer;
83 QIODevice* toDevice;
84 QIODevice* postDevice;
85
86 uint bytesDone;
87 uint bytesTotal;
88 Q_LONG chunkedSize;
89
90 QHttpRequestHeader header;
91
92 bool readHeader;
93 QString headerStr;
94 QHttpResponseHeader response;
95
96 int idleTimer;
97
98 QMembuf rba;
99};
100
101class QHttpRequest
102{
103public:
104 QHttpRequest()
105 {
106 id = ++idCounter;
107 }
108 virtual ~QHttpRequest()
109 { }
110
111 virtual void start( QHttp * ) = 0;
112 virtual bool hasRequestHeader();
113 virtual QHttpRequestHeader requestHeader();
114
115 virtual QIODevice* sourceDevice() = 0;
116 virtual QIODevice* destinationDevice() = 0;
117
118 int id;
119
120private:
121 static int idCounter;
122};
123
124int QHttpRequest::idCounter = 0;
125
126bool QHttpRequest::hasRequestHeader()
127{
128 return FALSE;
129}
130
131QHttpRequestHeader QHttpRequest::requestHeader()
132{
133 return QHttpRequestHeader();
134}
135
136/****************************************************
137 *
138 * QHttpNormalRequest
139 *
140 ****************************************************/
141
142class QHttpNormalRequest : public QHttpRequest
143{
144public:
145 QHttpNormalRequest( const QHttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
146 header(h), to(t)
147 {
148 is_ba = FALSE;
149 data.dev = d;
150 }
151
152 QHttpNormalRequest( const QHttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
153 header(h), to(t)
154 {
155 is_ba = TRUE;
156 data.ba = d;
157 }
158
159 ~QHttpNormalRequest()
160 {
161 if ( is_ba )
162 delete data.ba;
163 }
164
165 void start( QHttp * );
166 bool hasRequestHeader();
167 QHttpRequestHeader requestHeader();
168
169 QIODevice* sourceDevice();
170 QIODevice* destinationDevice();
171
172protected:
173 QHttpRequestHeader header;
174
175private:
176 union {
177 QByteArray *ba;
178 QIODevice *dev;
179 } data;
180 bool is_ba;
181 QIODevice *to;
182};
183
184void QHttpNormalRequest::start( QHttp *http )
185{
186 http->d->header = header;
187
188 if ( is_ba ) {
189 http->d->buffer = *data.ba;
190 if ( http->d->buffer.size() > 0 )
191 http->d->header.setContentLength( http->d->buffer.size() );
192
193 http->d->postDevice = 0;
194 } else {
195 http->d->buffer = QByteArray();
196
197 if ( data.dev && ( data.dev->isOpen() || data.dev->open(IO_ReadOnly) ) ) {
198 http->d->postDevice = data.dev;
199 if ( http->d->postDevice->size() > 0 )
200 http->d->header.setContentLength( http->d->postDevice->size() );
201 } else {
202 http->d->postDevice = 0;
203 }
204 }
205
206 if ( to && ( to->isOpen() || to->open(IO_WriteOnly) ) )
207 http->d->toDevice = to;
208 else
209 http->d->toDevice = 0;
210
211 http->sendRequest();
212}
213
214bool QHttpNormalRequest::hasRequestHeader()
215{
216 return TRUE;
217}
218
219QHttpRequestHeader QHttpNormalRequest::requestHeader()
220{
221 return header;
222}
223
224QIODevice* QHttpNormalRequest::sourceDevice()
225{
226 if ( is_ba )
227 return 0;
228 return data.dev;
229}
230
231QIODevice* QHttpNormalRequest::destinationDevice()
232{
233 return to;
234}
235
236/****************************************************
237 *
238 * QHttpPGHRequest
239 * (like a QHttpNormalRequest, but for the convenience
240 * functions put(), get() and head() -- i.e. set the
241 * host header field correctly before sending the
242 * request)
243 *
244 ****************************************************/
245
246class QHttpPGHRequest : public QHttpNormalRequest
247{
248public:
249 QHttpPGHRequest( const QHttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
250 QHttpNormalRequest( h, d, t )
251 { }
252
253 QHttpPGHRequest( const QHttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
254 QHttpNormalRequest( h, d, t )
255 { }
256
257 ~QHttpPGHRequest()
258 { }
259
260 void start( QHttp * );
261};
262
263void QHttpPGHRequest::start( QHttp *http )
264{
265 header.setValue( "Host", http->d->hostname );
266 QHttpNormalRequest::start( http );
267}
268
269/****************************************************
270 *
271 * QHttpSetHostRequest
272 *
273 ****************************************************/
274
275class QHttpSetHostRequest : public QHttpRequest
276{
277public:
278 QHttpSetHostRequest( const QString &h, Q_UINT16 p ) :
279 hostname(h), port(p)
280 { }
281
282 void start( QHttp * );
283
284 QIODevice* sourceDevice()
285 { return 0; }
286 QIODevice* destinationDevice()
287 { return 0; }
288
289private:
290 QString hostname;
291 Q_UINT16 port;
292};
293
294void QHttpSetHostRequest::start( QHttp *http )
295{
296 http->d->hostname = hostname;
297 http->d->port = port;
298 http->finishedWithSuccess();
299}
300
301/****************************************************
302 *
303 * QHttpCloseRequest
304 *
305 ****************************************************/
306
307class QHttpCloseRequest : public QHttpRequest
308{
309public:
310 QHttpCloseRequest()
311 { }
312 void start( QHttp * );
313
314 QIODevice* sourceDevice()
315 { return 0; }
316 QIODevice* destinationDevice()
317 { return 0; }
318};
319
320void QHttpCloseRequest::start( QHttp *http )
321{
322 http->close();
323}
324
325/****************************************************
326 *
327 * QHttpHeader
328 *
329 ****************************************************/
330
331/*!
332 \class QHttpHeader qhttp.h
333 \brief The QHttpHeader class contains header information for HTTP.
334\if defined(commercial)
335 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
336\endif
337
338 \ingroup io
339 \module network
340
341 In most cases you should use the more specialized derivatives of
342 this class, QHttpResponseHeader and QHttpRequestHeader, rather
343 than directly using QHttpHeader.
344
345 QHttpHeader provides the HTTP header fields. A HTTP header field
346 consists of a name followed by a colon, a single space, and the
347 field value. (See RFC 1945.) Field names are case-insensitive. A
348 typical header field looks like this:
349 \code
350 content-type: text/html
351 \endcode
352
353 In the API the header field name is called the "key" and the
354 content is called the "value". You can get and set a header
355 field's value by using its key with value() and setValue(), e.g.
356 \code
357 header.setValue( "content-type", "text/html" );
358 QString contentType = header.value( "content-type" );
359 \endcode
360
361 Some fields are so common that getters and setters are provided
362 for them as a convenient alternative to using \l value() and
363 \l setValue(), e.g. contentLength() and contentType(),
364 setContentLength() and setContentType().
365
366 Each header key has a \e single value associated with it. If you
367 set the value for a key which already exists the previous value
368 will be discarded.
369
370 \sa QHttpRequestHeader QHttpResponseHeader
371*/
372
373/*!
374 \fn int QHttpHeader::majorVersion() const
375
376 Returns the major protocol-version of the HTTP header.
377*/
378
379/*!
380 \fn int QHttpHeader::minorVersion() const
381
382 Returns the minor protocol-version of the HTTP header.
383*/
384
385/*!
386 Constructs an empty HTTP header.
387*/
388QHttpHeader::QHttpHeader()
389 : valid( TRUE )
390{
391}
392
393/*!
394 Constructs a copy of \a header.
395*/
396QHttpHeader::QHttpHeader( const QHttpHeader& header )
397 : valid( header.valid )
398{
399 values = header.values;
400}
401
402/*!
403 Constructs a HTTP header for \a str.
404
405 This constructor parses the string \a str for header fields and
406 adds this information. The \a str should consist of one or more
407 "\r\n" delimited lines; each of these lines should have the format
408 key, colon, space, value.
409*/
410QHttpHeader::QHttpHeader( const QString& str )
411 : valid( TRUE )
412{
413 parse( str );
414}
415
416/*!
417 Destructor.
418*/
419QHttpHeader::~QHttpHeader()
420{
421}
422
423/*!
424 Assigns \a h and returns a reference to this http header.
425*/
426QHttpHeader& QHttpHeader::operator=( const QHttpHeader& h )
427{
428 values = h.values;
429 valid = h.valid;
430 return *this;
431}
432
433/*!
434 Returns TRUE if the HTTP header is valid; otherwise returns FALSE.
435
436 A QHttpHeader is invalid if it was created by parsing a malformed string.
437*/
438bool QHttpHeader::isValid() const
439{
440 return valid;
441}
442
443/*! \internal
444 Parses the HTTP header string \a str for header fields and adds
445 the keys/values it finds. If the string is not parsed successfully
446 the QHttpHeader becomes \link isValid() invalid\endlink.
447
448 Returns TRUE if \a str was successfully parsed; otherwise returns FALSE.
449
450 \sa toString()
451*/
452bool QHttpHeader::parse( const QString& str )
453{
454 QStringList lst;
455 int pos = str.find( '\n' );
456 if ( pos > 0 && str.at( pos - 1 ) == '\r' )
457 lst = QStringList::split( "\r\n", str.stripWhiteSpace(), FALSE );
458 else
459 lst = QStringList::split( "\n", str.stripWhiteSpace(), FALSE );
460
461 if ( lst.isEmpty() )
462 return TRUE;
463
464 QStringList lines;
465 QStringList::Iterator it = lst.begin();
466 for( ; it != lst.end(); ++it ) {
467 if ( !(*it).isEmpty() ) {
468 if ( (*it)[0].isSpace() ) {
469 if ( !lines.isEmpty() ) {
470 lines.last() += " ";
471 lines.last() += (*it).stripWhiteSpace();
472 }
473 } else {
474 lines.append( (*it) );
475 }
476 }
477 }
478
479 int number = 0;
480 it = lines.begin();
481 for( ; it != lines.end(); ++it ) {
482 if ( !parseLine( *it, number++ ) ) {
483 valid = FALSE;
484 return FALSE;
485 }
486 }
487 return TRUE;
488}
489
490/*! \internal
491*/
492void QHttpHeader::setValid( bool v )
493{
494 valid = v;
495}
496
497/*!
498 Returns the value for the entry with the given \a key. If no entry
499 has this \a key, an empty string is returned.
500
501 \sa setValue() removeValue() hasKey() keys()
502*/
503QString QHttpHeader::value( const QString& key ) const
504{
505 return values[ key.lower() ];
506}
507
508/*!
509 Returns a list of the keys in the HTTP header.
510
511 \sa hasKey()
512*/
513QStringList QHttpHeader::keys() const
514{
515 return values.keys();
516}
517
518/*!
519 Returns TRUE if the HTTP header has an entry with the given \a
520 key; otherwise returns FALSE.
521
522 \sa value() setValue() keys()
523*/
524bool QHttpHeader::hasKey( const QString& key ) const
525{
526 return values.contains( key.lower() );
527}
528
529/*!
530 Sets the value of the entry with the \a key to \a value.
531
532 If no entry with \a key exists, a new entry with the given \a key
533 and \a value is created. If an entry with the \a key already
534 exists, its value is discarded and replaced with the given \a
535 value.
536
537 \sa value() hasKey() removeValue()
538*/
539void QHttpHeader::setValue( const QString& key, const QString& value )
540{
541 values[ key.lower() ] = value;
542}
543
544/*!
545 Removes the entry with the key \a key from the HTTP header.
546
547 \sa value() setValue()
548*/
549void QHttpHeader::removeValue( const QString& key )
550{
551 values.remove( key.lower() );
552}
553
554/*! \internal
555 Parses the single HTTP header line \a line which has the format
556 key, colon, space, value, and adds key/value to the headers. The
557 linenumber is \a number. Returns TRUE if the line was successfully
558 parsed and the key/value added; otherwise returns FALSE.
559
560 \sa parse()
561*/
562bool QHttpHeader::parseLine( const QString& line, int )
563{
564 int i = line.find( ":" );
565 if ( i == -1 )
566 return FALSE;
567
568 values.insert( line.left( i ).stripWhiteSpace().lower(), line.mid( i + 1 ).stripWhiteSpace() );
569
570 return TRUE;
571}
572
573/*!
574 Returns a string representation of the HTTP header.
575
576 The string is suitable for use by the constructor that takes a
577 QString. It consists of lines with the format: key, colon, space,
578 value, "\r\n".
579*/
580QString QHttpHeader::toString() const
581{
582 if ( !isValid() )
583 return "";
584
585 QString ret = "";
586
587 QMap<QString,QString>::ConstIterator it = values.begin();
588 for( ; it != values.end(); ++it )
589 ret += it.key() + ": " + it.data() + "\r\n";
590
591 return ret;
592}
593
594/*!
595 Returns TRUE if the header has an entry for the special HTTP
596 header field \c content-length; otherwise returns FALSE.
597
598 \sa contentLength() setContentLength()
599*/
600bool QHttpHeader::hasContentLength() const
601{
602 return hasKey( "content-length" );
603}
604
605/*!
606 Returns the value of the special HTTP header field \c
607 content-length.
608
609 \sa setContentLength() hasContentLength()
610*/
611uint QHttpHeader::contentLength() const
612{
613 return values[ "content-length" ].toUInt();
614}
615
616/*!
617 Sets the value of the special HTTP header field \c content-length
618 to \a len.
619
620 \sa contentLength() hasContentLength()
621*/
622void QHttpHeader::setContentLength( int len )
623{
624 values[ "content-length" ] = QString::number( len );
625}
626
627/*!
628 Returns TRUE if the header has an entry for the the special HTTP
629 header field \c content-type; otherwise returns FALSE.
630
631 \sa contentType() setContentType()
632*/
633bool QHttpHeader::hasContentType() const
634{
635 return hasKey( "content-type" );
636}
637
638/*!
639 Returns the value of the special HTTP header field \c content-type.
640
641 \sa setContentType() hasContentType()
642*/
643QString QHttpHeader::contentType() const
644{
645 QString type = values[ "content-type" ];
646 if ( type.isEmpty() )
647 return QString::null;
648
649 int pos = type.find( ";" );
650 if ( pos == -1 )
651 return type;
652
653 return type.left( pos ).stripWhiteSpace();
654}
655
656/*!
657 Sets the value of the special HTTP header field \c content-type to
658 \a type.
659
660 \sa contentType() hasContentType()
661*/
662void QHttpHeader::setContentType( const QString& type )
663{
664 values[ "content-type" ] = type;
665}
666
667/****************************************************
668 *
669 * QHttpResponseHeader
670 *
671 ****************************************************/
672
673/*!
674 \class QHttpResponseHeader qhttp.h
675 \brief The QHttpResponseHeader class contains response header information for HTTP.
676\if defined(commercial)
677 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
678\endif
679
680 \ingroup io
681 \module network
682
683 This class is used by the QHttp class to report the header
684 information that the client received from the server.
685
686 HTTP responses have a status code that indicates the status of the
687 response. This code is a 3-digit integer result code (for details
688 see to RFC 1945). In addition to the status code, you can also
689 specify a human-readable text that describes the reason for the
690 code ("reason phrase"). This class allows you to get the status
691 code and the reason phrase.
692
693 \sa QHttpRequestHeader QHttp
694*/
695
696/*!
697 Constructs an empty HTTP response header.
698*/
699QHttpResponseHeader::QHttpResponseHeader()
700{
701 setValid( FALSE );
702}
703
704/*!
705 Constructs a HTTP response header with the status code \a code,
706 the reason phrase \a text and the protocol-version \a majorVer and
707 \a minorVer.
708*/
709QHttpResponseHeader::QHttpResponseHeader( int code, const QString& text, int majorVer, int minorVer )
710 : QHttpHeader(), statCode( code ), reasonPhr( text ), majVer( majorVer ), minVer( minorVer )
711{
712}
713
714/*!
715 Constructs a copy of \a header.
716*/
717QHttpResponseHeader::QHttpResponseHeader( const QHttpResponseHeader& header )
718 : QHttpHeader( header ), statCode( header.statCode ), reasonPhr( header.reasonPhr ), majVer( header.majVer ), minVer( header.minVer )
719{
720}
721
722/*!
723 Constructs a HTTP response header from the string \a str. The
724 string is parsed and the information is set. The \a str should
725 consist of one or more "\r\n" delimited lines; the first line should be the
726 status-line (format: HTTP-version, space, status-code, space,
727 reason-phrase); each of remaining lines should have the format key, colon,
728 space, value.
729*/
730QHttpResponseHeader::QHttpResponseHeader( const QString& str )
731 : QHttpHeader()
732{
733 parse( str );
734}
735
736/*!
737 Sets the status code to \a code, the reason phrase to \a text and
738 the protocol-version to \a majorVer and \a minorVer.
739
740 \sa statusCode() reasonPhrase() majorVersion() minorVersion()
741*/
742void QHttpResponseHeader::setStatusLine( int code, const QString& text, int majorVer, int minorVer )
743{
744 setValid( TRUE );
745 statCode = code;
746 reasonPhr = text;
747 majVer = majorVer;
748 minVer = minorVer;
749}
750
751/*!
752 Returns the status code of the HTTP response header.
753
754 \sa reasonPhrase() majorVersion() minorVersion()
755*/
756int QHttpResponseHeader::statusCode() const
757{
758 return statCode;
759}
760
761/*!
762 Returns the reason phrase of the HTTP response header.
763
764 \sa statusCode() majorVersion() minorVersion()
765*/
766QString QHttpResponseHeader::reasonPhrase() const
767{
768 return reasonPhr;
769}
770
771/*!
772 Returns the major protocol-version of the HTTP response header.
773
774 \sa minorVersion() statusCode() reasonPhrase()
775*/
776int QHttpResponseHeader::majorVersion() const
777{
778 return majVer;
779}
780
781/*!
782 Returns the minor protocol-version of the HTTP response header.
783
784 \sa majorVersion() statusCode() reasonPhrase()
785*/
786int QHttpResponseHeader::minorVersion() const
787{
788 return minVer;
789}
790
791/*! \reimp
792*/
793bool QHttpResponseHeader::parseLine( const QString& line, int number )
794{
795 if ( number != 0 )
796 return QHttpHeader::parseLine( line, number );
797
798 QString l = line.simplifyWhiteSpace();
799 if ( l.length() < 10 )
800 return FALSE;
801
802 if ( l.left( 5 ) == "HTTP/" && l[5].isDigit() && l[6] == '.' &&
803 l[7].isDigit() && l[8] == ' ' && l[9].isDigit() ) {
804 majVer = l[5].latin1() - '0';
805 minVer = l[7].latin1() - '0';
806
807 int pos = l.find( ' ', 9 );
808 if ( pos != -1 ) {
809 reasonPhr = l.mid( pos + 1 );
810 statCode = l.mid( 9, pos - 9 ).toInt();
811 } else {
812 statCode = l.mid( 9 ).toInt();
813 reasonPhr = QString::null;
814 }
815 } else {
816 return FALSE;
817 }
818
819 return TRUE;
820}
821
822/*! \reimp
823*/
824QString QHttpResponseHeader::toString() const
825{
826 QString ret( "HTTP/%1.%2 %3 %4\r\n%5\r\n" );
827 return ret.arg( majVer ).arg ( minVer ).arg( statCode ).arg( reasonPhr ).arg( QHttpHeader::toString() );
828}
829
830/****************************************************
831 *
832 * QHttpRequestHeader
833 *
834 ****************************************************/
835
836/*!
837 \class QHttpRequestHeader qhttp.h
838 \brief The QHttpRequestHeader class contains request header information for
839\if defined(commercial)
840 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
841\endif
842 HTTP.
843\if defined(commercial_edition)
844 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
845\endif
846
847 \ingroup io
848 \module network
849
850 This class is used in the QHttp class to report the header
851 information if the client requests something from the server.
852
853 HTTP requests have a method which describes the request's action.
854 The most common requests are "GET" and "POST". In addition to the
855 request method the header also includes a request-URI to specify
856 the location for the method to use.
857
858 The method, request-URI and protocol-version can be set using a
859 constructor or later using setRequest(). The values can be
860 obtained using method(), path(), majorVersion() and
861 minorVersion().
862
863 This class is a QHttpHeader subclass so that class's functions,
864 e.g. \link QHttpHeader::setValue() setValue()\endlink, \link
865 QHttpHeader::value() value()\endlink, etc. are also available.
866
867 \sa QHttpResponseHeader QHttp
868
869 \important value() setValue()
870*/
871
872/*!
873 Constructs an empty HTTP request header.
874*/
875QHttpRequestHeader::QHttpRequestHeader()
876 : QHttpHeader()
877{
878 setValid( FALSE );
879}
880
881/*!
882 Constructs a HTTP request header for the method \a method, the
883 request-URI \a path and the protocol-version \a majorVer and \a minorVer.
884*/
885QHttpRequestHeader::QHttpRequestHeader( const QString& method, const QString& path, int majorVer, int minorVer )
886 : QHttpHeader(), m( method ), p( path ), majVer( majorVer ), minVer( minorVer )
887{
888}
889
890/*!
891 Constructs a copy of \a header.
892*/
893QHttpRequestHeader::QHttpRequestHeader( const QHttpRequestHeader& header )
894 : QHttpHeader( header ), m( header.m ), p( header.p ), majVer( header.majVer ), minVer( header.minVer )
895{
896}
897
898/*!
899 Constructs a HTTP request header from the string \a str. The \a
900 str should consist of one or more "\r\n" delimited lines; the first line
901 should be the request-line (format: method, space, request-URI, space
902 HTTP-version); each of the remaining lines should have the format key,
903 colon, space, value.
904*/
905QHttpRequestHeader::QHttpRequestHeader( const QString& str )
906 : QHttpHeader()
907{
908 parse( str );
909}
910
911/*!
912 This function sets the request method to \a method, the
913 request-URI to \a path and the protocol-version to \a majorVer and
914 \a minorVer.
915
916 \sa method() path() majorVersion() minorVersion()
917*/
918void QHttpRequestHeader::setRequest( const QString& method, const QString& path, int majorVer, int minorVer )
919{
920 setValid( TRUE );
921 m = method;
922 p = path;
923 majVer = majorVer;
924 minVer = minorVer;
925}
926
927/*!
928 Returns the method of the HTTP request header.
929
930 \sa path() majorVersion() minorVersion() setRequest()
931*/
932QString QHttpRequestHeader::method() const
933{
934 return m;
935}
936
937/*!
938 Returns the request-URI of the HTTP request header.
939
940 \sa method() majorVersion() minorVersion() setRequest()
941*/
942QString QHttpRequestHeader::path() const
943{
944 return p;
945}
946
947/*!
948 Returns the major protocol-version of the HTTP request header.
949
950 \sa minorVersion() method() path() setRequest()
951*/
952int QHttpRequestHeader::majorVersion() const
953{
954 return majVer;
955}
956
957/*!
958 Returns the minor protocol-version of the HTTP request header.
959
960 \sa majorVersion() method() path() setRequest()
961*/
962int QHttpRequestHeader::minorVersion() const
963{
964 return minVer;
965}
966
967/*! \reimp
968*/
969bool QHttpRequestHeader::parseLine( const QString& line, int number )
970{
971 if ( number != 0 )
972 return QHttpHeader::parseLine( line, number );
973
974 QStringList lst = QStringList::split( " ", line.simplifyWhiteSpace() );
975 if ( lst.count() > 0 ) {
976 m = lst[0];
977 if ( lst.count() > 1 ) {
978 p = lst[1];
979 if ( lst.count() > 2 ) {
980 QString v = lst[2];
981 if ( v.length() >= 8 && v.left( 5 ) == "HTTP/" &&
982 v[5].isDigit() && v[6] == '.' && v[7].isDigit() ) {
983 majVer = v[5].latin1() - '0';
984 minVer = v[7].latin1() - '0';
985 return TRUE;
986 }
987 }
988 }
989 }
990
991 return FALSE;
992}
993
994/*! \reimp
995*/
996QString QHttpRequestHeader::toString() const
997{
998 QString first( "%1 %2");
999 QString last(" HTTP/%3.%4\r\n%5\r\n" );
1000 return first.arg( m ).arg( p ) +
1001 last.arg( majVer ).arg( minVer ).arg( QHttpHeader::toString());
1002}
1003
1004
1005/****************************************************
1006 *
1007 * QHttp
1008 *
1009 ****************************************************/
1010/*!
1011 \class QHttp qhttp.h
1012 \brief The QHttp class provides an implementation of the HTTP protocol.
1013\if defined(commercial)
1014 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
1015\endif
1016
1017 \ingroup io
1018 \module network
1019
1020 This class provides two different interfaces: one is the
1021 QNetworkProtocol interface that allows you to use HTTP through the
1022 QUrlOperator abstraction. The other is a direct interface to HTTP
1023 that allows you to have more control over the requests and that
1024 allows you to access the response header fields.
1025
1026 Don't mix the two interfaces, since the behavior is not
1027 well-defined.
1028
1029 If you want to use QHttp with the QNetworkProtocol interface, you
1030 do not use it directly, but rather through a QUrlOperator, for
1031 example:
1032
1033 \code
1034 QUrlOperator op( "http://www.trolltech.com" );
1035 op.get( "index.html" );
1036 \endcode
1037
1038 This code will only work if the QHttp class is registered; to
1039 register the class, you must call qInitNetworkProtocols() before
1040 using a QUrlOperator with HTTP.
1041
1042 The QNetworkProtocol interface for HTTP only supports the
1043 operations operationGet() and operationPut(), i.e.
1044 QUrlOperator::get() and QUrlOperator::put(), if you use it with a
1045 QUrlOperator.
1046
1047 The rest of this descrption describes the direct interface to
1048 HTTP.
1049
1050 The class works asynchronously, so there are no blocking
1051 functions. If an operation cannot be executed immediately, the
1052 function will still return straight away and the operation will be
1053 scheduled for later execution. The results of scheduled operations
1054 are reported via signals. This approach depends on the event loop
1055 being in operation.
1056
1057 The operations that can be scheduled (they are called "requests"
1058 in the rest of the documentation) are the following: setHost(),
1059 get(), post(), head() and request().
1060
1061 All of these requests return a unique identifier that allows you
1062 to keep track of the request that is currently executed. When the
1063 execution of a request starts, the requestStarted() signal with
1064 the identifier is emitted and when the request is finished, the
1065 requestFinished() signal is emitted with the identifier and a bool
1066 that indicates if the request finished with an error.
1067
1068 To make an HTTP request you must set up suitable HTTP headers. The
1069 following example demonstrates, how to request the main HTML page
1070 from the Trolltech home page (i.e. the URL
1071 http://www.trolltech.com/index.html):
1072
1073 \code
1074 QHttpRequestHeader header( "GET", "/index.html" );
1075 header.setValue( "Host", "www.trolltech.com" );
1076 http->setHost( "www.trolltech.com" );
1077 http->request( header );
1078 \endcode
1079
1080 For the common HTTP requests \c GET, \c POST and \c HEAD, QHttp
1081 provides the convenience functions get(), post() and head(). They
1082 already use a reasonable header and if you don't have to set
1083 special header fields, they are easier to use. The above example
1084 can also be written as:
1085
1086 \code
1087 http->setHost( "www.trolltech.com" ); // id == 1
1088 http->get( "/index.html" ); // id == 2
1089 \endcode
1090
1091 For this example the following sequence of signals is emitted
1092 (with small variations, depending on network traffic, etc.):
1093
1094 \code
1095 requestStarted( 1 )
1096 requestFinished( 1, FALSE )
1097
1098 requestStarted( 2 )
1099 stateChanged( Connecting )
1100 stateChanged( Sending )
1101 dataSendProgress( 77, 77 )
1102 stateChanged( Reading )
1103 responseHeaderReceived( responseheader )
1104 dataReadProgress( 5388, 0 )
1105 readyRead( responseheader )
1106 dataReadProgress( 18300, 0 )
1107 readyRead( responseheader )
1108 stateChanged( Connected )
1109 requestFinished( 2, FALSE )
1110
1111 done( FALSE )
1112
1113 stateChanged( Closing )
1114 stateChanged( Unconnected )
1115 \endcode
1116
1117 The dataSendProgress() and dataReadProgress() signals in the above
1118 example are useful if you want to show a \link QProgressBar
1119 progressbar\endlink to inform the user about the progress of the
1120 download. The second argument is the total size of data. In
1121 certain cases it is not possible to know the total amount in
1122 advance, in which case the second argument is 0. (If you connect
1123 to a QProgressBar a total of 0 results in a busy indicator.)
1124
1125 When the response header is read, it is reported with the
1126 responseHeaderReceived() signal.
1127
1128 The readyRead() signal tells you that there is data ready to be
1129 read. The amount of data can then be queried with the
1130 bytesAvailable() function and it can be read with the readBlock()
1131 or readAll() functions.
1132
1133 If an error occurs during the execution of one of the commands in
1134 a sequence of commands, all the pending commands (i.e. scheduled,
1135 but not yet executed commands) are cleared and no signals are
1136 emitted for them.
1137
1138 For example, if you have the following sequence of reqeusts
1139
1140 \code
1141 http->setHost( "www.foo.bar" ); // id == 1
1142 http->get( "/index.html" ); // id == 2
1143 http->post( "register.html", data ); // id == 3
1144 \endcode
1145
1146 and the get() request fails because the host lookup fails, then
1147 the post() request is never executed and the signals would look
1148 like this:
1149
1150 \code
1151 requestStarted( 1 )
1152 requestFinished( 1, FALSE )
1153
1154 requestStarted( 2 )
1155 stateChanged( HostLookup )
1156 requestFinished( 2, TRUE )
1157
1158 done( TRUE )
1159
1160 stateChanged( Unconnected )
1161 \endcode
1162
1163 You can then get details about the error with the error() and
1164 errorString() functions. Note that only unexpected behaviour, like
1165 network failure is considered as an error. If the server response
1166 contains an error status, like a 404 response, this is reported as
1167 a normal response case. So you should always check the \link
1168 QHttpResponseHeader::statusCode() status code \endlink of the
1169 response header.
1170
1171 The functions currentId() and currentRequest() provide more
1172 information about the currently executing request.
1173
1174 The functions hasPendingRequests() and clearPendingRequests()
1175 allow you to query and clear the list of pending requests.
1176
1177 \sa \link network.html Qt Network Documentation \endlink QNetworkProtocol, QUrlOperator QFtp
1178*/
1179
1180/*!
1181 Constructs a QHttp object.
1182*/
1183QHttp::QHttp()
1184{
1185 init();
1186}
1187
1188/*!
1189 Constructs a QHttp object. The parameters \a parent and \a name
1190 are passed on to the QObject constructor.
1191*/
1192QHttp::QHttp( QObject* parent, const char* name )
1193{
1194 if ( parent )
1195 parent->insertChild( this );
1196 setName( name );
1197 init();
1198}
1199
1200/*!
1201 Constructs a QHttp object. Subsequent requests are done by
1202 connecting to the server \a hostname on port \a port. The
1203 parameters \a parent and \a name are passed on to the QObject
1204 constructor.
1205
1206 \sa setHost()
1207*/
1208QHttp::QHttp( const QString &hostname, Q_UINT16 port, QObject* parent, const char* name )
1209{
1210 if ( parent )
1211 parent->insertChild( this );
1212 setName( name );
1213 init();
1214
1215 d->hostname = hostname;
1216 d->port = port;
1217}
1218
1219void QHttp::init()
1220{
1221 bytesRead = 0;
1222 d = new QHttpPrivate;
1223 d->errorString = tr( "Unknown error" );
1224
1225 connect( &d->socket, SIGNAL( connected() ),
1226 this, SLOT( slotConnected() ) );
1227 connect( &d->socket, SIGNAL( connectionClosed() ),
1228 this, SLOT( slotClosed() ) );
1229 connect( &d->socket, SIGNAL( delayedCloseFinished() ),
1230 this, SLOT( slotClosed() ) );
1231 connect( &d->socket, SIGNAL( readyRead() ),
1232 this, SLOT( slotReadyRead() ) );
1233 connect( &d->socket, SIGNAL( error(int) ),
1234 this, SLOT( slotError(int) ) );
1235 connect( &d->socket, SIGNAL( bytesWritten(int) ),
1236 this, SLOT( slotBytesWritten(int) ) );
1237
1238 d->idleTimer = startTimer( 0 );
1239}
1240
1241/*!
1242 Destroys the QHttp object. If there is an open connection, it is
1243 closed.
1244*/
1245QHttp::~QHttp()
1246{
1247 abort();
1248 delete d;
1249}
1250
1251/*!
1252 \enum QHttp::State
1253
1254 This enum is used to specify the state the client is in:
1255
1256 \value Unconnected There is no connection to the host.
1257 \value HostLookup A host name lookup is in progress.
1258 \value Connecting An attempt to connect to the host is in progress.
1259 \value Sending The client is sending its request to the server.
1260 \value Reading The client's request has been sent and the client
1261 is reading the server's response.
1262 \value Connected The connection to the host is open, but the client is
1263 neither sending a request, nor waiting for a response.
1264 \value Closing The connection is closing down, but is not yet
1265 closed. (The state will be \c Unconnected when the connection is
1266 closed.)
1267
1268 \sa stateChanged() state()
1269*/
1270
1271/*! \enum QHttp::Error
1272
1273 This enum identifies the error that occurred.
1274
1275 \value NoError No error occurred.
1276 \value HostNotFound The host name lookup failed.
1277 \value ConnectionRefused The server refused the connection.
1278 \value UnexpectedClose The server closed the connection unexpectedly.
1279 \value InvalidResponseHeader The server sent an invalid response header.
1280 \value WrongContentLength The client could not read the content correctly
1281 because an error with respect to the content length occurred.
1282 \value Aborted The request was aborted with abort().
1283 \value UnknownError An error other than those specified above
1284 occurred.
1285
1286 \sa error()
1287*/
1288
1289/*!
1290 \fn void QHttp::stateChanged( int state )
1291
1292 This signal is emitted when the state of the QHttp object changes.
1293 The argument \a state is the new state of the connection; it is
1294 one of the \l State values.
1295
1296 This usually happens when a request is started, but it can also
1297 happen when the server closes the connection or when a call to
1298 closeConnection() succeeded.
1299
1300 \sa get() post() head() request() closeConnection() state() State
1301*/
1302
1303/*!
1304 \fn void QHttp::responseHeaderReceived( const QHttpResponseHeader& resp )
1305
1306 This signal is emitted when the HTTP header of a server response
1307 is available. The header is passed in \a resp.
1308
1309 \sa get() post() head() request() readyRead()
1310*/
1311
1312/*!
1313 \fn void QHttp::readyRead( const QHttpResponseHeader& resp )
1314
1315 This signal is emitted when there is new response data to read.
1316
1317 If you specified a device in the request where the data should be
1318 written to, then this signal is \e not emitted; instead the data
1319 is written directly to the device.
1320
1321 The response header is passed in \a resp.
1322
1323 You can read the data with the readAll() or readBlock() functions
1324
1325 This signal is useful if you want to process the data in chunks as
1326 soon as it becomes available. If you are only interested in the
1327 complete data, just connect to the requestFinished() signal and
1328 read the data then instead.
1329
1330 \sa get() post() request() readAll() readBlock() bytesAvailable()
1331*/
1332
1333/*!
1334 \fn void QHttp::dataSendProgress( int done, int total )
1335
1336 This signal is emitted when this object sends data to a HTTP
1337 server to inform it about the progress of the upload.
1338
1339 \a done is the amount of data that has already arrived and \a
1340 total is the total amount of data. It is possible that the total
1341 amount of data that should be transferred cannot be determined, in
1342 which case \a total is 0.(If you connect to a QProgressBar, the
1343 progress bar shows a busy indicator if the total is 0).
1344
1345 \warning \a done and \a total are not necessarily the size in
1346 bytes, since for large files these values might need to be
1347 "scaled" to avoid overflow.
1348
1349 \sa dataReadProgress() post() request() QProgressBar::setProgress()
1350*/
1351
1352/*!
1353 \fn void QHttp::dataReadProgress( int done, int total )
1354
1355 This signal is emitted when this object reads data from a HTTP
1356 server to indicate the current progress of the download.
1357
1358 \a done is the amount of data that has already arrived and \a
1359 total is the total amount of data. It is possible that the total
1360 amount of data that should be transferred cannot be determined, in
1361 which case \a total is 0.(If you connect to a QProgressBar, the
1362 progress bar shows a busy indicator if the total is 0).
1363
1364 \warning \a done and \a total are not necessarily the size in
1365 bytes, since for large files these values might need to be
1366 "scaled" to avoid overflow.
1367
1368 \sa dataSendProgress() get() post() request() QProgressBar::setProgress()
1369*/
1370
1371/*!
1372 \fn void QHttp::requestStarted( int id )
1373
1374 This signal is emitted when processing the request identified by
1375 \a id starts.
1376
1377 \sa requestFinished() done()
1378*/
1379
1380/*!
1381 \fn void QHttp::requestFinished( int id, bool error )
1382
1383 This signal is emitted when processing the request identified by
1384 \a id has finished. \a error is TRUE if an error occurred during
1385 the processing; otherwise \a error is FALSE.
1386
1387 \sa requestStarted() done() error() errorString()
1388*/
1389
1390/*!
1391 \fn void QHttp::done( bool error )
1392
1393 This signal is emitted when the last pending request has finished;
1394 (it is emitted after the last request's requestFinished() signal).
1395 \a error is TRUE if an error occurred during the processing;
1396 otherwise \a error is FALSE.
1397
1398 \sa requestFinished() error() errorString()
1399*/
1400
1401/*!
1402 Aborts the current request and deletes all scheduled requests.
1403
1404 For the current request, the requestFinished() signal with the \c
1405 error argument \c TRUE is emitted. For all other requests that are
1406 affected by the abort(), no signals are emitted.
1407
1408 Since this slot also deletes the scheduled requests, there are no
1409 requests left and the done() signal is emitted (with the \c error
1410 argument \c TRUE).
1411
1412 \sa clearPendingRequests()
1413*/
1414void QHttp::abort()
1415{
1416 QHttpRequest *r = d->pending.getFirst();
1417 if ( r == 0 )
1418 return;
1419
1420 finishedWithError( tr("Request aborted"), Aborted );
1421 clearPendingRequests();
1422 d->socket.clearPendingData();
1423 close();
1424}
1425
1426/*!
1427 Returns the number of bytes that can be read from the response
1428 content at the moment.
1429
1430 \sa get() post() request() readyRead() readBlock() readAll()
1431*/
1432Q_ULONG QHttp::bytesAvailable() const
1433{
1434#if defined(QHTTP_DEBUG)
1435 qDebug( "QHttp::bytesAvailable(): %d bytes", (int)d->rba.size() );
1436#endif
1437 return d->rba.size();
1438}
1439
1440/*!
1441 Reads \a maxlen bytes from the response content into \a data and
1442 returns the number of bytes read. Returns -1 if an error occurred.
1443
1444 \sa get() post() request() readyRead() bytesAvailable() readAll()
1445*/
1446Q_LONG QHttp::readBlock( char *data, Q_ULONG maxlen )
1447{
1448 if ( data == 0 && maxlen != 0 ) {
1449#if defined(QT_CHECK_NULL)
1450 qWarning( "QHttp::readBlock: Null pointer error" );
1451#endif
1452 return -1;
1453 }
1454 if ( maxlen >= d->rba.size() )
1455 maxlen = d->rba.size();
1456 d->rba.consumeBytes( maxlen, data );
1457
1458 d->bytesDone += maxlen;
1459#if defined(QHTTP_DEBUG)
1460 qDebug( "QHttp::readBlock(): read %d bytes (%d bytes done)", (int)maxlen, d->bytesDone );
1461#endif
1462 return maxlen;
1463}
1464
1465/*!
1466 Reads all the bytes from the response content and returns them.
1467
1468 \sa get() post() request() readyRead() bytesAvailable() readBlock()
1469*/
1470QByteArray QHttp::readAll()
1471{
1472 Q_ULONG avail = bytesAvailable();
1473 QByteArray tmp( avail );
1474 Q_LONG read = readBlock( tmp.data(), avail );
1475 tmp.resize( read );
1476 return tmp;
1477}
1478
1479/*!
1480 Returns the identifier of the HTTP request being executed or 0 if
1481 there is no request being executed (i.e. they've all finished).
1482
1483 \sa currentRequest()
1484*/
1485int QHttp::currentId() const
1486{
1487 QHttpRequest *r = d->pending.getFirst();
1488 if ( r == 0 )
1489 return 0;
1490 return r->id;
1491}
1492
1493/*!
1494 Returns the request header of the HTTP request being executed. If
1495 the request is one issued by setHost() or closeConnection(), it
1496 returns an invalid request header, i.e.
1497 QHttpRequestHeader::isValid() returns FALSE.
1498
1499 \sa currentId()
1500*/
1501QHttpRequestHeader QHttp::currentRequest() const
1502{
1503 QHttpRequest *r = d->pending.getFirst();
1504 if ( r != 0 && r->hasRequestHeader() )
1505 return r->requestHeader();
1506 return QHttpRequestHeader();
1507}
1508
1509/*!
1510 Returns the QIODevice pointer that is used as the data source of the HTTP
1511 request being executed. If there is no current request or if the request
1512 does not use an IO device as the data source, this function returns 0.
1513
1514 This function can be used to delete the QIODevice in the slot connected to
1515 the requestFinished() signal.
1516
1517 \sa currentDestinationDevice() post() request()
1518*/
1519QIODevice* QHttp::currentSourceDevice() const
1520{
1521 QHttpRequest *r = d->pending.getFirst();
1522 if ( !r )
1523 return 0;
1524 return r->sourceDevice();
1525}
1526
1527/*!
1528 Returns the QIODevice pointer that is used as to store the data of the HTTP
1529 request being executed. If there is no current request or if the request
1530 does not store the data to an IO device, this function returns 0.
1531
1532 This function can be used to delete the QIODevice in the slot connected to
1533 the requestFinished() signal.
1534
1535 \sa currentDestinationDevice() get() post() request()
1536*/
1537QIODevice* QHttp::currentDestinationDevice() const
1538{
1539 QHttpRequest *r = d->pending.getFirst();
1540 if ( !r )
1541 return 0;
1542 return r->destinationDevice();
1543}
1544
1545/*!
1546 Returns TRUE if there are any requests scheduled that have not yet
1547 been executed; otherwise returns FALSE.
1548
1549 The request that is being executed is \e not considered as a
1550 scheduled request.
1551
1552 \sa clearPendingRequests() currentId() currentRequest()
1553*/
1554bool QHttp::hasPendingRequests() const
1555{
1556 return d->pending.count() > 1;
1557}
1558
1559/*!
1560 Deletes all pending requests from the list of scheduled requests.
1561 This does not affect the request that is being executed. If
1562 you want to stop this this as well, use abort().
1563
1564 \sa hasPendingRequests() abort()
1565*/
1566void QHttp::clearPendingRequests()
1567{
1568 QHttpRequest *r = 0;
1569 if ( d->pending.count() > 0 )
1570 r = d->pending.take( 0 );
1571 d->pending.clear();
1572 if ( r )
1573 d->pending.append( r );
1574}
1575
1576/*!
1577 Sets the HTTP server that is used for requests to \a hostname on
1578 port \a port.
1579
1580 The function does not block and returns immediately. The request
1581 is scheduled, and its execution is performed asynchronously. The
1582 function returns a unique identifier which is passed by
1583 requestStarted() and requestFinished().
1584
1585 When the request is started the requestStarted() signal is
1586 emitted. When it is finished the requestFinished() signal is
1587 emitted.
1588
1589 \sa get() post() head() request() requestStarted() requestFinished() done()
1590*/
1591int QHttp::setHost(const QString &hostname, Q_UINT16 port )
1592{
1593 return addRequest( new QHttpSetHostRequest( hostname, port ) );
1594}
1595
1596/*!
1597 Sends a get request for \a path to the server set by setHost() or
1598 as specified in the constructor.
1599
1600 \a path must be an absolute path like \c /index.html or an
1601 absolute URI like \c http://www.trolltech.com/index.html.
1602
1603 If the IO device \a to is 0 the readyRead() signal is emitted
1604 every time new content data is available to read.
1605
1606 If the IO device \a to is not 0, the content data of the response
1607 is written directly to the device. Make sure that the \a to
1608 pointer is valid for the duration of the operation (it is safe to
1609 delete it when the requestFinished() signal is emitted).
1610
1611 The function does not block and returns immediately. The request
1612 is scheduled, and its execution is performed asynchronously. The
1613 function returns a unique identifier which is passed by
1614 requestStarted() and requestFinished().
1615
1616 When the request is started the requestStarted() signal is
1617 emitted. When it is finished the requestFinished() signal is
1618 emitted.
1619
1620 \sa setHost() post() head() request() requestStarted() requestFinished() done()
1621*/
1622int QHttp::get( const QString& path, QIODevice* to )
1623{
1624 QHttpRequestHeader header( "GET", path );
1625 header.setValue( "Connection", "Keep-Alive" );
1626 return addRequest( new QHttpPGHRequest( header, (QIODevice*)0, to ) );
1627}
1628
1629/*!
1630 Sends a post request for \a path to the server set by setHost() or
1631 as specified in the constructor.
1632
1633 \a path must be an absolute path like \c /index.html or an
1634 absolute URI like \c http://www.trolltech.com/index.html.
1635
1636 The incoming data comes via the \a data IO device.
1637
1638 If the IO device \a to is 0 the readyRead() signal is emitted
1639 every time new content data is available to read.
1640
1641 If the IO device \a to is not 0, the content data of the response
1642 is written directly to the device. Make sure that the \a to
1643 pointer is valid for the duration of the operation (it is safe to
1644 delete it when the requestFinished() signal is emitted).
1645
1646 The function does not block and returns immediately. The request
1647 is scheduled, and its execution is performed asynchronously. The
1648 function returns a unique identifier which is passed by
1649 requestStarted() and requestFinished().
1650
1651 When the request is started the requestStarted() signal is
1652 emitted. When it is finished the requestFinished() signal is
1653 emitted.
1654
1655 \sa setHost() get() head() request() requestStarted() requestFinished() done()
1656*/
1657int QHttp::post( const QString& path, QIODevice* data, QIODevice* to )
1658{
1659 QHttpRequestHeader header( "POST", path );
1660 header.setValue( "Connection", "Keep-Alive" );
1661 return addRequest( new QHttpPGHRequest( header, data, to ) );
1662}
1663
1664/*!
1665 \overload
1666
1667 \a data is used as the content data of the HTTP request.
1668*/
1669int QHttp::post( const QString& path, const QByteArray& data, QIODevice* to )
1670{
1671 QHttpRequestHeader header( "POST", path );
1672 header.setValue( "Connection", "Keep-Alive" );
1673 return addRequest( new QHttpPGHRequest( header, new QByteArray(data), to ) );
1674}
1675
1676/*!
1677 Sends a header request for \a path to the server set by setHost()
1678 or as specified in the constructor.
1679
1680 \a path must be an absolute path like \c /index.html or an
1681 absolute URI like \c http://www.trolltech.com/index.html.
1682
1683 The function does not block and returns immediately. The request
1684 is scheduled, and its execution is performed asynchronously. The
1685 function returns a unique identifier which is passed by
1686 requestStarted() and requestFinished().
1687
1688 When the request is started the requestStarted() signal is
1689 emitted. When it is finished the requestFinished() signal is
1690 emitted.
1691
1692 \sa setHost() get() post() request() requestStarted() requestFinished() done()
1693*/
1694int QHttp::head( const QString& path )
1695{
1696 QHttpRequestHeader header( "HEAD", path );
1697 header.setValue( "Connection", "Keep-Alive" );
1698 return addRequest( new QHttpPGHRequest( header, (QIODevice*)0, 0 ) );
1699}
1700
1701/*!
1702 Sends a request to the server set by setHost() or as specified in
1703 the constructor. Uses the \a header as the HTTP request header.
1704 You are responsible for setting up a header that is appropriate
1705 for your request.
1706
1707 The incoming data comes via the \a data IO device.
1708
1709 If the IO device \a to is 0 the readyRead() signal is emitted
1710 every time new content data is available to read.
1711
1712 If the IO device \a to is not 0, the content data of the response
1713 is written directly to the device. Make sure that the \a to
1714 pointer is valid for the duration of the operation (it is safe to
1715 delete it when the requestFinished() signal is emitted).
1716
1717 The function does not block and returns immediately. The request
1718 is scheduled, and its execution is performed asynchronously. The
1719 function returns a unique identifier which is passed by
1720 requestStarted() and requestFinished().
1721
1722 When the request is started the requestStarted() signal is
1723 emitted. When it is finished the requestFinished() signal is
1724 emitted.
1725
1726 \sa setHost() get() post() head() requestStarted() requestFinished() done()
1727*/
1728int QHttp::request( const QHttpRequestHeader &header, QIODevice *data, QIODevice *to )
1729{
1730 return addRequest( new QHttpNormalRequest( header, data, to ) );
1731}
1732
1733/*!
1734 \overload
1735
1736 \a data is used as the content data of the HTTP request.
1737*/
1738int QHttp::request( const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to )
1739{
1740 return addRequest( new QHttpNormalRequest( header, new QByteArray(data), to ) );
1741}
1742
1743/*!
1744 Closes the connection; this is useful if you have a keep-alive
1745 connection and want to close it.
1746
1747 For the requests issued with get(), post() and head(), QHttp sets
1748 the connection to be keep-alive. You can also do this using the
1749 header you pass to the request() function. QHttp only closes the
1750 connection to the HTTP server if the response header requires it
1751 to do so.
1752
1753 The function does not block and returns immediately. The request
1754 is scheduled, and its execution is performed asynchronously. The
1755 function returns a unique identifier which is passed by
1756 requestStarted() and requestFinished().
1757
1758 When the request is started the requestStarted() signal is
1759 emitted. When it is finished the requestFinished() signal is
1760 emitted.
1761
1762 If you want to close the connection immediately, you have to use
1763 abort() instead.
1764
1765 \sa stateChanged() abort() requestStarted() requestFinished() done()
1766*/
1767int QHttp::closeConnection()
1768{
1769 return addRequest( new QHttpCloseRequest() );
1770}
1771
1772int QHttp::addRequest( QHttpRequest *req )
1773{
1774 d->pending.append( req );
1775
1776 if ( d->pending.count() == 1 )
1777 // don't emit the requestStarted() signal before the id is returned
1778 QTimer::singleShot( 0, this, SLOT(startNextRequest()) );
1779
1780 return req->id;
1781}
1782
1783void QHttp::startNextRequest()
1784{
1785 QHttpRequest *r = d->pending.getFirst();
1786 if ( r == 0 )
1787 return;
1788
1789 d->error = NoError;
1790 d->errorString = tr( "Unknown error" );
1791
1792 if ( bytesAvailable() )
1793 readAll(); // clear the data
1794 emit requestStarted( r->id );
1795 r->start( this );
1796}
1797
1798void QHttp::sendRequest()
1799{
1800 if ( d->hostname.isNull() ) {
1801 finishedWithError( tr("No server set to connect to"), UnknownError );
1802 return;
1803 }
1804
1805 killIdleTimer();
1806
1807 // Do we need to setup a new connection or can we reuse an
1808 // existing one ?
1809 if ( d->socket.peerName() != d->hostname || d->socket.state() != QSocket::Connection ) {
1810 setState( QHttp::Connecting );
1811 d->socket.connectToHost( d->hostname, d->port );
1812 } else {
1813 slotConnected();
1814 }
1815
1816}
1817
1818void QHttp::finishedWithSuccess()
1819{
1820 QHttpRequest *r = d->pending.getFirst();
1821 if ( r == 0 )
1822 return;
1823
1824 emit requestFinished( r->id, FALSE );
1825 d->pending.removeFirst();
1826 if ( d->pending.isEmpty() ) {
1827 emit done( FALSE );
1828 } else {
1829 startNextRequest();
1830 }
1831}
1832
1833void QHttp::finishedWithError( const QString& detail, int errorCode )
1834{
1835 QHttpRequest *r = d->pending.getFirst();
1836 if ( r == 0 )
1837 return;
1838
1839 d->error = (Error)errorCode;
1840 d->errorString = detail;
1841 emit requestFinished( r->id, TRUE );
1842
1843 d->pending.clear();
1844 emit done( TRUE );
1845}
1846
1847void QHttp::slotClosed()
1848{
1849 if ( d->state == Closing )
1850 return;
1851
1852 if ( d->state == Reading ) {
1853 if ( d->response.hasKey( "content-length" ) ) {
1854 // We got Content-Length, so did we get all bytes?
1855 if ( d->bytesDone+bytesAvailable() != d->response.contentLength() ) {
1856 finishedWithError( tr("Wrong content length"), WrongContentLength );
1857 }
1858 }
1859 } else if ( d->state == Connecting || d->state == Sending ) {
1860 finishedWithError( tr("Server closed connection unexpectedly"), UnexpectedClose );
1861 }
1862
1863 d->postDevice = 0;
1864 setState( Closing );
1865 d->idleTimer = startTimer( 0 );
1866}
1867
1868void QHttp::slotConnected()
1869{
1870 if ( d->state != Sending ) {
1871 d->bytesDone = 0;
1872 setState( Sending );
1873 }
1874
1875 QString str = d->header.toString();
1876 d->bytesTotal = str.length();
1877 d->socket.writeBlock( str.latin1(), d->bytesTotal );
1878#if defined(QHTTP_DEBUG)
1879 qDebug( "QHttp: write request header:\n---{\n%s}---", str.latin1() );
1880#endif
1881
1882 if ( d->postDevice ) {
1883 d->bytesTotal += d->postDevice->size();
1884 } else {
1885 d->bytesTotal += d->buffer.size();
1886 d->socket.writeBlock( d->buffer.data(), d->buffer.size() );
1887 d->buffer = QByteArray(); // save memory
1888 }
1889}
1890
1891void QHttp::slotError( int err )
1892{
1893 d->postDevice = 0;
1894
1895 if ( d->state == Connecting || d->state == Reading || d->state == Sending ) {
1896 switch ( err ) {
1897 case QSocket::ErrConnectionRefused:
1898 finishedWithError( tr("Connection refused"), ConnectionRefused );
1899 break;
1900 case QSocket::ErrHostNotFound:
1901 finishedWithError( tr("Host %1 not found").arg(d->socket.peerName()), HostNotFound );
1902 break;
1903 default:
1904 finishedWithError( tr("HTTP request failed"), UnknownError );
1905 break;
1906 }
1907 }
1908
1909 close();
1910}
1911
1912void QHttp::slotBytesWritten( int written )
1913{
1914 d->bytesDone += written;
1915 emit dataSendProgress( d->bytesDone, d->bytesTotal );
1916
1917 if ( !d->postDevice )
1918 return;
1919
1920 if ( d->socket.bytesToWrite() == 0 ) {
1921 int max = QMIN( 4096, d->postDevice->size() - d->postDevice->at() );
1922 QByteArray arr( max );
1923
1924 int n = d->postDevice->readBlock( arr.data(), max );
1925 if ( n != max ) {
1926 qWarning("Could not read enough bytes from the device");
1927 close();
1928 return;
1929 }
1930 if ( d->postDevice->atEnd() ) {
1931 d->postDevice = 0;
1932 }
1933
1934 d->socket.writeBlock( arr.data(), max );
1935 }
1936}
1937
1938void QHttp::slotReadyRead()
1939{
1940 if ( d->state != Reading ) {
1941 setState( Reading );
1942 d->buffer = QByteArray();
1943 d->readHeader = TRUE;
1944 d->headerStr = "";
1945 d->bytesDone = 0;
1946 d->chunkedSize = -1;
1947 }
1948
1949 while ( d->readHeader ) {
1950 bool end = FALSE;
1951 QString tmp;
1952 while ( !end && d->socket.canReadLine() ) {
1953 tmp = d->socket.readLine();
1954 if ( tmp == "\r\n" || tmp == "\n" )
1955 end = TRUE;
1956 else
1957 d->headerStr += tmp;
1958 }
1959
1960 if ( !end )
1961 return;
1962
1963#if defined(QHTTP_DEBUG)
1964 qDebug( "QHttp: read response header:\n---{\n%s}---", d->headerStr.latin1() );
1965#endif
1966 d->response = QHttpResponseHeader( d->headerStr );
1967 d->headerStr = "";
1968#if defined(QHTTP_DEBUG)
1969 qDebug( "QHttp: read response header:\n---{\n%s}---", d->response.toString().latin1() );
1970#endif
1971 // Check header
1972 if ( !d->response.isValid() ) {
1973 finishedWithError( tr("Invalid HTTP response header"), InvalidResponseHeader );
1974 close();
1975 return;
1976 }
1977
1978 // The 100-continue header is ignored, because when using the
1979 // POST method, we send both the request header and data in
1980 // one chunk.
1981 if (d->response.statusCode() != 100) {
1982 d->readHeader = FALSE;
1983 if ( d->response.hasKey( "transfer-encoding" ) &&
1984 d->response.value( "transfer-encoding" ).lower().contains( "chunked" ) )
1985 d->chunkedSize = 0;
1986
1987 emit responseHeaderReceived( d->response );
1988 }
1989 }
1990
1991 if ( !d->readHeader ) {
1992 bool everythingRead = FALSE;
1993
1994 if ( currentRequest().method() == "HEAD" ) {
1995 everythingRead = TRUE;
1996 } else {
1997 Q_ULONG n = d->socket.bytesAvailable();
1998 QByteArray *arr = 0;
1999 if ( d->chunkedSize != -1 ) {
2000 // transfer-encoding is chunked
2001 for ( ;; ) {
2002 // get chunk size
2003 if ( d->chunkedSize == 0 ) {
2004 if ( !d->socket.canReadLine() )
2005 break;
2006 QString sizeString = d->socket.readLine();
2007 int tPos = sizeString.find( ';' );
2008 if ( tPos != -1 )
2009 sizeString.truncate( tPos );
2010 bool ok;
2011 d->chunkedSize = sizeString.toInt( &ok, 16 );
2012 if ( !ok ) {
2013 finishedWithError( tr("Invalid HTTP chunked body"), WrongContentLength );
2014 close();
2015 return;
2016 }
2017 if ( d->chunkedSize == 0 ) // last-chunk
2018 d->chunkedSize = -2;
2019 }
2020
2021 // read trailer
2022 while ( d->chunkedSize == -2 && d->socket.canReadLine() ) {
2023 QString read = d->socket.readLine();
2024 if ( read == "\r\n" || read == "\n" )
2025 d->chunkedSize = -1;
2026 }
2027 if ( d->chunkedSize == -1 ) {
2028 everythingRead = TRUE;
2029 break;
2030 }
2031
2032 // make sure that you can read the terminating CRLF,
2033 // otherwise wait until next time...
2034 n = d->socket.bytesAvailable();
2035 if ( n == 0 )
2036 break;
2037 if ( (Q_LONG)n == d->chunkedSize || (Q_LONG)n == d->chunkedSize+1 ) {
2038 n = d->chunkedSize - 1;
2039 if ( n == 0 )
2040 break;
2041 }
2042
2043 // read data
2044 uint toRead = QMIN( (Q_LONG)n, d->chunkedSize );
2045 if ( !arr )
2046 arr = new QByteArray( 0 );
2047 uint oldArrSize = arr->size();
2048 arr->resize( oldArrSize + toRead );
2049 Q_LONG read = d->socket.readBlock( arr->data()+oldArrSize, toRead );
2050 arr->resize( oldArrSize + read );
2051
2052 d->chunkedSize -= read;
2053
2054 if ( d->chunkedSize == 0 && n - read >= 2 ) {
2055 // read terminating CRLF
2056 char tmp[2];
2057 d->socket.readBlock( tmp, 2 );
2058 if ( tmp[0] != '\r' || tmp[1] != '\n' ) {
2059 finishedWithError( tr("Invalid HTTP chunked body"), WrongContentLength );
2060 close();
2061 return;
2062 }
2063 }
2064 }
2065 } else if ( d->response.hasContentLength() ) {
2066 n = QMIN( d->response.contentLength() - d->bytesDone, n );
2067 if ( n > 0 ) {
2068 arr = new QByteArray( n );
2069 Q_LONG read = d->socket.readBlock( arr->data(), n );
2070 arr->resize( read );
2071 }
2072 if ( d->bytesDone + bytesAvailable() + n == d->response.contentLength() )
2073 everythingRead = TRUE;
2074 } else if ( n > 0 ) {
2075 // workaround for VC++ bug
2076 QByteArray temp = d->socket.readAll();
2077 arr = new QByteArray( temp );
2078 }
2079
2080 if ( arr ) {
2081 n = arr->size();
2082 if ( d->toDevice ) {
2083 d->toDevice->writeBlock( arr->data(), n );
2084 delete arr;
2085 d->bytesDone += n;
2086#if defined(QHTTP_DEBUG)
2087 qDebug( "QHttp::slotReadyRead(): read %ld bytes (%d bytes done)", n, d->bytesDone );
2088#endif
2089 if ( d->response.hasContentLength() )
2090 emit dataReadProgress( d->bytesDone, d->response.contentLength() );
2091 else
2092 emit dataReadProgress( d->bytesDone, 0 );
2093 } else {
2094 d->rba.append( arr );
2095#if defined(QHTTP_DEBUG)
2096 qDebug( "QHttp::slotReadyRead(): read %ld bytes (%ld bytes done)", n, d->bytesDone + bytesAvailable() );
2097#endif
2098 if ( d->response.hasContentLength() )
2099 emit dataReadProgress( d->bytesDone + bytesAvailable(), d->response.contentLength() );
2100 else
2101 emit dataReadProgress( d->bytesDone + bytesAvailable(), 0 );
2102 emit readyRead( d->response );
2103 }
2104 }
2105 }
2106
2107 if ( everythingRead ) {
2108 // Handle "Connection: close"
2109 if ( d->response.value("connection").lower() == "close" ) {
2110 close();
2111 } else {
2112 setState( Connected );
2113 // Start a timer, so that we emit the keep alive signal
2114 // "after" this method returned.
2115 d->idleTimer = startTimer( 0 );
2116 }
2117 }
2118 }
2119}
2120
2121/*!
2122 Returns the current state of the object. When the state changes,
2123 the stateChanged() signal is emitted.
2124
2125 \sa State stateChanged()
2126*/
2127QHttp::State QHttp::state() const
2128{
2129 return d->state;
2130}
2131
2132/*!
2133 Returns the last error that occurred. This is useful to find out
2134 what happened when receiving a requestFinished() or a done()
2135 signal with the \c error argument \c TRUE.
2136
2137 If you start a new request, the error status is reset to \c NoError.
2138*/
2139QHttp::Error QHttp::error() const
2140{
2141 return d->error;
2142}
2143
2144/*!
2145 Returns a human-readable description of the last error that
2146 occurred. This is useful to present a error message to the user
2147 when receiving a requestFinished() or a done() signal with the \c
2148 error argument \c TRUE.
2149*/
2150QString QHttp::errorString() const
2151{
2152 return d->errorString;
2153}
2154
2155/*! \reimp
2156*/
2157void QHttp::timerEvent( QTimerEvent *e )
2158{
2159 if ( e->timerId() == d->idleTimer ) {
2160 killTimer( d->idleTimer );
2161 d->idleTimer = 0;
2162
2163 if ( d->state == Connected ) {
2164 finishedWithSuccess();
2165 } else if ( d->state != Unconnected ) {
2166 setState( Unconnected );
2167 finishedWithSuccess();
2168 }
2169 } else {
2170 QObject::timerEvent( e );
2171 }
2172}
2173
2174void QHttp::killIdleTimer()
2175{
2176 killTimer( d->idleTimer );
2177 d->idleTimer = 0;
2178}
2179
2180void QHttp::setState( int s )
2181{
2182#if defined(QHTTP_DEBUG)
2183 qDebug( "QHttp state changed %d -> %d", d->state, s );
2184#endif
2185 d->state = (State)s;
2186 emit stateChanged( s );
2187}
2188
2189void QHttp::close()
2190{
2191 // If no connection is open -> ignore
2192 if ( d->state == Closing || d->state == Unconnected )
2193 return;
2194
2195 d->postDevice = 0;
2196 setState( Closing );
2197
2198 // Already closed ?
2199 if ( !d->socket.isOpen() ) {
2200 d->idleTimer = startTimer( 0 );
2201 } else {
2202 // Close now.
2203 d->socket.close();
2204
2205 // Did close succeed immediately ?
2206 if ( d->socket.state() == QSocket::Idle ) {
2207 // Prepare to emit the requestFinished() signal.
2208 d->idleTimer = startTimer( 0 );
2209 }
2210 }
2211}
2212
2213/**********************************************************************
2214 *
2215 * QHttp implementation of the QNetworkProtocol interface
2216 *
2217 *********************************************************************/
2218/*! \reimp
2219*/
2220int QHttp::supportedOperations() const
2221{
2222 return OpGet | OpPut;
2223}
2224
2225/*! \reimp
2226*/
2227void QHttp::operationGet( QNetworkOperation *op )
2228{
2229 connect( this, SIGNAL(readyRead(const QHttpResponseHeader&)),
2230 this, SLOT(clientReply(const QHttpResponseHeader&)) );
2231 connect( this, SIGNAL(done(bool)),
2232 this, SLOT(clientDone(bool)) );
2233 connect( this, SIGNAL(stateChanged(int)),
2234 this, SLOT(clientStateChanged(int)) );
2235
2236 bytesRead = 0;
2237 op->setState( StInProgress );
2238 QUrl u( operationInProgress()->arg( 0 ) );
2239 QHttpRequestHeader header( "GET", u.encodedPathAndQuery(), 1, 0 );
2240 header.setValue( "Host", u.host() );
2241 setHost( u.host(), u.port() != -1 ? u.port() : 80 );
2242 request( header );
2243}
2244
2245/*! \reimp
2246*/
2247void QHttp::operationPut( QNetworkOperation *op )
2248{
2249 connect( this, SIGNAL(readyRead(const QHttpResponseHeader&)),
2250 this, SLOT(clientReply(const QHttpResponseHeader&)) );
2251 connect( this, SIGNAL(done(bool)),
2252 this, SLOT(clientDone(bool)) );
2253 connect( this, SIGNAL(stateChanged(int)),
2254 this, SLOT(clientStateChanged(int)) );
2255
2256 bytesRead = 0;
2257 op->setState( StInProgress );
2258 QUrl u( operationInProgress()->arg( 0 ) );
2259 QHttpRequestHeader header( "POST", u.encodedPathAndQuery(), 1, 0 );
2260 header.setValue( "Host", u.host() );
2261 setHost( u.host(), u.port() != -1 ? u.port() : 80 );
2262 request( header, op->rawArg(1) );
2263}
2264
2265void QHttp::clientReply( const QHttpResponseHeader &rep )
2266{
2267 QNetworkOperation *op = operationInProgress();
2268 if ( op ) {
2269 if ( rep.statusCode() >= 400 && rep.statusCode() < 600 ) {
2270 op->setState( StFailed );
2271 op->setProtocolDetail(
2272 QString("%1 %2").arg(rep.statusCode()).arg(rep.reasonPhrase())
2273 );
2274 switch ( rep.statusCode() ) {
2275 case 401:
2276 case 403:
2277 case 405:
2278 op->setErrorCode( ErrPermissionDenied );
2279 break;
2280 case 404:
2281 op->setErrorCode(ErrFileNotExisting );
2282 break;
2283 default:
2284 if ( op->operation() == OpGet )
2285 op->setErrorCode( ErrGet );
2286 else
2287 op->setErrorCode( ErrPut );
2288 break;
2289 }
2290 }
2291 // ### In cases of an error, should we still emit the data() signals?
2292 if ( op->operation() == OpGet && bytesAvailable() > 0 ) {
2293 QByteArray ba = readAll();
2294 emit data( ba, op );
2295 bytesRead += ba.size();
2296 if ( rep.hasContentLength() ) {
2297 emit dataTransferProgress( bytesRead, rep.contentLength(), op );
2298 }
2299 }
2300 }
2301}
2302
2303void QHttp::clientDone( bool err )
2304{
2305 disconnect( this, SIGNAL(readyRead(const QHttpResponseHeader&)),
2306 this, SLOT(clientReply(const QHttpResponseHeader&)) );
2307 disconnect( this, SIGNAL(done(bool)),
2308 this, SLOT(clientDone(bool)) );
2309 disconnect( this, SIGNAL(stateChanged(int)),
2310 this, SLOT(clientStateChanged(int)) );
2311
2312 if ( err ) {
2313 QNetworkOperation *op = operationInProgress();
2314 if ( op ) {
2315 op->setState( QNetworkProtocol::StFailed );
2316 op->setProtocolDetail( errorString() );
2317 switch ( error() ) {
2318 case ConnectionRefused:
2319 op->setErrorCode( ErrHostNotFound );
2320 break;
2321 case HostNotFound:
2322 op->setErrorCode( ErrHostNotFound );
2323 break;
2324 default:
2325 if ( op->operation() == OpGet )
2326 op->setErrorCode( ErrGet );
2327 else
2328 op->setErrorCode( ErrPut );
2329 break;
2330 }
2331 emit finished( op );
2332 }
2333 } else {
2334 QNetworkOperation *op = operationInProgress();
2335 if ( op ) {
2336 if ( op->state() != StFailed ) {
2337 op->setState( QNetworkProtocol::StDone );
2338 op->setErrorCode( QNetworkProtocol::NoError );
2339 }
2340 emit finished( op );
2341 }
2342 }
2343
2344}
2345
2346void QHttp::clientStateChanged( int state )
2347{
2348 if ( url() ) {
2349 switch ( (State)state ) {
2350 case Connecting:
2351 emit connectionStateChanged( ConHostFound, tr( "Host %1 found" ).arg( url()->host() ) );
2352 break;
2353 case Sending:
2354 emit connectionStateChanged( ConConnected, tr( "Connected to host %1" ).arg( url()->host() ) );
2355 break;
2356 case Unconnected:
2357 emit connectionStateChanged( ConClosed, tr( "Connection to %1 closed" ).arg( url()->host() ) );
2358 break;
2359 default:
2360 break;
2361 }
2362 } else {
2363 switch ( (State)state ) {
2364 case Connecting:
2365 emit connectionStateChanged( ConHostFound, tr( "Host found" ) );
2366 break;
2367 case Sending:
2368 emit connectionStateChanged( ConConnected, tr( "Connected to host" ) );
2369 break;
2370 case Unconnected:
2371 emit connectionStateChanged( ConClosed, tr( "Connection closed" ) );
2372 break;
2373 default:
2374 break;
2375 }
2376 }
2377}
2378
2379#endif
Note: See TracBrowser for help on using the repository browser.