📄 q3http.cpp
字号:
break; default: finishedWithError( QHttp::tr("HTTP request failed"), UnknownError ); break; } } close();}void Q3Http::slotBytesWritten( int written ){ d->bytesDone += written; emit dataSendProgress( d->bytesDone, d->bytesTotal ); if ( !d->postDevice ) return; if ( d->socket.bytesToWrite() == 0 ) { int max = qMin<int>( 4096, d->postDevice->size() - d->postDevice->at() ); QByteArray arr( max ); int n = d->postDevice->readBlock( arr.data(), max ); if ( n != max ) { qWarning("Could not read enough bytes from the device"); close(); return; } if ( d->postDevice->atEnd() ) { d->postDevice = 0; } d->socket.writeBlock( arr.data(), max ); }}void Q3Http::slotReadyRead(){ if ( d->state != Reading ) { setState( Reading ); d->buffer = QByteArray(); d->readHeader = true; d->headerStr = QLatin1String(""); d->bytesDone = 0; d->chunkedSize = -1; } while ( d->readHeader ) { bool end = false; QString tmp; while ( !end && d->socket.canReadLine() ) { tmp = QLatin1String(d->socket.readLine()); if ( tmp == QLatin1String("\r\n") || tmp == QLatin1String("\n") ) end = true; else d->headerStr += tmp; } if ( !end ) return;#if defined(Q3HTTP_DEBUG) qDebug( "Q3Http: read response header:\n---{\n%s}---", d->headerStr.latin1() );#endif d->response = Q3HttpResponseHeader( d->headerStr ); d->headerStr = QLatin1String("");#if defined(Q3HTTP_DEBUG) qDebug( "Q3Http: read response header:\n---{\n%s}---", d->response.toString().latin1() );#endif // Check header if ( !d->response.isValid() ) { finishedWithError( QHttp::tr("Invalid HTTP response header"), InvalidResponseHeader ); close(); return; } // The 100-continue header is ignored, because when using the // POST method, we send both the request header and data in // one chunk. if (d->response.statusCode() != 100) { d->readHeader = false; if ( d->response.hasKey( QLatin1String("transfer-encoding") ) && d->response.value( QLatin1String("transfer-encoding") ).lower().contains( QLatin1String("chunked") ) ) d->chunkedSize = 0; emit responseHeaderReceived( d->response ); } } if ( !d->readHeader ) { bool everythingRead = false; if ( currentRequest().method() == QLatin1String("HEAD") ) { everythingRead = true; } else { Q_ULONG n = d->socket.bytesAvailable(); QByteArray *arr = 0; if ( d->chunkedSize != -1 ) { // transfer-encoding is chunked for ( ;; ) { // get chunk size if ( d->chunkedSize == 0 ) { if ( !d->socket.canReadLine() ) break; QString sizeString = QLatin1String(d->socket.readLine()); int tPos = sizeString.find( QLatin1Char(';') ); if ( tPos != -1 ) sizeString.truncate( tPos ); bool ok; d->chunkedSize = sizeString.toInt( &ok, 16 ); if ( !ok ) { finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength ); close(); delete arr; return; } if ( d->chunkedSize == 0 ) // last-chunk d->chunkedSize = -2; } // read trailer while ( d->chunkedSize == -2 && d->socket.canReadLine() ) { QString read = QLatin1String(d->socket.readLine()); if ( read == QLatin1String("\r\n") || read == QLatin1String("\n") ) d->chunkedSize = -1; } if ( d->chunkedSize == -1 ) { everythingRead = true; break; } // make sure that you can read the terminating CRLF, // otherwise wait until next time... n = d->socket.bytesAvailable(); if ( n == 0 ) break; if ( (Q_LONG)n == d->chunkedSize || (Q_LONG)n == d->chunkedSize+1 ) { n = d->chunkedSize - 1; if ( n == 0 ) break; } // read data uint toRead = QMIN( (Q_LONG)n, (d->chunkedSize < 0 ? (Q_LONG)n : d->chunkedSize) ); if ( !arr ) arr = new QByteArray( 0 ); uint oldArrSize = arr->size(); arr->resize( oldArrSize + toRead ); Q_LONG read = d->socket.readBlock( arr->data()+oldArrSize, toRead ); arr->resize( oldArrSize + read ); d->chunkedSize -= read; if ( d->chunkedSize == 0 && n - read >= 2 ) { // read terminating CRLF char tmp[2]; d->socket.readBlock( tmp, 2 ); if ( tmp[0] != '\r' || tmp[1] != '\n' ) { finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength ); close(); delete arr; return; } } } } else if ( d->response.hasContentLength() ) { n = qMin<ulong>( d->response.contentLength() - d->bytesDone, n ); if ( n > 0 ) { arr = new QByteArray( n ); Q_LONG read = d->socket.readBlock( arr->data(), n ); arr->resize( read ); } if ( d->bytesDone + bytesAvailable() + n == d->response.contentLength() ) everythingRead = true; } else if ( n > 0 ) { // workaround for VC++ bug QByteArray temp = d->socket.readAll(); arr = new QByteArray( temp ); } if ( arr ) { n = arr->size(); if ( d->toDevice ) { d->toDevice->writeBlock( arr->data(), n ); delete arr; d->bytesDone += n;#if defined(Q3HTTP_DEBUG) qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%d bytes done)", n, d->bytesDone );#endif if ( d->response.hasContentLength() ) emit dataReadProgress( d->bytesDone, d->response.contentLength() ); else emit dataReadProgress( d->bytesDone, 0 ); } else { d->rba.append( arr );#if defined(Q3HTTP_DEBUG) qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%ld bytes done)", n, d->bytesDone + bytesAvailable() );#endif if ( d->response.hasContentLength() ) emit dataReadProgress( d->bytesDone + bytesAvailable(), d->response.contentLength() ); else emit dataReadProgress( d->bytesDone + bytesAvailable(), 0 ); emit readyRead( d->response ); } } } if ( everythingRead ) { // Handle "Connection: close" if ( d->response.value(QLatin1String("connection")).lower() == QLatin1String("close") ) { close(); } else { setState( Connected ); // Start a timer, so that we emit the keep alive signal // "after" this method returned. d->idleTimer = startTimer( 0 ); } } }}/*! Returns the current state of the object. When the state changes, the stateChanged() signal is emitted. \sa State stateChanged()*/Q3Http::State Q3Http::state() const{ return d->state;}/*! Returns the last error that occurred. This is useful to find out what happened when receiving a requestFinished() or a done() signal with the \c error argument \c true. If you start a new request, the error status is reset to \c NoError.*/Q3Http::Error Q3Http::error() const{ return d->error;}/*! Returns a human-readable description of the last error that occurred. This is useful to present a error message to the user when receiving a requestFinished() or a done() signal with the \c error argument \c true.*/QString Q3Http::errorString() const{ return d->errorString;}/*! \reimp*/void Q3Http::timerEvent( QTimerEvent *e ){ if ( e->timerId() == d->idleTimer ) { killTimer( d->idleTimer ); d->idleTimer = 0; if ( d->state == Connected ) { finishedWithSuccess(); } else if ( d->state != Unconnected ) { setState( Unconnected ); finishedWithSuccess(); } } else { QObject::timerEvent( e ); }}void Q3Http::killIdleTimer(){ if (d->idleTimer) killTimer( d->idleTimer ); d->idleTimer = 0;}void Q3Http::setState( int s ){#if defined(Q3HTTP_DEBUG) qDebug( "Q3Http state changed %d -> %d", d->state, s );#endif d->state = (State)s; emit stateChanged( s );}void Q3Http::close(){ // If no connection is open -> ignore if ( d->state == Closing || d->state == Unconnected ) return; d->postDevice = 0; setState( Closing ); // Already closed ? if ( !d->socket.isOpen() ) { d->idleTimer = startTimer( 0 ); } else { // Close now. d->socket.close(); // Did close succeed immediately ? if ( d->socket.state() == Q3Socket::Idle ) { // Prepare to emit the requestFinished() signal. d->idleTimer = startTimer( 0 ); } }}/********************************************************************** * * Q3Http implementation of the Q3NetworkProtocol interface * *********************************************************************//*! \reimp*/int Q3Http::supportedOperations() const{ return OpGet | OpPut;}/*! \reimp*/void Q3Http::operationGet( Q3NetworkOperation *op ){ connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)), this, SLOT(clientReply(Q3HttpResponseHeader)) ); connect( this, SIGNAL(done(bool)), this, SLOT(clientDone(bool)) ); connect( this, SIGNAL(stateChanged(int)), this, SLOT(clientStateChanged(int)) ); bytesRead = 0; op->setState( StInProgress ); Q3Url u( operationInProgress()->arg( 0 ) ); Q3HttpRequestHeader header( QLatin1String("GET"), u.encodedPathAndQuery(), 1, 0 ); header.setValue( QLatin1String("Host"), u.host() ); setHost( u.host(), u.port() != -1 ? u.port() : 80 ); request( header );}/*! \reimp*/void Q3Http::operationPut( Q3NetworkOperation *op ){ connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)), this, SLOT(clientReply(Q3HttpResponseHeader)) ); connect( this, SIGNAL(done(bool)), this, SLOT(clientDone(bool)) ); connect( this, SIGNAL(stateChanged(int)), this, SLOT(clientStateChanged(int)) ); bytesRead = 0; op->setState( StInProgress ); Q3Url u( operationInProgress()->arg( 0 ) ); Q3HttpRequestHeader header( QLatin1String("POST"), u.encodedPathAndQuery(), 1, 0 ); header.setValue( QLatin1String("Host"), u.host() ); setHost( u.host(), u.port() != -1 ? u.port() : 80 ); request( header, op->rawArg(1) );}void Q3Http::clientReply( const Q3HttpResponseHeader &rep ){ Q3NetworkOperation *op = operationInProgress(); if ( op ) { if ( rep.statusCode() >= 400 && rep.statusCode() < 600 ) { op->setState( StFailed ); op->setProtocolDetail( QString(QLatin1String("%1 %2")).arg(rep.statusCode()).arg(rep.reasonPhrase()) ); switch ( rep.statusCode() ) { case 401: case 403: case 405: op->setErrorCode( ErrPermissionDenied ); break; case 404: op->setErrorCode(ErrFileNotExisting ); break; default: if ( op->operation() == OpGet ) op->setErrorCode( ErrGet ); else op->setErrorCode( ErrPut ); break; } } // ### In cases of an error, should we still emit the data() signals? if ( op->operation() == OpGet && bytesAvailable() > 0 ) { QByteArray ba = readAll(); emit data( ba, op ); bytesRead += ba.size(); if ( rep.hasContentLength() ) { emit dataTransferProgress( bytesRead, rep.contentLength(), op ); } } }}void Q3Http::clientDone( bool err ){ disconnect( this, SIGNAL(readyRead(Q3HttpResponseHeader)), this, SLOT(clientReply(Q3HttpResponseHeader)) ); disconnect( this, SIGNAL(done(bool)), this, SLOT(clientDone(bool)) ); disconnect( this, SIGNAL(stateChanged(int)), this, SLOT(clientStateChanged(int)) ); if ( err ) { Q3NetworkOperation *op = operationInProgress(); if ( op ) { op->setState( Q3NetworkProtocol::StFailed ); op->setProtocolDetail( errorString() ); switch ( error() ) { case ConnectionRefused: op->setErrorCode( ErrHostNotFound ); break; case HostNotFound: op->setErrorCode( ErrHostNotFound ); break; default: if ( op->operation() == OpGet ) op->setErrorCode( ErrGet ); else op->setErrorCode( ErrPut ); break; } emit finished( op ); } } else { Q3NetworkOperation *op = operationInProgress(); if ( op ) { if ( op->state() != StFailed ) { op->setState( Q3NetworkProtocol::StDone ); op->setErrorCode( Q3NetworkProtocol::NoError ); } emit finished( op ); } }}void Q3Http::clientStateChanged( int state ){ if ( url() ) { switch ( (State)state ) { case Connecting: emit connectionStateChanged( ConHostFound, QHttp::tr( "Host %1 found" ).arg( url()->host() ) ); break; case Sending: emit connectionStateChanged( ConConnected, QHttp::tr( "Connected to host %1" ).arg( url()->host() ) ); break; case Unconnected: emit connectionStateChanged( ConClosed, QHttp::tr( "Connection to %1 closed" ).arg( url()->host() ) ); break; default: break; } } else { switch ( (State)state ) { case Connecting: emit connecti
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -