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"
|
|
|
|
#include "nsIStringStream.h"
|
2001-11-14 06:45:27 +00:00
|
|
|
#include "nsISeekableStream.h"
|
2001-09-21 03:59:02 +00:00
|
|
|
#include "nsISocketTransportService.h"
|
2001-05-11 21:04:09 +00:00
|
|
|
#include "pratom.h"
|
2001-05-25 23:18:50 +00:00
|
|
|
#include "plevent.h"
|
|
|
|
|
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 = '*';
|
|
|
|
}
|
|
|
|
LOG2((" %s\n", buf.get()));
|
|
|
|
lines = p + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2001-09-04 23:11:46 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction <public>
|
2001-05-11 21:04:09 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
nsHttpTransaction::nsHttpTransaction(nsIStreamListener *listener,
|
2001-09-04 23:02:26 +00:00
|
|
|
nsIInterfaceRequestor *callbacks,
|
|
|
|
PRUint8 caps)
|
2001-05-11 21:04:09 +00:00
|
|
|
: mListener(listener)
|
|
|
|
, mCallbacks(callbacks)
|
|
|
|
, mConnection(nsnull)
|
2002-09-11 03:23:36 +00:00
|
|
|
, mReqUploadStreamOffset(0)
|
|
|
|
, mReqUploadStreamLength(0)
|
2001-05-11 21:04:09 +00:00
|
|
|
, mResponseHead(nsnull)
|
|
|
|
, mContentLength(-1)
|
|
|
|
, mContentRead(0)
|
|
|
|
, mChunkedDecoder(nsnull)
|
|
|
|
, mTransactionDone(0)
|
|
|
|
, mStatus(NS_OK)
|
2001-08-14 01:03:27 +00:00
|
|
|
, mRestartCount(0)
|
2001-09-04 23:02:26 +00:00
|
|
|
, mCapabilities(caps)
|
2001-05-11 21:04:09 +00:00
|
|
|
, mHaveStatusLine(PR_FALSE)
|
|
|
|
, mHaveAllHeaders(PR_FALSE)
|
2002-03-26 23:33:19 +00:00
|
|
|
, mResponseIsComplete(PR_FALSE)
|
2001-05-11 21:04:09 +00:00
|
|
|
, mFiredOnStart(PR_FALSE)
|
|
|
|
, mNoContent(PR_FALSE)
|
2001-05-25 23:18:50 +00:00
|
|
|
, mPrematureEOF(PR_FALSE)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
|
|
|
LOG(("Creating nsHttpTransaction @%x\n", this));
|
|
|
|
|
|
|
|
NS_PRECONDITION(listener, "null listener");
|
|
|
|
}
|
|
|
|
|
|
|
|
nsHttpTransaction::~nsHttpTransaction()
|
|
|
|
{
|
|
|
|
LOG(("Destroying nsHttpTransaction @%x\n", this));
|
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
NS_IF_RELEASE(mConnection);
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-08-02 19:26:24 +00:00
|
|
|
delete mChunkedDecoder;
|
|
|
|
delete mResponseHead;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::SetupRequest(nsHttpRequestHead *requestHead,
|
2002-01-11 22:44:26 +00:00
|
|
|
nsIInputStream *requestBody,
|
2002-03-13 07:34:51 +00:00
|
|
|
PRBool requestBodyHasHeaders,
|
|
|
|
PRBool pruneProxyHeaders)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::SetupRequest [this=%x]\n", this));
|
|
|
|
|
|
|
|
NS_ENSURE_ARG_POINTER(requestHead);
|
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
// grab a reference to the calling thread's event queue.
|
|
|
|
nsCOMPtr<nsIEventQueueService> eqs;
|
|
|
|
nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs));
|
|
|
|
if (eqs)
|
|
|
|
eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(mConsumerEventQ));
|
|
|
|
|
2001-09-14 21:08:58 +00:00
|
|
|
// build a proxy for the progress event sink
|
2002-06-27 06:46:25 +00:00
|
|
|
if (!(mCapabilities & NS_HTTP_DONT_REPORT_PROGRESS)) {
|
|
|
|
if (mCallbacks && mConsumerEventQ) {
|
|
|
|
nsCOMPtr<nsIProgressEventSink> temp = do_GetInterface(mCallbacks);
|
|
|
|
if (temp) {
|
|
|
|
nsCOMPtr<nsIProxyObjectManager> mgr;
|
|
|
|
nsHttpHandler::get()->GetProxyObjectManager(getter_AddRefs(mgr));
|
|
|
|
if (mgr)
|
|
|
|
mgr->GetProxyForObject(mConsumerEventQ,
|
|
|
|
NS_GET_IID(nsIProgressEventSink),
|
|
|
|
temp,
|
|
|
|
PROXY_ASYNC | PROXY_ALWAYS,
|
|
|
|
getter_AddRefs(mProgressSink));
|
|
|
|
}
|
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;
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
mReqHeaderBuf.SetLength(0);
|
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)
|
|
|
|
if (LOG2_ENABLED()) {
|
|
|
|
LOG2(("http request [\n"));
|
|
|
|
LogHeaders(mReqHeaderBuf.get());
|
|
|
|
LOG2(("]\n"));
|
|
|
|
}
|
|
|
|
#endif
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2002-01-11 22:44:26 +00:00
|
|
|
mReqUploadStream = requestBody;
|
2002-09-11 03:23:36 +00:00
|
|
|
if (mReqUploadStream) {
|
|
|
|
mReqUploadStream->Available(&mReqUploadStreamLength);
|
|
|
|
mReqUploadStreamOffset = 0;
|
|
|
|
}
|
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");
|
|
|
|
|
|
|
|
// Create a string stream for the request header buf
|
|
|
|
nsCOMPtr<nsISupports> sup;
|
2001-11-04 05:50:37 +00:00
|
|
|
rv = NS_NewByteInputStream(getter_AddRefs(sup),
|
|
|
|
mReqHeaderBuf.get(),
|
|
|
|
mReqHeaderBuf.Length());
|
2001-05-11 21:04:09 +00:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
mReqHeaderStream = do_QueryInterface(sup, &rv);
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2002-04-19 22:25:23 +00:00
|
|
|
PRUint32
|
|
|
|
nsHttpTransaction::GetRequestSize()
|
|
|
|
{
|
|
|
|
PRUint32 n = 0;
|
|
|
|
if (mReqHeaderStream)
|
|
|
|
mReqHeaderStream->Available(&n);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
// called on the socket transport thread
|
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::OnDataWritable(nsIOutputStream *os)
|
|
|
|
{
|
|
|
|
PRUint32 n = 0;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::OnDataWritable [this=%x]\n", this));
|
|
|
|
|
|
|
|
// check if we're done writing the headers
|
|
|
|
nsresult rv = mReqHeaderStream->Available(&n);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
// let at most NS_HTTP_BUFFER_SIZE bytes be written at a time.
|
|
|
|
|
|
|
|
if (n != 0)
|
|
|
|
return os->WriteFrom(mReqHeaderStream, NS_HTTP_BUFFER_SIZE, &n);
|
|
|
|
|
2002-09-11 03:23:36 +00:00
|
|
|
if (mReqUploadStream) {
|
|
|
|
nsresult rv = os->WriteFrom(mReqUploadStream, NS_HTTP_BUFFER_SIZE, &n);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
mReqUploadStreamOffset += n;
|
|
|
|
if (mProgressSink)
|
|
|
|
mProgressSink->OnProgress(nsnull, nsnull,
|
|
|
|
mReqUploadStreamOffset,
|
|
|
|
mReqUploadStreamLength);
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// called on the socket transport thread
|
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::OnDataReadable(nsIInputStream *is)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::OnDataReadable [this=%x]\n", this));
|
|
|
|
|
|
|
|
if (!mListener) {
|
|
|
|
LOG(("nsHttpTransaction: no listener! closing stream\n"));
|
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSource = is;
|
|
|
|
|
|
|
|
// let our listener try to read up to NS_HTTP_BUFFER_SIZE from us.
|
|
|
|
rv = mListener->OnDataAvailable(this, nsnull, this,
|
|
|
|
mContentRead, NS_HTTP_BUFFER_SIZE);
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction: listener returned [rv=%x]\n", rv));
|
|
|
|
|
|
|
|
mSource = 0;
|
2001-05-25 23:18:50 +00:00
|
|
|
|
|
|
|
// check if this transaction needs to be restarted
|
|
|
|
if (mPrematureEOF) {
|
|
|
|
mPrematureEOF = PR_FALSE;
|
2001-09-21 03:59:02 +00:00
|
|
|
rv = Restart();
|
|
|
|
// if successfully restarted, then return an error to abort the
|
|
|
|
// socket transport.
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
rv = NS_BINDING_ABORTED;
|
2001-05-25 23:18:50 +00:00
|
|
|
}
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2001-06-20 01:21:43 +00:00
|
|
|
// called on any thread
|
2001-05-11 21:04:09 +00:00
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::OnStopTransaction(nsresult status)
|
|
|
|
{
|
|
|
|
LOG(("nsHttpTransaction::OnStopTransaction [this=%x status=%x]\n",
|
|
|
|
this, status));
|
|
|
|
|
2001-09-21 03:59:02 +00:00
|
|
|
// if the connection was reset before we read any part of the response,
|
|
|
|
// then we must try to restart the transaction.
|
2002-04-18 22:30:11 +00:00
|
|
|
if (status == NS_ERROR_NET_RESET) {
|
|
|
|
// if some data was read, then mask the reset error, so our listener
|
|
|
|
// will treat this as a normal failure. XXX we might want to map
|
|
|
|
// this error to a special error code to indicate that the transfer
|
|
|
|
// was abnormally interrupted.
|
|
|
|
if (mContentRead > 0)
|
|
|
|
status = NS_ERROR_ABORT;
|
2001-09-21 03:59:02 +00:00
|
|
|
// if restarting fails, then we must notify our listener.
|
2002-04-18 22:30:11 +00:00
|
|
|
else if (NS_SUCCEEDED(Restart()))
|
2001-09-21 03:59:02 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2002-04-19 22:25:23 +00:00
|
|
|
mTransactionDone = 1; // force this flag
|
2001-05-11 21:04:09 +00:00
|
|
|
mStatus = status;
|
|
|
|
|
|
|
|
if (mListener) {
|
|
|
|
if (!mFiredOnStart) {
|
|
|
|
mFiredOnStart = PR_TRUE;
|
|
|
|
mListener->OnStartRequest(this, nsnull);
|
|
|
|
}
|
|
|
|
mListener->OnStopRequest(this, nsnull, status);
|
|
|
|
mListener = 0;
|
2001-05-30 00:40:50 +00:00
|
|
|
|
|
|
|
// from this point forward we can't access the request head.
|
|
|
|
mRequestHead = nsnull;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-09-14 21:08:58 +00:00
|
|
|
void
|
|
|
|
nsHttpTransaction::OnStatus(nsresult status, const PRUnichar *statusText)
|
|
|
|
{
|
|
|
|
LOG(("nsHttpTransaction::OnStatus [this=%x status=%x]\n", this, status));
|
|
|
|
|
|
|
|
if (mProgressSink)
|
|
|
|
mProgressSink->OnStatus(nsnull, nsnull, status, statusText);
|
|
|
|
}
|
|
|
|
|
2001-09-04 23:11:46 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction <private>
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2001-09-21 03:59:02 +00:00
|
|
|
nsresult
|
|
|
|
nsHttpTransaction::Restart()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
// limit the number of restart attempts - bug 92224
|
|
|
|
if (++mRestartCount >= nsHttpHandler::get()->MaxRequestAttempts()) {
|
|
|
|
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
|
2001-11-14 06:45:27 +00:00
|
|
|
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mReqHeaderStream);
|
|
|
|
if (seekable)
|
|
|
|
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
|
|
|
seekable = do_QueryInterface(mReqUploadStream);
|
|
|
|
if (seekable)
|
|
|
|
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
2002-09-11 03:23:36 +00:00
|
|
|
mReqUploadStreamOffset = 0;
|
2001-09-21 03:59:02 +00:00
|
|
|
|
|
|
|
// just in case the connection is holding the last reference to us...
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
|
|
|
|
// we don't want the connection to send anymore notifications to us.
|
2001-10-02 00:31:30 +00:00
|
|
|
mConnection->DropTransaction(this);
|
2001-09-21 03:59:02 +00:00
|
|
|
|
2001-10-02 00:31:30 +00:00
|
|
|
nsHttpConnectionInfo *ci = nsnull;
|
|
|
|
mConnection->GetConnectionInfo(&ci);
|
|
|
|
NS_ASSERTION(ci, "connection info should be non-null");
|
|
|
|
if (ci) {
|
|
|
|
// we must release the connection before re-initiating this transaction
|
|
|
|
// since we'll be getting a new connection.
|
|
|
|
NS_RELEASE(mConnection);
|
2001-09-21 03:59:02 +00:00
|
|
|
|
2001-10-02 00:31:30 +00:00
|
|
|
rv = nsHttpHandler::get()->InitiateTransaction(this, ci);
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "InitiateTransaction failed");
|
2001-09-21 03:59:02 +00:00
|
|
|
|
2001-10-02 00:31:30 +00:00
|
|
|
NS_RELEASE(ci);
|
|
|
|
}
|
2001-09-21 03:59:02 +00:00
|
|
|
NS_RELEASE_THIS();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::HandleContentStart [this=%x response-head=%x]\n",
|
|
|
|
this, mResponseHead));
|
|
|
|
|
|
|
|
if (mResponseHead) {
|
|
|
|
#if defined(PR_LOGGING)
|
2001-09-28 22:23:26 +00:00
|
|
|
if (LOG2_ENABLED()) {
|
|
|
|
LOG2(("http response [\n"));
|
|
|
|
nsCAutoString headers;
|
|
|
|
mResponseHead->Flatten(headers, PR_FALSE);
|
|
|
|
LogHeaders(headers.get());
|
|
|
|
LOG2(("]\n"));
|
|
|
|
}
|
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...
|
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction [this=%x] sending OnStartRequest\n", this));
|
|
|
|
mFiredOnStart = PR_TRUE;
|
|
|
|
|
|
|
|
rv = mListener->OnStartRequest(this, nsnull);
|
|
|
|
LOG(("OnStartRequest returned rv=%x\n", rv));
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n",
|
|
|
|
this, count));
|
|
|
|
|
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
|
|
|
|
|
|
|
if (mTransactionDone)
|
|
|
|
return NS_OK;
|
|
|
|
|
2002-04-23 07:30:28 +00:00
|
|
|
NS_ASSERTION(mConnection, "no connection");
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
if (!mFiredOnStart) {
|
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;
|
2001-09-14 21:08:58 +00:00
|
|
|
if (mProgressSink)
|
|
|
|
mProgressSink->OnProgress(nsnull, nsnull, mContentRead, PR_MAX(0, mContentLength));
|
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())) {
|
|
|
|
// atomically mark the transaction as complete to ensure that
|
|
|
|
// OnTransactionComplete is fired only once!
|
|
|
|
PRInt32 priorVal = PR_AtomicSet(&mTransactionDone, 1);
|
2001-06-14 20:52:03 +00:00
|
|
|
if (priorVal == 0) {
|
2002-03-26 23:33:19 +00:00
|
|
|
mResponseIsComplete = PR_TRUE;
|
2001-05-11 21:04:09 +00:00
|
|
|
// let the connection know that we are done with it; this should
|
|
|
|
// result in OnStopTransaction being fired.
|
2001-06-20 01:21:43 +00:00
|
|
|
return mConnection->OnTransactionComplete(this, NS_OK);
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we didn't "read" anything and this is not a no-content response,
|
|
|
|
// then we must return NS_BASE_STREAM_WOULD_BLOCK so we'll be called again.
|
2002-04-23 07:30:28 +00:00
|
|
|
return (!mNoContent && !*contentRead) ? NS_BASE_STREAM_WOULD_BLOCK : NS_OK;
|
2001-05-11 21:04:09 +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));
|
2001-05-25 23:18:50 +00:00
|
|
|
|
2001-06-11 21:20:29 +00:00
|
|
|
nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs));
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
PL_InitEvent(event, this,
|
|
|
|
nsHttpTransaction::DeleteThis_EventHandlerFunc,
|
|
|
|
nsHttpTransaction::DeleteThis_EventCleanupFunc);
|
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
|
|
|
|
nsHttpTransaction::DeleteThis_EventHandlerFunc(PLEvent *ev)
|
|
|
|
{
|
|
|
|
nsHttpTransaction *trans =
|
|
|
|
NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev));
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::DeleteThis_EventHandlerFunc [trans=%x]\n", trans));
|
|
|
|
|
|
|
|
delete trans;
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PR_CALLBACK
|
|
|
|
nsHttpTransaction::DeleteThis_EventCleanupFunc(PLEvent *ev)
|
|
|
|
{
|
|
|
|
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,
|
|
|
|
nsIRequest,
|
|
|
|
nsIInputStream)
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction::nsIRequest
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2002-03-20 22:50:33 +00:00
|
|
|
nsHttpTransaction::GetName(nsACString &aName)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::IsPending(PRBool *_retval)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::GetStatus(nsresult *aStatus)
|
|
|
|
{
|
|
|
|
*aStatus = mStatus;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// called from any thread
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::Cancel(nsresult status)
|
|
|
|
{
|
|
|
|
LOG(("nsHttpTransaction::Cancel [this=%x status=%x]\n", this, status));
|
|
|
|
|
2001-06-26 00:01:22 +00:00
|
|
|
// ignore cancelation if the transaction already has an error status.
|
|
|
|
if (NS_FAILED(mStatus)) {
|
|
|
|
LOG(("ignoring cancel since transaction has already failed "
|
|
|
|
"[this=%x mStatus=%x]\n", this, mStatus));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
// if the transaction is already "done" then there is nothing more to do.
|
|
|
|
// ie., our consumer _will_ eventually receive their OnStopRequest.
|
2001-06-14 20:52:03 +00:00
|
|
|
PRInt32 priorVal = PR_AtomicSet(&mTransactionDone, 1);
|
|
|
|
if (priorVal == 1) {
|
2001-05-11 21:04:09 +00:00
|
|
|
LOG(("ignoring cancel since transaction is already done [this=%x]\n", this));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2002-04-19 22:25:23 +00:00
|
|
|
// the status must be set immediately as the cancelation may only take
|
|
|
|
// action asynchronously.
|
|
|
|
mStatus = status;
|
|
|
|
|
2001-06-14 20:52:03 +00:00
|
|
|
return nsHttpHandler::get()->CancelTransaction(this, status);
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::Suspend()
|
|
|
|
{
|
2001-06-22 06:14:55 +00:00
|
|
|
LOG(("nsHttpTransaction::Suspend [this=%x]\n", this));
|
|
|
|
if (mConnection && !mTransactionDone)
|
2001-10-02 00:31:30 +00:00
|
|
|
mConnection->OnSuspend();
|
2001-06-22 06:14:55 +00:00
|
|
|
return NS_OK;
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
|
2001-06-14 20:52:03 +00:00
|
|
|
// called from the consumer thread, while nothing is happening on the socket thread.
|
2001-05-11 21:04:09 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::Resume()
|
|
|
|
{
|
|
|
|
LOG(("nsHttpTransaction::Resume [this=%x]\n", this));
|
2001-06-22 06:14:55 +00:00
|
|
|
if (mConnection && !mTransactionDone)
|
2001-10-02 00:31:30 +00:00
|
|
|
mConnection->OnResume();
|
2001-05-11 21:04:09 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::GetLoadGroup(nsILoadGroup **aLoadGroup)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::SetLoadGroup(nsILoadGroup *aLoadGroup)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::GetLoadFlags(nsLoadFlags *aLoadFlags)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::SetLoadFlags(nsLoadFlags aLoadFlags)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsHttpTransaction::nsIInputStream
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::Close()
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::Available(PRUint32 *result)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::Read(char *buf, PRUint32 count, PRUint32 *bytesWritten)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
LOG(("nsHttpTransaction::Read [this=%x count=%u]\n", this, count));
|
|
|
|
|
|
|
|
NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
|
|
|
|
if (mTransactionDone)
|
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
|
2001-05-25 23:18:50 +00:00
|
|
|
*bytesWritten = 0;
|
|
|
|
|
2001-05-11 21:04:09 +00:00
|
|
|
// read some data from our source and put it in the given buf
|
|
|
|
rv = mSource->Read(buf, count, bytesWritten);
|
2001-05-25 23:18:50 +00:00
|
|
|
LOG(("mSource->Read [rv=%x count=%u countRead=%u]\n", rv, count, *bytesWritten));
|
2002-11-01 01:11:32 +00:00
|
|
|
|
|
|
|
// detect explicit socket RESET
|
|
|
|
if (rv == NS_ERROR_NET_RESET) {
|
|
|
|
LOG(("got NS_ERROR_NET_RESET\n"));
|
|
|
|
*bytesWritten = 0;
|
|
|
|
}
|
|
|
|
else if (NS_FAILED(rv)) {
|
2001-05-11 21:04:09 +00:00
|
|
|
LOG(("nsHttpTransaction: mSource->Read() returned [rv=%x]\n", rv));
|
|
|
|
return rv;
|
|
|
|
}
|
2002-11-01 01:11:32 +00:00
|
|
|
|
2001-09-04 23:11:46 +00:00
|
|
|
if (*bytesWritten == 0) {
|
2001-05-25 23:18:50 +00:00
|
|
|
LOG(("nsHttpTransaction: reached EOF\n"));
|
|
|
|
if (!mHaveStatusLine) {
|
|
|
|
// we've read nothing from the socket...
|
|
|
|
mPrematureEOF = PR_TRUE;
|
|
|
|
// return would block to prevent being called again.
|
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
}
|
2001-08-02 19:28:08 +00:00
|
|
|
if (!mHaveAllHeaders && !mLineBuf.IsEmpty()) {
|
|
|
|
// 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).
|
2002-08-27 02:26:43 +00:00
|
|
|
rv = ParseLineSegment("\n", 1);
|
2001-08-02 19:28:08 +00:00
|
|
|
}
|
2001-05-25 23:18:50 +00:00
|
|
|
return rv;
|
|
|
|
}
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
// pretend that no bytes were written (since we're just borrowing the
|
|
|
|
// given buf anyways).
|
|
|
|
count = *bytesWritten;
|
|
|
|
*bytesWritten = 0;
|
|
|
|
|
|
|
|
// we may not have read all of the headers yet...
|
|
|
|
if (!mHaveAllHeaders) {
|
2001-05-25 23:07:44 +00:00
|
|
|
PRUint32 bytesConsumed = 0;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
rv = ParseHead(buf, count, &bytesConsumed);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
count -= bytesConsumed;
|
2001-05-11 21:04:09 +00:00
|
|
|
|
2001-05-25 23:07:44 +00:00
|
|
|
if (count && bytesConsumed) {
|
2001-05-11 21:04:09 +00:00
|
|
|
// buf has some content in it; shift bytes to top of buf.
|
2001-05-25 23:07:44 +00:00
|
|
|
memmove(buf, buf + bytesConsumed, count);
|
2001-05-11 21:04:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2002-04-19 22:25:23 +00:00
|
|
|
if (mHaveAllHeaders) {
|
2002-06-17 21:08:46 +00:00
|
|
|
PRUint32 bytesRemaining = 0;
|
2002-04-23 07:30:28 +00:00
|
|
|
//
|
|
|
|
// buf layout:
|
|
|
|
//
|
2002-06-17 21:08:46 +00:00
|
|
|
// +-----------------------------------------+----------------+-----+
|
|
|
|
// | bytesWritten | bytesRemaining | |
|
|
|
|
// +-----------------------------------------+----------------+-----+
|
2002-04-23 07:30:28 +00:00
|
|
|
//
|
2002-06-17 21:08:46 +00:00
|
|
|
// count : bytes read from the socket
|
|
|
|
// bytesWritten : bytes corresponding to this transaction
|
|
|
|
// bytesRemaining : bytes corresponding to next pipelined transaction
|
2002-04-23 07:30:28 +00:00
|
|
|
//
|
2002-06-17 21:08:46 +00:00
|
|
|
// NOTE:
|
|
|
|
// count > bytesWritten + bytesRemaining <==> chunked transfer encoding
|
|
|
|
//
|
|
|
|
rv = HandleContent(buf, count, bytesWritten, &bytesRemaining);
|
2002-04-19 22:25:23 +00:00
|
|
|
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
|
2002-06-17 21:08:46 +00:00
|
|
|
if (mResponseIsComplete && bytesRemaining) {
|
2002-04-23 07:30:28 +00:00
|
|
|
NS_ASSERTION(mConnection, "no connection");
|
2002-06-17 21:08:46 +00:00
|
|
|
mConnection->PushBack(buf + *bytesWritten, bytesRemaining);
|
2002-04-23 07:30:28 +00:00
|
|
|
}
|
2002-06-12 23:44:31 +00:00
|
|
|
return rv;
|
2002-04-19 22:25:23 +00:00
|
|
|
}
|
2001-05-11 21:04:09 +00:00
|
|
|
|
|
|
|
// wait for more data
|
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHttpTransaction::ReadSegments(nsWriteSegmentFun writer, void *closure,
|
|
|
|
PRUint32 count, PRUint32 *countRead)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2002-03-12 00:59:06 +00:00
|
|
|
nsHttpTransaction::IsNonBlocking(PRBool *result)
|
2001-05-11 21:04:09 +00:00
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|