зеркало из https://github.com/mozilla/pjs.git
Laying the foundation for the incorporation of caching, by
reorganizing/splitting protocol handler methods, though not yet adding any new functionality: Allow for multiple instances of nsHTTPResponse to be associated with the same connection, i.e. so that response headers from the cache and response headers from the server can coexist simultaneously. To wit: Moved content-length, charset and content-type information from nsHTTPChannel into nsHTTPResponse Split into separate functions the accumulation of a single line of HTTP header data (from the input stream) and the parsing of that line. This permits cached response headers and server response headers to be parsed from separate data sources, the latter arriving from a nsIBufferInputStream and the former retrieved from the cache as a string. Moved the newly-created header-parsing methods to nsHTTPResponse from nsHTTPResponseListener Fixed some bugs in the interaction between AsyncOpen and AsyncRead. It was possible for an OnHeadersAvailable event to be triggered *after* the associated OnDataAvailable, rather than the other way around. It was also possible, in a rare case, for mOpenObserver->OnStopRequest() to be called without ever having called mOpenObserver->OnStartRequest(). I think my changes made the logic a bit more foolproof. Removed ancient ifdef NSPIPE2
This commit is contained in:
Родитель
0de3c034b5
Коммит
cb5751940a
|
@ -45,6 +45,7 @@ HTTP_ATOM(Age, "age")
|
|||
HTTP_ATOM(Allow, "allow")
|
||||
HTTP_ATOM(Authentication, "authentication")
|
||||
HTTP_ATOM(Authorization, "authorization")
|
||||
HTTP_ATOM(Cache_Control, "cache-control")
|
||||
HTTP_ATOM(Connection, "connection")
|
||||
HTTP_ATOM(Content_Base, "content-base")
|
||||
HTTP_ATOM(Content_Encoding, "content-encoding")
|
||||
|
|
|
@ -90,9 +90,6 @@ nsHTTPChannel::nsHTTPChannel(nsIURI* i_URL,
|
|||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("Creating nsHTTPChannel [this=%x].\n", this));
|
||||
|
||||
// The content length is unknown...
|
||||
mContentLength = -1;
|
||||
|
||||
mVerb = i_Verb;
|
||||
}
|
||||
|
||||
|
@ -278,12 +275,9 @@ nsHTTPChannel::GetContentType(char * *aContentType)
|
|||
//
|
||||
// If the content type has been returned by the server then return that...
|
||||
//
|
||||
if (mContentType.Length()) {
|
||||
*aContentType = mContentType.ToNewCString();
|
||||
if (!*aContentType) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return rv;
|
||||
if (mResponse) {
|
||||
rv = mResponse->GetContentType(aContentType);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) return rv;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -312,8 +306,9 @@ nsHTTPChannel::GetContentType(char * *aContentType)
|
|||
NS_IMETHODIMP
|
||||
nsHTTPChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
*aContentLength = mContentLength;
|
||||
return NS_OK;
|
||||
if (!mResponse)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
return mResponse->GetContentLength(aContentLength);
|
||||
}
|
||||
|
||||
|
||||
|
@ -538,14 +533,9 @@ nsHTTPChannel::GetResponseDataListener(nsIStreamListener* *aListener)
|
|||
NS_IMETHODIMP
|
||||
nsHTTPChannel::GetCharset(char* *o_String)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
*o_String = mCharset.ToNewCString();
|
||||
if (!*o_String) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return rv;
|
||||
if (!mResponse)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
return mResponse->GetCharset(o_String);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -678,7 +668,8 @@ nsHTTPChannel::Open(void)
|
|||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> pModules;
|
||||
rv = pNetModuleMgr->EnumerateModules(NS_NETWORK_MODULE_MANAGER_HTTP_REQUEST_PROGID, getter_AddRefs(pModules));
|
||||
rv = pNetModuleMgr->EnumerateModules(NS_NETWORK_MODULE_MANAGER_HTTP_REQUEST_PROGID,
|
||||
getter_AddRefs(pModules));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Go through the external modules and notify each one.
|
||||
|
@ -705,15 +696,11 @@ nsHTTPChannel::Open(void)
|
|||
rv = pModules->GetNext(getter_AddRefs(supEntry)); // go around again
|
||||
}
|
||||
|
||||
if (transport) {
|
||||
rv = mRequest->WriteRequest(transport, mUsingProxy);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mState = HS_WAITING_FOR_RESPONSE;
|
||||
mConnected = PR_TRUE;
|
||||
}
|
||||
else
|
||||
NS_ERROR("Failed to create/get a transport!");
|
||||
rv = mRequest->WriteRequest(transport, mUsingProxy);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mState = HS_WAITING_FOR_RESPONSE;
|
||||
mConnected = PR_TRUE;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -868,27 +855,6 @@ nsresult nsHTTPChannel::GetResponseContext(nsISupports** aContext)
|
|||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
nsresult nsHTTPChannel::SetContentLength(PRInt32 aContentLength)
|
||||
{
|
||||
mContentLength = aContentLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsHTTPChannel::SetContentType(const char* aContentType)
|
||||
{
|
||||
nsCAutoString cType(aContentType);
|
||||
cType.ToLowerCase();
|
||||
mContentType = cType.GetBuffer();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult nsHTTPChannel::SetCharset(const char *aCharset)
|
||||
{
|
||||
mCharset = aCharset;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsHTTPChannel::OnHeadersAvailable()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include "nsIStreamObserver.h"
|
||||
|
||||
class nsHTTPRequest;
|
||||
class nsHTTPResponse;
|
||||
|
||||
/*
|
||||
The nsHTTPChannel class is an example implementation of a
|
||||
protocol instnce that is active on a per-URL basis.
|
||||
|
@ -85,9 +87,6 @@ public:
|
|||
const PRUnichar* aMsg);
|
||||
nsresult SetResponse(nsHTTPResponse* i_pResp);
|
||||
nsresult GetResponseContext(nsISupports** aContext);
|
||||
nsresult SetContentLength(PRInt32 aContentLength);
|
||||
nsresult SetContentType(const char* aContentType);
|
||||
nsresult SetCharset(const char *aCharset);
|
||||
|
||||
nsresult OnHeadersAvailable();
|
||||
|
||||
|
@ -107,7 +106,6 @@ protected:
|
|||
nsCOMPtr<nsIURI> mURI;
|
||||
PRBool mConnected;
|
||||
HTTPState mState;
|
||||
|
||||
nsCString mVerb;
|
||||
nsCOMPtr<nsIHTTPEventSink> mEventSink;
|
||||
nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
|
||||
|
@ -121,9 +119,6 @@ protected:
|
|||
nsCOMPtr<nsISupports> mResponseContext;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
|
||||
PRInt32 mContentLength;
|
||||
nsCString mContentType;
|
||||
nsCString mCharset;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
|
||||
// Auth related stuff-
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
* Contributor(s):
|
||||
*/
|
||||
|
||||
#include "prlog.h"
|
||||
#include "nsHTTPResponse.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIURL.h"
|
||||
|
@ -29,12 +30,19 @@
|
|||
#include "nsXPIDLString.h"
|
||||
#include "nsHTTPAtoms.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
extern PRLogModuleInfo* gHTTPLog;
|
||||
#endif /* PR_LOGGING */
|
||||
|
||||
nsHTTPResponse::nsHTTPResponse()
|
||||
{
|
||||
NS_INIT_REFCNT();
|
||||
|
||||
mStatus = 0;
|
||||
mServerVersion = HTTP_ONE_ZERO;
|
||||
|
||||
// The content length is unknown...
|
||||
mContentLength = -1;
|
||||
}
|
||||
|
||||
nsHTTPResponse::~nsHTTPResponse()
|
||||
|
@ -44,25 +52,72 @@ nsHTTPResponse::~nsHTTPResponse()
|
|||
NS_IMPL_ISUPPORTS(nsHTTPResponse, NS_GET_IID(nsISupports))
|
||||
|
||||
|
||||
nsresult nsHTTPResponse::GetContentLength(PRInt32* o_Value)
|
||||
nsresult nsHTTPResponse::GetCharset(char* *o_Charset)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (o_Value) {
|
||||
PRInt32 err;
|
||||
nsXPIDLCString value;
|
||||
nsAutoString str;
|
||||
NS_ENSURE_ARG_POINTER(o_Charset);
|
||||
|
||||
// Check if status header has been parsed yet
|
||||
if (mCharset.Length() == 0)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*o_Charset = mCharset.ToNewCString();
|
||||
if (!*o_Charset)
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mHeaders.GetHeader(nsHTTPAtoms::Content_Length, getter_Copies(value));
|
||||
str = value;
|
||||
*o_Value = str.ToInteger(&err);
|
||||
}
|
||||
else {
|
||||
rv = NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::SetCharset(const char* i_Charset)
|
||||
{
|
||||
mCharset = i_Charset;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::GetContentType(char* *o_ContentType)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
NS_ENSURE_ARG_POINTER(o_ContentType);
|
||||
|
||||
// Check if status header has been parsed yet
|
||||
if (mContentType.Length() == 0)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*o_ContentType = mContentType.ToNewCString();
|
||||
if (!*o_ContentType)
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::SetContentType(const char* i_ContentType)
|
||||
{
|
||||
nsCAutoString cType(i_ContentType);
|
||||
cType.ToLowerCase();
|
||||
mContentType = cType.GetBuffer();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::GetContentLength(PRInt32* o_ContentLength)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(o_ContentLength);
|
||||
|
||||
// Check if content-length header was received yet
|
||||
if (mContentLength == -1)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*o_ContentLength = mContentLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::SetContentLength(PRInt32 i_ContentLength)
|
||||
{
|
||||
mContentLength = i_ContentLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::GetStatus(PRUint32* o_Value)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -128,3 +183,172 @@ nsresult nsHTTPResponse::GetHeaderEnumerator(nsISimpleEnumerator** aResult)
|
|||
{
|
||||
return mHeaders.GetEnumerator(aResult);
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::ParseStatusLine(nsCString& aStatusLine)
|
||||
{
|
||||
//
|
||||
// The Status Line has the following: format:
|
||||
// HTTP-Version SP Status-Code SP Reason-Phrase CRLF
|
||||
//
|
||||
|
||||
const char *token;
|
||||
nsCAutoString str;
|
||||
PRInt32 offset, error;
|
||||
|
||||
//
|
||||
// Parse the HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT
|
||||
//
|
||||
|
||||
offset = aStatusLine.FindChar(' ');
|
||||
(void) aStatusLine.Left(str, offset);
|
||||
if (!str.Length()) {
|
||||
// The status line is bogus...
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
token = str.GetBuffer();
|
||||
SetServerVersion(token);
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tParseStatusLine [this=%x].\tHTTP-Version: %s\n",
|
||||
this, token));
|
||||
|
||||
aStatusLine.Cut(0, offset+1);
|
||||
|
||||
//
|
||||
// Parse the Status-Code:: 3DIGIT
|
||||
//
|
||||
PRInt32 statusCode;
|
||||
|
||||
offset = aStatusLine.FindChar(' ');
|
||||
(void) aStatusLine.Left(str, offset);
|
||||
if (3 != str.Length()) {
|
||||
// The status line is bogus...
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
statusCode = str.ToInteger(&error);
|
||||
if (NS_FAILED(error)) return NS_ERROR_FAILURE;
|
||||
|
||||
SetStatus(statusCode);
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tParseStatusLine [this=%x].\tStatus-Code: %d\n",
|
||||
this, statusCode));
|
||||
|
||||
aStatusLine.Cut(0, offset+1);
|
||||
|
||||
//
|
||||
// Parse the Reason-Phrase:: *<TEXT excluding CR,LF>
|
||||
//
|
||||
token = aStatusLine.GetBuffer();
|
||||
SetStatusString(token);
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tParseStatusLine [this=%x].\tReason-Phrase: %s\n",
|
||||
this, token));
|
||||
|
||||
aStatusLine.Truncate();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::ParseHeader(nsCString& aHeaderString)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
//
|
||||
// Extract the key field - everything up to the ':'
|
||||
// The header name is case-insensitive...
|
||||
//
|
||||
PRInt32 colonOffset;
|
||||
nsCAutoString headerKey;
|
||||
nsCOMPtr<nsIAtom> headerAtom;
|
||||
|
||||
colonOffset = aHeaderString.FindChar(':');
|
||||
if (kNotFound == colonOffset) {
|
||||
//
|
||||
// The header is malformed... Just clear it.
|
||||
//
|
||||
aHeaderString.Truncate();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
(void) aHeaderString.Left(headerKey, colonOffset);
|
||||
headerKey.ToLowerCase();
|
||||
//
|
||||
// Extract the value field - everything past the ':'
|
||||
// Trim any leading or trailing whitespace...
|
||||
//
|
||||
aHeaderString.Cut(0, colonOffset+1);
|
||||
aHeaderString.Trim(" ");
|
||||
|
||||
headerAtom = NS_NewAtom(headerKey.GetBuffer());
|
||||
if (headerAtom) {
|
||||
rv = ProcessHeader(headerAtom, aHeaderString);
|
||||
} else {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
aHeaderString.Truncate();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsHTTPResponse::ProcessHeader(nsIAtom* aHeader, nsCString& aValue)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
//
|
||||
// When the Content-Type response header is processed, the Content-Type
|
||||
// and Charset information must be set into the nsHTTPChannel...
|
||||
//
|
||||
if (nsHTTPAtoms::Content_Type == aHeader) {
|
||||
nsCAutoString buffer;
|
||||
PRInt32 semicolon;
|
||||
|
||||
// Set the content-type in the HTTPChannel...
|
||||
semicolon = aValue.FindChar(';');
|
||||
if (kNotFound != semicolon) {
|
||||
aValue.Left(buffer, semicolon);
|
||||
SetContentType(buffer.GetBuffer());
|
||||
|
||||
// Does the Content-Type contain a charset attribute?
|
||||
aValue.Mid(buffer, semicolon+1, -1);
|
||||
buffer.Trim(" ");
|
||||
if (0 == buffer.Find("charset=", PR_TRUE)) {
|
||||
//
|
||||
// Set the charset in the HTTPChannel...
|
||||
//
|
||||
// XXX: Currently, the charset is *everything* past the "charset="
|
||||
// This includes comments :-(
|
||||
//
|
||||
buffer.Cut(0, 8);
|
||||
SetCharset(buffer.GetBuffer());
|
||||
}
|
||||
}
|
||||
else {
|
||||
SetContentType(aValue.GetBuffer());
|
||||
}
|
||||
}
|
||||
//
|
||||
// When the Content-Length response header is processed, set the
|
||||
// ContentLength in the Channel...
|
||||
//
|
||||
else if (nsHTTPAtoms::Content_Length == aHeader) {
|
||||
PRInt32 length, status;
|
||||
|
||||
length = aValue.ToInteger(&status);
|
||||
rv = (nsresult)status;
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
SetContentLength(length);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Set the response header...
|
||||
//
|
||||
rv = SetHeader(aHeader, aValue.GetBuffer());
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,12 @@ public:
|
|||
|
||||
// Finally our own methods...
|
||||
|
||||
nsresult GetContentType(char* *o_ContentType);
|
||||
nsresult SetContentType(const char* i_ContentType);
|
||||
nsresult GetCharset(char* *o_Charset);
|
||||
nsresult SetCharset(const char* i_Charset);
|
||||
nsresult GetContentLength(PRInt32* o_Value);
|
||||
nsresult SetContentLength(PRInt32 i_Value);
|
||||
nsresult GetStatus(PRUint32* o_Value);
|
||||
nsresult GetStatusString(char* *o_String);
|
||||
nsresult GetServer(char* *o_String);
|
||||
|
@ -65,12 +70,19 @@ public:
|
|||
nsresult SetStatus(PRInt32 i_Value) { mStatus = i_Value; return NS_OK;};
|
||||
nsresult SetStatusString(const char* i_Value);
|
||||
|
||||
nsresult ParseStatusLine(nsCString& aStatusLine);
|
||||
nsresult ParseHeader(nsCString& aHeaderString);
|
||||
nsresult ProcessHeader(nsIAtom* aHeader, nsCString& aValue);
|
||||
|
||||
protected:
|
||||
virtual ~nsHTTPResponse();
|
||||
|
||||
HTTPVersion mServerVersion;
|
||||
nsCString mStatusString;
|
||||
nsCString mContentType;
|
||||
nsCString mCharset;
|
||||
PRUint32 mStatus;
|
||||
PRInt32 mContentLength;
|
||||
|
||||
nsHTTPHeaderArray mHeaders;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
|
@ -59,7 +59,9 @@ static const int kMAX_HEADER_SIZE = 60000;
|
|||
nsHTTPResponseListener::nsHTTPResponseListener(nsHTTPChannel* aConnection):
|
||||
mFirstLineParsed(PR_FALSE),
|
||||
mHeadersDone(PR_FALSE),
|
||||
mDataOnly(PR_FALSE),
|
||||
mFiredOnHeadersAvailable(PR_FALSE),
|
||||
mFiredOpenOnStartRequest(PR_FALSE),
|
||||
mAsyncReadAfterAsyncOpen(PR_FALSE),
|
||||
mReadLength(0),
|
||||
mResponse(nsnull),
|
||||
mResponseContext(nsnull),
|
||||
|
@ -147,35 +149,21 @@ nsHTTPResponseListener::OnDataAvailable(nsIChannel* channel,
|
|||
}
|
||||
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Don't do anything else until all headers have been parsed
|
||||
if (!mHeadersDone)
|
||||
return NS_OK;
|
||||
|
||||
//
|
||||
// All the headers have been read.
|
||||
//
|
||||
// All the headers have been read. Check the status code of the
|
||||
// response to see if any special action should be taken.
|
||||
|
||||
// We want to defer header completion notification until the
|
||||
// caller actually does an AsyncRead();
|
||||
if (mHeadersDone) {
|
||||
if (mConnection->mOpenObserver) {
|
||||
mConnection->mRawResponseListener = this;
|
||||
rv = mConnection->mOpenObserver->OnStartRequest(mConnection,
|
||||
mConnection->mOpenContext);
|
||||
mDataStream = i_pStream;
|
||||
} else {
|
||||
rv = FinishedResponseHeaders();
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDataOnly && mDataStream) {
|
||||
// fire on headers *once* if we're in data only mode.
|
||||
mDataStream = 0; // we're done w/ this stream.
|
||||
rv = FinishedResponseHeaders();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
// At this point we've digested headers from the server and we're
|
||||
// onto the actual data. If this transaction was initiated without
|
||||
// an AsyncOpen, we just want to pass the OnData() notifications
|
||||
// an AsyncRead, we just want to pass the OnData() notifications
|
||||
// straight through to the consumer.
|
||||
//
|
||||
// .... otherwise...
|
||||
|
@ -184,8 +172,9 @@ nsHTTPResponseListener::OnDataAvailable(nsIChannel* channel,
|
|||
// so when we finally push the stream to the consumer via AsyncRead,
|
||||
// we're sure to pass him all the data that has queued up.
|
||||
|
||||
if (mConnection->mOpenObserver && !mDataOnly) {
|
||||
if (mConnection->mOpenObserver && !mAsyncReadAfterAsyncOpen) {
|
||||
mBytesReceived += i_Length;
|
||||
mDataStream = i_pStream;
|
||||
} else {
|
||||
|
||||
//
|
||||
|
@ -271,9 +260,8 @@ nsHTTPResponseListener::OnStopRequest(nsIChannel* channel,
|
|||
mConnection->ResponseCompleted(channel, i_Status, i_pMsg);
|
||||
}
|
||||
|
||||
if (mDataOnly) {
|
||||
if (mConnection->mOpenObserver) {
|
||||
// we're done processing the data
|
||||
NS_ASSERTION(mConnection->mOpenObserver, "HTTP: HTTP should still have an observer.");
|
||||
rv = mConnection->mOpenObserver->OnStopRequest(mConnection,
|
||||
mConnection->mOpenContext,
|
||||
i_Status, i_pMsg);
|
||||
|
@ -295,11 +283,22 @@ nsHTTPResponseListener::OnStopRequest(nsIChannel* channel,
|
|||
nsresult nsHTTPResponseListener::FireSingleOnData(nsIStreamListener *aListener, nsISupports *aContext)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
mConsumer = aListener;
|
||||
mResponseContext = aContext;
|
||||
rv = mConsumer->OnDataAvailable(mConnection, mResponseContext,
|
||||
mDataStream, 0, mBytesReceived);
|
||||
mDataOnly = PR_TRUE;
|
||||
|
||||
if (mHeadersDone) {
|
||||
rv = FinishedResponseHeaders();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (mBytesReceived) {
|
||||
rv = mConsumer->OnDataAvailable(mConnection, mResponseContext,
|
||||
mDataStream, 0, mBytesReceived);
|
||||
}
|
||||
mDataStream = 0;
|
||||
}
|
||||
|
||||
mAsyncReadAfterAsyncOpen = PR_TRUE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -390,95 +389,12 @@ nsresult nsHTTPResponseListener::ParseStatusLine(nsIBufferInputStream* in,
|
|||
mHeaderBuffer.CompressSet(" \t", ' ');
|
||||
mHeaderBuffer.StripChars("\r\n");
|
||||
|
||||
//
|
||||
// The Status Line has the following: format:
|
||||
// HTTP-Version SP Status-Code SP Reason-Phrase CRLF
|
||||
//
|
||||
rv = mResponse->ParseStatusLine(mHeaderBuffer);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
const char *token;
|
||||
nsCAutoString str;
|
||||
PRInt32 offset, error;
|
||||
|
||||
//
|
||||
// Parse the HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT
|
||||
//
|
||||
|
||||
offset = mHeaderBuffer.FindChar(' ');
|
||||
(void) mHeaderBuffer.Left(str, offset);
|
||||
if (!str.Length()) {
|
||||
// The status line is bogus...
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
token = str.GetBuffer();
|
||||
mResponse->SetServerVersion(token);
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tParseStatusLine [this=%x].\tHTTP-Version: %s\n",
|
||||
this, token));
|
||||
|
||||
mHeaderBuffer.Cut(0, offset+1);
|
||||
|
||||
//
|
||||
// Parse the Status-Code:: 3DIGIT
|
||||
//
|
||||
PRInt32 statusCode;
|
||||
|
||||
offset = mHeaderBuffer.FindChar(' ');
|
||||
(void) mHeaderBuffer.Left(str, offset);
|
||||
if (3 != str.Length()) {
|
||||
// The status line is bogus...
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
statusCode = str.ToInteger(&error);
|
||||
if (NS_FAILED(error)) return NS_ERROR_FAILURE;
|
||||
|
||||
mResponse->SetStatus(statusCode);
|
||||
|
||||
PRBool authAttempt = PR_FALSE;
|
||||
mConnection->GetAuthTriedWithPrehost(&authAttempt);
|
||||
|
||||
if ( statusCode != 401 && authAttempt) {
|
||||
// we know this auth challenge response wassuccessful. cache any authentication
|
||||
// now so URLs within this body can use it.
|
||||
nsAuthEngine* pEngine;
|
||||
NS_ASSERTION(mConnection->mHandler, "HTTP handler went away");
|
||||
if (NS_SUCCEEDED(mConnection->mHandler->GetAuthEngine(&pEngine)) )
|
||||
{
|
||||
nsXPIDLCString authString;
|
||||
NS_ASSERTION(mConnection->mRequest, "HTTP request went away");
|
||||
rv = mConnection->mRequest->GetHeader(nsHTTPAtoms::Authorization,
|
||||
getter_Copies(authString));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIURI> luri;
|
||||
rv = mConnection->GetURI(getter_AddRefs(luri));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
pEngine->SetAuthString(luri, authString);
|
||||
}
|
||||
}
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tParseStatusLine [this=%x].\tStatus-Code: %d\n",
|
||||
this, statusCode));
|
||||
|
||||
mHeaderBuffer.Cut(0, offset+1);
|
||||
|
||||
//
|
||||
// Parse the Reason-Phrase:: *<TEXT excluding CR,LF>
|
||||
//
|
||||
token = mHeaderBuffer.GetBuffer();
|
||||
mResponse->SetStatusString(token);
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tParseStatusLine [this=%x].\tReason-Phrase: %s\n",
|
||||
this, token));
|
||||
|
||||
mHeaderBuffer.Truncate();
|
||||
mFirstLineParsed = PR_TRUE;
|
||||
|
||||
return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -502,7 +418,7 @@ nsresult nsHTTPResponseListener::ParseHTTPHeader(nsIBufferInputStream* in,
|
|||
//
|
||||
// Read the header from the input buffer... A header is terminated by
|
||||
// a CRLF. Header values may be extended over multiple lines by preceeding
|
||||
// each extran line with LWS...
|
||||
// each extra line with linear white space...
|
||||
//
|
||||
do {
|
||||
//
|
||||
|
@ -572,59 +488,42 @@ nsresult nsHTTPResponseListener::ParseHTTPHeader(nsIBufferInputStream* in,
|
|||
mHeaderBuffer.CompressSet(" \t", ' ');
|
||||
mHeaderBuffer.StripChars("\r\n");
|
||||
|
||||
if (!mHeaderBuffer.Length()) {
|
||||
if (mHeaderBuffer.Length() == 0) {
|
||||
mHeadersDone = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Extract the key field - everything up to the ':'
|
||||
// The header name is case-insensitive...
|
||||
//
|
||||
PRInt32 colonOffset;
|
||||
nsCAutoString headerKey;
|
||||
nsCOMPtr<nsIAtom> headerAtom;
|
||||
|
||||
colonOffset = mHeaderBuffer.FindChar(':');
|
||||
if (kNotFound == colonOffset) {
|
||||
//
|
||||
// The header is malformed... Just clear it.
|
||||
//
|
||||
mHeaderBuffer.Truncate();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
(void) mHeaderBuffer.Left(headerKey, colonOffset);
|
||||
headerKey.ToLowerCase();
|
||||
//
|
||||
// Extract the value field - everything past the ':'
|
||||
// Trim any leading or trailing whitespace...
|
||||
//
|
||||
mHeaderBuffer.Cut(0, colonOffset+1);
|
||||
mHeaderBuffer.Trim(" ");
|
||||
|
||||
headerAtom = NS_NewAtom(headerKey.GetBuffer());
|
||||
if (headerAtom) {
|
||||
rv = ProcessHeader(headerAtom, mHeaderBuffer);
|
||||
} else {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mHeaderBuffer.Truncate();
|
||||
|
||||
return rv;
|
||||
return mResponse->ParseHeader(mHeaderBuffer);
|
||||
}
|
||||
|
||||
|
||||
nsresult nsHTTPResponseListener::FinishedResponseHeaders(void)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
nsresult rv;
|
||||
|
||||
if (mFiredOnHeadersAvailable)
|
||||
return NS_OK;
|
||||
|
||||
rv = NS_OK;
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("nsHTTPResponseListener::FinishedResponseHeaders [this=%x].\n",
|
||||
this));
|
||||
|
||||
if (mConnection->mOpenObserver && !mFiredOpenOnStartRequest) {
|
||||
mConnection->mRawResponseListener = this;
|
||||
rv = mConnection->mOpenObserver->OnStartRequest(mConnection,
|
||||
mConnection->mOpenContext);
|
||||
mFiredOpenOnStartRequest = PR_TRUE;
|
||||
|
||||
// We want to defer header completion notification until the
|
||||
// caller actually does an AsyncRead();
|
||||
if (!mAsyncReadAfterAsyncOpen)
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Notify the consumer that headers are available...
|
||||
FireOnHeadersAvailable();
|
||||
mFiredOnHeadersAvailable = PR_TRUE;
|
||||
|
||||
//
|
||||
// Check the status code to see if any special processing is necessary.
|
||||
|
@ -649,68 +548,6 @@ nsresult nsHTTPResponseListener::FinishedResponseHeaders(void)
|
|||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult nsHTTPResponseListener::ProcessHeader(nsIAtom* aHeader,
|
||||
nsCString& aValue)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
//
|
||||
// When the Content-Type response header is processed, the Content-Type
|
||||
// and Charset information must be set into the nsHTTPChannel...
|
||||
//
|
||||
if (nsHTTPAtoms::Content_Type == aHeader) {
|
||||
nsCAutoString buffer;
|
||||
PRInt32 semicolon;
|
||||
|
||||
// Set the content-type in the HTTPChannel...
|
||||
semicolon = aValue.FindChar(';');
|
||||
if (kNotFound != semicolon) {
|
||||
aValue.Left(buffer, semicolon);
|
||||
mConnection->SetContentType(buffer.GetBuffer());
|
||||
|
||||
// Does the Content-Type contain a charset attribute?
|
||||
aValue.Mid(buffer, semicolon+1, -1);
|
||||
buffer.Trim(" ");
|
||||
if (0 == buffer.Find("charset=", PR_TRUE)) {
|
||||
//
|
||||
// Set the charset in the HTTPChannel...
|
||||
//
|
||||
// XXX: Currently, the charset is *everything* past the "charset="
|
||||
// This includes comments :-(
|
||||
//
|
||||
buffer.Cut(0, 8);
|
||||
mConnection->SetCharset(buffer.GetBuffer());
|
||||
}
|
||||
}
|
||||
else {
|
||||
mConnection->SetContentType(aValue.GetBuffer());
|
||||
}
|
||||
}
|
||||
//
|
||||
// When the Content-Length response header is processed, set the
|
||||
// ContentLength in the Channel...
|
||||
//
|
||||
else if (nsHTTPAtoms::Content_Length == aHeader) {
|
||||
PRInt32 length, status;
|
||||
|
||||
length = aValue.ToInteger(&status);
|
||||
rv = (nsresult)status;
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mConnection->SetContentLength(length);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Set the response header...
|
||||
//
|
||||
rv = mResponse->SetHeader(aHeader, aValue.GetBuffer());
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult nsHTTPResponseListener::ProcessStatusCode(void)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -718,8 +555,32 @@ nsresult nsHTTPResponseListener::ProcessStatusCode(void)
|
|||
|
||||
statusCode = 0;
|
||||
rv = mResponse->GetStatus(&statusCode);
|
||||
statusClass = statusCode / 100;
|
||||
|
||||
PRBool authAttempt = PR_FALSE;
|
||||
mConnection->GetAuthTriedWithPrehost(&authAttempt);
|
||||
|
||||
if ( statusCode != 401 && authAttempt) {
|
||||
// we know this auth challenge response was successful. cache any authentication
|
||||
// now so URLs within this body can use it.
|
||||
nsAuthEngine* pEngine;
|
||||
NS_ASSERTION(mConnection->mHandler, "HTTP handler went away");
|
||||
if (NS_SUCCEEDED(mConnection->mHandler->GetAuthEngine(&pEngine)) )
|
||||
{
|
||||
nsXPIDLCString authString;
|
||||
NS_ASSERTION(mConnection->mRequest, "HTTP request went away");
|
||||
rv = mConnection->mRequest->GetHeader(nsHTTPAtoms::Authorization,
|
||||
getter_Copies(authString));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIURI> luri;
|
||||
rv = mConnection->GetURI(getter_AddRefs(luri));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
pEngine->SetAuthString(luri, authString);
|
||||
}
|
||||
}
|
||||
|
||||
statusClass = statusCode / 100;
|
||||
|
||||
switch (statusClass) {
|
||||
//
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#ifndef _nsHTTPResponseListener_h_
|
||||
#define _nsHTTPResponseListener_h_
|
||||
|
||||
#define NSPIPE2
|
||||
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIStreamListener.h"
|
||||
|
@ -31,11 +30,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIInputStream.h"
|
||||
|
||||
#ifndef NSPIPE2
|
||||
class nsIBuffer;
|
||||
#else
|
||||
class nsIBufferInputStream;
|
||||
#endif
|
||||
class nsHTTPResponse;
|
||||
class nsHTTPChannel;
|
||||
|
||||
|
@ -73,23 +68,15 @@ protected:
|
|||
// nsHTTPResponseListener methods...
|
||||
nsresult FireOnHeadersAvailable();
|
||||
|
||||
#ifndef NSPIPE2
|
||||
nsresult ParseStatusLine(nsIBuffer* aBuffer, PRUint32 aLength,
|
||||
PRUint32 *aBytesRead);
|
||||
|
||||
nsresult ParseHTTPHeader(nsIBuffer* aBuffer, PRUint32 aLength,
|
||||
PRUint32* aBytesRead);
|
||||
#else
|
||||
nsresult ParseStatusLine(nsIBufferInputStream* in, PRUint32 aLength,
|
||||
PRUint32 *aBytesRead);
|
||||
|
||||
nsresult ParseHTTPHeader(nsIBufferInputStream* in, PRUint32 aLength,
|
||||
PRUint32* aBytesRead);
|
||||
#endif
|
||||
|
||||
nsresult FinishedResponseHeaders();
|
||||
|
||||
nsresult ProcessHeader(nsIAtom* aHeader, nsCString& aValue);
|
||||
nsresult ProcessHeader(nsIAtom* aHeader, nsCString& aValue, nsHTTPResponse& aResponse);
|
||||
nsresult ProcessStatusCode();
|
||||
nsresult ProcessRedirection(PRInt32 aStatusCode);
|
||||
nsresult ProcessAuthentication(PRInt32 aStatusCode);
|
||||
|
@ -101,7 +88,9 @@ protected:
|
|||
nsCOMPtr<nsIStreamListener> mConsumer;
|
||||
PRBool mFirstLineParsed;
|
||||
PRBool mHeadersDone;
|
||||
PRBool mDataOnly; // we're only listening for data
|
||||
PRBool mAsyncReadAfterAsyncOpen; // we're only listening for data
|
||||
PRBool mFiredOnHeadersAvailable; // Called OnHeadersAvailable()
|
||||
PRBool mFiredOpenOnStartRequest; // Called mOpenObserver->OnStartRequest
|
||||
PRUint32 mReadLength; // Already read
|
||||
nsHTTPResponse* mResponse;
|
||||
nsCOMPtr<nsISupports> mResponseContext;
|
||||
|
|
Загрузка…
Ссылка в новой задаче