2001-05-11 21:04:09 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Mozilla Public
|
|
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of
|
|
|
|
* the License at http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS
|
|
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
|
|
* implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Netscape
|
|
|
|
* Communications. Portions created by Netscape Communications are
|
|
|
|
* Copyright (C) 2001 by Netscape Communications. All
|
|
|
|
* Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Darin Fisher <darin@netscape.com> (original author)
|
2001-05-25 23:07:44 +00:00
|
|
|
* Andreas M. Schneider <clarence@clarence.de>
|
2001-05-11 21:04:09 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nsHttpHandler.h"
|
|
|
|
#include "nsHttpTransaction.h"
|
|
|
|
#include "nsHttpConnection.h"
|
|
|
|
#include "nsHttpRequestHead.h"
|
|
|
|
#include "nsHttpResponseHead.h"
|
|
|
|
#include "nsHttpChunkedDecoder.h"
|
2003-01-18 02:15:14 +00:00
|
|
|
#include "nsIOService.h"
|
|
|
|
#include "nsAutoLock.h"
|
2001-05-11 21:04:09 +00:00
|
|
|
#include "pratom.h"
|
2001-05-25 23:18:50 +00:00
|
|
|
#include "plevent.h"
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
#include "nsIStringStream.h"
|
|
|
|
#include "nsISeekableStream.h"
|
|
|
|
#include "nsISocketTransport.h"
|
|
|
|
#include "nsMultiplexInputStream.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// defined by the socket transport service while active
|
|
|
|
extern PRThread *gSocketThread;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
|
|
|
|
|
2002-08-27 02:26:43 +00:00
|
|
|
// mLineBuf is limited to this number of bytes.
|
|
|
|
#define MAX_LINEBUF_LENGTH (1024 * 10)
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
2001-09-04 23:11:46 +00:00
|
|
|
// helpers
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static char *
|
|
|
|
LocateHttpStart(char *buf, PRUint32 len)
|
|
|
|
{
|
|
|
|
// if we have received less than 4 bytes of data, then we'll have to
|
|
|
|
// just accept a partial match, which may not be correct.
|
|
|
|
if (len < 4)
|
|
|
|
return (PL_strncasecmp(buf, "HTTP", len) == 0) ? buf : 0;
|
|
|
|
|
|
|
|
// PL_strncasestr would be perfect for this, but unfortunately bug 96571
|
|
|
|
// prevents its use here.
|
|
|
|
while (len >= 4) {
|
|
|
|
if (PL_strncasecmp(buf, "HTTP", 4) == 0)
|
|
|
|
return buf;
|
|
|
|
buf++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-09-28 22:23:26 +00:00
|
|
|
#if defined(PR_LOGGING)
|
|
|
|
static void
|
|
|
|
LogHeaders(const char *lines)
|
|
|
|
{
|
|
|
|
nsCAutoString buf;
|
|
|
|
char *p;
|
|
|
|
while ((p = PL_strstr(lines, "\r\n")) != nsnull) {
|
|
|
|
buf.Assign(lines, p - lines);
|
|
|
|
if (PL_strcasestr(buf.get(), "authorization: ") != nsnull) {
|
|
|
|
char *p = PL_strchr(PL_strchr(buf.get(), ' ')+1, ' ');
|
|
|
|
while (*++p) *p = '*';
|
|
|
|
}
|
2003-07-11 22:43:28 +00:00
|
|
|
LOG3((" %s\n", buf.get()));
|
2001-09-28 22:23:26 +00:00
|
|
|
lines = p + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2001-09-04 23:11:46 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction <public>
|
2001-05-11 21:04:09 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::nsHttpTransaction()
|
|
|
|
: mRequestSize(0)
|
2001-05-11 21:04:09 +00:00
|
|
|
, mConnection(nsnull)
|
2003-01-18 02:15:14 +00:00
|
|
|
, mConnInfo(nsnull)
|
|
|
|
, mRequestHead(nsnull)
|
2001-05-11 21:04:09 +00:00
|
|
|
, mResponseHead(nsnull)
|
|
|
|
, mContentLength(-1)
|
|
|
|
, mContentRead(0)
|
|
|
|
, mChunkedDecoder(nsnull)
|
|
|
|
, mStatus(NS_OK)
|
2003-01-18 02:15:14 +00:00
|
|
|
, mLock(PR_NewLock())
|
|
|
|
, mTransportStatus(0)
|
|
|
|
, mTransportProgress(0)
|
|
|
|
, mTransportProgressMax(0)
|
2001-08-14 01:03:27 +00:00
|
|
|
, mRestartCount(0)
|
2003-01-18 02:15:14 +00:00
|
|
|
, mCaps(0)
|
2003-03-26 05:05:49 +00:00
|
|
|
, mClosed(PR_FALSE)
|
|
|
|
, mDestroying(PR_FALSE)
|
2003-01-18 02:15:14 +00:00
|
|
|
, mConnected(PR_FALSE)
|
2001-05-11 21:04:09 +00:00
|
|
|
, mHaveStatusLine(PR_FALSE)
|
|
|
|
, mHaveAllHeaders(PR_FALSE)
|
2003-01-18 02:15:14 +00:00
|
|
|
, mTransactionDone(PR_FALSE)
|
2002-03-26 23:33:19 +00:00
|
|
|
, mResponseIsComplete(PR_FALSE)
|
2003-01-18 02:15:14 +00:00
|
|
|
, mDidContentStart(PR_FALSE)
|
2001-05-11 21:04:09 +00:00
|
|
|
, mNoContent(PR_FALSE)
|
2003-04-10 19:06:24 +00:00
|
|
|
, mSentData(PR_FALSE)
|
2003-02-09 20:13:46 +00:00
|
|
|
, mReceivedData(PR_FALSE)
|
2003-03-26 05:05:49 +00:00
|
|
|
, mStatusEventPending(PR_FALSE)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
|
|
|
LOG(("Creating nsHttpTransaction @%x\n", this));
|
|
|
|
}
|
|
|
|
|
|
|
|
nsHttpTransaction::~nsHttpTransaction()
|
|
|
|
{
|
|
|
|
LOG(("Destroying nsHttpTransaction @%x\n", this));
|
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
NS_IF_RELEASE(mConnection);
|
2003-01-18 02:15:14 +00:00
|
|
|
NS_IF_RELEASE(mConnInfo);
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-08-02 19:26:24 +00:00
|
|
|
delete mResponseHead;
|
2003-01-18 02:15:14 +00:00
|
|
|
delete mChunkedDecoder;
|
|
|
|
|
|
|
|
PR_DestroyLock(mLock);
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::Init(PRUint8 caps,
|
|
|
|
nsHttpConnectionInfo *cinfo,
|
|
|
|
nsHttpRequestHead *requestHead,
|
|
|
|
nsIInputStream *requestBody,
|
|
|
|
PRBool requestBodyHasHeaders,
|
|
|
|
nsIEventQueue *queue,
|
|
|
|
nsIInterfaceRequestor *callbacks,
|
|
|
|
nsITransportEventSink *eventsink,
|
|
|
|
nsIAsyncInputStream **responseBody)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
LOG(("nsHttpTransaction::Init [this=%x caps=%x]\n", this, caps));
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
NS_ASSERTION(cinfo, "ouch");
|
|
|
|
NS_ASSERTION(requestHead, "ouch");
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
NS_ADDREF(mConnInfo = cinfo);
|
|
|
|
mCallbacks = callbacks;
|
|
|
|
mTransportSink = eventsink;
|
|
|
|
mConsumerEventQ = queue;
|
|
|
|
mCaps = caps;
|
2001-09-14 21:08:58 +00:00
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
if (requestHead->Method() == nsHttp::Head)
|
|
|
|
mNoContent = PR_TRUE;
|
|
|
|
|
2001-05-30 00:40:50 +00:00
|
|
|
// grab a weak reference to the request head
|
|
|
|
mRequestHead = requestHead;
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
// make sure we eliminate any proxy specific headers from
|
|
|
|
// the request if we are talking HTTPS via a SSL tunnel.
|
|
|
|
PRBool pruneProxyHeaders = cinfo->UsingSSL() &&
|
|
|
|
cinfo->UsingHttpProxy();
|
|
|
|
mReqHeaderBuf.Truncate();
|
2002-03-13 07:34:51 +00:00
|
|
|
requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-09-28 22:23:26 +00:00
|
|
|
#if defined(PR_LOGGING)
|
2003-07-11 22:43:28 +00:00
|
|
|
if (LOG3_ENABLED()) {
|
|
|
|
LOG3(("http request [\n"));
|
2001-09-28 22:23:26 +00:00
|
|
|
LogHeaders(mReqHeaderBuf.get());
|
2003-07-11 22:43:28 +00:00
|
|
|
LOG3(("]\n"));
|
2001-09-28 22:23:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2002-01-11 22:44:26 +00:00
|
|
|
// If the request body does not include headers or if there is no request
|
|
|
|
// body, then we must add the header/body separator manually.
|
|
|
|
if (!requestBodyHasHeaders || !requestBody)
|
2001-05-11 21:04:09 +00:00
|
|
|
mReqHeaderBuf.Append("\r\n");
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
// Create a string stream for the request header buf (the stream holds
|
|
|
|
// a non-owning reference to the request header data, so we MUST keep
|
|
|
|
// mReqHeaderBuf around).
|
2003-07-11 23:10:27 +00:00
|
|
|
nsCOMPtr<nsIInputStream> headers;
|
|
|
|
rv = NS_NewByteInputStream(getter_AddRefs(headers),
|
2001-11-04 05:50:37 +00:00
|
|
|
mReqHeaderBuf.get(),
|
|
|
|
mReqHeaderBuf.Length());
|
2001-05-11 21:04:09 +00:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
if (requestBody) {
|
|
|
|
// wrap the headers and request body in a multiplexed input stream.
|
|
|
|
nsCOMPtr<nsIMultiplexInputStream> multi =
|
|
|
|
do_CreateInstance(kMultiplexInputStream, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = multi->AppendStream(headers);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = multi->AppendStream(requestBody);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
mRequestStream = multi;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mRequestStream = headers;
|
|
|
|
|
|
|
|
rv = mRequestStream->Available(&mRequestSize);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
// create pipe for response stream
|
|
|
|
rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
|
|
|
|
getter_AddRefs(mPipeOut),
|
|
|
|
PR_TRUE, PR_TRUE,
|
|
|
|
NS_HTTP_SEGMENT_SIZE,
|
|
|
|
NS_HTTP_SEGMENT_COUNT,
|
|
|
|
nsIOService::gBufferCache);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
NS_ADDREF(*responseBody = mPipeIn);
|
|
|
|
return NS_OK;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsHttpResponseHead *
|
|
|
|
nsHttpTransaction::TakeResponseHead()
|
|
|
|
{
|
2001-08-02 19:26:24 +00:00
|
|
|
if (!mHaveAllHeaders)
|
|
|
|
return nsnull;
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
nsHttpResponseHead *head = mResponseHead;
|
|
|
|
mResponseHead = nsnull;
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
2001-10-02 00:31:30 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction::nsAHttpTransaction
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2003-03-26 05:05:49 +00:00
|
|
|
void
|
|
|
|
nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
|
|
|
|
{
|
|
|
|
NS_IF_RELEASE(mConnection);
|
|
|
|
NS_IF_ADDREF(mConnection = conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
|
|
|
|
{
|
|
|
|
NS_IF_ADDREF(*cb = mCallbacks);
|
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
void
|
|
|
|
nsHttpTransaction::OnTransportStatus(nsresult status, PRUint32 progress)
|
|
|
|
{
|
|
|
|
LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%u]\n",
|
|
|
|
this, status, progress));
|
|
|
|
|
|
|
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
|
|
|
PRBool postEvent;
|
|
|
|
{
|
|
|
|
nsAutoLock lock(mLock);
|
|
|
|
|
|
|
|
// stamp latest socket status
|
|
|
|
mTransportStatus = status;
|
|
|
|
|
|
|
|
if (status == nsISocketTransport::STATUS_RECEIVING_FROM) {
|
|
|
|
// ignore the progress argument and use our own. as a result,
|
|
|
|
// the progress reported will not include the size of the response
|
|
|
|
// headers. this is OK b/c we only want to report the progress
|
|
|
|
// downloading the body of the response.
|
|
|
|
mTransportProgress = mContentRead;
|
|
|
|
mTransportProgressMax = mContentLength;
|
|
|
|
}
|
2003-01-24 00:53:00 +00:00
|
|
|
else if (status == nsISocketTransport::STATUS_SENDING_TO) {
|
2003-01-18 02:15:14 +00:00
|
|
|
// when uploading, we include the request headers in the progress
|
|
|
|
// notifications.
|
|
|
|
mTransportProgress = progress;
|
|
|
|
mTransportProgressMax = mRequestSize;
|
|
|
|
}
|
2003-01-24 00:53:00 +00:00
|
|
|
else {
|
|
|
|
mTransportProgress = 0;
|
|
|
|
mTransportProgressMax = 0;
|
|
|
|
}
|
2003-01-18 02:15:14 +00:00
|
|
|
|
2003-03-26 05:05:49 +00:00
|
|
|
postEvent = !mStatusEventPending;
|
|
|
|
mStatusEventPending = PR_TRUE;
|
2003-01-18 02:15:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// only post an event if there is not already an event in progress. we
|
|
|
|
// do this as an optimization to avoid an excessive number of status events.
|
|
|
|
if (postEvent) {
|
|
|
|
PLEvent *ev = new PLEvent;
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
PL_InitEvent(ev, this, TransportStatus_Handler, TransportStatus_Cleanup);
|
|
|
|
if (mConsumerEventQ->PostEvent(ev) != PR_SUCCESS) {
|
|
|
|
NS_RELEASE_THIS();
|
|
|
|
delete ev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-26 05:05:49 +00:00
|
|
|
PRBool
|
|
|
|
nsHttpTransaction::IsDone()
|
|
|
|
{
|
|
|
|
return mTransactionDone;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::Status()
|
|
|
|
{
|
|
|
|
return mStatus;
|
|
|
|
}
|
|
|
|
|
2002-04-19 22:25:23 +00:00
|
|
|
PRUint32
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::Available()
|
2002-04-19 22:25:23 +00:00
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
PRUint32 size;
|
|
|
|
if (NS_FAILED(mRequestStream->Available(&size)))
|
|
|
|
size = 0;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_METHOD
|
|
|
|
nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
|
|
|
|
void *closure,
|
|
|
|
const char *buf,
|
|
|
|
PRUint32 offset,
|
|
|
|
PRUint32 count,
|
|
|
|
PRUint32 *countRead)
|
|
|
|
{
|
|
|
|
nsHttpTransaction *trans = (nsHttpTransaction *) closure;
|
2003-04-10 19:06:24 +00:00
|
|
|
nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
trans->mSentData = PR_TRUE;
|
|
|
|
return NS_OK;
|
2002-04-19 22:25:23 +00:00
|
|
|
}
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
nsresult
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
|
|
|
|
PRUint32 count, PRUint32 *countRead)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
NS_ASSERTION(PR_CurrentThread() == gSocketThread, "wrong thread");
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-03-25 02:23:11 +00:00
|
|
|
if (mTransactionDone) {
|
|
|
|
*countRead = 0;
|
|
|
|
return mStatus;
|
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
if (!mConnected) {
|
|
|
|
mConnected = PR_TRUE;
|
|
|
|
mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
|
|
|
|
}
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
mReader = reader;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
mReader = nsnull;
|
|
|
|
return rv;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
NS_METHOD
|
|
|
|
nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
|
|
|
|
void *closure,
|
|
|
|
char *buf,
|
|
|
|
PRUint32 offset,
|
|
|
|
PRUint32 count,
|
|
|
|
PRUint32 *countWritten)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction *trans = (nsHttpTransaction *) closure;
|
|
|
|
|
|
|
|
if (trans->mTransactionDone)
|
2003-03-25 02:23:11 +00:00
|
|
|
return NS_BASE_STREAM_CLOSED; // stop iterating
|
2003-01-18 02:15:14 +00:00
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
nsresult rv;
|
2003-01-18 02:15:14 +00:00
|
|
|
//
|
|
|
|
// OK, now let the caller fill this segment with data.
|
|
|
|
//
|
|
|
|
rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
|
|
|
|
if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
NS_ASSERTION(*countWritten > 0, "bad writer");
|
2003-02-09 20:13:46 +00:00
|
|
|
trans->mReceivedData = PR_TRUE;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
// now let the transaction "play" with the buffer. it is free to modify
|
|
|
|
// the contents of the buffer and/or modify countWritten.
|
|
|
|
rv = trans->ProcessData(buf, *countWritten, countWritten);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
trans->Close(rv);
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
return rv; // failure code only stops WriteSegments; it is not propogated.
|
|
|
|
}
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
|
|
|
PRUint32 count, PRUint32 *countWritten)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(PR_CurrentThread() == gSocketThread, "wrong thread");
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
if (mTransactionDone)
|
2003-03-25 02:23:11 +00:00
|
|
|
return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
mWriter = writer;
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
|
|
|
|
|
|
|
|
mWriter = nsnull;
|
|
|
|
|
|
|
|
// if pipe would block then we need to AsyncWait on it.
|
|
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
|
|
|
mPipeOut->AsyncWait(this, 0, nsnull);
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
void
|
|
|
|
nsHttpTransaction::Close(nsresult reason)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
LOG(("nsHttpTransaction::Close [this=%x reason=%x]\n", this, reason));
|
|
|
|
|
|
|
|
NS_ASSERTION(PR_CurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2003-03-25 02:23:11 +00:00
|
|
|
if (mClosed) {
|
2003-01-18 02:15:14 +00:00
|
|
|
LOG((" already closed\n"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-02-09 20:13:46 +00:00
|
|
|
// we must no longer reference the connection! find out if the
|
|
|
|
// connection was being reused before letting it go.
|
|
|
|
PRBool connReused = PR_FALSE;
|
2003-03-26 05:05:49 +00:00
|
|
|
if (mConnection)
|
2003-02-09 20:13:46 +00:00
|
|
|
connReused = mConnection->IsReused();
|
2003-01-18 02:15:14 +00:00
|
|
|
mConnected = PR_FALSE;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-02-09 20:13:46 +00:00
|
|
|
//
|
2003-04-10 19:06:24 +00:00
|
|
|
// if the connection was reset or closed before we wrote any part of the
|
|
|
|
// request or if we wrote the request but didn't receive any part of the
|
|
|
|
// response and the connection was being reused, then we can (and really
|
|
|
|
// should) assume that we wrote to a stale connection and we must therefore
|
|
|
|
// repeat the request over a new connection.
|
2003-02-09 20:13:46 +00:00
|
|
|
//
|
2003-04-10 19:06:24 +00:00
|
|
|
// NOTE: the conditions under which we will automatically retry the HTTP
|
|
|
|
// request have to be carefully selected to avoid duplication of the
|
|
|
|
// request from the point-of-view of the server. such duplication could
|
|
|
|
// have dire consequences including repeated purchases, etc.
|
|
|
|
//
|
2003-05-05 21:41:30 +00:00
|
|
|
// NOTE: because of the way SSL proxy CONNECT is implemented, it is
|
|
|
|
// possible that the transaction may have received data without having
|
|
|
|
// sent any data. for this reason, mSendData == FALSE does not imply
|
|
|
|
// mReceivedData == FALSE. (see bug 203057 for more info.)
|
|
|
|
//
|
2003-04-10 19:06:24 +00:00
|
|
|
if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
|
2003-05-05 21:41:30 +00:00
|
|
|
if (!mReceivedData && (!mSentData || connReused)) {
|
2003-04-10 19:06:24 +00:00
|
|
|
// if restarting fails, then we must proceed to close the pipe,
|
|
|
|
// which will notify the channel that the transaction failed.
|
|
|
|
if (NS_SUCCEEDED(Restart()))
|
|
|
|
return;
|
|
|
|
}
|
2001-09-21 03:59:02 +00:00
|
|
|
}
|
|
|
|
|
2003-03-26 05:05:49 +00:00
|
|
|
PRBool relConn = PR_TRUE;
|
|
|
|
if (NS_SUCCEEDED(reason)) {
|
2003-01-18 02:15:14 +00:00
|
|
|
// the server has not sent the final \r\n terminating the header section,
|
|
|
|
// and there is still a header line unparsed. let's make sure we parse
|
|
|
|
// the remaining header line, and then hopefully, the response will be
|
|
|
|
// usable (see bug 88792).
|
2003-03-26 05:05:49 +00:00
|
|
|
if (!mHaveAllHeaders && !mLineBuf.IsEmpty())
|
|
|
|
ParseLineSegment("\n", 1);
|
|
|
|
|
|
|
|
// honor the sticky connection flag...
|
|
|
|
if (mCaps & NS_HTTP_STICKY_CONNECTION)
|
|
|
|
relConn = PR_FALSE;
|
2003-01-18 02:15:14 +00:00
|
|
|
}
|
2003-03-26 05:05:49 +00:00
|
|
|
if (relConn && mConnection)
|
|
|
|
NS_RELEASE(mConnection);
|
2001-05-30 00:40:50 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
mStatus = reason;
|
2003-03-26 05:05:49 +00:00
|
|
|
mTransactionDone = PR_TRUE; // force this flag
|
2003-03-25 02:23:11 +00:00
|
|
|
mClosed = PR_TRUE;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-03-26 05:05:49 +00:00
|
|
|
// release some resources that we no longer need
|
|
|
|
mRequestStream = nsnull;
|
|
|
|
mReqHeaderBuf.Truncate();
|
|
|
|
mLineBuf.Truncate();
|
|
|
|
if (mChunkedDecoder) {
|
|
|
|
delete mChunkedDecoder;
|
|
|
|
mChunkedDecoder = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// closing this pipe signals triggers the channel's OnStopRequest method.
|
2003-01-18 02:15:14 +00:00
|
|
|
mPipeOut->CloseEx(reason);
|
2001-09-14 21:08:58 +00:00
|
|
|
}
|
|
|
|
|
2001-09-04 23:11:46 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction <private>
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2001-09-21 03:59:02 +00:00
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::Restart()
|
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
2001-09-21 03:59:02 +00:00
|
|
|
|
|
|
|
// limit the number of restart attempts - bug 92224
|
2003-01-18 02:15:14 +00:00
|
|
|
if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
|
2001-09-21 03:59:02 +00:00
|
|
|
LOG(("reached max request attempts, failing transaction @%x\n", this));
|
2002-03-22 21:30:41 +00:00
|
|
|
return NS_ERROR_NET_RESET;
|
2001-09-21 03:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("restarting transaction @%x\n", this));
|
|
|
|
|
|
|
|
// rewind streams in case we already wrote out the request
|
2003-01-18 02:15:14 +00:00
|
|
|
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
|
2001-11-14 06:45:27 +00:00
|
|
|
if (seekable)
|
|
|
|
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
2001-09-21 03:59:02 +00:00
|
|
|
|
2003-03-26 05:05:49 +00:00
|
|
|
// clear old connection state...
|
2003-01-18 02:15:14 +00:00
|
|
|
mSecurityInfo = 0;
|
2003-03-26 05:05:49 +00:00
|
|
|
NS_IF_RELEASE(mConnection);
|
2001-09-21 03:59:02 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
return gHttpHandler->InitiateTransaction(this);
|
2001-09-21 03:59:02 +00:00
|
|
|
}
|
|
|
|
|
2001-08-02 19:26:24 +00:00
|
|
|
void
|
2001-05-11 21:04:09 +00:00
|
|
|
nsHttpTransaction::ParseLine(char *line)
|
|
|
|
{
|
|
|
|
LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
|
|
|
|
|
|
|
|
if (!mHaveStatusLine) {
|
|
|
|
mResponseHead->ParseStatusLine(line);
|
|
|
|
mHaveStatusLine = PR_TRUE;
|
2001-05-25 23:07:44 +00:00
|
|
|
// XXX this should probably never happen
|
2001-05-18 02:03:08 +00:00
|
|
|
if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
|
|
|
|
mHaveAllHeaders = PR_TRUE;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
mResponseHead->ParseHeaderLine(line);
|
2001-05-25 23:07:44 +00:00
|
|
|
}
|
|
|
|
|
2002-08-27 02:26:43 +00:00
|
|
|
nsresult
|
2001-05-25 23:07:44 +00:00
|
|
|
nsHttpTransaction::ParseLineSegment(char *segment, PRUint32 len)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mHaveAllHeaders, "already have all headers");
|
|
|
|
|
|
|
|
if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
|
2002-08-27 02:26:43 +00:00
|
|
|
// trim off the new line char, and if this segment is
|
|
|
|
// not a continuation of the previous or if we haven't
|
|
|
|
// parsed the status line yet, then parse the contents
|
|
|
|
// of mLineBuf.
|
|
|
|
mLineBuf.Truncate(mLineBuf.Length() - 1);
|
|
|
|
if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
|
2001-08-02 19:26:24 +00:00
|
|
|
ParseLine(NS_CONST_CAST(char*,mLineBuf.get()));
|
2002-08-27 02:26:43 +00:00
|
|
|
mLineBuf.Truncate();
|
2001-05-25 23:07:44 +00:00
|
|
|
}
|
|
|
|
}
|
2002-08-27 02:26:43 +00:00
|
|
|
|
|
|
|
// append segment to mLineBuf...
|
|
|
|
if (mLineBuf.Length() + len > MAX_LINEBUF_LENGTH) {
|
|
|
|
LOG(("excessively long header received, canceling transaction [trans=%x]", this));
|
|
|
|
return NS_ERROR_ABORT;
|
|
|
|
}
|
|
|
|
mLineBuf.Append(segment, len);
|
2001-08-01 22:56:50 +00:00
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
// a line buf with only a new line char signifies the end of headers.
|
|
|
|
if (mLineBuf.First() == '\n') {
|
|
|
|
mLineBuf.Truncate();
|
2001-08-02 19:26:24 +00:00
|
|
|
// discard this response if it is a 100 continue.
|
|
|
|
if (mResponseHead->Status() == 100) {
|
|
|
|
LOG(("ignoring 100 response\n"));
|
|
|
|
mHaveStatusLine = PR_FALSE;
|
|
|
|
mResponseHead->Reset();
|
2002-08-27 02:26:43 +00:00
|
|
|
return NS_OK;
|
2001-08-02 19:26:24 +00:00
|
|
|
}
|
|
|
|
mHaveAllHeaders = PR_TRUE;
|
2001-05-25 23:07:44 +00:00
|
|
|
}
|
2002-08-27 02:26:43 +00:00
|
|
|
return NS_OK;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::ParseHead(char *buf,
|
|
|
|
PRUint32 count,
|
|
|
|
PRUint32 *countRead)
|
|
|
|
{
|
2002-08-27 02:26:43 +00:00
|
|
|
nsresult rv;
|
2001-05-25 23:07:44 +00:00
|
|
|
PRUint32 len;
|
2001-05-11 21:04:09 +00:00
|
|
|
char *eol;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
|
|
|
|
|
|
|
|
*countRead = 0;
|
|
|
|
|
|
|
|
NS_PRECONDITION(!mHaveAllHeaders, "oops");
|
2001-08-02 19:26:24 +00:00
|
|
|
|
|
|
|
// allocate the response head object if necessary
|
|
|
|
if (!mResponseHead) {
|
|
|
|
mResponseHead = new nsHttpResponseHead();
|
|
|
|
if (!mResponseHead)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
2001-05-25 23:07:44 +00:00
|
|
|
|
|
|
|
// if we don't have a status line and the line buf is empty, then
|
|
|
|
// this must be the first time we've been called.
|
2001-09-04 23:11:46 +00:00
|
|
|
if (!mHaveStatusLine && mLineBuf.IsEmpty()) {
|
|
|
|
// tolerate some junk before the status line
|
2002-02-12 20:24:53 +00:00
|
|
|
char *p = LocateHttpStart(buf, PR_MIN(count, 8));
|
2001-09-04 23:11:46 +00:00
|
|
|
if (!p) {
|
2003-06-03 21:21:53 +00:00
|
|
|
// Treat any 0.9 style response of a put as a failure.
|
|
|
|
if (mRequestHead->Method() == nsHttp::Put)
|
|
|
|
return NS_ERROR_ABORT;
|
|
|
|
|
2001-09-04 23:11:46 +00:00
|
|
|
mResponseHead->ParseStatusLine("");
|
|
|
|
mHaveStatusLine = PR_TRUE;
|
|
|
|
mHaveAllHeaders = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
if (p > buf) {
|
|
|
|
// skip over the junk
|
|
|
|
*countRead = p - buf;
|
|
|
|
buf = p;
|
|
|
|
}
|
2001-05-25 23:07:44 +00:00
|
|
|
}
|
|
|
|
// otherwise we can assume that we don't have a HTTP/0.9 response.
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-06-05 18:21:53 +00:00
|
|
|
while ((eol = NS_STATIC_CAST(char *, memchr(buf, '\n', count - *countRead))) != nsnull) {
|
2001-05-11 21:04:09 +00:00
|
|
|
// found line in range [buf:eol]
|
2001-05-25 23:07:44 +00:00
|
|
|
len = eol - buf + 1;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
*countRead += len;
|
|
|
|
|
|
|
|
// actually, the line is in the range [buf:eol-1]
|
2001-05-11 21:04:09 +00:00
|
|
|
if ((eol > buf) && (*(eol-1) == '\r'))
|
2001-05-25 23:07:44 +00:00
|
|
|
len--;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
buf[len-1] = '\n';
|
2002-08-27 02:26:43 +00:00
|
|
|
rv = ParseLineSegment(buf, len);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
if (mHaveAllHeaders)
|
|
|
|
return NS_OK;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
// skip over line
|
|
|
|
buf = eol + 1;
|
|
|
|
}
|
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
// do something about a partial header line
|
|
|
|
if (!mHaveAllHeaders && (len = count - *countRead)) {
|
2001-05-11 21:04:09 +00:00
|
|
|
*countRead = count;
|
2001-05-25 23:07:44 +00:00
|
|
|
// ignore a trailing carriage return, and don't bother calling
|
|
|
|
// ParseLineSegment if buf only contains a carriage return.
|
|
|
|
if ((buf[len-1] == '\r') && (--len == 0))
|
|
|
|
return NS_OK;
|
2002-08-27 02:26:43 +00:00
|
|
|
rv = ParseLineSegment(buf, len);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
2001-08-02 19:26:24 +00:00
|
|
|
return NS_OK;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
2001-06-14 20:52:03 +00:00
|
|
|
// called on the socket thread
|
2001-06-13 22:44:47 +00:00
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::HandleContentStart()
|
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
LOG(("nsHttpTransaction::HandleContentStart [this=%x]\n", this));
|
2001-06-13 22:44:47 +00:00
|
|
|
|
|
|
|
if (mResponseHead) {
|
|
|
|
#if defined(PR_LOGGING)
|
2003-07-11 22:43:28 +00:00
|
|
|
if (LOG3_ENABLED()) {
|
|
|
|
LOG3(("http response [\n"));
|
2001-09-28 22:23:26 +00:00
|
|
|
nsCAutoString headers;
|
|
|
|
mResponseHead->Flatten(headers, PR_FALSE);
|
|
|
|
LogHeaders(headers.get());
|
2003-07-11 22:43:28 +00:00
|
|
|
LOG3(("]\n"));
|
2001-09-28 22:23:26 +00:00
|
|
|
}
|
2001-06-13 22:44:47 +00:00
|
|
|
#endif
|
|
|
|
// notify the connection, give it a chance to cause a reset.
|
2001-08-02 19:26:24 +00:00
|
|
|
PRBool reset = PR_FALSE;
|
2002-04-18 22:36:39 +00:00
|
|
|
mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
|
2001-06-13 22:44:47 +00:00
|
|
|
|
|
|
|
// looks like we should ignore this response, resetting...
|
|
|
|
if (reset) {
|
|
|
|
LOG(("resetting transaction's response head\n"));
|
|
|
|
mHaveAllHeaders = PR_FALSE;
|
|
|
|
mHaveStatusLine = PR_FALSE;
|
|
|
|
mResponseHead->Reset();
|
|
|
|
// wait to be called again...
|
2003-01-18 02:15:14 +00:00
|
|
|
return NS_OK;
|
2001-06-13 22:44:47 +00:00
|
|
|
}
|
|
|
|
|
2001-08-10 20:42:42 +00:00
|
|
|
// check if this is a no-content response
|
|
|
|
switch (mResponseHead->Status()) {
|
|
|
|
case 204:
|
|
|
|
case 205:
|
|
|
|
case 304:
|
|
|
|
mNoContent = PR_TRUE;
|
|
|
|
LOG(("this response should not contain a body.\n"));
|
|
|
|
break;
|
2001-06-13 22:44:47 +00:00
|
|
|
}
|
2001-07-17 01:19:19 +00:00
|
|
|
|
2001-08-08 08:16:39 +00:00
|
|
|
if (mNoContent)
|
|
|
|
mContentLength = 0;
|
2001-08-10 20:42:42 +00:00
|
|
|
else {
|
|
|
|
// grab the content-length from the response headers
|
|
|
|
mContentLength = mResponseHead->ContentLength();
|
|
|
|
|
|
|
|
// handle chunked encoding here, so we'll know immediately when
|
|
|
|
// we're done with the socket. please note that _all_ other
|
|
|
|
// decoding is done when the channel receives the content data
|
|
|
|
// so as not to block the socket transport thread too much.
|
|
|
|
const char *val = mResponseHead->PeekHeader(nsHttp::Transfer_Encoding);
|
|
|
|
if (PL_strcasestr(val, "chunked")) {
|
|
|
|
// we only support the "chunked" transfer encoding right now.
|
|
|
|
mChunkedDecoder = new nsHttpChunkedDecoder();
|
|
|
|
if (!mChunkedDecoder)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
LOG(("chunked decoder created\n"));
|
2001-12-20 21:13:25 +00:00
|
|
|
// Ignore server specified Content-Length.
|
|
|
|
mContentLength = -1;
|
2001-08-10 20:42:42 +00:00
|
|
|
}
|
|
|
|
#if defined(PR_LOGGING)
|
|
|
|
else if (mContentLength == -1)
|
|
|
|
LOG(("waiting for the server to close the connection.\n"));
|
|
|
|
#endif
|
|
|
|
}
|
2001-06-13 22:44:47 +00:00
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
mDidContentStart = PR_TRUE;
|
|
|
|
return NS_OK;
|
2001-06-13 22:44:47 +00:00
|
|
|
}
|
|
|
|
|
2001-06-14 20:52:03 +00:00
|
|
|
// called on the socket thread
|
2001-05-11 21:04:09 +00:00
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::HandleContent(char *buf,
|
|
|
|
PRUint32 count,
|
2002-04-23 07:30:28 +00:00
|
|
|
PRUint32 *contentRead,
|
2002-06-17 21:08:46 +00:00
|
|
|
PRUint32 *contentRemaining)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n", this, count));
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2002-04-23 07:30:28 +00:00
|
|
|
*contentRead = 0;
|
2002-06-17 21:08:46 +00:00
|
|
|
*contentRemaining = 0;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2002-04-23 07:30:28 +00:00
|
|
|
NS_ASSERTION(mConnection, "no connection");
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
if (!mDidContentStart) {
|
2001-06-13 22:44:47 +00:00
|
|
|
rv = HandleContentStart();
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mChunkedDecoder) {
|
|
|
|
// give the buf over to the chunked decoder so it can reformat the
|
|
|
|
// data and tell us how much is really there.
|
2002-06-17 21:08:46 +00:00
|
|
|
rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
|
2001-05-11 21:04:09 +00:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
}
|
2001-08-10 20:42:42 +00:00
|
|
|
else if (mContentLength >= 0) {
|
|
|
|
// HTTP/1.0 servers have been known to send erroneous Content-Length
|
2001-10-02 00:31:30 +00:00
|
|
|
// headers. So, unless the connection is persistent, we must make
|
2001-08-10 20:42:42 +00:00
|
|
|
// allowances for a possibly invalid Content-Length header. Thus, if
|
2001-10-02 00:31:30 +00:00
|
|
|
// NOT persistent, we simply accept everything in |buf|.
|
|
|
|
if (mConnection->IsPersistent()) {
|
2002-06-17 21:08:46 +00:00
|
|
|
*contentRead = PRUint32(mContentLength) - mContentRead;
|
|
|
|
*contentRead = PR_MIN(count, *contentRead);
|
2001-08-14 01:03:27 +00:00
|
|
|
}
|
2001-08-10 20:42:42 +00:00
|
|
|
else {
|
2002-06-17 21:08:46 +00:00
|
|
|
*contentRead = count;
|
2001-08-10 20:42:42 +00:00
|
|
|
// mContentLength might need to be increased...
|
2002-06-17 21:08:46 +00:00
|
|
|
if (*contentRead + mContentRead > (PRUint32) mContentLength) {
|
|
|
|
mContentLength = *contentRead + mContentRead;
|
2001-08-11 20:01:53 +00:00
|
|
|
//mResponseHead->SetContentLength(mContentLength);
|
2001-08-10 20:42:42 +00:00
|
|
|
}
|
|
|
|
}
|
2002-06-17 21:08:46 +00:00
|
|
|
*contentRemaining = (count - *contentRead);
|
2001-08-10 20:42:42 +00:00
|
|
|
}
|
2002-04-23 07:30:28 +00:00
|
|
|
else {
|
2001-05-11 21:04:09 +00:00
|
|
|
// when we are just waiting for the server to close the connection...
|
2002-06-17 21:08:46 +00:00
|
|
|
*contentRead = count;
|
2002-04-23 07:30:28 +00:00
|
|
|
}
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2002-04-23 07:30:28 +00:00
|
|
|
if (*contentRead) {
|
2001-06-14 20:52:03 +00:00
|
|
|
// update count of content bytes read and report progress...
|
2002-04-23 07:30:28 +00:00
|
|
|
mContentRead += *contentRead;
|
2003-01-18 02:15:14 +00:00
|
|
|
/*
|
2001-09-14 21:08:58 +00:00
|
|
|
if (mProgressSink)
|
|
|
|
mProgressSink->OnProgress(nsnull, nsnull, mContentRead, PR_MAX(0, mContentLength));
|
2003-01-18 02:15:14 +00:00
|
|
|
*/
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction [this=%x count=%u read=%u mContentRead=%u mContentLength=%d]\n",
|
2002-04-23 07:30:28 +00:00
|
|
|
this, count, *contentRead, mContentRead, mContentLength));
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
// check for end-of-file
|
|
|
|
if ((mContentRead == PRUint32(mContentLength)) ||
|
|
|
|
(mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
|
2003-01-18 02:15:14 +00:00
|
|
|
// the transaction is done with a complete response.
|
|
|
|
mTransactionDone = PR_TRUE;
|
|
|
|
mResponseIsComplete = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::ProcessData(char *buf, PRUint32 count, PRUint32 *countRead)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::ProcessData [this=%x count=%u]\n", this, count));
|
|
|
|
|
|
|
|
*countRead = 0;
|
|
|
|
|
|
|
|
// we may not have read all of the headers yet...
|
|
|
|
if (!mHaveAllHeaders) {
|
|
|
|
PRUint32 bytesConsumed = 0;
|
|
|
|
|
|
|
|
rv = ParseHead(buf, count, &bytesConsumed);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
count -= bytesConsumed;
|
|
|
|
|
|
|
|
// if buf has some content in it, shift bytes to top of buf.
|
|
|
|
if (count && bytesConsumed)
|
|
|
|
memmove(buf, buf + bytesConsumed, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
// even though count may be 0, we still want to call HandleContent
|
|
|
|
// so it can complete the transaction if this is a "no-content" response.
|
|
|
|
if (mHaveAllHeaders) {
|
|
|
|
PRUint32 countRemaining = 0;
|
|
|
|
//
|
|
|
|
// buf layout:
|
|
|
|
//
|
|
|
|
// +--------------------------------------+----------------+-----+
|
|
|
|
// | countRead | countRemaining | |
|
|
|
|
// +--------------------------------------+----------------+-----+
|
|
|
|
//
|
|
|
|
// count : bytes read from the socket
|
|
|
|
// countRead : bytes corresponding to this transaction
|
|
|
|
// countRemaining : bytes corresponding to next pipelined transaction
|
|
|
|
//
|
|
|
|
// NOTE:
|
|
|
|
// count > countRead + countRemaining <==> chunked transfer encoding
|
|
|
|
//
|
|
|
|
rv = HandleContent(buf, count, countRead, &countRemaining);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// we may have read more than our share, in which case we must give
|
|
|
|
// the excess bytes back to the connection
|
|
|
|
if (mResponseIsComplete && countRemaining) {
|
|
|
|
NS_ASSERTION(mConnection, "no connection");
|
|
|
|
mConnection->PushBack(buf + *countRead, countRemaining);
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction events
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void *PR_CALLBACK
|
|
|
|
nsHttpTransaction::TransportStatus_Handler(PLEvent *ev)
|
|
|
|
{
|
|
|
|
nsHttpTransaction *trans =
|
|
|
|
NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev));
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::SocketStatus_Handler [trans=%x]\n", trans));
|
|
|
|
|
|
|
|
nsresult status;
|
|
|
|
PRUint32 progress, progressMax;
|
|
|
|
{
|
|
|
|
nsAutoLock lock(trans->mLock);
|
|
|
|
|
|
|
|
status = trans->mTransportStatus;
|
|
|
|
progress = trans->mTransportProgress;
|
|
|
|
progressMax = trans->mTransportProgressMax;
|
|
|
|
|
2003-03-26 05:05:49 +00:00
|
|
|
trans->mStatusEventPending = PR_FALSE;
|
2003-01-18 02:15:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
trans->mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax);
|
|
|
|
|
|
|
|
NS_RELEASE(trans);
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PR_CALLBACK
|
|
|
|
nsHttpTransaction::TransportStatus_Cleanup(PLEvent *ev)
|
|
|
|
{
|
|
|
|
delete ev;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
void
|
|
|
|
nsHttpTransaction::DeleteSelfOnConsumerThread()
|
2001-05-25 23:18:50 +00:00
|
|
|
{
|
2001-06-11 21:20:29 +00:00
|
|
|
nsCOMPtr<nsIEventQueueService> eqs;
|
|
|
|
nsCOMPtr<nsIEventQueue> currentEventQ;
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%x]\n", this));
|
2003-01-18 02:15:14 +00:00
|
|
|
|
|
|
|
NS_ASSERTION(!mDestroying, "deleting self again");
|
|
|
|
mDestroying = PR_TRUE;
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
gHttpHandler->GetEventQueueService(getter_AddRefs(eqs));
|
2001-06-11 21:20:29 +00:00
|
|
|
if (eqs)
|
|
|
|
eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(currentEventQ));
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
if (currentEventQ == mConsumerEventQ)
|
|
|
|
delete this;
|
|
|
|
else {
|
|
|
|
LOG(("proxying delete to consumer thread...\n"));
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
PLEvent *event = new PLEvent;
|
|
|
|
if (!event) {
|
|
|
|
NS_WARNING("out of memory");
|
|
|
|
// probably better to leak |this| than to delete it on this thread.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
PL_InitEvent(event, this, DeleteThis_Handler, DeleteThis_Cleanup);
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
PRStatus status = mConsumerEventQ->PostEvent(event);
|
|
|
|
NS_ASSERTION(status == PR_SUCCESS, "PostEvent failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *PR_CALLBACK
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::DeleteThis_Handler(PLEvent *ev)
|
2001-06-11 21:20:29 +00:00
|
|
|
{
|
|
|
|
nsHttpTransaction *trans =
|
|
|
|
NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev));
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::DeleteThis_EventHandlerFunc [trans=%x]\n", trans));
|
|
|
|
|
|
|
|
delete trans;
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PR_CALLBACK
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::DeleteThis_Cleanup(PLEvent *ev)
|
2001-06-11 21:20:29 +00:00
|
|
|
{
|
|
|
|
delete ev;
|
2001-05-25 23:18:50 +00:00
|
|
|
}
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction::nsISupports
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
NS_IMPL_THREADSAFE_ADDREF(nsHttpTransaction)
|
|
|
|
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
|
|
nsHttpTransaction::Release()
|
|
|
|
{
|
|
|
|
nsrefcnt count;
|
|
|
|
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
|
|
|
count = PR_AtomicDecrement((PRInt32 *) &mRefCnt);
|
|
|
|
NS_LOG_RELEASE(this, count, "nsHttpTransaction");
|
|
|
|
if (0 == count) {
|
|
|
|
mRefCnt = 1; /* stablize */
|
|
|
|
// it is essential that the transaction be destroyed on the consumer
|
|
|
|
// thread (we could be holding the last reference to our consumer).
|
|
|
|
DeleteSelfOnConsumerThread();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHttpTransaction,
|
2003-01-18 02:15:14 +00:00
|
|
|
nsIOutputStreamNotify,
|
|
|
|
nsISocketEventHandler)
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2003-01-18 02:15:14 +00:00
|
|
|
// nsHttpTransaction::nsIOutputStreamNotify
|
2001-05-11 21:04:09 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
// proxy this event to the socket thread
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2003-01-18 02:15:14 +00:00
|
|
|
nsCOMPtr<nsISocketTransportService> sts;
|
|
|
|
gHttpHandler->ConnMgr()->GetSTS(getter_AddRefs(sts));
|
|
|
|
if (sts)
|
|
|
|
sts->PostEvent(this, 0, 0, nsnull); // only one type of event so far
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2003-01-18 02:15:14 +00:00
|
|
|
// nsHttpTransaction::nsISocketEventHandler
|
2001-05-11 21:04:09 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2003-01-18 02:15:14 +00:00
|
|
|
nsHttpTransaction::OnSocketEvent(PRUint32 type, PRUint32 param1, void *param2)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
2003-01-18 02:15:14 +00:00
|
|
|
if (mConnection) {
|
|
|
|
nsresult rv = mConnection->ResumeRecv();
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "ResumeSend failed");
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
2003-01-18 02:15:14 +00:00
|
|
|
return NS_OK;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|