LCOV - code coverage report
Current view: directory - netwerk/protocol/http - nsHttpTransaction.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 536 391 72.9 %
Date: 2012-06-02 Functions: 40 31 77.5 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* vim:set ts=4 sw=4 sts=4 et cin: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Darin Fisher <darin@netscape.com> (original author)
      25                 :  *   Andreas M. Schneider <clarence@clarence.de>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "base/basictypes.h"
      42                 : 
      43                 : #include "nsIOService.h"
      44                 : #include "nsHttpHandler.h"
      45                 : #include "nsHttpTransaction.h"
      46                 : #include "nsHttpConnection.h"
      47                 : #include "nsHttpRequestHead.h"
      48                 : #include "nsHttpResponseHead.h"
      49                 : #include "nsHttpChunkedDecoder.h"
      50                 : #include "nsTransportUtils.h"
      51                 : #include "nsNetUtil.h"
      52                 : #include "nsProxyRelease.h"
      53                 : #include "nsIOService.h"
      54                 : #include "nsAtomicRefcnt.h"
      55                 : 
      56                 : #include "nsISeekableStream.h"
      57                 : #include "nsISocketTransport.h"
      58                 : #include "nsMultiplexInputStream.h"
      59                 : #include "nsStringStream.h"
      60                 : 
      61                 : #include "nsComponentManagerUtils.h" // do_CreateInstance
      62                 : #include "nsServiceManagerUtils.h"   // do_GetService
      63                 : #include "nsIHttpActivityObserver.h"
      64                 : 
      65                 : #include "mozilla/FunctionTimer.h"
      66                 : 
      67                 : //-----------------------------------------------------------------------------
      68                 : 
      69                 : #ifdef DEBUG
      70                 : // defined by the socket transport service while active
      71                 : extern PRThread *gSocketThread;
      72                 : #endif
      73                 : 
      74                 : //-----------------------------------------------------------------------------
      75                 : 
      76                 : static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
      77                 : 
      78                 : // Place a limit on how much non-compliant HTTP can be skipped while
      79                 : // looking for a response header
      80                 : #define MAX_INVALID_RESPONSE_BODY_SIZE (1024 * 128)
      81                 : 
      82                 : //-----------------------------------------------------------------------------
      83                 : // helpers
      84                 : //-----------------------------------------------------------------------------
      85                 : 
      86                 : #if defined(PR_LOGGING)
      87                 : static void
      88               0 : LogHeaders(const char *lines)
      89                 : {
      90               0 :     nsCAutoString buf;
      91                 :     char *p;
      92               0 :     while ((p = PL_strstr(lines, "\r\n")) != nsnull) {
      93               0 :         buf.Assign(lines, p - lines);
      94               0 :         if (PL_strcasestr(buf.get(), "authorization: ") != nsnull) {
      95               0 :             char *p = PL_strchr(PL_strchr(buf.get(), ' ')+1, ' ');
      96               0 :             while (*++p) *p = '*';
      97                 :         }
      98               0 :         LOG3(("  %s\n", buf.get()));
      99               0 :         lines = p + 2;
     100                 :     }
     101               0 : }
     102                 : #endif
     103                 : 
     104                 : //-----------------------------------------------------------------------------
     105                 : // nsHttpTransaction <public>
     106                 : //-----------------------------------------------------------------------------
     107                 : 
     108            2989 : nsHttpTransaction::nsHttpTransaction()
     109                 :     : mRequestSize(0)
     110                 :     , mConnection(nsnull)
     111                 :     , mConnInfo(nsnull)
     112                 :     , mRequestHead(nsnull)
     113                 :     , mResponseHead(nsnull)
     114                 :     , mContentLength(-1)
     115                 :     , mContentRead(0)
     116                 :     , mInvalidResponseBytesRead(0)
     117                 :     , mChunkedDecoder(nsnull)
     118                 :     , mStatus(NS_OK)
     119                 :     , mPriority(0)
     120                 :     , mRestartCount(0)
     121                 :     , mCaps(0)
     122                 :     , mClosed(false)
     123                 :     , mConnected(false)
     124                 :     , mHaveStatusLine(false)
     125                 :     , mHaveAllHeaders(false)
     126                 :     , mTransactionDone(false)
     127                 :     , mResponseIsComplete(false)
     128                 :     , mDidContentStart(false)
     129                 :     , mNoContent(false)
     130                 :     , mSentData(false)
     131                 :     , mReceivedData(false)
     132                 :     , mStatusEventPending(false)
     133                 :     , mHasRequestBody(false)
     134                 :     , mSSLConnectFailed(false)
     135                 :     , mHttpResponseMatched(false)
     136            2989 :     , mPreserveStream(false)
     137                 : {
     138            2989 :     LOG(("Creating nsHttpTransaction @%x\n", this));
     139            2989 : }
     140                 : 
     141            8931 : nsHttpTransaction::~nsHttpTransaction()
     142                 : {
     143            2977 :     LOG(("Destroying nsHttpTransaction @%x\n", this));
     144                 : 
     145            2977 :     NS_IF_RELEASE(mConnection);
     146            2977 :     NS_IF_RELEASE(mConnInfo);
     147                 : 
     148            2977 :     delete mResponseHead;
     149            2977 :     delete mChunkedDecoder;
     150           11908 : }
     151                 : 
     152                 : nsresult
     153            2989 : nsHttpTransaction::Init(PRUint8 caps,
     154                 :                         nsHttpConnectionInfo *cinfo,
     155                 :                         nsHttpRequestHead *requestHead,
     156                 :                         nsIInputStream *requestBody,
     157                 :                         bool requestBodyHasHeaders,
     158                 :                         nsIEventTarget *target,
     159                 :                         nsIInterfaceRequestor *callbacks,
     160                 :                         nsITransportEventSink *eventsink,
     161                 :                         nsIAsyncInputStream **responseBody)
     162                 : {
     163                 :     NS_TIME_FUNCTION;
     164                 : 
     165                 :     nsresult rv;
     166                 : 
     167            2989 :     LOG(("nsHttpTransaction::Init [this=%x caps=%x]\n", this, caps));
     168                 : 
     169            2989 :     NS_ASSERTION(cinfo, "ouch");
     170            2989 :     NS_ASSERTION(requestHead, "ouch");
     171            2989 :     NS_ASSERTION(target, "ouch");
     172                 : 
     173            2989 :     mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv);
     174            2989 :     if (NS_FAILED(rv)) return rv;
     175                 : 
     176                 :     bool activityDistributorActive;
     177            2989 :     rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
     178            2989 :     if (NS_SUCCEEDED(rv) && activityDistributorActive) {
     179                 :         // there are some observers registered at activity distributor, gather
     180                 :         // nsISupports for the channel that called Init()
     181               8 :         mChannel = do_QueryInterface(eventsink);
     182               8 :         LOG(("nsHttpTransaction::Init() " \
     183                 :              "mActivityDistributor is active " \
     184                 :              "this=%x", this));
     185                 :     } else {
     186                 :         // there is no observer, so don't use it
     187            2981 :         activityDistributorActive = false;
     188            2981 :         mActivityDistributor = nsnull;
     189                 :     }
     190                 : 
     191                 :     // create transport event sink proxy. it coalesces all events if and only 
     192                 :     // if the activity observer is not active. when the observer is active
     193                 :     // we need not to coalesce any events to get all expected notifications
     194                 :     // of the transaction state, necessary for correct debugging and logging.
     195            2989 :     rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
     196                 :                                         eventsink, target,
     197            2989 :                                         !activityDistributorActive);
     198            2989 :     if (NS_FAILED(rv)) return rv;
     199                 : 
     200            2989 :     NS_ADDREF(mConnInfo = cinfo);
     201            2989 :     mCallbacks = callbacks;
     202            2989 :     mConsumerTarget = target;
     203            2989 :     mCaps = caps;
     204                 : 
     205            2989 :     if (requestHead->Method() == nsHttp::Head)
     206              14 :         mNoContent = true;
     207                 : 
     208                 :     // Make sure that there is "Content-Length: 0" header in the requestHead
     209                 :     // in case of POST and PUT methods when there is no requestBody and
     210                 :     // requestHead doesn't contain "Transfer-Encoding" header.
     211                 :     //
     212                 :     // RFC1945 section 7.2.2:
     213                 :     //   HTTP/1.0 requests containing an entity body must include a valid
     214                 :     //   Content-Length header field.
     215                 :     //
     216                 :     // RFC2616 section 4.4:
     217                 :     //   For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
     218                 :     //   containing a message-body MUST include a valid Content-Length header
     219                 :     //   field unless the server is known to be HTTP/1.1 compliant.
     220            3008 :     if ((requestHead->Method() == nsHttp::Post || requestHead->Method() == nsHttp::Put) &&
     221              19 :         !requestBody && !requestHead->PeekHeader(nsHttp::Transfer_Encoding)) {
     222              19 :         requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
     223                 :     }
     224                 : 
     225                 :     // grab a weak reference to the request head
     226            2989 :     mRequestHead = requestHead;
     227                 : 
     228                 :     // make sure we eliminate any proxy specific headers from 
     229                 :     // the request if we are talking HTTPS via a SSL tunnel.
     230                 :     bool pruneProxyHeaders = 
     231            2989 :         cinfo->ShouldForceConnectMethod() ||
     232            2989 :         (cinfo->UsingSSL() && cinfo->UsingHttpProxy());
     233                 :     
     234            2989 :     mReqHeaderBuf.Truncate();
     235            2989 :     requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
     236                 : 
     237                 : #if defined(PR_LOGGING)
     238            2989 :     if (LOG3_ENABLED()) {
     239               0 :         LOG3(("http request [\n"));
     240               0 :         LogHeaders(mReqHeaderBuf.get());
     241               0 :         LOG3(("]\n"));
     242                 :     }
     243                 : #endif
     244                 : 
     245                 :     // If the request body does not include headers or if there is no request
     246                 :     // body, then we must add the header/body separator manually.
     247            2989 :     if (!requestBodyHasHeaders || !requestBody)
     248            2986 :         mReqHeaderBuf.AppendLiteral("\r\n");
     249                 : 
     250                 :     // report the request header
     251            2989 :     if (mActivityDistributor)
     252               8 :         mActivityDistributor->ObserveActivity(
     253                 :             mChannel,
     254                 :             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
     255                 :             NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
     256                 :             PR_Now(), LL_ZERO,
     257               8 :             mReqHeaderBuf);
     258                 : 
     259                 :     // Create a string stream for the request header buf (the stream holds
     260                 :     // a non-owning reference to the request header data, so we MUST keep
     261                 :     // mReqHeaderBuf around).
     262            5978 :     nsCOMPtr<nsIInputStream> headers;
     263            2989 :     rv = NS_NewByteInputStream(getter_AddRefs(headers),
     264                 :                                mReqHeaderBuf.get(),
     265            2989 :                                mReqHeaderBuf.Length());
     266            2989 :     if (NS_FAILED(rv)) return rv;
     267                 : 
     268            2989 :     if (requestBody) {
     269             385 :         mHasRequestBody = true;
     270                 : 
     271                 :         // wrap the headers and request body in a multiplexed input stream.
     272                 :         nsCOMPtr<nsIMultiplexInputStream> multi =
     273             770 :             do_CreateInstance(kMultiplexInputStream, &rv);
     274             385 :         if (NS_FAILED(rv)) return rv;
     275                 : 
     276             385 :         rv = multi->AppendStream(headers);
     277             385 :         if (NS_FAILED(rv)) return rv;
     278                 : 
     279             385 :         rv = multi->AppendStream(requestBody);
     280             385 :         if (NS_FAILED(rv)) return rv;
     281                 : 
     282                 :         // wrap the multiplexed input stream with a buffered input stream, so
     283                 :         // that we write data in the largest chunks possible.  this is actually
     284                 :         // necessary to workaround some common server bugs (see bug 137155).
     285             385 :         rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi,
     286             385 :                                        nsIOService::gDefaultSegmentSize);
     287             385 :         if (NS_FAILED(rv)) return rv;
     288                 :     }
     289                 :     else
     290            2604 :         mRequestStream = headers;
     291                 : 
     292            2989 :     rv = mRequestStream->Available(&mRequestSize);
     293            2989 :     if (NS_FAILED(rv)) return rv;
     294                 : 
     295                 :     // create pipe for response stream
     296            2989 :     rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
     297            2989 :                      getter_AddRefs(mPipeOut),
     298                 :                      true, true,
     299                 :                      nsIOService::gDefaultSegmentSize,
     300            2989 :                      nsIOService::gDefaultSegmentCount);
     301            2989 :     if (NS_FAILED(rv)) return rv;
     302                 : 
     303            2989 :     NS_ADDREF(*responseBody = mPipeIn);
     304            2989 :     return NS_OK;
     305                 : }
     306                 : 
     307                 : nsAHttpConnection *
     308            3173 : nsHttpTransaction::Connection()
     309                 : {
     310            3173 :     return mConnection;
     311                 : }
     312                 : 
     313                 : nsHttpResponseHead *
     314            2819 : nsHttpTransaction::TakeResponseHead()
     315                 : {
     316            2819 :     if (!mHaveAllHeaders) {
     317               0 :         NS_WARNING("response headers not available or incomplete");
     318               0 :         return nsnull;
     319                 :     }
     320                 : 
     321            2819 :     nsHttpResponseHead *head = mResponseHead;
     322            2819 :     mResponseHead = nsnull;
     323            2819 :     return head;
     324                 : }
     325                 : 
     326                 : void
     327               0 : nsHttpTransaction::SetSSLConnectFailed()
     328                 : {
     329               0 :     mSSLConnectFailed = true;
     330               0 : }
     331                 : 
     332                 : nsHttpRequestHead *
     333               0 : nsHttpTransaction::RequestHead()
     334                 : {
     335               0 :     return mRequestHead;
     336                 : }
     337                 : 
     338                 : PRUint32
     339            2975 : nsHttpTransaction::Http1xTransactionCount()
     340                 : {
     341            2975 :   return 1;
     342                 : }
     343                 : 
     344                 : nsresult
     345               0 : nsHttpTransaction::TakeSubTransactions(
     346                 :     nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
     347                 : {
     348               0 :     return NS_ERROR_NOT_IMPLEMENTED;
     349                 : }
     350                 : 
     351                 : //----------------------------------------------------------------------------
     352                 : // nsHttpTransaction::nsAHttpTransaction
     353                 : //----------------------------------------------------------------------------
     354                 : 
     355                 : void
     356            2975 : nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
     357                 : {
     358            2975 :     NS_IF_RELEASE(mConnection);
     359            2975 :     NS_IF_ADDREF(mConnection = conn);
     360            2975 : }
     361                 : 
     362                 : void
     363            5951 : nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb,
     364                 :                                         nsIEventTarget        **target)
     365                 : {
     366            5951 :     NS_IF_ADDREF(*cb = mCallbacks);
     367            5951 :     if (target)
     368            5951 :         NS_IF_ADDREF(*target = mConsumerTarget);
     369            5951 : }
     370                 : 
     371                 : void
     372           23147 : nsHttpTransaction::OnTransportStatus(nsITransport* transport,
     373                 :                                      nsresult status, PRUint64 progress)
     374                 : {
     375           23147 :     LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n",
     376                 :         this, status, progress));
     377                 : 
     378           23147 :     if (TimingEnabled()) {
     379              16 :         if (status == nsISocketTransport::STATUS_RESOLVING) {
     380               2 :             mTimings.domainLookupStart = mozilla::TimeStamp::Now();
     381              14 :         } else if (status == nsISocketTransport::STATUS_RESOLVED) {
     382               2 :             mTimings.domainLookupEnd = mozilla::TimeStamp::Now();
     383              12 :         } else if (status == nsISocketTransport::STATUS_CONNECTING_TO) {
     384               2 :             mTimings.connectStart = mozilla::TimeStamp::Now();
     385              10 :         } else if (status == nsISocketTransport::STATUS_CONNECTED_TO) {
     386               2 :             mTimings.connectEnd = mozilla::TimeStamp::Now();
     387                 :         }
     388                 :     }
     389                 : 
     390           23147 :     if (!mTransportSink)
     391               0 :         return;
     392                 : 
     393           23147 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     394                 : 
     395                 :     // Need to do this before the STATUS_RECEIVING_FROM check below, to make
     396                 :     // sure that the activity distributor gets told about all status events.
     397           23147 :     if (mActivityDistributor) {
     398                 :         // upon STATUS_WAITING_FOR; report request body sent
     399              60 :         if ((mHasRequestBody) &&
     400                 :             (status == nsISocketTransport::STATUS_WAITING_FOR))
     401               0 :             mActivityDistributor->ObserveActivity(
     402                 :                 mChannel,
     403                 :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
     404                 :                 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
     405               0 :                 PR_Now(), LL_ZERO, EmptyCString());
     406                 : 
     407                 :         // report the status and progress
     408              60 :         mActivityDistributor->ObserveActivity(
     409                 :             mChannel,
     410                 :             NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
     411                 :             static_cast<PRUint32>(status),
     412                 :             PR_Now(),
     413                 :             progress,
     414              60 :             EmptyCString());
     415                 :     }
     416                 : 
     417                 :     // nsHttpChannel synthesizes progress events in OnDataAvailable
     418           23147 :     if (status == nsISocketTransport::STATUS_RECEIVING_FROM)
     419            5721 :         return;
     420                 : 
     421                 :     PRUint64 progressMax;
     422                 : 
     423           17426 :     if (status == nsISocketTransport::STATUS_SENDING_TO) {
     424                 :         // suppress progress when only writing request headers
     425            2836 :         if (!mHasRequestBody)
     426            2451 :             return;
     427                 : 
     428             770 :         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
     429             385 :         NS_ASSERTION(seekable, "Request stream isn't seekable?!?");
     430                 : 
     431             385 :         PRInt64 prog = 0;
     432             385 :         seekable->Tell(&prog);
     433             385 :         progress = prog;
     434                 : 
     435                 :         // when uploading, we include the request headers in the progress
     436                 :         // notifications.
     437             385 :         progressMax = mRequestSize; // XXX mRequestSize is 32-bit!
     438                 :     }
     439                 :     else {
     440           14590 :         progress = LL_ZERO;
     441           14590 :         progressMax = 0;
     442                 :     }
     443                 : 
     444           14975 :     mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
     445                 : }
     446                 : 
     447                 : bool
     448              10 : nsHttpTransaction::IsDone()
     449                 : {
     450              10 :     return mTransactionDone;
     451                 : }
     452                 : 
     453                 : nsresult
     454            2977 : nsHttpTransaction::Status()
     455                 : {
     456            2977 :     return mStatus;
     457                 : }
     458                 : 
     459                 : PRUint32
     460               0 : nsHttpTransaction::Available()
     461                 : {
     462                 :     PRUint32 size;
     463               0 :     if (NS_FAILED(mRequestStream->Available(&size)))
     464               0 :         size = 0;
     465               0 :     return size;
     466                 : }
     467                 : 
     468                 : NS_METHOD
     469            2978 : nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
     470                 :                                       void *closure,
     471                 :                                       const char *buf,
     472                 :                                       PRUint32 offset,
     473                 :                                       PRUint32 count,
     474                 :                                       PRUint32 *countRead)
     475                 : {
     476            2978 :     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
     477            2978 :     nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
     478            2978 :     if (NS_FAILED(rv)) return rv;
     479                 : 
     480            2836 :     if (trans->TimingEnabled() && trans->mTimings.requestStart.IsNull()) {
     481                 :         // First data we're sending -> this is requestStart
     482               2 :         trans->mTimings.requestStart = mozilla::TimeStamp::Now();
     483                 :     }
     484            2836 :     trans->mSentData = true;
     485            2836 :     return NS_OK;
     486                 : }
     487                 : 
     488                 : nsresult
     489            5812 : nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
     490                 :                                 PRUint32 count, PRUint32 *countRead)
     491                 : {
     492            5812 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     493                 : 
     494            5812 :     if (mTransactionDone) {
     495               0 :         *countRead = 0;
     496               0 :         return mStatus;
     497                 :     }
     498                 : 
     499            5812 :     if (!mConnected) {
     500            2975 :         mConnected = true;
     501            2975 :         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
     502                 :     }
     503                 : 
     504            5812 :     mReader = reader;
     505                 : 
     506            5812 :     nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
     507                 : 
     508            5812 :     mReader = nsnull;
     509                 : 
     510                 :     // if read would block then we need to AsyncWait on the request stream.
     511                 :     // have callback occur on socket thread so we stay synchronized.
     512            5812 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
     513                 :         nsCOMPtr<nsIAsyncInputStream> asyncIn =
     514               0 :                 do_QueryInterface(mRequestStream);
     515               0 :         if (asyncIn) {
     516               0 :             nsCOMPtr<nsIEventTarget> target;
     517               0 :             gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
     518               0 :             if (target)
     519               0 :                 asyncIn->AsyncWait(this, 0, 0, target);
     520                 :             else {
     521               0 :                 NS_ERROR("no socket thread event target");
     522               0 :                 rv = NS_ERROR_UNEXPECTED;
     523                 :             }
     524                 :         }
     525                 :     }
     526                 : 
     527            5812 :     return rv;
     528                 : }
     529                 : 
     530                 : NS_METHOD
     531           11122 : nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
     532                 :                                     void *closure,
     533                 :                                     char *buf,
     534                 :                                     PRUint32 offset,
     535                 :                                     PRUint32 count,
     536                 :                                     PRUint32 *countWritten)
     537                 : {
     538           11122 :     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
     539                 : 
     540           11122 :     if (trans->mTransactionDone)
     541            2482 :         return NS_BASE_STREAM_CLOSED; // stop iterating
     542                 : 
     543            8640 :     if (trans->TimingEnabled() && trans->mTimings.responseStart.IsNull()) {
     544               2 :         trans->mTimings.responseStart = mozilla::TimeStamp::Now();
     545                 :     }
     546                 : 
     547                 :     nsresult rv;
     548                 :     //
     549                 :     // OK, now let the caller fill this segment with data.
     550                 :     //
     551            8640 :     rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
     552            8640 :     if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
     553                 : 
     554            5721 :     NS_ASSERTION(*countWritten > 0, "bad writer");
     555            5721 :     trans->mReceivedData = true;
     556                 : 
     557                 :     // Let the transaction "play" with the buffer.  It is free to modify
     558                 :     // the contents of the buffer and/or modify countWritten.
     559                 :     // - Bytes in HTTP headers don't count towards countWritten, so the input
     560                 :     // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
     561                 :     // OnInputStreamReady until all headers have been parsed.
     562                 :     //    
     563            5721 :     rv = trans->ProcessData(buf, *countWritten, countWritten);
     564            5721 :     if (NS_FAILED(rv))
     565              11 :         trans->Close(rv);
     566                 : 
     567            5721 :     return rv; // failure code only stops WriteSegments; it is not propagated.
     568                 : }
     569                 : 
     570                 : nsresult
     571           11025 : nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
     572                 :                                  PRUint32 count, PRUint32 *countWritten)
     573                 : {
     574           11025 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     575                 : 
     576           11025 :     if (mTransactionDone)
     577            2811 :         return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
     578                 : 
     579            8214 :     mWriter = writer;
     580                 : 
     581            8214 :     nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
     582                 : 
     583            8214 :     mWriter = nsnull;
     584                 : 
     585                 :     // if pipe would block then we need to AsyncWait on it.  have callback
     586                 :     // occur on socket thread so we stay synchronized.
     587            8214 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
     588               0 :         nsCOMPtr<nsIEventTarget> target;
     589               0 :         gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
     590               0 :         if (target)
     591               0 :             mPipeOut->AsyncWait(this, 0, 0, target);
     592                 :         else {
     593               0 :             NS_ERROR("no socket thread event target");
     594               0 :             rv = NS_ERROR_UNEXPECTED;
     595                 :         }
     596                 :     }
     597                 : 
     598            8214 :     return rv;
     599                 : }
     600                 : 
     601                 : void
     602            3141 : nsHttpTransaction::Close(nsresult reason)
     603                 : {
     604            3141 :     LOG(("nsHttpTransaction::Close [this=%x reason=%x]\n", this, reason));
     605                 : 
     606            3141 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     607                 : 
     608            3141 :     if (mClosed) {
     609             164 :         LOG(("  already closed\n"));
     610             164 :         return;
     611                 :     }
     612                 : 
     613            2977 :     if (mActivityDistributor) {
     614                 :         // report the reponse is complete if not already reported
     615               8 :         if (!mResponseIsComplete)
     616               0 :             mActivityDistributor->ObserveActivity(
     617                 :                 mChannel,
     618                 :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
     619                 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
     620                 :                 PR_Now(),
     621                 :                 static_cast<PRUint64>(mContentRead),
     622               0 :                 EmptyCString());
     623                 : 
     624                 :         // report that this transaction is closing
     625               8 :         mActivityDistributor->ObserveActivity(
     626                 :             mChannel,
     627                 :             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
     628                 :             NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
     629               8 :             PR_Now(), LL_ZERO, EmptyCString());
     630                 :     }
     631                 : 
     632                 :     // we must no longer reference the connection!  find out if the 
     633                 :     // connection was being reused before letting it go.
     634            2977 :     bool connReused = false;
     635            2977 :     if (mConnection)
     636            2975 :         connReused = mConnection->IsReused();
     637            2977 :     mConnected = false;
     638                 : 
     639                 :     //
     640                 :     // if the connection was reset or closed before we wrote any part of the
     641                 :     // request or if we wrote the request but didn't receive any part of the
     642                 :     // response and the connection was being reused, then we can (and really
     643                 :     // should) assume that we wrote to a stale connection and we must therefore
     644                 :     // repeat the request over a new connection.
     645                 :     //
     646                 :     // NOTE: the conditions under which we will automatically retry the HTTP
     647                 :     // request have to be carefully selected to avoid duplication of the
     648                 :     // request from the point-of-view of the server.  such duplication could
     649                 :     // have dire consequences including repeated purchases, etc.
     650                 :     //
     651                 :     // NOTE: because of the way SSL proxy CONNECT is implemented, it is
     652                 :     // possible that the transaction may have received data without having
     653                 :     // sent any data.  for this reason, mSendData == FALSE does not imply
     654                 :     // mReceivedData == FALSE.  (see bug 203057 for more info.)
     655                 :     //
     656            2977 :     if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
     657            2814 :         if (!mReceivedData && (!mSentData || connReused)) {
     658                 :             // if restarting fails, then we must proceed to close the pipe,
     659                 :             // which will notify the channel that the transaction failed.
     660               0 :             if (NS_SUCCEEDED(Restart()))
     661               0 :                 return;
     662                 :         }
     663                 :     }
     664                 : 
     665            2977 :     bool relConn = true;
     666            2977 :     if (NS_SUCCEEDED(reason)) {
     667                 :         // the server has not sent the final \r\n terminating the header
     668                 :         // section, and there may still be a header line unparsed.  let's make
     669                 :         // sure we parse the remaining header line, and then hopefully, the
     670                 :         // response will be usable (see bug 88792).
     671            2814 :         if (!mHaveAllHeaders) {
     672               0 :             char data = '\n';
     673                 :             PRUint32 unused;
     674               0 :             ParseHead(&data, 1, &unused);
     675                 : 
     676               0 :             if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) {
     677                 :                 // Reject 0 byte HTTP/0.9 Responses - bug 423506
     678               0 :                 LOG(("nsHttpTransaction::Close %p 0 Byte 0.9 Response", this));
     679               0 :                 reason = NS_ERROR_NET_RESET;
     680                 :             }
     681                 :         }
     682                 : 
     683                 :         // honor the sticky connection flag...
     684            2814 :         if (mCaps & NS_HTTP_STICKY_CONNECTION)
     685              23 :             relConn = false;
     686                 :     }
     687            2977 :     if (relConn && mConnection)
     688            2952 :         NS_RELEASE(mConnection);
     689                 : 
     690            2977 :     mStatus = reason;
     691            2977 :     mTransactionDone = true; // forcibly flag the transaction as complete
     692            2977 :     mClosed = true;
     693                 : 
     694                 :     // release some resources that we no longer need
     695            2977 :     mRequestStream = nsnull;
     696            2977 :     mReqHeaderBuf.Truncate();
     697            2977 :     mLineBuf.Truncate();
     698            2977 :     if (mChunkedDecoder) {
     699               4 :         delete mChunkedDecoder;
     700               4 :         mChunkedDecoder = nsnull;
     701                 :     }
     702                 : 
     703                 :     // closing this pipe triggers the channel's OnStopRequest method.
     704            2977 :     mPipeOut->CloseWithStatus(reason);
     705                 : }
     706                 : 
     707                 : //-----------------------------------------------------------------------------
     708                 : // nsHttpTransaction <private>
     709                 : //-----------------------------------------------------------------------------
     710                 : 
     711                 : nsresult
     712               0 : nsHttpTransaction::Restart()
     713                 : {
     714               0 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     715                 : 
     716                 :     // limit the number of restart attempts - bug 92224
     717               0 :     if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
     718               0 :         LOG(("reached max request attempts, failing transaction @%x\n", this));
     719               0 :         return NS_ERROR_NET_RESET;
     720                 :     }
     721                 : 
     722               0 :     LOG(("restarting transaction @%x\n", this));
     723                 : 
     724                 :     // rewind streams in case we already wrote out the request
     725               0 :     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
     726               0 :     if (seekable)
     727               0 :         seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     728                 : 
     729                 :     // clear old connection state...
     730               0 :     mSecurityInfo = 0;
     731               0 :     NS_IF_RELEASE(mConnection);
     732                 : 
     733                 :     // disable pipelining for the next attempt in case pipelining caused the
     734                 :     // reset.  this is being overly cautious since we don't know if pipelining
     735                 :     // was the problem here.
     736               0 :     mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
     737                 : 
     738               0 :     return gHttpHandler->InitiateTransaction(this, mPriority);
     739                 : }
     740                 : 
     741                 : char *
     742            2830 : nsHttpTransaction::LocateHttpStart(char *buf, PRUint32 len,
     743                 :                                    bool aAllowPartialMatch)
     744                 : {
     745            2830 :     NS_ASSERTION(!aAllowPartialMatch || mLineBuf.IsEmpty(), "ouch");
     746                 : 
     747                 :     static const char HTTPHeader[] = "HTTP/1.";
     748                 :     static const PRUint32 HTTPHeaderLen = sizeof(HTTPHeader) - 1;
     749                 :     static const char HTTP2Header[] = "HTTP/2.0";
     750                 :     static const PRUint32 HTTP2HeaderLen = sizeof(HTTP2Header) - 1;
     751                 :     
     752            2830 :     if (aAllowPartialMatch && (len < HTTPHeaderLen))
     753               0 :         return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nsnull;
     754                 : 
     755                 :     // mLineBuf can contain partial match from previous search
     756            2830 :     if (!mLineBuf.IsEmpty()) {
     757               0 :         NS_ASSERTION(mLineBuf.Length() < HTTPHeaderLen, "ouch");
     758               0 :         PRInt32 checkChars = NS_MIN(len, HTTPHeaderLen - mLineBuf.Length());
     759               0 :         if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
     760               0 :                            checkChars) == 0) {
     761               0 :             mLineBuf.Append(buf, checkChars);
     762               0 :             if (mLineBuf.Length() == HTTPHeaderLen) {
     763                 :                 // We've found whole HTTPHeader sequence. Return pointer at the
     764                 :                 // end of matched sequence since it is stored in mLineBuf.
     765               0 :                 return (buf + checkChars);
     766                 :             }
     767                 :             // Response matches pattern but is still incomplete.
     768               0 :             return 0;
     769                 :         }
     770                 :         // Previous partial match together with new data doesn't match the
     771                 :         // pattern. Start the search again.
     772               0 :         mLineBuf.Truncate();
     773                 :     }
     774                 : 
     775            2830 :     bool firstByte = true;
     776            5671 :     while (len > 0) {
     777            2840 :         if (PL_strncasecmp(buf, HTTPHeader, NS_MIN<PRUint32>(len, HTTPHeaderLen)) == 0) {
     778            2829 :             if (len < HTTPHeaderLen) {
     779                 :                 // partial HTTPHeader sequence found
     780                 :                 // save partial match to mLineBuf
     781               0 :                 mLineBuf.Assign(buf, len);
     782               0 :                 return 0;
     783                 :             }
     784                 : 
     785                 :             // whole HTTPHeader sequence found
     786            2829 :             return buf;
     787                 :         }
     788                 : 
     789                 :         // At least "SmarterTools/2.0.3974.16813" generates nonsensical
     790                 :         // HTTP/2.0 responses to our HTTP/1 requests. Treat the minimal case of
     791                 :         // it as HTTP/1.1 to be compatible with old versions of ourselves and
     792                 :         // other browsers
     793                 : 
     794              12 :         if (firstByte && !mInvalidResponseBytesRead && len >= HTTP2HeaderLen &&
     795               1 :             (PL_strncasecmp(buf, HTTP2Header, HTTP2HeaderLen) == 0)) {
     796               0 :             LOG(("nsHttpTransaction:: Identified HTTP/2.0 treating as 1.x\n"));
     797               0 :             return buf;
     798                 :         }
     799                 : 
     800              11 :         if (!nsCRT::IsAsciiSpace(*buf))
     801              11 :             firstByte = false;
     802              11 :         buf++;
     803              11 :         len--;
     804                 :     }
     805               1 :     return 0;
     806                 : }
     807                 : 
     808                 : nsresult
     809           19019 : nsHttpTransaction::ParseLine(char *line)
     810                 : {
     811           19019 :     LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
     812           19019 :     nsresult rv = NS_OK;
     813                 :     
     814           19019 :     if (!mHaveStatusLine) {
     815            2829 :         mResponseHead->ParseStatusLine(line);
     816            2829 :         mHaveStatusLine = true;
     817                 :         // XXX this should probably never happen
     818            2829 :         if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
     819               0 :             mHaveAllHeaders = true;
     820                 :     }
     821                 :     else {
     822           16190 :         rv = mResponseHead->ParseHeaderLine(line);
     823                 :     }
     824           19019 :     return rv;
     825                 : }
     826                 : 
     827                 : nsresult
     828           21861 : nsHttpTransaction::ParseLineSegment(char *segment, PRUint32 len)
     829                 : {
     830           21861 :     NS_PRECONDITION(!mHaveAllHeaders, "already have all headers");
     831                 : 
     832           21861 :     if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
     833                 :         // trim off the new line char, and if this segment is
     834                 :         // not a continuation of the previous or if we haven't
     835                 :         // parsed the status line yet, then parse the contents
     836                 :         // of mLineBuf.
     837           19019 :         mLineBuf.Truncate(mLineBuf.Length() - 1);
     838           19019 :         if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
     839           19019 :             nsresult rv = ParseLine(mLineBuf.BeginWriting());
     840           19019 :             mLineBuf.Truncate();
     841           19019 :             if (NS_FAILED(rv)) {
     842              11 :                 return rv;
     843                 :             }
     844                 :         }
     845                 :     }
     846                 : 
     847                 :     // append segment to mLineBuf...
     848           21850 :     mLineBuf.Append(segment, len);
     849                 :     
     850                 :     // a line buf with only a new line char signifies the end of headers.
     851           21850 :     if (mLineBuf.First() == '\n') {
     852            2818 :         mLineBuf.Truncate();
     853                 :         // discard this response if it is a 100 continue or other 1xx status.
     854            2818 :         PRUint16 status = mResponseHead->Status();
     855            2818 :         if ((status != 101) && (status / 100 == 1)) {
     856               0 :             LOG(("ignoring 1xx response\n"));
     857               0 :             mHaveStatusLine = false;
     858               0 :             mHttpResponseMatched = false;
     859               0 :             mConnection->SetLastTransactionExpectedNoContent(true);
     860               0 :             mResponseHead->Reset();
     861               0 :             return NS_OK;
     862                 :         }
     863            2818 :         mHaveAllHeaders = true;
     864                 :     }
     865           21850 :     return NS_OK;
     866                 : }
     867                 : 
     868                 : nsresult
     869            2843 : nsHttpTransaction::ParseHead(char *buf,
     870                 :                              PRUint32 count,
     871                 :                              PRUint32 *countRead)
     872                 : {
     873                 :     nsresult rv;
     874                 :     PRUint32 len;
     875                 :     char *eol;
     876                 : 
     877            2843 :     LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
     878                 : 
     879            2843 :     *countRead = 0;
     880                 : 
     881            2843 :     NS_PRECONDITION(!mHaveAllHeaders, "oops");
     882                 :         
     883                 :     // allocate the response head object if necessary
     884            2843 :     if (!mResponseHead) {
     885            2830 :         mResponseHead = new nsHttpResponseHead();
     886            2830 :         if (!mResponseHead)
     887               0 :             return NS_ERROR_OUT_OF_MEMORY;
     888                 : 
     889                 :         // report that we have a least some of the response
     890            2830 :         if (mActivityDistributor)
     891               8 :             mActivityDistributor->ObserveActivity(
     892                 :                 mChannel,
     893                 :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
     894                 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
     895               8 :                 PR_Now(), LL_ZERO, EmptyCString());
     896                 :     }
     897                 : 
     898            2843 :     if (!mHttpResponseMatched) {
     899                 :         // Normally we insist on seeing HTTP/1.x in the first few bytes,
     900                 :         // but if we are on a persistent connection and the previous transaction
     901                 :         // was not supposed to have any content then we need to be prepared
     902                 :         // to skip over a response body that the server may have sent even
     903                 :         // though it wasn't allowed.
     904            2830 :         if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
     905                 :             // tolerate only minor junk before the status line
     906            2830 :             mHttpResponseMatched = true;
     907            2830 :             char *p = LocateHttpStart(buf, NS_MIN<PRUint32>(count, 11), true);
     908            2830 :             if (!p) {
     909                 :                 // Treat any 0.9 style response of a put as a failure.
     910               1 :                 if (mRequestHead->Method() == nsHttp::Put)
     911               0 :                     return NS_ERROR_ABORT;
     912                 : 
     913               1 :                 mResponseHead->ParseStatusLine("");
     914               1 :                 mHaveStatusLine = true;
     915               1 :                 mHaveAllHeaders = true;
     916               1 :                 return NS_OK;
     917                 :             }
     918            2829 :             if (p > buf) {
     919                 :                 // skip over the junk
     920               0 :                 mInvalidResponseBytesRead += p - buf;
     921               0 :                 *countRead = p - buf;
     922               0 :                 buf = p;
     923                 :             }
     924                 :         }
     925                 :         else {
     926               0 :             char *p = LocateHttpStart(buf, count, false);
     927               0 :             if (p) {
     928               0 :                 mInvalidResponseBytesRead += p - buf;
     929               0 :                 *countRead = p - buf;
     930               0 :                 buf = p;
     931               0 :                 mHttpResponseMatched = true;
     932                 :             } else {
     933               0 :                 mInvalidResponseBytesRead += count;
     934               0 :                 *countRead = count;
     935               0 :                 if (mInvalidResponseBytesRead > MAX_INVALID_RESPONSE_BODY_SIZE) {
     936               0 :                     LOG(("nsHttpTransaction::ParseHead() "
     937                 :                          "Cannot find Response Header\n"));
     938                 :                     // cannot go back and call this 0.9 anymore as we
     939                 :                     // have thrown away a lot of the leading junk
     940               0 :                     return NS_ERROR_ABORT;
     941                 :                 }
     942               0 :                 return NS_OK;
     943                 :             }
     944                 :         }
     945                 :     }
     946                 :     // otherwise we can assume that we don't have a HTTP/0.9 response.
     947                 : 
     948            2842 :     NS_ABORT_IF_FALSE (mHttpResponseMatched, "inconsistent");
     949           24703 :     while ((eol = static_cast<char *>(memchr(buf, '\n', count - *countRead))) != nsnull) {
     950                 :         // found line in range [buf:eol]
     951           21848 :         len = eol - buf + 1;
     952                 : 
     953           21848 :         *countRead += len;
     954                 : 
     955                 :         // actually, the line is in the range [buf:eol-1]
     956           21848 :         if ((eol > buf) && (*(eol-1) == '\r'))
     957           21848 :             len--;
     958                 : 
     959           21848 :         buf[len-1] = '\n';
     960           21848 :         rv = ParseLineSegment(buf, len);
     961           21848 :         if (NS_FAILED(rv))
     962              11 :             return rv;
     963                 : 
     964           21837 :         if (mHaveAllHeaders)
     965            2818 :             return NS_OK;
     966                 : 
     967                 :         // skip over line
     968           19019 :         buf = eol + 1;
     969                 : 
     970           19019 :         if (!mHttpResponseMatched) {
     971                 :             // a 100 class response has caused us to throw away that set of
     972                 :             // response headers and look for the next response
     973               0 :             return NS_ERROR_NET_INTERRUPT;
     974                 :         }
     975                 :     }
     976                 : 
     977                 :     // do something about a partial header line
     978              13 :     if (!mHaveAllHeaders && (len = count - *countRead)) {
     979              13 :         *countRead = count;
     980                 :         // ignore a trailing carriage return, and don't bother calling
     981                 :         // ParseLineSegment if buf only contains a carriage return.
     982              13 :         if ((buf[len-1] == '\r') && (--len == 0))
     983               0 :             return NS_OK;
     984              13 :         rv = ParseLineSegment(buf, len);
     985              13 :         if (NS_FAILED(rv))
     986               0 :             return rv;
     987                 :     }
     988              13 :     return NS_OK;
     989                 : }
     990                 : 
     991                 : // called on the socket thread
     992                 : nsresult
     993            2819 : nsHttpTransaction::HandleContentStart()
     994                 : {
     995            2819 :     LOG(("nsHttpTransaction::HandleContentStart [this=%x]\n", this));
     996                 : 
     997            2819 :     if (mResponseHead) {
     998                 : #if defined(PR_LOGGING)
     999            2819 :         if (LOG3_ENABLED()) {
    1000               0 :             LOG3(("http response [\n"));
    1001               0 :             nsCAutoString headers;
    1002               0 :             mResponseHead->Flatten(headers, false);
    1003               0 :             LogHeaders(headers.get());
    1004               0 :             LOG3(("]\n"));
    1005                 :         }
    1006                 : #endif
    1007                 :         // notify the connection, give it a chance to cause a reset.
    1008            2819 :         bool reset = false;
    1009            2819 :         mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
    1010                 : 
    1011                 :         // looks like we should ignore this response, resetting...
    1012            2819 :         if (reset) {
    1013               0 :             LOG(("resetting transaction's response head\n"));
    1014               0 :             mHaveAllHeaders = false;
    1015               0 :             mHaveStatusLine = false;
    1016               0 :             mReceivedData = false;
    1017               0 :             mSentData = false;
    1018               0 :             mHttpResponseMatched = false;
    1019               0 :             mResponseHead->Reset();
    1020                 :             // wait to be called again...
    1021               0 :             return NS_OK;
    1022                 :         }
    1023                 : 
    1024                 :         // check if this is a no-content response
    1025            2819 :         switch (mResponseHead->Status()) {
    1026                 :         case 101:
    1027               0 :             mPreserveStream = true;    // fall through to other no content
    1028                 :         case 204:
    1029                 :         case 205:
    1030                 :         case 304:
    1031              26 :             mNoContent = true;
    1032              26 :             LOG(("this response should not contain a body.\n"));
    1033              26 :             break;
    1034                 :         }
    1035            2819 :         mConnection->SetLastTransactionExpectedNoContent(mNoContent);
    1036                 : 
    1037            2819 :         if (mNoContent)
    1038              40 :             mContentLength = 0;
    1039                 :         else {
    1040                 :             // grab the content-length from the response headers
    1041            2779 :             mContentLength = mResponseHead->ContentLength();
    1042                 : 
    1043                 :             // handle chunked encoding here, so we'll know immediately when
    1044                 :             // we're done with the socket.  please note that _all_ other
    1045                 :             // decoding is done when the channel receives the content data
    1046                 :             // so as not to block the socket transport thread too much.
    1047                 :             // ignore chunked responses from HTTP/1.0 servers and proxies.
    1048            5526 :             if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 &&
    1049            2747 :                 mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
    1050                 :                 // we only support the "chunked" transfer encoding right now.
    1051               4 :                 mChunkedDecoder = new nsHttpChunkedDecoder();
    1052               4 :                 if (!mChunkedDecoder)
    1053               0 :                     return NS_ERROR_OUT_OF_MEMORY;
    1054               4 :                 LOG(("chunked decoder created\n"));
    1055                 :                 // Ignore server specified Content-Length.
    1056               4 :                 mContentLength = -1;
    1057                 :             }
    1058                 : #if defined(PR_LOGGING)
    1059            2775 :             else if (mContentLength == PRInt64(-1))
    1060               9 :                 LOG(("waiting for the server to close the connection.\n"));
    1061                 : #endif
    1062                 :         }
    1063                 :     }
    1064                 : 
    1065            2819 :     mDidContentStart = true;
    1066            2819 :     return NS_OK;
    1067                 : }
    1068                 : 
    1069                 : // called on the socket thread
    1070                 : nsresult
    1071            5697 : nsHttpTransaction::HandleContent(char *buf,
    1072                 :                                  PRUint32 count,
    1073                 :                                  PRUint32 *contentRead,
    1074                 :                                  PRUint32 *contentRemaining)
    1075                 : {
    1076                 :     nsresult rv;
    1077                 : 
    1078            5697 :     LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n", this, count));
    1079                 : 
    1080            5697 :     *contentRead = 0;
    1081            5697 :     *contentRemaining = 0;
    1082                 : 
    1083            5697 :     NS_ASSERTION(mConnection, "no connection");
    1084                 : 
    1085            5697 :     if (!mDidContentStart) {
    1086            2819 :         rv = HandleContentStart();
    1087            2819 :         if (NS_FAILED(rv)) return rv;
    1088                 :         // Do not write content to the pipe if we haven't started streaming yet
    1089            2819 :         if (!mDidContentStart)
    1090               0 :             return NS_OK;
    1091                 :     }
    1092                 : 
    1093            5697 :     if (mChunkedDecoder) {
    1094                 :         // give the buf over to the chunked decoder so it can reformat the
    1095                 :         // data and tell us how much is really there.
    1096               8 :         rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
    1097               8 :         if (NS_FAILED(rv)) return rv;
    1098                 :     }
    1099            5689 :     else if (mContentLength >= PRInt64(0)) {
    1100                 :         // HTTP/1.0 servers have been known to send erroneous Content-Length
    1101                 :         // headers. So, unless the connection is persistent, we must make
    1102                 :         // allowances for a possibly invalid Content-Length header. Thus, if
    1103                 :         // NOT persistent, we simply accept everything in |buf|.
    1104            5530 :         if (mConnection->IsPersistent() || mPreserveStream) {
    1105               0 :             PRInt64 remaining = mContentLength - mContentRead;
    1106               0 :             *contentRead = PRUint32(NS_MIN<PRInt64>(count, remaining));
    1107               0 :             *contentRemaining = count - *contentRead;
    1108                 :         }
    1109                 :         else {
    1110            5530 :             *contentRead = count;
    1111                 :             // mContentLength might need to be increased...
    1112            5530 :             PRInt64 position = mContentRead + PRInt64(count);
    1113            5530 :             if (position > mContentLength) {
    1114               0 :                 mContentLength = position;
    1115                 :                 //mResponseHead->SetContentLength(mContentLength);
    1116                 :             }
    1117                 :         }
    1118                 :     }
    1119                 :     else {
    1120                 :         // when we are just waiting for the server to close the connection...
    1121                 :         // (no explicit content-length given)
    1122             159 :         *contentRead = count;
    1123                 :     }
    1124                 : 
    1125            5697 :     if (*contentRead) {
    1126                 :         // update count of content bytes read and report progress...
    1127            2908 :         mContentRead += *contentRead;
    1128                 :         /* when uncommenting, take care of 64-bit integers w/ NS_MAX...
    1129                 :         if (mProgressSink)
    1130                 :             mProgressSink->OnProgress(nsnull, nsnull, mContentRead, NS_MAX(0, mContentLength));
    1131                 :         */
    1132                 :     }
    1133                 : 
    1134            5697 :     LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
    1135                 :         this, count, *contentRead, mContentRead, mContentLength));
    1136                 : 
    1137                 :     // check for end-of-file
    1138            5705 :     if ((mContentRead == mContentLength) ||
    1139               8 :         (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
    1140                 :         // the transaction is done with a complete response.
    1141            2800 :         mTransactionDone = true;
    1142            2800 :         mResponseIsComplete = true;
    1143                 : 
    1144            2800 :         if (TimingEnabled())
    1145               2 :             mTimings.responseEnd = mozilla::TimeStamp::Now();
    1146                 : 
    1147                 :         // report the entire response has arrived
    1148            2800 :         if (mActivityDistributor)
    1149               8 :             mActivityDistributor->ObserveActivity(
    1150                 :                 mChannel,
    1151                 :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
    1152                 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
    1153                 :                 PR_Now(),
    1154                 :                 static_cast<PRUint64>(mContentRead),
    1155               8 :                 EmptyCString());
    1156                 :     }
    1157                 : 
    1158            5697 :     return NS_OK;
    1159                 : }
    1160                 : 
    1161                 : nsresult
    1162            5721 : nsHttpTransaction::ProcessData(char *buf, PRUint32 count, PRUint32 *countRead)
    1163                 : {
    1164                 :     nsresult rv;
    1165                 : 
    1166            5721 :     LOG(("nsHttpTransaction::ProcessData [this=%x count=%u]\n", this, count));
    1167                 : 
    1168            5721 :     *countRead = 0;
    1169                 : 
    1170                 :     // we may not have read all of the headers yet...
    1171            5721 :     if (!mHaveAllHeaders) {
    1172            2843 :         PRUint32 bytesConsumed = 0;
    1173                 : 
    1174            2832 :         do {
    1175            2843 :             PRUint32 localBytesConsumed = 0;
    1176            2843 :             char *localBuf = buf + bytesConsumed;
    1177            2843 :             PRUint32 localCount = count - bytesConsumed;
    1178                 :             
    1179            2843 :             rv = ParseHead(localBuf, localCount, &localBytesConsumed);
    1180            2843 :             if (NS_FAILED(rv) && rv != NS_ERROR_NET_INTERRUPT)
    1181              11 :                 return rv;
    1182            2832 :             bytesConsumed += localBytesConsumed;
    1183                 :         } while (rv == NS_ERROR_NET_INTERRUPT);
    1184                 :         
    1185            2832 :         count -= bytesConsumed;
    1186                 : 
    1187                 :         // if buf has some content in it, shift bytes to top of buf.
    1188            2832 :         if (count && bytesConsumed)
    1189              29 :             memmove(buf, buf + bytesConsumed, count);
    1190                 : 
    1191                 :         // report the completed response header
    1192            2832 :         if (mActivityDistributor && mResponseHead && mHaveAllHeaders) {
    1193              16 :             nsCAutoString completeResponseHeaders;
    1194               8 :             mResponseHead->Flatten(completeResponseHeaders, false);
    1195               8 :             completeResponseHeaders.AppendLiteral("\r\n");
    1196               8 :             mActivityDistributor->ObserveActivity(
    1197                 :                 mChannel,
    1198                 :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
    1199                 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
    1200                 :                 PR_Now(), LL_ZERO,
    1201               8 :                 completeResponseHeaders);
    1202                 :         }
    1203                 :     }
    1204                 : 
    1205                 :     // even though count may be 0, we still want to call HandleContent
    1206                 :     // so it can complete the transaction if this is a "no-content" response.
    1207            5710 :     if (mHaveAllHeaders) {
    1208            5697 :         PRUint32 countRemaining = 0;
    1209                 :         //
    1210                 :         // buf layout:
    1211                 :         // 
    1212                 :         // +--------------------------------------+----------------+-----+
    1213                 :         // |              countRead               | countRemaining |     |
    1214                 :         // +--------------------------------------+----------------+-----+
    1215                 :         //
    1216                 :         // count          : bytes read from the socket
    1217                 :         // countRead      : bytes corresponding to this transaction
    1218                 :         // countRemaining : bytes corresponding to next pipelined transaction
    1219                 :         //
    1220                 :         // NOTE:
    1221                 :         // count > countRead + countRemaining <==> chunked transfer encoding
    1222                 :         //
    1223            5697 :         rv = HandleContent(buf, count, countRead, &countRemaining);
    1224            5697 :         if (NS_FAILED(rv)) return rv;
    1225                 :         // we may have read more than our share, in which case we must give
    1226                 :         // the excess bytes back to the connection
    1227            5697 :         if (mResponseIsComplete && countRemaining) {
    1228               0 :             NS_ASSERTION(mConnection, "no connection");
    1229               0 :             mConnection->PushBack(buf + *countRead, countRemaining);
    1230                 :         }
    1231                 :     }
    1232                 : 
    1233            5710 :     return NS_OK;
    1234                 : }
    1235                 : 
    1236                 : //-----------------------------------------------------------------------------
    1237                 : // nsHttpTransaction deletion event
    1238                 : //-----------------------------------------------------------------------------
    1239                 : 
    1240              28 : class nsDeleteHttpTransaction : public nsRunnable {
    1241                 : public:
    1242               7 :     nsDeleteHttpTransaction(nsHttpTransaction *trans)
    1243               7 :         : mTrans(trans)
    1244               7 :     {}
    1245                 : 
    1246               7 :     NS_IMETHOD Run()
    1247                 :     {
    1248               7 :         delete mTrans;
    1249               7 :         return NS_OK;
    1250                 :     }
    1251                 : private:
    1252                 :     nsHttpTransaction *mTrans;
    1253                 : };
    1254                 : 
    1255                 : void
    1256            2977 : nsHttpTransaction::DeleteSelfOnConsumerThread()
    1257                 : {
    1258            2977 :     LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%x]\n", this));
    1259                 :     
    1260                 :     bool val;
    1261            5954 :     if (!mConsumerTarget ||
    1262            2977 :         (NS_SUCCEEDED(mConsumerTarget->IsOnCurrentThread(&val)) && val)) {
    1263            2970 :         delete this;
    1264                 :     } else {
    1265               7 :         LOG(("proxying delete to consumer thread...\n"));
    1266              14 :         nsCOMPtr<nsIRunnable> event = new nsDeleteHttpTransaction(this);
    1267               7 :         if (NS_FAILED(mConsumerTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
    1268               0 :             NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
    1269                 :     }
    1270            2977 : }
    1271                 : 
    1272                 : //-----------------------------------------------------------------------------
    1273                 : // nsHttpTransaction::nsISupports
    1274                 : //-----------------------------------------------------------------------------
    1275                 : 
    1276           15069 : NS_IMPL_THREADSAFE_ADDREF(nsHttpTransaction)
    1277                 : 
    1278                 : NS_IMETHODIMP_(nsrefcnt)
    1279           15057 : nsHttpTransaction::Release()
    1280                 : {
    1281                 :     nsrefcnt count;
    1282           15057 :     NS_PRECONDITION(0 != mRefCnt, "dup release");
    1283           15057 :     count = NS_AtomicDecrementRefcnt(mRefCnt);
    1284           15057 :     NS_LOG_RELEASE(this, count, "nsHttpTransaction");
    1285           15057 :     if (0 == count) {
    1286            2977 :         mRefCnt = 1; /* stablize */
    1287                 :         // it is essential that the transaction be destroyed on the consumer 
    1288                 :         // thread (we could be holding the last reference to our consumer).
    1289            2977 :         DeleteSelfOnConsumerThread();
    1290            2977 :         return 0;
    1291                 :     }
    1292           12080 :     return count;
    1293                 : }
    1294                 : 
    1295               0 : NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHttpTransaction,
    1296                 :                                     nsIInputStreamCallback,
    1297                 :                                     nsIOutputStreamCallback)
    1298                 : 
    1299                 : //-----------------------------------------------------------------------------
    1300                 : // nsHttpTransaction::nsIInputStreamCallback
    1301                 : //-----------------------------------------------------------------------------
    1302                 : 
    1303                 : // called on the socket thread
    1304                 : NS_IMETHODIMP
    1305               0 : nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
    1306                 : {
    1307               0 :     if (mConnection) {
    1308               0 :         mConnection->TransactionHasDataToWrite(this);
    1309               0 :         nsresult rv = mConnection->ResumeSend();
    1310               0 :         if (NS_FAILED(rv))
    1311               0 :             NS_ERROR("ResumeSend failed");
    1312                 :     }
    1313               0 :     return NS_OK;
    1314                 : }
    1315                 : 
    1316                 : //-----------------------------------------------------------------------------
    1317                 : // nsHttpTransaction::nsIOutputStreamCallback
    1318                 : //-----------------------------------------------------------------------------
    1319                 : 
    1320                 : // called on the socket thread
    1321                 : NS_IMETHODIMP
    1322               0 : nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
    1323                 : {
    1324               0 :     if (mConnection) {
    1325               0 :         nsresult rv = mConnection->ResumeRecv();
    1326               0 :         if (NS_FAILED(rv))
    1327               0 :             NS_ERROR("ResumeRecv failed");
    1328                 :     }
    1329               0 :     return NS_OK;
    1330                 : }

Generated by: LCOV version 1.7