|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt3Support module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
61 QT_BEGIN_NAMESPACE |
|
62 |
|
63 //#define Q3HTTP_DEBUG |
|
64 |
|
65 class Q3HttpPrivate |
|
66 { |
|
67 public: |
|
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 |
|
111 class Q3HttpRequest |
|
112 { |
|
113 public: |
|
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 |
|
130 private: |
|
131 static int idCounter; |
|
132 }; |
|
133 |
|
134 int Q3HttpRequest::idCounter = 0; |
|
135 |
|
136 bool Q3HttpRequest::hasRequestHeader() |
|
137 { |
|
138 return false; |
|
139 } |
|
140 |
|
141 Q3HttpRequestHeader Q3HttpRequest::requestHeader() |
|
142 { |
|
143 return Q3HttpRequestHeader(); |
|
144 } |
|
145 |
|
146 /**************************************************** |
|
147 * |
|
148 * Q3HttpNormalRequest |
|
149 * |
|
150 ****************************************************/ |
|
151 |
|
152 class Q3HttpNormalRequest : public Q3HttpRequest |
|
153 { |
|
154 public: |
|
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 |
|
182 protected: |
|
183 Q3HttpRequestHeader header; |
|
184 |
|
185 private: |
|
186 union { |
|
187 QByteArray *ba; |
|
188 QIODevice *dev; |
|
189 } data; |
|
190 bool is_ba; |
|
191 QIODevice *to; |
|
192 }; |
|
193 |
|
194 void 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 |
|
224 bool Q3HttpNormalRequest::hasRequestHeader() |
|
225 { |
|
226 return true; |
|
227 } |
|
228 |
|
229 Q3HttpRequestHeader Q3HttpNormalRequest::requestHeader() |
|
230 { |
|
231 return header; |
|
232 } |
|
233 |
|
234 QIODevice* Q3HttpNormalRequest::sourceDevice() |
|
235 { |
|
236 if ( is_ba ) |
|
237 return 0; |
|
238 return data.dev; |
|
239 } |
|
240 |
|
241 QIODevice* 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 |
|
256 class Q3HttpPGHRequest : public Q3HttpNormalRequest |
|
257 { |
|
258 public: |
|
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 |
|
273 void 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 |
|
285 class Q3HttpSetHostRequest : public Q3HttpRequest |
|
286 { |
|
287 public: |
|
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 |
|
299 private: |
|
300 QString hostname; |
|
301 Q_UINT16 port; |
|
302 }; |
|
303 |
|
304 void 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 |
|
317 class Q3HttpCloseRequest : public Q3HttpRequest |
|
318 { |
|
319 public: |
|
320 Q3HttpCloseRequest() |
|
321 { } |
|
322 void start( Q3Http * ); |
|
323 |
|
324 QIODevice* sourceDevice() |
|
325 { return 0; } |
|
326 QIODevice* destinationDevice() |
|
327 { return 0; } |
|
328 }; |
|
329 |
|
330 void 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 */ |
|
389 Q3HttpHeader::Q3HttpHeader() |
|
390 : valid( true ) |
|
391 { |
|
392 } |
|
393 |
|
394 /*! |
|
395 Constructs a copy of \a header. |
|
396 */ |
|
397 Q3HttpHeader::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 */ |
|
411 Q3HttpHeader::Q3HttpHeader( const QString& str ) |
|
412 : valid( true ) |
|
413 { |
|
414 parse( str ); |
|
415 } |
|
416 |
|
417 /*! |
|
418 Destructor. |
|
419 */ |
|
420 Q3HttpHeader::~Q3HttpHeader() |
|
421 { |
|
422 } |
|
423 |
|
424 /*! |
|
425 Assigns \a h and returns a reference to this http header. |
|
426 */ |
|
427 Q3HttpHeader& 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 */ |
|
439 bool 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 */ |
|
453 bool 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 */ |
|
493 void 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 */ |
|
504 QString 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 */ |
|
514 QStringList 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 */ |
|
525 bool 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 */ |
|
540 void 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 */ |
|
550 void 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 */ |
|
563 bool 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 */ |
|
581 QString 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 */ |
|
601 bool 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 */ |
|
612 uint 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 */ |
|
623 void 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 */ |
|
634 bool 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 */ |
|
644 QString 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 */ |
|
663 void 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 */ |
|
696 Q3HttpResponseHeader::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 */ |
|
706 Q3HttpResponseHeader::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 */ |
|
714 Q3HttpResponseHeader::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 */ |
|
727 Q3HttpResponseHeader::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 */ |
|
739 void 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 */ |
|
753 int 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 */ |
|
763 QString 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 */ |
|
773 int 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 */ |
|
783 int Q3HttpResponseHeader::minorVersion() const |
|
784 { |
|
785 return minVer; |
|
786 } |
|
787 |
|
788 /*! \internal |
|
789 */ |
|
790 bool 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 */ |
|
821 QString 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 */ |
|
863 Q3HttpRequestHeader::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 */ |
|
873 Q3HttpRequestHeader::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 */ |
|
881 Q3HttpRequestHeader::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 */ |
|
893 Q3HttpRequestHeader::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 */ |
|
906 void 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 */ |
|
920 QString 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 */ |
|
930 QString 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 */ |
|
940 int 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 */ |
|
950 int Q3HttpRequestHeader::minorVersion() const |
|
951 { |
|
952 return minVer; |
|
953 } |
|
954 |
|
955 /*! \internal |
|
956 */ |
|
957 bool 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 */ |
|
984 QString 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 */ |
|
1119 Q3Http::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 */ |
|
1128 Q3Http::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 */ |
|
1144 Q3Http::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 |
|
1155 void 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 */ |
|
1181 Q3Http::~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 */ |
|
1350 void 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 */ |
|
1368 Q_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 */ |
|
1382 Q_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 */ |
|
1406 QByteArray 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 */ |
|
1421 int 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 */ |
|
1437 Q3HttpRequestHeader 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 */ |
|
1455 QIODevice* 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 */ |
|
1473 QIODevice* 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 */ |
|
1490 bool 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 */ |
|
1502 void 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 */ |
|
1527 int 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 */ |
|
1558 int 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 */ |
|
1593 int 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 */ |
|
1605 int 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 */ |
|
1630 int 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 */ |
|
1664 int 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 */ |
|
1674 int 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 */ |
|
1703 int Q3Http::closeConnection() |
|
1704 { |
|
1705 return addRequest( new Q3HttpCloseRequest() ); |
|
1706 } |
|
1707 |
|
1708 int 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 |
|
1719 void 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 |
|
1734 void 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 |
|
1755 void 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 |
|
1770 void 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 |
|
1784 void 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 |
|
1805 void 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 |
|
1828 void 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 |
|
1849 void 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 |
|
1875 void 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 */ |
|
2066 Q3Http::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 */ |
|
2078 Q3Http::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 */ |
|
2089 QString Q3Http::errorString() const |
|
2090 { |
|
2091 return d->errorString; |
|
2092 } |
|
2093 |
|
2094 /*! \reimp |
|
2095 */ |
|
2096 void 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 |
|
2113 void Q3Http::killIdleTimer() |
|
2114 { |
|
2115 if (d->idleTimer) |
|
2116 killTimer( d->idleTimer ); |
|
2117 d->idleTimer = 0; |
|
2118 } |
|
2119 |
|
2120 void 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 |
|
2129 void 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 */ |
|
2160 int Q3Http::supportedOperations() const |
|
2161 { |
|
2162 return OpGet | OpPut; |
|
2163 } |
|
2164 |
|
2165 /*! \reimp |
|
2166 */ |
|
2167 void 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 */ |
|
2187 void 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 |
|
2205 void 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 |
|
2243 void 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 |
|
2286 void 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 |
|
2319 QT_END_NAMESPACE |
|
2320 |
|
2321 #endif |