changeset 7 | f7bc934e204c |
parent 3 | 41300fa6a67c |
3:41300fa6a67c | 7:f7bc934e204c |
---|---|
1 /**************************************************************************** |
1 /**************************************************************************** |
2 ** |
2 ** |
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
4 ** All rights reserved. |
4 ** All rights reserved. |
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
6 ** |
6 ** |
7 ** This file is part of the QtNetwork module of the Qt Toolkit. |
7 ** This file is part of the QtNetwork module of the Qt Toolkit. |
8 ** |
8 ** |
55 #endif |
55 #endif |
56 |
56 |
57 QT_BEGIN_NAMESPACE |
57 QT_BEGIN_NAMESPACE |
58 |
58 |
59 // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp |
59 // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp |
60 |
|
61 QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel() |
|
62 : socket(0) |
|
63 , state(IdleState) |
|
64 , reply(0) |
|
65 , written(0) |
|
66 , bytesTotal(0) |
|
67 , resendCurrent(false) |
|
68 , lastStatus(0) |
|
69 , pendingEncrypt(false) |
|
70 , reconnectAttempts(2) |
|
71 , authMehtod(QAuthenticatorPrivate::None) |
|
72 , proxyAuthMehtod(QAuthenticatorPrivate::None) |
|
73 #ifndef QT_NO_OPENSSL |
|
74 , ignoreAllSslErrors(false) |
|
75 #endif |
|
76 , pipeliningSupported(PipeliningSupportUnknown) |
|
77 , connection(0) |
|
78 { |
|
79 // Inlining this function in the header leads to compiler error on |
|
80 // release-armv5, on at least timebox 9.2 and 10.1. |
|
81 } |
|
60 |
82 |
61 void QHttpNetworkConnectionChannel::init() |
83 void QHttpNetworkConnectionChannel::init() |
62 { |
84 { |
63 #ifndef QT_NO_OPENSSL |
85 #ifndef QT_NO_OPENSSL |
64 if (connection->d_func()->encrypt) |
86 if (connection->d_func()->encrypt) |
122 } |
144 } |
123 |
145 |
124 |
146 |
125 bool QHttpNetworkConnectionChannel::sendRequest() |
147 bool QHttpNetworkConnectionChannel::sendRequest() |
126 { |
148 { |
149 if (!reply) { |
|
150 // heh, how should that happen! |
|
151 qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply"; |
|
152 state = QHttpNetworkConnectionChannel::IdleState; |
|
153 return false; |
|
154 } |
|
155 |
|
127 switch (state) { |
156 switch (state) { |
128 case QHttpNetworkConnectionChannel::IdleState: { // write the header |
157 case QHttpNetworkConnectionChannel::IdleState: { // write the header |
129 if (!ensureConnection()) { |
158 if (!ensureConnection()) { |
130 // wait for the connection (and encryption) to be done |
159 // wait for the connection (and encryption) to be done |
131 // sendRequest will be called again from either |
160 // sendRequest will be called again from either |
132 // _q_connected or _q_encrypted |
161 // _q_connected or _q_encrypted |
133 return false; |
162 return false; |
134 } |
163 } |
135 written = 0; // excluding the header |
164 written = 0; // excluding the header |
136 bytesTotal = 0; |
165 bytesTotal = 0; |
137 if (reply) { |
166 |
138 reply->d_func()->clear(); |
167 reply->d_func()->clear(); |
139 reply->d_func()->connection = connection; |
168 reply->d_func()->connection = connection; |
140 reply->d_func()->autoDecompress = request.d->autoDecompress; |
169 reply->d_func()->connectionChannel = this; |
141 reply->d_func()->pipeliningUsed = false; |
170 reply->d_func()->autoDecompress = request.d->autoDecompress; |
142 } |
171 reply->d_func()->pipeliningUsed = false; |
143 state = QHttpNetworkConnectionChannel::WritingState; |
172 |
144 pendingEncrypt = false; |
173 pendingEncrypt = false; |
145 // if the url contains authentication parameters, use the new ones |
174 // if the url contains authentication parameters, use the new ones |
146 // both channels will use the new authentication parameters |
175 // both channels will use the new authentication parameters |
147 if (!request.url().userInfo().isEmpty()) { |
176 if (!request.url().userInfo().isEmpty()) { |
148 QUrl url = request.url(); |
177 QUrl url = request.url(); |
164 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); |
193 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); |
165 #else |
194 #else |
166 QByteArray header = QHttpNetworkRequestPrivate::header(request, false); |
195 QByteArray header = QHttpNetworkRequestPrivate::header(request, false); |
167 #endif |
196 #endif |
168 socket->write(header); |
197 socket->write(header); |
198 // flushing is dangerous (QSslSocket calls transmit which might read or error) |
|
199 // socket->flush(); |
|
169 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); |
200 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); |
170 if (uploadByteDevice) { |
201 if (uploadByteDevice) { |
171 // connect the signals so this function gets called again |
202 // connect the signals so this function gets called again |
172 QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead())); |
203 QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead())); |
173 |
204 |
174 bytesTotal = request.contentLength(); |
205 bytesTotal = request.contentLength(); |
206 |
|
207 state = QHttpNetworkConnectionChannel::WritingState; // start writing data |
|
208 sendRequest(); //recurse |
|
175 } else { |
209 } else { |
176 state = QHttpNetworkConnectionChannel::WaitingState; |
210 state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response |
177 sendRequest(); |
211 sendRequest(); //recurse |
178 break; |
212 } |
179 } |
213 |
180 // write the initial chunk together with the headers |
214 break; |
181 // fall through |
|
182 } |
215 } |
183 case QHttpNetworkConnectionChannel::WritingState: |
216 case QHttpNetworkConnectionChannel::WritingState: |
184 { |
217 { |
185 // write the data |
218 // write the data |
186 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); |
219 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); |
187 if (!uploadByteDevice || bytesTotal == written) { |
220 if (!uploadByteDevice || bytesTotal == written) { |
188 if (uploadByteDevice) |
221 if (uploadByteDevice) |
189 emit reply->dataSendProgress(written, bytesTotal); |
222 emit reply->dataSendProgress(written, bytesTotal); |
190 state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response |
223 state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response |
191 sendRequest(); |
224 sendRequest(); // recurse |
192 break; |
225 break; |
193 } |
226 } |
194 |
227 |
195 // only feed the QTcpSocket buffer when there is less than 32 kB in it |
228 // only feed the QTcpSocket buffer when there is less than 32 kB in it |
196 const qint64 socketBufferFill = 32*1024; |
229 const qint64 socketBufferFill = 32*1024; |
256 socket->flush(); |
289 socket->flush(); |
257 |
290 |
258 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called |
291 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called |
259 // this is needed if the sends an reply before we have finished sending the request. In that |
292 // this is needed if the sends an reply before we have finished sending the request. In that |
260 // case receiveReply had been called before but ignored the server reply |
293 // case receiveReply had been called before but ignored the server reply |
261 receiveReply(); |
294 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection); |
262 break; |
295 break; |
263 } |
296 } |
264 case QHttpNetworkConnectionChannel::ReadingState: |
297 case QHttpNetworkConnectionChannel::ReadingState: |
265 case QHttpNetworkConnectionChannel::Wait4AuthState: |
298 case QHttpNetworkConnectionChannel::Wait4AuthState: |
266 // ignore _q_bytesWritten in these states |
299 // ignore _q_bytesWritten in these states |
270 } |
303 } |
271 return true; |
304 return true; |
272 } |
305 } |
273 |
306 |
274 |
307 |
275 void QHttpNetworkConnectionChannel::receiveReply() |
308 void QHttpNetworkConnectionChannel::_q_receiveReply() |
276 { |
309 { |
277 Q_ASSERT(socket); |
310 Q_ASSERT(socket); |
311 |
|
312 if (!reply) { |
|
313 // heh, how should that happen! |
|
314 qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply," |
|
315 << socket->bytesAvailable() << "bytes on socket."; |
|
316 close(); |
|
317 return; |
|
318 } |
|
278 |
319 |
279 qint64 bytes = 0; |
320 qint64 bytes = 0; |
280 QAbstractSocket::SocketState socketState = socket->state(); |
321 QAbstractSocket::SocketState socketState = socket->state(); |
281 |
322 |
282 // connection might be closed to signal the end of data |
323 // connection might be closed to signal the end of data |
283 if (socketState == QAbstractSocket::UnconnectedState) { |
324 if (socketState == QAbstractSocket::UnconnectedState) { |
284 if (!socket->bytesAvailable()) { |
325 if (socket->bytesAvailable() <= 0) { |
285 if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { |
326 if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { |
327 // finish this reply. this case happens when the server did not send a content length |
|
286 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; |
328 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; |
287 this->state = QHttpNetworkConnectionChannel::IdleState; |
|
288 allDone(); |
329 allDone(); |
330 return; |
|
289 } else { |
331 } else { |
290 // try to reconnect/resend before sending an error. |
332 handleUnexpectedEOF(); |
291 if (reconnectAttempts-- > 0) { |
333 return; |
292 closeAndResendCurrentRequest(); |
334 } |
293 } else if (reply) { |
335 } else { |
294 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket); |
336 // socket not connected but still bytes for reading.. just continue in this function |
295 emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); |
|
296 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
|
297 } |
|
298 } |
|
299 } |
337 } |
300 } |
338 } |
301 |
339 |
302 // read loop for the response |
340 // read loop for the response |
303 while (socket->bytesAvailable()) { |
341 while (socket->bytesAvailable()) { |
304 QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState; |
342 QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state; |
305 switch (state) { |
343 switch (state) { |
306 case QHttpNetworkReplyPrivate::NothingDoneState: |
344 case QHttpNetworkReplyPrivate::NothingDoneState: { |
345 // only eat whitespace on the first call |
|
346 eatWhitespace(); |
|
347 state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; |
|
348 // fallthrough |
|
349 } |
|
307 case QHttpNetworkReplyPrivate::ReadingStatusState: { |
350 case QHttpNetworkReplyPrivate::ReadingStatusState: { |
308 eatWhitespace(); |
|
309 qint64 statusBytes = reply->d_func()->readStatus(socket); |
351 qint64 statusBytes = reply->d_func()->readStatus(socket); |
310 if (statusBytes == -1 && reconnectAttempts <= 0) { |
352 if (statusBytes == -1) { |
311 // too many errors reading/receiving/parsing the status, close the socket and emit error |
353 // connection broke while reading status. also handled if later _q_disconnected is called |
312 close(); |
354 handleUnexpectedEOF(); |
313 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::ProtocolFailure, socket); |
355 return; |
314 emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString); |
|
315 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
|
316 break; |
|
317 } else if (statusBytes == -1) { |
|
318 reconnectAttempts--; |
|
319 reply->d_func()->clear(); |
|
320 closeAndResendCurrentRequest(); |
|
321 break; |
|
322 } |
356 } |
323 bytes += statusBytes; |
357 bytes += statusBytes; |
324 lastStatus = reply->d_func()->statusCode; |
358 lastStatus = reply->d_func()->statusCode; |
325 break; |
359 break; |
326 } |
360 } |
327 case QHttpNetworkReplyPrivate::ReadingHeaderState: |
361 case QHttpNetworkReplyPrivate::ReadingHeaderState: { |
328 bytes += reply->d_func()->readHeader(socket); |
362 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); |
329 if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { |
363 qint64 headerBytes = replyPrivate->readHeader(socket); |
330 if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) { |
364 if (headerBytes == -1) { |
365 // connection broke while reading headers. also handled if later _q_disconnected is called |
|
366 handleUnexpectedEOF(); |
|
367 return; |
|
368 } |
|
369 bytes += headerBytes; |
|
370 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) { |
|
371 if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) { |
|
331 // remove the Content-Length from header |
372 // remove the Content-Length from header |
332 reply->d_func()->removeAutoDecompressHeader(); |
373 replyPrivate->removeAutoDecompressHeader(); |
333 } else { |
374 } else { |
334 reply->d_func()->autoDecompress = false; |
375 replyPrivate->autoDecompress = false; |
335 } |
376 } |
336 if (reply && reply->d_func()->statusCode == 100) { |
377 if (replyPrivate->statusCode == 100) { |
337 reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; |
378 replyPrivate->clearHttpLayerInformation(); |
379 replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState; |
|
338 break; // ignore |
380 break; // ignore |
339 } |
381 } |
340 if (reply->d_func()->shouldEmitSignals()) |
382 if (replyPrivate->shouldEmitSignals()) |
341 emit reply->headerChanged(); |
383 emit reply->headerChanged(); |
342 if (!reply->d_func()->expectContent()) { |
384 if (!replyPrivate->expectContent()) { |
343 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; |
385 replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState; |
344 this->state = QHttpNetworkConnectionChannel::IdleState; |
|
345 allDone(); |
386 allDone(); |
346 return; |
387 return; |
347 } |
388 } |
348 } |
389 } |
349 break; |
390 break; |
391 } |
|
350 case QHttpNetworkReplyPrivate::ReadingDataState: { |
392 case QHttpNetworkReplyPrivate::ReadingDataState: { |
351 if (!reply->d_func()->isChunked() && !reply->d_func()->autoDecompress |
393 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); |
352 && reply->d_func()->bodyLength > 0) { |
394 if (replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) { |
395 // We already have some HTTP body data. We don't read more from the socket until |
|
396 // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more, |
|
397 // we could not limit our read buffer usage. |
|
398 // We only do this when shouldEmitSignals==true because our HTTP parsing |
|
399 // always needs to parse the 401/407 replies. Therefore they don't really obey |
|
400 // to the read buffer maximum size, but we don't care since they should be small. |
|
401 return; |
|
402 } |
|
403 |
|
404 if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress |
|
405 && replyPrivate->bodyLength > 0) { |
|
353 // bulk files like images should fulfill these properties and |
406 // bulk files like images should fulfill these properties and |
354 // we can therefore save on memory copying |
407 // we can therefore save on memory copying |
355 bytes = reply->d_func()->readBodyFast(socket, &reply->d_func()->responseData); |
408 bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData); |
356 reply->d_func()->totalProgress += bytes; |
409 replyPrivate->totalProgress += bytes; |
357 if (reply->d_func()->shouldEmitSignals()) { |
410 if (replyPrivate->shouldEmitSignals()) { |
358 QPointer<QHttpNetworkReply> replyPointer = reply; |
411 QPointer<QHttpNetworkReply> replyPointer = reply; |
359 emit reply->readyRead(); |
412 emit reply->readyRead(); |
360 // make sure that the reply is valid |
413 // make sure that the reply is valid |
361 if (replyPointer.isNull()) |
414 if (replyPointer.isNull()) |
362 return; |
415 return; |
363 emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); |
416 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); |
364 // make sure that the reply is valid |
417 // make sure that the reply is valid |
365 if (replyPointer.isNull()) |
418 if (replyPointer.isNull()) |
366 return; |
419 return; |
367 } |
420 } |
368 } |
421 } |
369 else |
422 else |
370 { |
423 { |
371 // use the traditional slower reading (for compressed encoding, chunked encoding, |
424 // use the traditional slower reading (for compressed encoding, chunked encoding, |
372 // no content-length etc) |
425 // no content-length etc) |
373 QByteDataBuffer byteDatas; |
426 QByteDataBuffer byteDatas; |
374 bytes = reply->d_func()->readBody(socket, &byteDatas); |
427 bytes = replyPrivate->readBody(socket, &byteDatas); |
375 if (bytes) { |
428 if (bytes) { |
376 if (reply->d_func()->autoDecompress) |
429 if (replyPrivate->autoDecompress) |
377 reply->d_func()->appendCompressedReplyData(byteDatas); |
430 replyPrivate->appendCompressedReplyData(byteDatas); |
378 else |
431 else |
379 reply->d_func()->appendUncompressedReplyData(byteDatas); |
432 replyPrivate->appendUncompressedReplyData(byteDatas); |
380 |
433 |
381 if (!reply->d_func()->autoDecompress) { |
434 if (!replyPrivate->autoDecompress) { |
382 reply->d_func()->totalProgress += bytes; |
435 replyPrivate->totalProgress += bytes; |
383 if (reply->d_func()->shouldEmitSignals()) { |
436 if (replyPrivate->shouldEmitSignals()) { |
384 QPointer<QHttpNetworkReply> replyPointer = reply; |
437 QPointer<QHttpNetworkReply> replyPointer = reply; |
385 // important: At the point of this readyRead(), the byteDatas list must be empty, |
438 // important: At the point of this readyRead(), the byteDatas list must be empty, |
386 // else implicit sharing will trigger memcpy when the user is reading data! |
439 // else implicit sharing will trigger memcpy when the user is reading data! |
387 emit reply->readyRead(); |
440 emit reply->readyRead(); |
388 // make sure that the reply is valid |
441 // make sure that the reply is valid |
389 if (replyPointer.isNull()) |
442 if (replyPointer.isNull()) |
390 return; |
443 return; |
391 emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); |
444 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); |
392 // make sure that the reply is valid |
445 // make sure that the reply is valid |
393 if (replyPointer.isNull()) |
446 if (replyPointer.isNull()) |
394 return; |
447 return; |
395 } |
448 } |
396 } |
449 } |
399 return; // ### expand failed |
452 return; // ### expand failed |
400 } |
453 } |
401 #endif |
454 #endif |
402 } |
455 } |
403 } |
456 } |
404 if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) |
457 // still in ReadingDataState? This function will be called again by the socket's readyRead |
458 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) |
|
405 break; |
459 break; |
460 |
|
406 // everything done, fall through |
461 // everything done, fall through |
407 } |
462 } |
408 case QHttpNetworkReplyPrivate::AllDoneState: |
463 case QHttpNetworkReplyPrivate::AllDoneState: |
409 this->state = QHttpNetworkConnectionChannel::IdleState; |
|
410 allDone(); |
464 allDone(); |
411 break; |
465 break; |
412 default: |
466 default: |
413 break; |
467 break; |
414 } |
468 } |
415 } |
469 } |
416 } |
470 } |
417 |
471 |
472 // called when unexpectedly reading a -1 or when data is expected but socket is closed |
|
473 void QHttpNetworkConnectionChannel::handleUnexpectedEOF() |
|
474 { |
|
475 if (reconnectAttempts <= 0) { |
|
476 // too many errors reading/receiving/parsing the status, close the socket and emit error |
|
477 requeueCurrentlyPipelinedRequests(); |
|
478 close(); |
|
479 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket); |
|
480 emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); |
|
481 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
|
482 } else { |
|
483 reconnectAttempts--; |
|
484 reply->d_func()->clear(); |
|
485 reply->d_func()->connection = connection; |
|
486 reply->d_func()->connectionChannel = this; |
|
487 closeAndResendCurrentRequest(); |
|
488 } |
|
489 } |
|
490 |
|
418 bool QHttpNetworkConnectionChannel::ensureConnection() |
491 bool QHttpNetworkConnectionChannel::ensureConnection() |
419 { |
492 { |
493 QAbstractSocket::SocketState socketState = socket->state(); |
|
494 |
|
495 // resend this request after we receive the disconnected signal |
|
496 if (socketState == QAbstractSocket::ClosingState) { |
|
497 resendCurrent = true; |
|
498 return false; |
|
499 } |
|
500 |
|
501 // already trying to connect? |
|
502 if (socketState == QAbstractSocket::HostLookupState || |
|
503 socketState == QAbstractSocket::ConnectingState) { |
|
504 return false; |
|
505 } |
|
506 |
|
420 // make sure that this socket is in a connected state, if not initiate |
507 // make sure that this socket is in a connected state, if not initiate |
421 // connection to the host. |
508 // connection to the host. |
422 if (socket->state() != QAbstractSocket::ConnectedState) { |
509 if (socketState != QAbstractSocket::ConnectedState) { |
423 // connect to the host if not already connected. |
510 // connect to the host if not already connected. |
424 // resend this request after we receive the disconnected signal |
|
425 if (socket->state() == QAbstractSocket::ClosingState) { |
|
426 resendCurrent = true; |
|
427 return false; |
|
428 } |
|
429 state = QHttpNetworkConnectionChannel::ConnectingState; |
511 state = QHttpNetworkConnectionChannel::ConnectingState; |
430 pendingEncrypt = connection->d_func()->encrypt; |
512 pendingEncrypt = connection->d_func()->encrypt; |
431 |
513 |
432 // reset state |
514 // reset state |
433 pipeliningSupported = PipeliningSupportUnknown; |
515 pipeliningSupported = PipeliningSupportUnknown; |
530 // while handling 401 & 407, we might reset the status code, so save this. |
612 // while handling 401 & 407, we might reset the status code, so save this. |
531 bool emitFinished = reply->d_func()->shouldEmitSignals(); |
613 bool emitFinished = reply->d_func()->shouldEmitSignals(); |
532 handleStatus(); |
614 handleStatus(); |
533 // ### at this point there should be no more data on the socket |
615 // ### at this point there should be no more data on the socket |
534 // close if server requested |
616 // close if server requested |
535 if (reply->d_func()->isConnectionCloseEnabled()) |
617 bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled(); |
618 if (connectionCloseEnabled) |
|
536 close(); |
619 close(); |
537 // queue the finished signal, this is required since we might send new requests from |
620 // queue the finished signal, this is required since we might send new requests from |
538 // slot connected to it. The socket will not fire readyRead signal, if we are already |
621 // slot connected to it. The socket will not fire readyRead signal, if we are already |
539 // in the slot connected to readyRead |
622 // in the slot connected to readyRead |
540 if (emitFinished) |
623 if (emitFinished) |
543 // in case of failures, each channel will attempt two reconnects before emitting error. |
626 // in case of failures, each channel will attempt two reconnects before emitting error. |
544 reconnectAttempts = 2; |
627 reconnectAttempts = 2; |
545 |
628 |
546 detectPipeliningSupport(); |
629 detectPipeliningSupport(); |
547 |
630 |
631 // now the channel can be seen as free/idle again, all signal emissions for the reply have been done |
|
632 this->state = QHttpNetworkConnectionChannel::IdleState; |
|
633 |
|
634 // if it does not need to be sent again we can set it to 0 |
|
635 // the previous code did not do that and we had problems with accidental re-sending of a |
|
636 // finished request. |
|
637 // Note that this may trigger a segfault at some other point. But then we can fix the underlying |
|
638 // problem. |
|
639 if (!resendCurrent) |
|
640 reply = 0; |
|
641 |
|
548 // move next from pipeline to current request |
642 // move next from pipeline to current request |
549 if (!alreadyPipelinedRequests.isEmpty()) { |
643 if (!alreadyPipelinedRequests.isEmpty()) { |
550 if (resendCurrent || reply->d_func()->isConnectionCloseEnabled() || socket->state() != QAbstractSocket::ConnectedState) { |
644 if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) { |
551 // move the pipelined ones back to the main queue |
645 // move the pipelined ones back to the main queue |
552 requeueCurrentlyPipelinedRequests(); |
646 requeueCurrentlyPipelinedRequests(); |
553 close(); |
647 close(); |
554 } else { |
648 } else { |
555 // there were requests pipelined in and we can continue |
649 // there were requests pipelined in and we can continue |
565 |
659 |
566 // pipeline even more |
660 // pipeline even more |
567 connection->d_func()->fillPipeline(socket); |
661 connection->d_func()->fillPipeline(socket); |
568 |
662 |
569 // continue reading |
663 // continue reading |
570 receiveReply(); |
664 _q_receiveReply(); |
571 } |
665 } |
572 } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) { |
666 } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) { |
573 eatWhitespace(); |
667 eatWhitespace(); |
574 // this is weird. we had nothing pipelined but still bytes available. better close it. |
668 // this is weird. we had nothing pipelined but still bytes available. better close it. |
575 if (socket->bytesAvailable() > 0) |
669 if (socket->bytesAvailable() > 0) |
581 } |
675 } |
582 |
676 |
583 void QHttpNetworkConnectionChannel::detectPipeliningSupport() |
677 void QHttpNetworkConnectionChannel::detectPipeliningSupport() |
584 { |
678 { |
585 // detect HTTP Pipelining support |
679 // detect HTTP Pipelining support |
586 QByteArray serverHeaderField = reply->headerField("Server"); |
680 QByteArray serverHeaderField; |
587 if ( |
681 if ( |
588 // check for broken servers in server reply header |
|
589 // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining |
|
590 (!serverHeaderField.contains("Microsoft-IIS/4.")) |
|
591 && (!serverHeaderField.contains("Microsoft-IIS/5.")) |
|
592 && (!serverHeaderField.contains("Netscape-Enterprise/3.")) |
|
593 // check for HTTP/1.1 |
682 // check for HTTP/1.1 |
594 && (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1) |
683 (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1) |
595 // check for not having connection close |
684 // check for not having connection close |
596 && (!reply->d_func()->isConnectionCloseEnabled()) |
685 && (!reply->d_func()->isConnectionCloseEnabled()) |
597 // check if it is still connected |
686 // check if it is still connected |
598 && (socket->state() == QAbstractSocket::ConnectedState) |
687 && (socket->state() == QAbstractSocket::ConnectedState) |
688 // check for broken servers in server reply header |
|
689 // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining |
|
690 && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4.")) |
|
691 && (!serverHeaderField.contains("Microsoft-IIS/5.")) |
|
692 && (!serverHeaderField.contains("Netscape-Enterprise/3.")) |
|
599 ) { |
693 ) { |
600 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported; |
694 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported; |
601 } else { |
695 } else { |
602 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; |
696 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; |
603 } |
697 } |
608 { |
702 { |
609 for (int i = 0; i < alreadyPipelinedRequests.length(); i++) |
703 for (int i = 0; i < alreadyPipelinedRequests.length(); i++) |
610 connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i)); |
704 connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i)); |
611 alreadyPipelinedRequests.clear(); |
705 alreadyPipelinedRequests.clear(); |
612 |
706 |
613 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
707 // only run when the QHttpNetworkConnection is not currently being destructed, e.g. |
708 // this function is called from _q_disconnected which is called because |
|
709 // of ~QHttpNetworkConnectionPrivate |
|
710 if (qobject_cast<QHttpNetworkConnection*>(connection)) |
|
711 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
|
614 } |
712 } |
615 |
713 |
616 void QHttpNetworkConnectionChannel::eatWhitespace() |
714 void QHttpNetworkConnectionChannel::eatWhitespace() |
617 { |
715 { |
618 char c; |
716 char c; |
619 while (socket->bytesAvailable()) { |
717 do { |
620 if (socket->peek(&c, 1) != 1) |
718 qint64 ret = socket->peek(&c, 1); |
719 |
|
720 // nothing read, fine. |
|
721 if (ret == 0) |
|
621 return; |
722 return; |
723 |
|
724 // EOF from socket? |
|
725 if (ret == -1) |
|
726 return; // FIXME, we need to stop processing. however the next stuff done will also do that. |
|
622 |
727 |
623 // read all whitespace and line endings |
728 // read all whitespace and line endings |
624 if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) { |
729 if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) { |
625 socket->read(&c, 1); |
730 socket->read(&c, 1); |
626 continue; |
731 continue; |
627 } else { |
732 } else { |
628 break; |
733 break; |
629 } |
734 } |
630 } |
735 } while(true); |
631 } |
736 } |
632 |
737 |
633 void QHttpNetworkConnectionChannel::handleStatus() |
738 void QHttpNetworkConnectionChannel::handleStatus() |
634 { |
739 { |
635 Q_ASSERT(socket); |
740 Q_ASSERT(socket); |
641 switch (statusCode) { |
746 switch (statusCode) { |
642 case 401: // auth required |
747 case 401: // auth required |
643 case 407: // proxy auth required |
748 case 407: // proxy auth required |
644 if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { |
749 if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { |
645 if (resend) { |
750 if (resend) { |
646 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); |
751 if (!resetUploadData()) |
647 if (uploadByteDevice) { |
752 break; |
648 if (uploadByteDevice->reset()) { |
|
649 written = 0; |
|
650 } else { |
|
651 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError); |
|
652 break; |
|
653 } |
|
654 } |
|
655 |
753 |
656 reply->d_func()->eraseData(); |
754 reply->d_func()->eraseData(); |
657 |
755 |
658 if (alreadyPipelinedRequests.isEmpty()) { |
756 if (alreadyPipelinedRequests.isEmpty()) { |
659 // this does a re-send without closing the connection |
757 // this does a re-send without closing the connection |
679 default: |
777 default: |
680 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
778 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
681 } |
779 } |
682 } |
780 } |
683 |
781 |
782 bool QHttpNetworkConnectionChannel::resetUploadData() |
|
783 { |
|
784 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); |
|
785 if (!uploadByteDevice) |
|
786 return true; |
|
787 |
|
788 if (uploadByteDevice->reset()) { |
|
789 written = 0; |
|
790 return true; |
|
791 } else { |
|
792 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError); |
|
793 return false; |
|
794 } |
|
795 } |
|
796 |
|
797 |
|
684 void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) |
798 void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) |
685 { |
799 { |
686 // this is only called for simple GET |
800 // this is only called for simple GET |
687 |
801 |
688 QHttpNetworkRequest &request = pair.first; |
802 QHttpNetworkRequest &request = pair.first; |
689 QHttpNetworkReply *reply = pair.second; |
803 QHttpNetworkReply *reply = pair.second; |
690 if (reply) { |
804 reply->d_func()->clear(); |
691 reply->d_func()->clear(); |
805 reply->d_func()->connection = connection; |
692 reply->d_func()->connection = connection; |
806 reply->d_func()->connectionChannel = this; |
693 reply->d_func()->autoDecompress = request.d->autoDecompress; |
807 reply->d_func()->autoDecompress = request.d->autoDecompress; |
694 reply->d_func()->pipeliningUsed = true; |
808 reply->d_func()->pipeliningUsed = true; |
695 } |
|
696 |
809 |
697 #ifndef QT_NO_NETWORKPROXY |
810 #ifndef QT_NO_NETWORKPROXY |
698 QByteArray header = QHttpNetworkRequestPrivate::header(request, |
811 QByteArray header = QHttpNetworkRequestPrivate::header(request, |
699 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); |
812 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); |
700 #else |
813 #else |
737 void QHttpNetworkConnectionChannel::_q_readyRead() |
850 void QHttpNetworkConnectionChannel::_q_readyRead() |
738 { |
851 { |
739 if (isSocketWaiting() || isSocketReading()) { |
852 if (isSocketWaiting() || isSocketReading()) { |
740 state = QHttpNetworkConnectionChannel::ReadingState; |
853 state = QHttpNetworkConnectionChannel::ReadingState; |
741 if (reply) |
854 if (reply) |
742 receiveReply(); |
855 _q_receiveReply(); |
743 } |
856 } |
744 } |
857 } |
745 |
858 |
746 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) |
859 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) |
747 { |
860 { |
754 |
867 |
755 void QHttpNetworkConnectionChannel::_q_disconnected() |
868 void QHttpNetworkConnectionChannel::_q_disconnected() |
756 { |
869 { |
757 // read the available data before closing |
870 // read the available data before closing |
758 if (isSocketWaiting() || isSocketReading()) { |
871 if (isSocketWaiting() || isSocketReading()) { |
759 state = QHttpNetworkConnectionChannel::ReadingState; |
872 if (reply) { |
760 if (reply) |
873 state = QHttpNetworkConnectionChannel::ReadingState; |
761 receiveReply(); |
874 _q_receiveReply(); |
875 } |
|
762 } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) { |
876 } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) { |
763 // re-sending request because the socket was in ClosingState |
877 // re-sending request because the socket was in ClosingState |
764 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
878 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); |
765 } |
879 } |
766 state = QHttpNetworkConnectionChannel::IdleState; |
880 state = QHttpNetworkConnectionChannel::IdleState; |
771 |
885 |
772 |
886 |
773 void QHttpNetworkConnectionChannel::_q_connected() |
887 void QHttpNetworkConnectionChannel::_q_connected() |
774 { |
888 { |
775 // improve performance since we get the request sent by the kernel ASAP |
889 // improve performance since we get the request sent by the kernel ASAP |
776 socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); |
890 //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); |
891 // We have this commented out now. It did not have the effect we wanted. If we want to |
|
892 // do this properly, Qt has to combine multiple HTTP requests into one buffer |
|
893 // and send this to the kernel in one syscall and then the kernel immediately sends |
|
894 // it as one TCP packet because of TCP_NODELAY. |
|
895 // However, this code is currently not in Qt, so we rely on the kernel combining |
|
896 // the requests into one TCP packet. |
|
897 |
|
777 // not sure yet if it helps, but it makes sense |
898 // not sure yet if it helps, but it makes sense |
778 socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); |
899 socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); |
779 |
900 |
780 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; |
901 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; |
781 |
902 |
838 default: |
959 default: |
839 // all other errors are treated as NetworkError |
960 // all other errors are treated as NetworkError |
840 errorCode = QNetworkReply::UnknownNetworkError; |
961 errorCode = QNetworkReply::UnknownNetworkError; |
841 break; |
962 break; |
842 } |
963 } |
843 QPointer<QObject> that = connection; |
964 QPointer<QHttpNetworkConnection> that = connection; |
844 QString errorString = connection->d_func()->errorDetail(errorCode, socket); |
965 QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString()); |
845 if (send2Reply) { |
966 if (send2Reply) { |
846 if (reply) { |
967 if (reply) { |
847 reply->d_func()->errorString = errorString; |
968 reply->d_func()->errorString = errorString; |
848 // this error matters only to this reply |
969 // this error matters only to this reply |
849 emit reply->finishedWithError(errorCode, errorString); |
970 emit reply->finishedWithError(errorCode, errorString); |
893 // bytes have been written to the socket. write even more of them :) |
1014 // bytes have been written to the socket. write even more of them :) |
894 if (isSocketWriting()) |
1015 if (isSocketWriting()) |
895 sendRequest(); |
1016 sendRequest(); |
896 // otherwise we do nothing |
1017 // otherwise we do nothing |
897 } |
1018 } |
898 #endif |
1019 |
1020 #endif |
|
1021 |
|
1022 void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c) |
|
1023 { |
|
1024 // Inlining this function in the header leads to compiler error on |
|
1025 // release-armv5, on at least timebox 9.2 and 10.1. |
|
1026 connection = c; |
|
1027 } |
|
899 |
1028 |
900 QT_END_NAMESPACE |
1029 QT_END_NAMESPACE |
901 |
1030 |
902 #include "moc_qhttpnetworkconnectionchannel_p.cpp" |
1031 #include "moc_qhttpnetworkconnectionchannel_p.cpp" |
903 |
1032 |