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
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/
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
16 * The Original Code is Mozilla.
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.
24 * Darin Fisher <darin@netscape.com> (original author)
25 * Andreas M. Schneider <clarence@clarence.de>
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.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsHttpHandler.h"
42 #include "nsHttpTransaction.h"
43 #include "nsHttpConnection.h"
44 #include "nsHttpRequestHead.h"
45 #include "nsHttpResponseHead.h"
46 #include "nsHttpChunkedDecoder.h"
47 #include "nsNetSegmentUtils.h"
48 #include "nsTransportUtils.h"
49 #include "nsNetUtil.h"
50 #include "nsProxyRelease.h"
51 #include "nsIOService.h"
52 #include "nsAutoLock.h"
55 #include "nsISeekableStream.h"
56 #include "nsISocketTransport.h"
57 #include "nsMultiplexInputStream.h"
58 #include "nsStringStream.h"
60 #include "nsComponentManagerUtils.h" // do_CreateInstance
61 #include "nsServiceManagerUtils.h" // do_GetService
62 #include "nsIHttpActivityObserver.h"
64 //-----------------------------------------------------------------------------
67 // defined by the socket transport service while active
68 extern PRThread
*gSocketThread
;
71 //-----------------------------------------------------------------------------
73 static NS_DEFINE_CID(kMultiplexInputStream
, NS_MULTIPLEXINPUTSTREAM_CID
);
75 // mLineBuf is limited to this number of bytes.
76 #define MAX_LINEBUF_LENGTH (1024 * 10)
78 //-----------------------------------------------------------------------------
80 //-----------------------------------------------------------------------------
83 LocateHttpStart(char *buf
, PRUint32 len
)
85 // if we have received less than 4 bytes of data, then we'll have to
86 // just accept a partial match, which may not be correct.
88 return (PL_strncasecmp(buf
, "HTTP", len
) == 0) ? buf
: 0;
90 // PL_strncasestr would be perfect for this, but unfortunately bug 96571
91 // prevents its use here.
93 if (PL_strncasecmp(buf
, "HTTP", 4) == 0)
101 #if defined(PR_LOGGING)
103 LogHeaders(const char *lines
)
107 while ((p
= PL_strstr(lines
, "\r\n")) != nsnull
) {
108 buf
.Assign(lines
, p
- lines
);
109 if (PL_strcasestr(buf
.get(), "authorization: ") != nsnull
) {
110 char *p
= PL_strchr(PL_strchr(buf
.get(), ' ')+1, ' ');
111 while (*++p
) *p
= '*';
113 LOG3((" %s\n", buf
.get()));
119 //-----------------------------------------------------------------------------
120 // nsHttpTransaction <public>
121 //-----------------------------------------------------------------------------
123 nsHttpTransaction::nsHttpTransaction()
125 , mConnection(nsnull
)
127 , mRequestHead(nsnull
)
128 , mResponseHead(nsnull
)
131 , mChunkedDecoder(nsnull
)
137 , mConnected(PR_FALSE
)
138 , mHaveStatusLine(PR_FALSE
)
139 , mHaveAllHeaders(PR_FALSE
)
140 , mTransactionDone(PR_FALSE
)
141 , mResponseIsComplete(PR_FALSE
)
142 , mDidContentStart(PR_FALSE
)
143 , mNoContent(PR_FALSE
)
144 , mSentData(PR_FALSE
)
145 , mReceivedData(PR_FALSE
)
146 , mStatusEventPending(PR_FALSE
)
147 , mHasRequestBody(PR_FALSE
)
148 , mSSLConnectFailed(PR_FALSE
)
150 LOG(("Creating nsHttpTransaction @%x\n", this));
153 nsHttpTransaction::~nsHttpTransaction()
155 LOG(("Destroying nsHttpTransaction @%x\n", this));
157 NS_IF_RELEASE(mConnection
);
158 NS_IF_RELEASE(mConnInfo
);
160 delete mResponseHead
;
161 delete mChunkedDecoder
;
165 nsHttpTransaction::Init(PRUint8 caps
,
166 nsHttpConnectionInfo
*cinfo
,
167 nsHttpRequestHead
*requestHead
,
168 nsIInputStream
*requestBody
,
169 PRBool requestBodyHasHeaders
,
170 nsIEventTarget
*target
,
171 nsIInterfaceRequestor
*callbacks
,
172 nsITransportEventSink
*eventsink
,
173 nsIAsyncInputStream
**responseBody
)
177 LOG(("nsHttpTransaction::Init [this=%x caps=%x]\n", this, caps
));
179 NS_ASSERTION(cinfo
, "ouch");
180 NS_ASSERTION(requestHead
, "ouch");
181 NS_ASSERTION(target
, "ouch");
183 // create transport event sink proxy that coalesces all events
184 rv
= net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink
),
185 eventsink
, target
, PR_TRUE
);
186 if (NS_FAILED(rv
)) return rv
;
188 // try to get the nsIHttpActivityObserver distributor
189 mActivityDistributor
= do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID
, &rv
);
191 // mActivityDistributor may not be valid
192 if (NS_SUCCEEDED(rv
) && mActivityDistributor
) {
193 // the service is valid, now check if it is active
195 rv
= mActivityDistributor
->GetIsActive(&active
);
196 if (NS_SUCCEEDED(rv
) && active
) {
197 // the service is valid and active, gather nsISupports
198 // for the channel that called Init()
199 mChannel
= do_QueryInterface(eventsink
);
200 LOG(("nsHttpTransaction::Init() " \
201 "mActivityDistributor is active " \
204 // the interface in valid but not active, so don't use it
205 mActivityDistributor
= nsnull
;
208 NS_ADDREF(mConnInfo
= cinfo
);
209 mCallbacks
= callbacks
;
210 mConsumerTarget
= target
;
213 if (requestHead
->Method() == nsHttp::Head
)
214 mNoContent
= PR_TRUE
;
216 // Make sure that there is "Content-Length: 0" header in the requestHead
217 // in case of POST and PUT methods when there is no requestBody and
218 // requestHead doesn't contain "Transfer-Encoding" header.
220 // RFC1945 section 7.2.2:
221 // HTTP/1.0 requests containing an entity body must include a valid
222 // Content-Length header field.
224 // RFC2616 section 4.4:
225 // For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
226 // containing a message-body MUST include a valid Content-Length header
227 // field unless the server is known to be HTTP/1.1 compliant.
228 if ((requestHead
->Method() == nsHttp::Post
|| requestHead
->Method() == nsHttp::Put
) &&
229 !requestBody
&& !requestHead
->PeekHeader(nsHttp::Transfer_Encoding
)) {
230 requestHead
->SetHeader(nsHttp::Content_Length
, NS_LITERAL_CSTRING("0"));
233 // grab a weak reference to the request head
234 mRequestHead
= requestHead
;
236 // make sure we eliminate any proxy specific headers from
237 // the request if we are talking HTTPS via a SSL tunnel.
238 PRBool pruneProxyHeaders
= cinfo
->UsingSSL() &&
239 cinfo
->UsingHttpProxy();
240 mReqHeaderBuf
.Truncate();
241 requestHead
->Flatten(mReqHeaderBuf
, pruneProxyHeaders
);
243 #if defined(PR_LOGGING)
244 if (LOG3_ENABLED()) {
245 LOG3(("http request [\n"));
246 LogHeaders(mReqHeaderBuf
.get());
251 // If the request body does not include headers or if there is no request
252 // body, then we must add the header/body separator manually.
253 if (!requestBodyHasHeaders
|| !requestBody
)
254 mReqHeaderBuf
.AppendLiteral("\r\n");
256 // report the request header
257 if (mActivityDistributor
)
258 mActivityDistributor
->ObserveActivity(
260 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION
,
261 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER
,
265 // Create a string stream for the request header buf (the stream holds
266 // a non-owning reference to the request header data, so we MUST keep
267 // mReqHeaderBuf around).
268 nsCOMPtr
<nsIInputStream
> headers
;
269 rv
= NS_NewByteInputStream(getter_AddRefs(headers
),
271 mReqHeaderBuf
.Length());
272 if (NS_FAILED(rv
)) return rv
;
275 mHasRequestBody
= PR_TRUE
;
277 // wrap the headers and request body in a multiplexed input stream.
278 nsCOMPtr
<nsIMultiplexInputStream
> multi
=
279 do_CreateInstance(kMultiplexInputStream
, &rv
);
280 if (NS_FAILED(rv
)) return rv
;
282 rv
= multi
->AppendStream(headers
);
283 if (NS_FAILED(rv
)) return rv
;
285 rv
= multi
->AppendStream(requestBody
);
286 if (NS_FAILED(rv
)) return rv
;
288 // wrap the multiplexed input stream with a buffered input stream, so
289 // that we write data in the largest chunks possible. this is actually
290 // necessary to workaround some common server bugs (see bug 137155).
291 rv
= NS_NewBufferedInputStream(getter_AddRefs(mRequestStream
), multi
,
292 NET_DEFAULT_SEGMENT_SIZE
);
293 if (NS_FAILED(rv
)) return rv
;
296 mRequestStream
= headers
;
298 rv
= mRequestStream
->Available(&mRequestSize
);
299 if (NS_FAILED(rv
)) return rv
;
301 // create pipe for response stream
302 rv
= NS_NewPipe2(getter_AddRefs(mPipeIn
),
303 getter_AddRefs(mPipeOut
),
305 NS_HTTP_SEGMENT_SIZE
,
306 NS_HTTP_SEGMENT_COUNT
,
307 nsIOService::gBufferCache
);
308 if (NS_FAILED(rv
)) return rv
;
310 NS_ADDREF(*responseBody
= mPipeIn
);
315 nsHttpTransaction::TakeResponseHead()
317 if (!mHaveAllHeaders
) {
318 NS_WARNING("response headers not available or incomplete");
322 nsHttpResponseHead
*head
= mResponseHead
;
323 mResponseHead
= nsnull
;
327 //----------------------------------------------------------------------------
328 // nsHttpTransaction::nsAHttpTransaction
329 //----------------------------------------------------------------------------
332 nsHttpTransaction::SetConnection(nsAHttpConnection
*conn
)
334 NS_IF_RELEASE(mConnection
);
335 NS_IF_ADDREF(mConnection
= conn
);
339 nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor
**cb
)
341 NS_IF_ADDREF(*cb
= mCallbacks
);
345 nsHttpTransaction::OnTransportStatus(nsresult status
, PRUint64 progress
)
347 LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n",
348 this, status
, progress
));
353 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
355 // nsHttpChannel synthesizes progress events in OnDataAvailable
356 if (status
== nsISocketTransport::STATUS_RECEIVING_FROM
)
359 if (mActivityDistributor
) {
360 // upon STATUS_WAITING_FOR; report request body sent
361 if ((mHasRequestBody
) &&
362 (status
== nsISocketTransport::STATUS_WAITING_FOR
))
363 mActivityDistributor
->ObserveActivity(
365 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION
,
366 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT
,
367 LL_ZERO
, LL_ZERO
, EmptyCString());
369 // report the status and progress
370 mActivityDistributor
->ObserveActivity(
372 NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT
,
373 static_cast<PRUint32
>(status
),
379 nsUint64 progressMax
;
381 if (status
== nsISocketTransport::STATUS_SENDING_TO
) {
382 // suppress progress when only writing request headers
383 if (!mHasRequestBody
)
386 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mRequestStream
);
387 NS_ASSERTION(seekable
, "Request stream isn't seekable?!?");
390 seekable
->Tell(&prog
);
393 // when uploading, we include the request headers in the progress
395 progressMax
= mRequestSize
; // XXX mRequestSize is 32-bit!
402 mTransportSink
->OnTransportStatus(nsnull
, status
, progress
, progressMax
);
406 nsHttpTransaction::IsDone()
408 return mTransactionDone
;
412 nsHttpTransaction::Status()
418 nsHttpTransaction::Available()
421 if (NS_FAILED(mRequestStream
->Available(&size
)))
427 nsHttpTransaction::ReadRequestSegment(nsIInputStream
*stream
,
434 nsHttpTransaction
*trans
= (nsHttpTransaction
*) closure
;
435 nsresult rv
= trans
->mReader
->OnReadSegment(buf
, count
, countRead
);
436 if (NS_FAILED(rv
)) return rv
;
438 trans
->mSentData
= PR_TRUE
;
443 nsHttpTransaction::ReadSegments(nsAHttpSegmentReader
*reader
,
444 PRUint32 count
, PRUint32
*countRead
)
446 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
448 if (mTransactionDone
) {
454 mConnected
= PR_TRUE
;
455 mConnection
->GetSecurityInfo(getter_AddRefs(mSecurityInfo
));
460 nsresult rv
= mRequestStream
->ReadSegments(ReadRequestSegment
, this, count
, countRead
);
464 // if read would block then we need to AsyncWait on the request stream.
465 // have callback occur on socket thread so we stay synchronized.
466 if (rv
== NS_BASE_STREAM_WOULD_BLOCK
) {
467 nsCOMPtr
<nsIAsyncInputStream
> asyncIn
=
468 do_QueryInterface(mRequestStream
);
470 nsCOMPtr
<nsIEventTarget
> target
;
471 gHttpHandler
->GetSocketThreadTarget(getter_AddRefs(target
));
473 asyncIn
->AsyncWait(this, 0, 0, target
);
475 NS_ERROR("no socket thread event target");
476 rv
= NS_ERROR_UNEXPECTED
;
485 nsHttpTransaction::WritePipeSegment(nsIOutputStream
*stream
,
490 PRUint32
*countWritten
)
492 nsHttpTransaction
*trans
= (nsHttpTransaction
*) closure
;
494 if (trans
->mTransactionDone
)
495 return NS_BASE_STREAM_CLOSED
; // stop iterating
499 // OK, now let the caller fill this segment with data.
501 rv
= trans
->mWriter
->OnWriteSegment(buf
, count
, countWritten
);
502 if (NS_FAILED(rv
)) return rv
; // caller didn't want to write anything
504 NS_ASSERTION(*countWritten
> 0, "bad writer");
505 trans
->mReceivedData
= PR_TRUE
;
507 // now let the transaction "play" with the buffer. it is free to modify
508 // the contents of the buffer and/or modify countWritten.
509 rv
= trans
->ProcessData(buf
, *countWritten
, countWritten
);
513 return rv
; // failure code only stops WriteSegments; it is not propagated.
517 nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter
*writer
,
518 PRUint32 count
, PRUint32
*countWritten
)
520 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
522 if (mTransactionDone
)
523 return NS_SUCCEEDED(mStatus
) ? NS_BASE_STREAM_CLOSED
: mStatus
;
527 nsresult rv
= mPipeOut
->WriteSegments(WritePipeSegment
, this, count
, countWritten
);
531 // if pipe would block then we need to AsyncWait on it. have callback
532 // occur on socket thread so we stay synchronized.
533 if (rv
== NS_BASE_STREAM_WOULD_BLOCK
) {
534 nsCOMPtr
<nsIEventTarget
> target
;
535 gHttpHandler
->GetSocketThreadTarget(getter_AddRefs(target
));
537 mPipeOut
->AsyncWait(this, 0, 0, target
);
539 NS_ERROR("no socket thread event target");
540 rv
= NS_ERROR_UNEXPECTED
;
548 nsHttpTransaction::Close(nsresult reason
)
550 LOG(("nsHttpTransaction::Close [this=%x reason=%x]\n", this, reason
));
552 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
555 LOG((" already closed\n"));
559 if (mActivityDistributor
) {
560 // report the reponse is complete if not already reported
561 if (!mResponseIsComplete
)
562 mActivityDistributor
->ObserveActivity(
564 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION
,
565 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE
,
567 static_cast<PRUint64
>(mContentRead
.mValue
),
570 // report that this transaction is closing
571 mActivityDistributor
->ObserveActivity(
573 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION
,
574 NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE
,
575 LL_ZERO
, LL_ZERO
, EmptyCString());
578 // we must no longer reference the connection! find out if the
579 // connection was being reused before letting it go.
580 PRBool connReused
= PR_FALSE
;
582 connReused
= mConnection
->IsReused();
583 mConnected
= PR_FALSE
;
586 // if the connection was reset or closed before we wrote any part of the
587 // request or if we wrote the request but didn't receive any part of the
588 // response and the connection was being reused, then we can (and really
589 // should) assume that we wrote to a stale connection and we must therefore
590 // repeat the request over a new connection.
592 // NOTE: the conditions under which we will automatically retry the HTTP
593 // request have to be carefully selected to avoid duplication of the
594 // request from the point-of-view of the server. such duplication could
595 // have dire consequences including repeated purchases, etc.
597 // NOTE: because of the way SSL proxy CONNECT is implemented, it is
598 // possible that the transaction may have received data without having
599 // sent any data. for this reason, mSendData == FALSE does not imply
600 // mReceivedData == FALSE. (see bug 203057 for more info.)
602 if (reason
== NS_ERROR_NET_RESET
|| reason
== NS_OK
) {
603 if (!mReceivedData
&& (!mSentData
|| connReused
)) {
604 // if restarting fails, then we must proceed to close the pipe,
605 // which will notify the channel that the transaction failed.
606 if (NS_SUCCEEDED(Restart()))
611 PRBool relConn
= PR_TRUE
;
612 if (NS_SUCCEEDED(reason
)) {
613 // the server has not sent the final \r\n terminating the header
614 // section, and there may still be a header line unparsed. let's make
615 // sure we parse the remaining header line, and then hopefully, the
616 // response will be usable (see bug 88792). related to that, we may
617 // also have an empty response containing no headers. we should treat
618 // that as an empty HTTP/0.9 response (see bug 300613).
619 if (!mHaveAllHeaders
) {
622 ParseHead(&data
, 1, &unused
);
625 // honor the sticky connection flag...
626 if (mCaps
& NS_HTTP_STICKY_CONNECTION
)
629 if (relConn
&& mConnection
)
630 NS_RELEASE(mConnection
);
633 mTransactionDone
= PR_TRUE
; // forcibly flag the transaction as complete
636 // release some resources that we no longer need
637 mRequestStream
= nsnull
;
638 mReqHeaderBuf
.Truncate();
640 if (mChunkedDecoder
) {
641 delete mChunkedDecoder
;
642 mChunkedDecoder
= nsnull
;
645 // closing this pipe triggers the channel's OnStopRequest method.
646 mPipeOut
->CloseWithStatus(reason
);
649 //-----------------------------------------------------------------------------
650 // nsHttpTransaction <private>
651 //-----------------------------------------------------------------------------
654 nsHttpTransaction::Restart()
656 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
658 // limit the number of restart attempts - bug 92224
659 if (++mRestartCount
>= gHttpHandler
->MaxRequestAttempts()) {
660 LOG(("reached max request attempts, failing transaction @%x\n", this));
661 return NS_ERROR_NET_RESET
;
664 LOG(("restarting transaction @%x\n", this));
666 // rewind streams in case we already wrote out the request
667 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mRequestStream
);
669 seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, 0);
671 // clear old connection state...
673 NS_IF_RELEASE(mConnection
);
675 // disable pipelining for the next attempt in case pipelining caused the
676 // reset. this is being overly cautious since we don't know if pipelining
677 // was the problem here.
678 mCaps
&= ~NS_HTTP_ALLOW_PIPELINING
;
680 return gHttpHandler
->InitiateTransaction(this, mPriority
);
684 nsHttpTransaction::ParseLine(char *line
)
686 LOG(("nsHttpTransaction::ParseLine [%s]\n", line
));
688 if (!mHaveStatusLine
) {
689 mResponseHead
->ParseStatusLine(line
);
690 mHaveStatusLine
= PR_TRUE
;
691 // XXX this should probably never happen
692 if (mResponseHead
->Version() == NS_HTTP_VERSION_0_9
)
693 mHaveAllHeaders
= PR_TRUE
;
696 mResponseHead
->ParseHeaderLine(line
);
700 nsHttpTransaction::ParseLineSegment(char *segment
, PRUint32 len
)
702 NS_PRECONDITION(!mHaveAllHeaders
, "already have all headers");
704 if (!mLineBuf
.IsEmpty() && mLineBuf
.Last() == '\n') {
705 // trim off the new line char, and if this segment is
706 // not a continuation of the previous or if we haven't
707 // parsed the status line yet, then parse the contents
709 mLineBuf
.Truncate(mLineBuf
.Length() - 1);
710 if (!mHaveStatusLine
|| (*segment
!= ' ' && *segment
!= '\t')) {
711 ParseLine(mLineBuf
.BeginWriting());
716 // append segment to mLineBuf...
717 if (mLineBuf
.Length() + len
> MAX_LINEBUF_LENGTH
) {
718 LOG(("excessively long header received, canceling transaction [trans=%x]", this));
719 return NS_ERROR_ABORT
;
721 mLineBuf
.Append(segment
, len
);
723 // a line buf with only a new line char signifies the end of headers.
724 if (mLineBuf
.First() == '\n') {
726 // discard this response if it is a 100 continue or other 1xx status.
727 if (mResponseHead
->Status() / 100 == 1) {
728 LOG(("ignoring 1xx response\n"));
729 mHaveStatusLine
= PR_FALSE
;
730 mResponseHead
->Reset();
733 mHaveAllHeaders
= PR_TRUE
;
739 nsHttpTransaction::ParseHead(char *buf
,
747 LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count
));
751 NS_PRECONDITION(!mHaveAllHeaders
, "oops");
753 // allocate the response head object if necessary
754 if (!mResponseHead
) {
755 mResponseHead
= new nsHttpResponseHead();
757 return NS_ERROR_OUT_OF_MEMORY
;
759 // report that we have a least some of the response
760 if (mActivityDistributor
)
761 mActivityDistributor
->ObserveActivity(
763 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION
,
764 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START
,
765 LL_ZERO
, LL_ZERO
, EmptyCString());
768 // if we don't have a status line and the line buf is empty, then
769 // this must be the first time we've been called.
770 if (!mHaveStatusLine
&& mLineBuf
.IsEmpty()) {
771 // tolerate some junk before the status line
772 char *p
= LocateHttpStart(buf
, PR_MIN(count
, 8));
774 // Treat any 0.9 style response of a put as a failure.
775 if (mRequestHead
->Method() == nsHttp::Put
)
776 return NS_ERROR_ABORT
;
778 mResponseHead
->ParseStatusLine("");
779 mHaveStatusLine
= PR_TRUE
;
780 mHaveAllHeaders
= PR_TRUE
;
784 // skip over the junk
785 *countRead
= p
- buf
;
789 // otherwise we can assume that we don't have a HTTP/0.9 response.
791 while ((eol
= static_cast<char *>(memchr(buf
, '\n', count
- *countRead
))) != nsnull
) {
792 // found line in range [buf:eol]
797 // actually, the line is in the range [buf:eol-1]
798 if ((eol
> buf
) && (*(eol
-1) == '\r'))
802 rv
= ParseLineSegment(buf
, len
);
813 // do something about a partial header line
814 if (!mHaveAllHeaders
&& (len
= count
- *countRead
)) {
816 // ignore a trailing carriage return, and don't bother calling
817 // ParseLineSegment if buf only contains a carriage return.
818 if ((buf
[len
-1] == '\r') && (--len
== 0))
820 rv
= ParseLineSegment(buf
, len
);
827 // called on the socket thread
829 nsHttpTransaction::HandleContentStart()
831 LOG(("nsHttpTransaction::HandleContentStart [this=%x]\n", this));
834 #if defined(PR_LOGGING)
835 if (LOG3_ENABLED()) {
836 LOG3(("http response [\n"));
837 nsCAutoString headers
;
838 mResponseHead
->Flatten(headers
, PR_FALSE
);
839 LogHeaders(headers
.get());
843 // notify the connection, give it a chance to cause a reset.
844 PRBool reset
= PR_FALSE
;
845 mConnection
->OnHeadersAvailable(this, mRequestHead
, mResponseHead
, &reset
);
847 // looks like we should ignore this response, resetting...
849 LOG(("resetting transaction's response head\n"));
850 mHaveAllHeaders
= PR_FALSE
;
851 mHaveStatusLine
= PR_FALSE
;
852 mReceivedData
= PR_FALSE
;
853 mSentData
= PR_FALSE
;
854 mResponseHead
->Reset();
855 // wait to be called again...
859 // check if this is a no-content response
860 switch (mResponseHead
->Status()) {
864 mNoContent
= PR_TRUE
;
865 LOG(("this response should not contain a body.\n"));
872 // grab the content-length from the response headers
873 mContentLength
= mResponseHead
->ContentLength();
875 // handle chunked encoding here, so we'll know immediately when
876 // we're done with the socket. please note that _all_ other
877 // decoding is done when the channel receives the content data
878 // so as not to block the socket transport thread too much.
879 // ignore chunked responses from HTTP/1.0 servers and proxies.
880 if (mResponseHead
->Version() >= NS_HTTP_VERSION_1_1
&&
881 mResponseHead
->HasHeaderValue(nsHttp::Transfer_Encoding
, "chunked")) {
882 // we only support the "chunked" transfer encoding right now.
883 mChunkedDecoder
= new nsHttpChunkedDecoder();
884 if (!mChunkedDecoder
)
885 return NS_ERROR_OUT_OF_MEMORY
;
886 LOG(("chunked decoder created\n"));
887 // Ignore server specified Content-Length.
890 #if defined(PR_LOGGING)
891 else if (mContentLength
== nsInt64(-1))
892 LOG(("waiting for the server to close the connection.\n"));
897 mDidContentStart
= PR_TRUE
;
901 // called on the socket thread
903 nsHttpTransaction::HandleContent(char *buf
,
905 PRUint32
*contentRead
,
906 PRUint32
*contentRemaining
)
910 LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n", this, count
));
913 *contentRemaining
= 0;
915 NS_ASSERTION(mConnection
, "no connection");
917 if (!mDidContentStart
) {
918 rv
= HandleContentStart();
919 if (NS_FAILED(rv
)) return rv
;
920 // Do not write content to the pipe if we haven't started streaming yet
921 if (!mDidContentStart
)
925 if (mChunkedDecoder
) {
926 // give the buf over to the chunked decoder so it can reformat the
927 // data and tell us how much is really there.
928 rv
= mChunkedDecoder
->HandleChunkedContent(buf
, count
, contentRead
, contentRemaining
);
929 if (NS_FAILED(rv
)) return rv
;
931 else if (mContentLength
>= nsInt64(0)) {
932 // HTTP/1.0 servers have been known to send erroneous Content-Length
933 // headers. So, unless the connection is persistent, we must make
934 // allowances for a possibly invalid Content-Length header. Thus, if
935 // NOT persistent, we simply accept everything in |buf|.
936 if (mConnection
->IsPersistent()) {
937 nsInt64 remaining
= mContentLength
- mContentRead
;
938 nsInt64 count64
= count
;
939 *contentRead
= PR_MIN(count64
, remaining
);
940 *contentRemaining
= count
- *contentRead
;
943 *contentRead
= count
;
944 // mContentLength might need to be increased...
945 nsInt64 position
= mContentRead
+ nsInt64(count
);
946 if (position
> mContentLength
) {
947 mContentLength
= position
;
948 //mResponseHead->SetContentLength(mContentLength);
953 // when we are just waiting for the server to close the connection...
954 // (no explicit content-length given)
955 *contentRead
= count
;
959 // update count of content bytes read and report progress...
960 mContentRead
+= *contentRead
;
961 /* when uncommenting, take care of 64-bit integers w/ PR_MAX...
963 mProgressSink->OnProgress(nsnull, nsnull, mContentRead, PR_MAX(0, mContentLength));
967 LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
968 this, count
, *contentRead
, mContentRead
.mValue
, mContentLength
.mValue
));
970 // check for end-of-file
971 if ((mContentRead
== mContentLength
) ||
972 (mChunkedDecoder
&& mChunkedDecoder
->ReachedEOF())) {
973 // the transaction is done with a complete response.
974 mTransactionDone
= PR_TRUE
;
975 mResponseIsComplete
= PR_TRUE
;
977 // report the entire response has arrived
978 if (mActivityDistributor
)
979 mActivityDistributor
->ObserveActivity(
981 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION
,
982 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE
,
984 static_cast<PRUint64
>(mContentRead
.mValue
),
992 nsHttpTransaction::ProcessData(char *buf
, PRUint32 count
, PRUint32
*countRead
)
996 LOG(("nsHttpTransaction::ProcessData [this=%x count=%u]\n", this, count
));
1000 // we may not have read all of the headers yet...
1001 if (!mHaveAllHeaders
) {
1002 PRUint32 bytesConsumed
= 0;
1004 rv
= ParseHead(buf
, count
, &bytesConsumed
);
1005 if (NS_FAILED(rv
)) return rv
;
1007 count
-= bytesConsumed
;
1009 // if buf has some content in it, shift bytes to top of buf.
1010 if (count
&& bytesConsumed
)
1011 memmove(buf
, buf
+ bytesConsumed
, count
);
1013 // report the completed response header
1014 if (mActivityDistributor
&& mResponseHead
&& mHaveAllHeaders
) {
1015 nsCAutoString completeResponseHeaders
;
1016 mResponseHead
->Flatten(completeResponseHeaders
, PR_FALSE
);
1017 completeResponseHeaders
.AppendLiteral("\r\n");
1018 mActivityDistributor
->ObserveActivity(
1020 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION
,
1021 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER
,
1023 completeResponseHeaders
);
1027 // even though count may be 0, we still want to call HandleContent
1028 // so it can complete the transaction if this is a "no-content" response.
1029 if (mHaveAllHeaders
) {
1030 PRUint32 countRemaining
= 0;
1034 // +--------------------------------------+----------------+-----+
1035 // | countRead | countRemaining | |
1036 // +--------------------------------------+----------------+-----+
1038 // count : bytes read from the socket
1039 // countRead : bytes corresponding to this transaction
1040 // countRemaining : bytes corresponding to next pipelined transaction
1043 // count > countRead + countRemaining <==> chunked transfer encoding
1045 rv
= HandleContent(buf
, count
, countRead
, &countRemaining
);
1046 if (NS_FAILED(rv
)) return rv
;
1047 // we may have read more than our share, in which case we must give
1048 // the excess bytes back to the connection
1049 if (mResponseIsComplete
&& countRemaining
) {
1050 NS_ASSERTION(mConnection
, "no connection");
1051 mConnection
->PushBack(buf
+ *countRead
, countRemaining
);
1058 //-----------------------------------------------------------------------------
1059 // nsHttpTransaction deletion event
1060 //-----------------------------------------------------------------------------
1062 class nsDeleteHttpTransaction
: public nsRunnable
{
1064 nsDeleteHttpTransaction(nsHttpTransaction
*trans
)
1074 nsHttpTransaction
*mTrans
;
1078 nsHttpTransaction::DeleteSelfOnConsumerThread()
1080 LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%x]\n", this));
1083 if (NS_SUCCEEDED(mConsumerTarget
->IsOnCurrentThread(&val
)) && val
)
1086 LOG(("proxying delete to consumer thread...\n"));
1087 nsCOMPtr
<nsIRunnable
> event
= new nsDeleteHttpTransaction(this);
1088 if (NS_FAILED(mConsumerTarget
->Dispatch(event
, NS_DISPATCH_NORMAL
)))
1089 NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
1093 //-----------------------------------------------------------------------------
1094 // nsHttpTransaction::nsISupports
1095 //-----------------------------------------------------------------------------
1097 NS_IMPL_THREADSAFE_ADDREF(nsHttpTransaction
)
1099 NS_IMETHODIMP_(nsrefcnt
)
1100 nsHttpTransaction::Release()
1103 NS_PRECONDITION(0 != mRefCnt
, "dup release");
1104 count
= PR_AtomicDecrement((PRInt32
*) &mRefCnt
);
1105 NS_LOG_RELEASE(this, count
, "nsHttpTransaction");
1107 mRefCnt
= 1; /* stablize */
1108 // it is essential that the transaction be destroyed on the consumer
1109 // thread (we could be holding the last reference to our consumer).
1110 DeleteSelfOnConsumerThread();
1116 NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHttpTransaction
,
1117 nsIInputStreamCallback
,
1118 nsIOutputStreamCallback
)
1120 //-----------------------------------------------------------------------------
1121 // nsHttpTransaction::nsIInputStreamCallback
1122 //-----------------------------------------------------------------------------
1124 // called on the socket thread
1126 nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream
*out
)
1129 nsresult rv
= mConnection
->ResumeSend();
1131 NS_ERROR("ResumeSend failed");
1136 //-----------------------------------------------------------------------------
1137 // nsHttpTransaction::nsIOutputStreamCallback
1138 //-----------------------------------------------------------------------------
1140 // called on the socket thread
1142 nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream
*out
)
1145 nsresult rv
= mConnection
->ResumeRecv();
1147 NS_ERROR("ResumeRecv failed");