Back to index

lightning-sunbird  0.9+nobinonly
nsHttpTransaction.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:set ts=4 sw=4 sts=4 et cin: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Darin Fisher <darin@netscape.com> (original author)
00025  *   Andreas M. Schneider <clarence@clarence.de>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsHttpHandler.h"
00042 #include "nsHttpTransaction.h"
00043 #include "nsHttpConnection.h"
00044 #include "nsHttpRequestHead.h"
00045 #include "nsHttpResponseHead.h"
00046 #include "nsHttpChunkedDecoder.h"
00047 #include "nsTransportUtils.h"
00048 #include "nsIOService.h"
00049 #include "nsAutoLock.h"
00050 #include "pratom.h"
00051 #include "plevent.h"
00052 
00053 #include "nsIStringStream.h"
00054 #include "nsISeekableStream.h"
00055 #include "nsISocketTransport.h"
00056 #include "nsMultiplexInputStream.h"
00057 
00058 #include "nsComponentManagerUtils.h" // do_CreateInstance
00059 #include "nsServiceManagerUtils.h"   // do_GetService
00060 #include "nsIHttpActivityObserver.h"
00061 
00062 //-----------------------------------------------------------------------------
00063 
00064 #ifdef DEBUG
00065 // defined by the socket transport service while active
00066 extern PRThread *gSocketThread;
00067 #endif
00068 
00069 //-----------------------------------------------------------------------------
00070 
00071 static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
00072 
00073 // mLineBuf is limited to this number of bytes.
00074 #define MAX_LINEBUF_LENGTH (1024 * 10)
00075 
00076 //-----------------------------------------------------------------------------
00077 // helpers
00078 //-----------------------------------------------------------------------------
00079 
00080 static char *
00081 LocateHttpStart(char *buf, PRUint32 len)
00082 {
00083     // if we have received less than 4 bytes of data, then we'll have to
00084     // just accept a partial match, which may not be correct.
00085     if (len < 4)
00086         return (PL_strncasecmp(buf, "HTTP", len) == 0) ? buf : 0;
00087 
00088     // PL_strncasestr would be perfect for this, but unfortunately bug 96571
00089     // prevents its use here.
00090     while (len >= 4) {
00091         if (PL_strncasecmp(buf, "HTTP", 4) == 0)
00092             return buf;
00093         buf++;
00094         len--;
00095     }
00096     return 0;
00097 }
00098 
00099 #if defined(PR_LOGGING)
00100 static void
00101 LogHeaders(const char *lines)
00102 {
00103     nsCAutoString buf;
00104     char *p;
00105     while ((p = PL_strstr(lines, "\r\n")) != nsnull) {
00106         buf.Assign(lines, p - lines);
00107         if (PL_strcasestr(buf.get(), "authorization: ") != nsnull) {
00108             char *p = PL_strchr(PL_strchr(buf.get(), ' ')+1, ' ');
00109             while (*++p) *p = '*';
00110         }
00111         LOG3(("  %s\n", buf.get()));
00112         lines = p + 2;
00113     }
00114 }
00115 #endif
00116 
00117 //-----------------------------------------------------------------------------
00118 // nsHttpTransaction <public>
00119 //-----------------------------------------------------------------------------
00120 
00121 nsHttpTransaction::nsHttpTransaction()
00122     : mRequestSize(0)
00123     , mConnection(nsnull)
00124     , mConnInfo(nsnull)
00125     , mRequestHead(nsnull)
00126     , mResponseHead(nsnull)
00127     , mContentLength(-1)
00128     , mContentRead(0)
00129     , mChunkedDecoder(nsnull)
00130     , mStatus(NS_OK)
00131     , mPriority(0)
00132     , mRestartCount(0)
00133     , mCaps(0)
00134     , mClosed(PR_FALSE)
00135     , mDestroying(PR_FALSE)
00136     , mConnected(PR_FALSE)
00137     , mHaveStatusLine(PR_FALSE)
00138     , mHaveAllHeaders(PR_FALSE)
00139     , mTransactionDone(PR_FALSE)
00140     , mResponseIsComplete(PR_FALSE)
00141     , mDidContentStart(PR_FALSE)
00142     , mNoContent(PR_FALSE)
00143     , mSentData(PR_FALSE)
00144     , mReceivedData(PR_FALSE)
00145     , mStatusEventPending(PR_FALSE)
00146     , mHasRequestBody(PR_FALSE)
00147     , mSSLConnectFailed(PR_FALSE)
00148 {
00149     LOG(("Creating nsHttpTransaction @%x\n", this));
00150 }
00151 
00152 nsHttpTransaction::~nsHttpTransaction()
00153 {
00154     LOG(("Destroying nsHttpTransaction @%x\n", this));
00155 
00156     NS_IF_RELEASE(mConnection);
00157     NS_IF_RELEASE(mConnInfo);
00158 
00159     delete mResponseHead;
00160     delete mChunkedDecoder;
00161 }
00162 
00163 nsresult
00164 nsHttpTransaction::Init(PRUint8 caps,
00165                         nsHttpConnectionInfo *cinfo,
00166                         nsHttpRequestHead *requestHead,
00167                         nsIInputStream *requestBody,
00168                         PRBool requestBodyHasHeaders,
00169                         nsIEventQueue *queue,
00170                         nsIInterfaceRequestor *callbacks,
00171                         nsITransportEventSink *eventsink,
00172                         nsIAsyncInputStream **responseBody)
00173 {
00174     nsresult rv;
00175 
00176     LOG(("nsHttpTransaction::Init [this=%x caps=%x]\n", this, caps));
00177 
00178     NS_ASSERTION(cinfo, "ouch");
00179     NS_ASSERTION(requestHead, "ouch");
00180     NS_ASSERTION(queue, "ouch");
00181 
00182     // create transport event sink proxy that coalesces all events
00183     rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
00184                                         eventsink, queue, PR_TRUE);
00185     if (NS_FAILED(rv)) return rv;
00186 
00187     // try to get the nsIHttpActivityObserver distributor
00188     mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv);
00189 
00190     // mActivityDistributor may not be valid
00191     if (NS_SUCCEEDED(rv) && mActivityDistributor) {
00192         // the service is valid, now check if it is active
00193         PRBool active;
00194         rv = mActivityDistributor->GetIsActive(&active);
00195         if (NS_SUCCEEDED(rv) && active) {
00196             // the service is valid and active, gather nsISupports
00197             // for the channel that called Init()
00198             mChannel = do_QueryInterface(eventsink);
00199             LOG(("nsHttpTransaction::Init() " \
00200                  "mActivityDistributor is active " \
00201                  "this=%x", this));
00202         } else
00203             // the interface in valid but not active, so don't use it
00204             mActivityDistributor = nsnull;
00205     }
00206 
00207     NS_ADDREF(mConnInfo = cinfo);
00208     mCallbacks = callbacks;
00209     mConsumerEventQ = queue;
00210     mCaps = caps;
00211 
00212     if (requestHead->Method() == nsHttp::Head)
00213         mNoContent = PR_TRUE;
00214 
00215     // grab a weak reference to the request head
00216     mRequestHead = requestHead;
00217 
00218     // make sure we eliminate any proxy specific headers from 
00219     // the request if we are talking HTTPS via a SSL tunnel.
00220     PRBool pruneProxyHeaders = cinfo->UsingSSL() &&
00221                                cinfo->UsingHttpProxy();
00222     mReqHeaderBuf.Truncate();
00223     requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
00224 
00225 #if defined(PR_LOGGING)
00226     if (LOG3_ENABLED()) {
00227         LOG3(("http request [\n"));
00228         LogHeaders(mReqHeaderBuf.get());
00229         LOG3(("]\n"));
00230     }
00231 #endif
00232 
00233     // If the request body does not include headers or if there is no request
00234     // body, then we must add the header/body separator manually.
00235     if (!requestBodyHasHeaders || !requestBody)
00236         mReqHeaderBuf.AppendLiteral("\r\n");
00237 
00238     // report the request header
00239     if (mActivityDistributor)
00240         mActivityDistributor->ObserveActivity(
00241             mChannel,
00242             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
00243             NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
00244             LL_ZERO, LL_ZERO,
00245             mReqHeaderBuf);
00246 
00247     // Create a string stream for the request header buf (the stream holds
00248     // a non-owning reference to the request header data, so we MUST keep
00249     // mReqHeaderBuf around).
00250     nsCOMPtr<nsIInputStream> headers;
00251     rv = NS_NewByteInputStream(getter_AddRefs(headers),
00252                                mReqHeaderBuf.get(),
00253                                mReqHeaderBuf.Length());
00254     if (NS_FAILED(rv)) return rv;
00255 
00256     if (requestBody) {
00257         mHasRequestBody = PR_TRUE;
00258 
00259         // wrap the headers and request body in a multiplexed input stream.
00260         nsCOMPtr<nsIMultiplexInputStream> multi =
00261             do_CreateInstance(kMultiplexInputStream, &rv);
00262         if (NS_FAILED(rv)) return rv;
00263 
00264         rv = multi->AppendStream(headers);
00265         if (NS_FAILED(rv)) return rv;
00266 
00267         rv = multi->AppendStream(requestBody);
00268         if (NS_FAILED(rv)) return rv;
00269 
00270         mRequestStream = multi;
00271     }
00272     else
00273         mRequestStream = headers;
00274 
00275     rv = mRequestStream->Available(&mRequestSize);
00276     if (NS_FAILED(rv)) return rv;
00277 
00278     // create pipe for response stream
00279     rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
00280                      getter_AddRefs(mPipeOut),
00281                      PR_TRUE, PR_TRUE,
00282                      NS_HTTP_SEGMENT_SIZE,
00283                      NS_HTTP_SEGMENT_COUNT,
00284                      nsIOService::gBufferCache);
00285     if (NS_FAILED(rv)) return rv;
00286 
00287     NS_ADDREF(*responseBody = mPipeIn);
00288     return NS_OK;
00289 }
00290 
00291 nsHttpResponseHead *
00292 nsHttpTransaction::TakeResponseHead()
00293 {
00294     if (!mHaveAllHeaders) {
00295         NS_WARNING("response headers not available or incomplete");
00296         return nsnull;
00297     }
00298 
00299     nsHttpResponseHead *head = mResponseHead;
00300     mResponseHead = nsnull;
00301     return head;
00302 }
00303 
00304 //----------------------------------------------------------------------------
00305 // nsHttpTransaction::nsAHttpTransaction
00306 //----------------------------------------------------------------------------
00307 
00308 void
00309 nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
00310 {
00311     NS_IF_RELEASE(mConnection);
00312     NS_IF_ADDREF(mConnection = conn);
00313 }
00314 
00315 void
00316 nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
00317 {
00318     NS_IF_ADDREF(*cb = mCallbacks);
00319 }
00320 
00321 void
00322 nsHttpTransaction::OnTransportStatus(nsresult status, PRUint64 progress)
00323 {
00324     LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n",
00325         this, status, progress));
00326 
00327     if (!mTransportSink)
00328         return;
00329     
00330     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00331 
00332     // nsHttpChannel synthesizes progress events in OnDataAvailable
00333     if (status == nsISocketTransport::STATUS_RECEIVING_FROM)
00334         return;
00335 
00336     if (mActivityDistributor) {
00337         // upon STATUS_WAITING_FOR; report request body sent
00338         if ((mHasRequestBody) &&
00339             (status == nsISocketTransport::STATUS_WAITING_FOR))
00340             mActivityDistributor->ObserveActivity(
00341                 mChannel,
00342                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
00343                 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
00344                 LL_ZERO, LL_ZERO, EmptyCString());
00345 
00346         // report the status and progress
00347         mActivityDistributor->ObserveActivity(
00348             mChannel,
00349             NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
00350             NS_STATIC_CAST(PRUint32, status),
00351             LL_ZERO,
00352             progress,
00353             EmptyCString());
00354     }
00355 
00356     nsUint64 progressMax;
00357 
00358     if (status == nsISocketTransport::STATUS_SENDING_TO) {
00359         // suppress progress when only writing request headers
00360         if (!mHasRequestBody)
00361             return;
00362 
00363         // when uploading, we include the request headers in the progress
00364         // notifications.
00365         progressMax = mRequestSize; // XXX mRequestSize is 32-bit!
00366     }
00367     else {
00368         progress = LL_ZERO;
00369         progressMax = 0;
00370     }
00371 
00372     mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax);
00373 }
00374 
00375 PRBool
00376 nsHttpTransaction::IsDone()
00377 {
00378     return mTransactionDone;
00379 }
00380 
00381 nsresult
00382 nsHttpTransaction::Status()
00383 {
00384     return mStatus;
00385 }
00386 
00387 PRUint32
00388 nsHttpTransaction::Available()
00389 {
00390     PRUint32 size;
00391     if (NS_FAILED(mRequestStream->Available(&size)))
00392         size = 0;
00393     return size;
00394 }
00395 
00396 NS_METHOD
00397 nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
00398                                       void *closure,
00399                                       const char *buf,
00400                                       PRUint32 offset,
00401                                       PRUint32 count,
00402                                       PRUint32 *countRead)
00403 {
00404     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
00405     nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
00406     if (NS_FAILED(rv)) return rv;
00407 
00408     trans->mSentData = PR_TRUE;
00409     return NS_OK;
00410 }
00411 
00412 nsresult
00413 nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
00414                                 PRUint32 count, PRUint32 *countRead)
00415 {
00416     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00417 
00418     if (mTransactionDone) {
00419         *countRead = 0;
00420         return mStatus;
00421     }
00422 
00423     if (!mConnected) {
00424         mConnected = PR_TRUE;
00425         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
00426     }
00427 
00428     mReader = reader;
00429 
00430     nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
00431 
00432     mReader = nsnull;
00433 
00434     // if read would block then we need to AsyncWait on the request stream.
00435     // have callback occur on socket thread so we stay synchronized.
00436     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
00437         nsCOMPtr<nsIAsyncInputStream> asyncIn =
00438                 do_QueryInterface(mRequestStream);
00439         if (asyncIn) {
00440             nsCOMPtr<nsIEventTarget> target;
00441             gHttpHandler->GetSocketThreadEventTarget(getter_AddRefs(target));
00442             if (target)
00443                 asyncIn->AsyncWait(this, 0, 0, target);
00444             else {
00445                 NS_ERROR("no socket thread event target");
00446                 rv = NS_ERROR_UNEXPECTED;
00447             }
00448         }
00449     }
00450 
00451     return rv;
00452 }
00453 
00454 NS_METHOD
00455 nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
00456                                     void *closure,
00457                                     char *buf,
00458                                     PRUint32 offset,
00459                                     PRUint32 count,
00460                                     PRUint32 *countWritten)
00461 {
00462     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
00463 
00464     if (trans->mTransactionDone)
00465         return NS_BASE_STREAM_CLOSED; // stop iterating
00466 
00467     nsresult rv;
00468     //
00469     // OK, now let the caller fill this segment with data.
00470     //
00471     rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
00472     if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
00473 
00474     NS_ASSERTION(*countWritten > 0, "bad writer");
00475     trans->mReceivedData = PR_TRUE;
00476 
00477     // now let the transaction "play" with the buffer.  it is free to modify
00478     // the contents of the buffer and/or modify countWritten.
00479     rv = trans->ProcessData(buf, *countWritten, countWritten);
00480     if (NS_FAILED(rv))
00481         trans->Close(rv);
00482 
00483     return rv; // failure code only stops WriteSegments; it is not propogated.
00484 }
00485 
00486 nsresult
00487 nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
00488                                  PRUint32 count, PRUint32 *countWritten)
00489 {
00490     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00491 
00492     if (mTransactionDone)
00493         return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
00494 
00495     mWriter = writer;
00496 
00497     nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
00498 
00499     mWriter = nsnull;
00500 
00501     // if pipe would block then we need to AsyncWait on it.  have callback
00502     // occur on socket thread so we stay synchronized.
00503     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
00504         nsCOMPtr<nsIEventTarget> target;
00505         gHttpHandler->GetSocketThreadEventTarget(getter_AddRefs(target));
00506         if (target)
00507             mPipeOut->AsyncWait(this, 0, 0, target);
00508         else {
00509             NS_ERROR("no socket thread event target");
00510             rv = NS_ERROR_UNEXPECTED;
00511         }
00512     }
00513 
00514     return rv;
00515 }
00516 
00517 void
00518 nsHttpTransaction::Close(nsresult reason)
00519 {
00520     LOG(("nsHttpTransaction::Close [this=%x reason=%x]\n", this, reason));
00521 
00522     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00523 
00524     if (mClosed) {
00525         LOG(("  already closed\n"));
00526         return;
00527     }
00528 
00529     if (mActivityDistributor) {
00530         // report the reponse is complete if not already reported
00531         if (!mResponseIsComplete)
00532             mActivityDistributor->ObserveActivity(
00533                 mChannel,
00534                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
00535                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
00536                 LL_ZERO,
00537                 NS_STATIC_CAST(PRUint64, mContentRead.mValue),
00538                 EmptyCString());
00539 
00540         // report that this transaction is closing
00541         mActivityDistributor->ObserveActivity(
00542             mChannel,
00543             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
00544             NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
00545             LL_ZERO, LL_ZERO, EmptyCString());
00546     }
00547 
00548     // we must no longer reference the connection!  find out if the 
00549     // connection was being reused before letting it go.
00550     PRBool connReused = PR_FALSE;
00551     if (mConnection)
00552         connReused = mConnection->IsReused();
00553     mConnected = PR_FALSE;
00554 
00555     //
00556     // if the connection was reset or closed before we wrote any part of the
00557     // request or if we wrote the request but didn't receive any part of the
00558     // response and the connection was being reused, then we can (and really
00559     // should) assume that we wrote to a stale connection and we must therefore
00560     // repeat the request over a new connection.
00561     //
00562     // NOTE: the conditions under which we will automatically retry the HTTP
00563     // request have to be carefully selected to avoid duplication of the
00564     // request from the point-of-view of the server.  such duplication could
00565     // have dire consequences including repeated purchases, etc.
00566     //
00567     // NOTE: because of the way SSL proxy CONNECT is implemented, it is
00568     // possible that the transaction may have received data without having
00569     // sent any data.  for this reason, mSendData == FALSE does not imply
00570     // mReceivedData == FALSE.  (see bug 203057 for more info.)
00571     //
00572     if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
00573         if (!mReceivedData && (!mSentData || connReused)) {
00574             // if restarting fails, then we must proceed to close the pipe,
00575             // which will notify the channel that the transaction failed.
00576             if (NS_SUCCEEDED(Restart()))
00577                 return;
00578         }
00579     }
00580 
00581     PRBool relConn = PR_TRUE;
00582     if (NS_SUCCEEDED(reason)) {
00583         // the server has not sent the final \r\n terminating the header
00584         // section, and there may still be a header line unparsed.  let's make
00585         // sure we parse the remaining header line, and then hopefully, the
00586         // response will be usable (see bug 88792).  related to that, we may
00587         // also have an empty response containing no headers.  we should treat
00588         // that as an empty HTTP/0.9 response (see bug 300613).
00589         if (!mHaveAllHeaders) {
00590             char data = '\n';
00591             PRUint32 unused;
00592             ParseHead(&data, 1, &unused);
00593         }
00594 
00595         // honor the sticky connection flag...
00596         if (mCaps & NS_HTTP_STICKY_CONNECTION)
00597             relConn = PR_FALSE;
00598     }
00599     if (relConn && mConnection)
00600         NS_RELEASE(mConnection);
00601 
00602     mStatus = reason;
00603     mTransactionDone = PR_TRUE; // forcibly flag the transaction as complete
00604     mClosed = PR_TRUE;
00605 
00606     // release some resources that we no longer need
00607     mRequestStream = nsnull;
00608     mReqHeaderBuf.Truncate();
00609     mLineBuf.Truncate();
00610     if (mChunkedDecoder) {
00611         delete mChunkedDecoder;
00612         mChunkedDecoder = nsnull;
00613     }
00614 
00615     // closing this pipe triggers the channel's OnStopRequest method.
00616     mPipeOut->CloseWithStatus(reason);
00617 }
00618 
00619 //-----------------------------------------------------------------------------
00620 // nsHttpTransaction <private>
00621 //-----------------------------------------------------------------------------
00622 
00623 nsresult
00624 nsHttpTransaction::Restart()
00625 {
00626     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00627 
00628     // limit the number of restart attempts - bug 92224
00629     if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
00630         LOG(("reached max request attempts, failing transaction @%x\n", this));
00631         return NS_ERROR_NET_RESET;
00632     }
00633 
00634     LOG(("restarting transaction @%x\n", this));
00635 
00636     // rewind streams in case we already wrote out the request
00637     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
00638     if (seekable)
00639         seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
00640 
00641     // clear old connection state...
00642     mSecurityInfo = 0;
00643     NS_IF_RELEASE(mConnection);
00644 
00645     // disable pipelining for the next attempt in case pipelining caused the
00646     // reset.  this is being overly cautious since we don't know if pipelining
00647     // was the problem here.
00648     mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
00649 
00650     return gHttpHandler->InitiateTransaction(this, mPriority);
00651 }
00652 
00653 void
00654 nsHttpTransaction::ParseLine(char *line)
00655 {
00656     LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
00657 
00658     if (!mHaveStatusLine) {
00659         mResponseHead->ParseStatusLine(line);
00660         mHaveStatusLine = PR_TRUE;
00661         // XXX this should probably never happen
00662         if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
00663             mHaveAllHeaders = PR_TRUE;
00664     }
00665     else
00666         mResponseHead->ParseHeaderLine(line);
00667 }
00668 
00669 nsresult
00670 nsHttpTransaction::ParseLineSegment(char *segment, PRUint32 len)
00671 {
00672     NS_PRECONDITION(!mHaveAllHeaders, "already have all headers");
00673 
00674     if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
00675         // trim off the new line char, and if this segment is
00676         // not a continuation of the previous or if we haven't
00677         // parsed the status line yet, then parse the contents
00678         // of mLineBuf.
00679         mLineBuf.Truncate(mLineBuf.Length() - 1);
00680         if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
00681             ParseLine(mLineBuf.BeginWriting());
00682             mLineBuf.Truncate();
00683         }
00684     }
00685 
00686     // append segment to mLineBuf...
00687     if (mLineBuf.Length() + len > MAX_LINEBUF_LENGTH) {
00688         LOG(("excessively long header received, canceling transaction [trans=%x]", this));
00689         return NS_ERROR_ABORT;
00690     }
00691     mLineBuf.Append(segment, len);
00692     
00693     // a line buf with only a new line char signifies the end of headers.
00694     if (mLineBuf.First() == '\n') {
00695         mLineBuf.Truncate();
00696         // discard this response if it is a 100 continue or other 1xx status.
00697         if (mResponseHead->Status() / 100 == 1) {
00698             LOG(("ignoring 1xx response\n"));
00699             mHaveStatusLine = PR_FALSE;
00700             mResponseHead->Reset();
00701             return NS_OK;
00702         }
00703         mHaveAllHeaders = PR_TRUE;
00704     }
00705     return NS_OK;
00706 }
00707 
00708 nsresult
00709 nsHttpTransaction::ParseHead(char *buf,
00710                              PRUint32 count,
00711                              PRUint32 *countRead)
00712 {
00713     nsresult rv;
00714     PRUint32 len;
00715     char *eol;
00716 
00717     LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
00718 
00719     *countRead = 0;
00720 
00721     NS_PRECONDITION(!mHaveAllHeaders, "oops");
00722         
00723     // allocate the response head object if necessary
00724     if (!mResponseHead) {
00725         mResponseHead = new nsHttpResponseHead();
00726         if (!mResponseHead)
00727             return NS_ERROR_OUT_OF_MEMORY;
00728 
00729         // report that we have a least some of the response
00730         if (mActivityDistributor)
00731             mActivityDistributor->ObserveActivity(
00732                 mChannel,
00733                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
00734                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
00735                 LL_ZERO, LL_ZERO, EmptyCString());
00736     }
00737 
00738     // if we don't have a status line and the line buf is empty, then
00739     // this must be the first time we've been called.
00740     if (!mHaveStatusLine && mLineBuf.IsEmpty()) {
00741         // tolerate some junk before the status line
00742         char *p = LocateHttpStart(buf, PR_MIN(count, 8));
00743         if (!p) {
00744             // Treat any 0.9 style response of a put as a failure.
00745             if (mRequestHead->Method() == nsHttp::Put)
00746                 return NS_ERROR_ABORT;
00747 
00748             mResponseHead->ParseStatusLine("");
00749             mHaveStatusLine = PR_TRUE;
00750             mHaveAllHeaders = PR_TRUE;
00751             return NS_OK;
00752         }
00753         if (p > buf) {
00754             // skip over the junk
00755             *countRead = p - buf;
00756             buf = p;
00757         }
00758     }
00759     // otherwise we can assume that we don't have a HTTP/0.9 response.
00760 
00761     while ((eol = NS_STATIC_CAST(char *, memchr(buf, '\n', count - *countRead))) != nsnull) {
00762         // found line in range [buf:eol]
00763         len = eol - buf + 1;
00764 
00765         *countRead += len;
00766 
00767         // actually, the line is in the range [buf:eol-1]
00768         if ((eol > buf) && (*(eol-1) == '\r'))
00769             len--;
00770 
00771         buf[len-1] = '\n';
00772         rv = ParseLineSegment(buf, len);
00773         if (NS_FAILED(rv))
00774             return rv;
00775 
00776         if (mHaveAllHeaders)
00777             return NS_OK;
00778 
00779         // skip over line
00780         buf = eol + 1;
00781     }
00782 
00783     // do something about a partial header line
00784     if (!mHaveAllHeaders && (len = count - *countRead)) {
00785         *countRead = count;
00786         // ignore a trailing carriage return, and don't bother calling
00787         // ParseLineSegment if buf only contains a carriage return.
00788         if ((buf[len-1] == '\r') && (--len == 0))
00789             return NS_OK;
00790         rv = ParseLineSegment(buf, len);
00791         if (NS_FAILED(rv))
00792             return rv;
00793     }
00794     return NS_OK;
00795 }
00796 
00797 // called on the socket thread
00798 nsresult
00799 nsHttpTransaction::HandleContentStart()
00800 {
00801     LOG(("nsHttpTransaction::HandleContentStart [this=%x]\n", this));
00802 
00803     if (mResponseHead) {
00804 #if defined(PR_LOGGING)
00805         if (LOG3_ENABLED()) {
00806             LOG3(("http response [\n"));
00807             nsCAutoString headers;
00808             mResponseHead->Flatten(headers, PR_FALSE);
00809             LogHeaders(headers.get());
00810             LOG3(("]\n"));
00811         }
00812 #endif
00813         // notify the connection, give it a chance to cause a reset.
00814         PRBool reset = PR_FALSE;
00815         mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
00816 
00817         // looks like we should ignore this response, resetting...
00818         if (reset) {
00819             LOG(("resetting transaction's response head\n"));
00820             mHaveAllHeaders = PR_FALSE;
00821             mHaveStatusLine = PR_FALSE;
00822             mReceivedData = PR_FALSE;
00823             mSentData = PR_FALSE;
00824             mResponseHead->Reset();
00825             // wait to be called again...
00826             return NS_OK;
00827         }
00828 
00829         // check if this is a no-content response
00830         switch (mResponseHead->Status()) {
00831         case 204:
00832         case 205:
00833         case 304:
00834             mNoContent = PR_TRUE;
00835             LOG(("this response should not contain a body.\n"));
00836             break;
00837         }
00838 
00839         if (mNoContent)
00840             mContentLength = 0;
00841         else {
00842             // grab the content-length from the response headers
00843             mContentLength = mResponseHead->ContentLength();
00844 
00845             // handle chunked encoding here, so we'll know immediately when
00846             // we're done with the socket.  please note that _all_ other
00847             // decoding is done when the channel receives the content data
00848             // so as not to block the socket transport thread too much.
00849             // ignore chunked responses from HTTP/1.0 servers and proxies.
00850             const char *val = mResponseHead->PeekHeader(nsHttp::Transfer_Encoding);
00851             if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 &&
00852                 PL_strcasestr(val, "chunked")) {
00853                 // we only support the "chunked" transfer encoding right now.
00854                 mChunkedDecoder = new nsHttpChunkedDecoder();
00855                 if (!mChunkedDecoder)
00856                     return NS_ERROR_OUT_OF_MEMORY;
00857                 LOG(("chunked decoder created\n"));
00858                 // Ignore server specified Content-Length.
00859                 mContentLength = -1;
00860             }
00861 #if defined(PR_LOGGING)
00862             else if (mContentLength == nsInt64(-1))
00863                 LOG(("waiting for the server to close the connection.\n"));
00864 #endif
00865         }
00866     }
00867 
00868     mDidContentStart = PR_TRUE;
00869     return NS_OK;
00870 }
00871 
00872 // called on the socket thread
00873 nsresult
00874 nsHttpTransaction::HandleContent(char *buf,
00875                                  PRUint32 count,
00876                                  PRUint32 *contentRead,
00877                                  PRUint32 *contentRemaining)
00878 {
00879     nsresult rv;
00880 
00881     LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n", this, count));
00882 
00883     *contentRead = 0;
00884     *contentRemaining = 0;
00885 
00886     NS_ASSERTION(mConnection, "no connection");
00887 
00888     if (!mDidContentStart) {
00889         rv = HandleContentStart();
00890         if (NS_FAILED(rv)) return rv;
00891         // Do not write content to the pipe if we haven't started streaming yet
00892         if (!mDidContentStart)
00893             return NS_OK;
00894     }
00895 
00896     if (mChunkedDecoder) {
00897         // give the buf over to the chunked decoder so it can reformat the
00898         // data and tell us how much is really there.
00899         rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
00900         if (NS_FAILED(rv)) return rv;
00901     }
00902     else if (mContentLength >= nsInt64(0)) {
00903         // HTTP/1.0 servers have been known to send erroneous Content-Length
00904         // headers. So, unless the connection is persistent, we must make
00905         // allowances for a possibly invalid Content-Length header. Thus, if
00906         // NOT persistent, we simply accept everything in |buf|.
00907         if (mConnection->IsPersistent()) {
00908             nsInt64 remaining = mContentLength - mContentRead;
00909             nsInt64 count64 = count;
00910             *contentRead = PR_MIN(count64, remaining);
00911             *contentRemaining = count - *contentRead;
00912         }
00913         else {
00914             *contentRead = count;
00915             // mContentLength might need to be increased...
00916             nsInt64 position = mContentRead + nsInt64(count);
00917             if (position > mContentLength) {
00918                 mContentLength = position;
00919                 //mResponseHead->SetContentLength(mContentLength);
00920             }
00921         }
00922     }
00923     else {
00924         // when we are just waiting for the server to close the connection...
00925         // (no explicit content-length given)
00926         *contentRead = count;
00927     }
00928 
00929     if (*contentRead) {
00930         // update count of content bytes read and report progress...
00931         mContentRead += *contentRead;
00932         /* when uncommenting, take care of 64-bit integers w/ PR_MAX...
00933         if (mProgressSink)
00934             mProgressSink->OnProgress(nsnull, nsnull, mContentRead, PR_MAX(0, mContentLength));
00935         */
00936     }
00937 
00938     LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
00939         this, count, *contentRead, mContentRead.mValue, mContentLength.mValue));
00940 
00941     // check for end-of-file
00942     if ((mContentRead == mContentLength) ||
00943         (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
00944         // the transaction is done with a complete response.
00945         mTransactionDone = PR_TRUE;
00946         mResponseIsComplete = PR_TRUE;
00947 
00948         // report the entire response has arrived
00949         if (mActivityDistributor)
00950             mActivityDistributor->ObserveActivity(
00951                 mChannel,
00952                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
00953                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
00954                 LL_ZERO,
00955                 NS_STATIC_CAST(PRUint64, mContentRead.mValue),
00956                 EmptyCString());
00957     }
00958 
00959     return NS_OK;
00960 }
00961 
00962 nsresult
00963 nsHttpTransaction::ProcessData(char *buf, PRUint32 count, PRUint32 *countRead)
00964 {
00965     nsresult rv;
00966 
00967     LOG(("nsHttpTransaction::ProcessData [this=%x count=%u]\n", this, count));
00968 
00969     *countRead = 0;
00970 
00971     // we may not have read all of the headers yet...
00972     if (!mHaveAllHeaders) {
00973         PRUint32 bytesConsumed = 0;
00974 
00975         rv = ParseHead(buf, count, &bytesConsumed);
00976         if (NS_FAILED(rv)) return rv;
00977 
00978         count -= bytesConsumed;
00979 
00980         // if buf has some content in it, shift bytes to top of buf.
00981         if (count && bytesConsumed)
00982             memmove(buf, buf + bytesConsumed, count);
00983 
00984         // report the completed response header
00985         if (mActivityDistributor && mResponseHead && mHaveAllHeaders) {
00986             nsCAutoString completeResponseHeaders;
00987             mResponseHead->Flatten(completeResponseHeaders, PR_FALSE);
00988             completeResponseHeaders.AppendLiteral("\r\n");
00989             mActivityDistributor->ObserveActivity(
00990                 mChannel,
00991                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
00992                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
00993                 LL_ZERO, LL_ZERO,
00994                 completeResponseHeaders);
00995         }
00996     }
00997 
00998     // even though count may be 0, we still want to call HandleContent
00999     // so it can complete the transaction if this is a "no-content" response.
01000     if (mHaveAllHeaders) {
01001         PRUint32 countRemaining = 0;
01002         //
01003         // buf layout:
01004         // 
01005         // +--------------------------------------+----------------+-----+
01006         // |              countRead               | countRemaining |     |
01007         // +--------------------------------------+----------------+-----+
01008         //
01009         // count          : bytes read from the socket
01010         // countRead      : bytes corresponding to this transaction
01011         // countRemaining : bytes corresponding to next pipelined transaction
01012         //
01013         // NOTE:
01014         // count > countRead + countRemaining <==> chunked transfer encoding
01015         //
01016         rv = HandleContent(buf, count, countRead, &countRemaining);
01017         if (NS_FAILED(rv)) return rv;
01018         // we may have read more than our share, in which case we must give
01019         // the excess bytes back to the connection
01020         if (mResponseIsComplete && countRemaining) {
01021             NS_ASSERTION(mConnection, "no connection");
01022             mConnection->PushBack(buf + *countRead, countRemaining);
01023         }
01024     }
01025 
01026     return NS_OK;
01027 }
01028 
01029 //-----------------------------------------------------------------------------
01030 // nsHttpTransaction deletion event
01031 //-----------------------------------------------------------------------------
01032 
01033 void
01034 nsHttpTransaction::DeleteSelfOnConsumerThread()
01035 {
01036     nsCOMPtr<nsIEventQueueService> eqs;
01037     nsCOMPtr<nsIEventQueue> currentEventQ;
01038 
01039     LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%x]\n", this));
01040     
01041     NS_ASSERTION(!mDestroying, "deleting self again");
01042     mDestroying = PR_TRUE;
01043 
01044     gHttpHandler->GetCurrentEventQ(getter_AddRefs(currentEventQ));
01045 
01046     if (currentEventQ == mConsumerEventQ)
01047         delete this;
01048     else {
01049         LOG(("proxying delete to consumer thread...\n"));
01050 
01051         PLEvent *event = new PLEvent;
01052         if (!event) {
01053             NS_WARNING("out of memory");
01054             // probably better to leak |this| than to delete it on this thread.
01055             return;
01056         }
01057 
01058         PL_InitEvent(event, this, DeleteThis_Handler, DeleteThis_Cleanup);
01059 
01060         nsresult status = mConsumerEventQ->PostEvent(event);
01061         if (NS_FAILED(status))
01062             NS_ERROR("PostEvent failed");
01063     }
01064 }
01065 
01066 void *PR_CALLBACK
01067 nsHttpTransaction::DeleteThis_Handler(PLEvent *ev)
01068 {
01069     nsHttpTransaction *trans =
01070             NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev));
01071 
01072     LOG(("nsHttpTransaction::DeleteThis_EventHandlerFunc [trans=%x]\n", trans));
01073 
01074     delete trans;
01075     return nsnull;
01076 }
01077 
01078 void PR_CALLBACK
01079 nsHttpTransaction::DeleteThis_Cleanup(PLEvent *ev)
01080 {
01081     delete ev;
01082 }
01083 
01084 //-----------------------------------------------------------------------------
01085 // nsHttpTransaction::nsISupports
01086 //-----------------------------------------------------------------------------
01087 
01088 NS_IMPL_THREADSAFE_ADDREF(nsHttpTransaction)
01089 
01090 NS_IMETHODIMP_(nsrefcnt)
01091 nsHttpTransaction::Release()
01092 {
01093     nsrefcnt count;
01094     NS_PRECONDITION(0 != mRefCnt, "dup release");
01095     count = PR_AtomicDecrement((PRInt32 *) &mRefCnt);
01096     NS_LOG_RELEASE(this, count, "nsHttpTransaction");
01097     if (0 == count) {
01098         mRefCnt = 1; /* stablize */
01099         // it is essential that the transaction be destroyed on the consumer 
01100         // thread (we could be holding the last reference to our consumer).
01101         DeleteSelfOnConsumerThread();
01102         return 0;
01103     }
01104     return count;
01105 }
01106 
01107 NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHttpTransaction,
01108                                     nsIInputStreamCallback,
01109                                     nsIOutputStreamCallback)
01110 
01111 //-----------------------------------------------------------------------------
01112 // nsHttpTransaction::nsIInputStreamCallback
01113 //-----------------------------------------------------------------------------
01114 
01115 // called on the socket thread
01116 NS_IMETHODIMP
01117 nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
01118 {
01119     if (mConnection) {
01120         nsresult rv = mConnection->ResumeSend();
01121         if (NS_FAILED(rv))
01122             NS_ERROR("ResumeSend failed");
01123     }
01124     return NS_OK;
01125 }
01126 
01127 //-----------------------------------------------------------------------------
01128 // nsHttpTransaction::nsIOutputStreamCallback
01129 //-----------------------------------------------------------------------------
01130 
01131 // called on the socket thread
01132 NS_IMETHODIMP
01133 nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
01134 {
01135     if (mConnection) {
01136         nsresult rv = mConnection->ResumeRecv();
01137         if (NS_FAILED(rv))
01138             NS_ERROR("ResumeRecv failed");
01139     }
01140     return NS_OK;
01141 }