source: trunk/src/qt3support/network/q3http.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

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

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