From 7cf8d22c22cb46a8091015417f38dcb42ffd3b0f Mon Sep 17 00:00:00 2001 From: "rpotts%netscape.com" Date: Tue, 29 Feb 2000 04:44:37 +0000 Subject: [PATCH] bug #24711 (r=gagan). Changed cache channels to behave like transports rather than protocol channels... --- netwerk/protocol/http/src/nsHTTPChannel.cpp | 237 ++++++++-------- netwerk/protocol/http/src/nsHTTPChannel.h | 6 +- netwerk/protocol/http/src/nsHTTPRequest.cpp | 62 +++-- netwerk/protocol/http/src/nsHTTPRequest.h | 9 +- netwerk/protocol/http/src/nsHTTPResponse.cpp | 101 +++++++ netwerk/protocol/http/src/nsHTTPResponse.h | 4 +- .../http/src/nsHTTPResponseListener.cpp | 257 ++++++++++++++---- .../http/src/nsHTTPResponseListener.h | 69 ++++- 8 files changed, 550 insertions(+), 195 deletions(-) diff --git a/netwerk/protocol/http/src/nsHTTPChannel.cpp b/netwerk/protocol/http/src/nsHTTPChannel.cpp index 3c5cd272fcb5..3e1d229e8008 100644 --- a/netwerk/protocol/http/src/nsHTTPChannel.cpp +++ b/netwerk/protocol/http/src/nsHTTPChannel.cpp @@ -91,7 +91,7 @@ nsHTTPChannel::nsHTTPChannel(nsIURI* i_URL, PRUint32 bufferMaxSize): mResponse(nsnull), mHandler(dont_QueryInterface(i_Handler)), - mRawResponseListener(nsnull), + mHTTPServerListener(nsnull), mResponseContext(nsnull), mOriginalURI(dont_QueryInterface(originalURI ? originalURI : i_URL)), mURI(dont_QueryInterface(i_URL)), @@ -112,8 +112,14 @@ nsHTTPChannel::nsHTTPChannel(nsIURI* i_URL, { NS_INIT_REFCNT(); - PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("Creating nsHTTPChannel [this=%x].\n", this)); +#if defined(PR_LOGGING) + nsXPIDLCString urlCString; + mURI->GetSpec(getter_Copies(urlCString)); + + PR_LOG(gHTTPLog, PR_LOG_DEBUG, + ("Creating nsHTTPChannel [this=%x] for URI: %s.\n", + this, (const char *)urlCString)); +#endif mVerb = i_Verb; } @@ -280,8 +286,12 @@ nsHTTPChannel::AsyncRead(PRUint32 startPosition, PRInt32 readCount, ReadFromCache(startPosition, readCount); } else if (mOpenObserver) { // we were AsyncOpen()'d - NS_ASSERTION(mRawResponseListener, "our pointer to the response was never set"); - return mRawResponseListener->FireSingleOnData(listener, aContext); + NS_ASSERTION(mHTTPServerListener, "ResponseListener was not set!."); + if (mHTTPServerListener) { + rv = mHTTPServerListener->FireSingleOnData(listener, aContext); + } else { + rv = NS_ERROR_NULL_POINTER; + } } return rv; @@ -873,47 +883,6 @@ nsHTTPChannel::CheckCache() return NS_OK; } -class nsCachedHTTPListener : public nsIStreamListener { -public: - nsCachedHTTPListener(nsIStreamListener *aListener, nsHTTPChannel* aChannel): - mListener(aListener), mChannel(aChannel) { - NS_INIT_REFCNT(); - NS_IF_ADDREF(mChannel); - } - - - virtual ~nsCachedHTTPListener() { NS_IF_RELEASE(mChannel); } - -private: - - NS_DECL_ISUPPORTS - - NS_IMETHOD - OnDataAvailable(nsIChannel *aChannel, nsISupports *aContext, - nsIInputStream *aStream, PRUint32 aSourceOffset, - PRUint32 aCount) - { - return mListener->OnDataAvailable(mChannel, aContext, - aStream, aSourceOffset, aCount); - } - - NS_IMETHOD - OnStartRequest(nsIChannel *aChannel, nsISupports *aContext) { - return mListener->OnStartRequest(mChannel, aContext); - } - - NS_IMETHOD - OnStopRequest(nsIChannel *aChannel, nsISupports *aContext, - nsresult aStatus, const PRUnichar *aErrorMsg) { - return mChannel->ResponseCompleted(nsnull, mListener, aStatus, aErrorMsg); - } - -protected: - nsCOMPtr mListener; - nsHTTPChannel* mChannel; -}; - -NS_IMPL_ISUPPORTS2(nsCachedHTTPListener, nsIStreamListener, nsIStreamObserver) // If the data in the cache hasn't expired, then there's no need to // talk with the server, not even to do an if-modified-since. This @@ -934,48 +903,52 @@ nsHTTPChannel::ReadFromCache(PRUint32 aStartPosition, PRInt32 aReadCount) if (!mResponseDataListener) return NS_ERROR_FAILURE; - nsCOMPtr cacheChannel; - rv = mCacheEntry->NewChannel(mLoadGroup, this, getter_AddRefs(cacheChannel)); +#if defined(PR_LOGGING) + nsresult log_rv; + char *URLSpec; + + log_rv = mURI->GetSpec(&URLSpec); + if (NS_FAILED(log_rv)) { + URLSpec = nsCRT::strdup("?"); + } + + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("nsHTTPChannel::ReadFromCache [this=%x].\t" + "Using cache copy for: %s\n", + this, URLSpec)); + nsAllocator::Free(URLSpec); +#endif /* PR_LOGGING */ + + // Create a cache transport to read the cached response... + nsCOMPtr cacheTransport; + rv = mCacheEntry->NewChannel(mLoadGroup, this, getter_AddRefs(cacheTransport)); if (NS_FAILED(rv)) return rv; + mRequest->SetTransport(cacheTransport); + // Fake it so that HTTP headers come from cached versions - NS_IF_RELEASE(mResponse); - mResponse = mCachedResponse; - NS_ADDREF(mResponse); + SetResponse(mCachedResponse); - nsCOMPtr loadGroupListener = mResponseDataListener; - if (mLoadGroup) { - nsCOMPtr factory; - - // - // Create a load group "proxy" listener... - // - rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory)); - if (NS_SUCCEEDED(rv) && factory) { - factory->CreateLoadGroupListener(mResponseDataListener, - getter_AddRefs(loadGroupListener)); - } - } - // Create a listener that intercepts cache reads and fires off the appropriate // events such as OnHeadersAvailable - nsCachedHTTPListener* listener; - listener = new nsCachedHTTPListener(loadGroupListener, this); + nsHTTPResponseListener* listener; + listener = new nsHTTPCacheListener(this); if (!listener) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(listener); - + + listener->SetListener(mResponseDataListener); mConnected = PR_TRUE; // Fire all the normal events for header arrival FinishedResponseHeaders(); // Pump the cache data downstream - rv = cacheChannel->AsyncRead(aStartPosition, aReadCount, + rv = cacheTransport->AsyncRead(aStartPosition, aReadCount, mResponseContext, listener); NS_RELEASE(listener); if (NS_FAILED(rv)) { - ResponseCompleted(0, nsnull, rv, 0); + ResponseCompleted(nsnull, rv, 0); } return rv; } @@ -1161,7 +1134,7 @@ nsHTTPChannel::Open(void) } if (NS_FAILED(rv)) { // Unable to create a transport... End the request... - (void) ResponseCompleted(nsnull, mResponseDataListener, rv, nsnull); + (void) ResponseCompleted(mResponseDataListener, rv, nsnull); return rv; } @@ -1169,7 +1142,8 @@ nsHTTPChannel::Open(void) rv = transport->SetNotificationCallbacks(this); if (NS_FAILED(rv)) { // Unable to create a transport... End the request... - (void) ResponseCompleted(nsnull, mResponseDataListener, rv, nsnull); + (void) ResponseCompleted(mResponseDataListener, rv, nsnull); + (void) ReleaseTransport(transport); return rv; } @@ -1207,7 +1181,9 @@ nsHTTPChannel::Open(void) rv = pModules->GetNext(getter_AddRefs(supEntry)); // go around again } - rv = mRequest->WriteRequest(transport, (mProxy && *mProxy)); + mRequest->SetTransport(transport); + + rv = mRequest->WriteRequest((mProxy && *mProxy)); if (NS_FAILED(rv)) return rv; mState = HS_WAITING_FOR_RESPONSE; @@ -1324,8 +1300,7 @@ nsresult nsHTTPChannel::Redirect(const char *aNewLocation, } -nsresult nsHTTPChannel::ResponseCompleted(nsIChannel* aTransport, - nsIStreamListener *aListener, +nsresult nsHTTPChannel::ResponseCompleted(nsIStreamListener *aListener, nsresult aStatus, const PRUnichar* aMsg) { @@ -1346,11 +1321,6 @@ nsresult nsHTTPChannel::ResponseCompleted(nsIChannel* aTransport, } } - // Release the transport... - if (aTransport) { - (void)mHandler->ReleaseTransport(aTransport); - } - // // After the consumer has been notified, remove the channel from its // load group... This will trigger an OnStopRequest from the load group. @@ -1378,6 +1348,17 @@ nsresult nsHTTPChannel::ResponseCompleted(nsIChannel* aTransport, return rv; } +nsresult nsHTTPChannel::ReleaseTransport(nsIChannel *aTransport) +{ + nsresult rv = NS_OK; + if (aTransport) { + (void) mRequest->ReleaseTransport(aTransport); + rv = mHandler->ReleaseTransport(aTransport); + } + + return rv; +} + nsresult nsHTTPChannel::SetResponse(nsHTTPResponse* i_pResp) { NS_IF_RELEASE(mResponse); @@ -1404,8 +1385,8 @@ nsresult nsHTTPChannel::Abort() // Disconnect the consumer from this response listener... // This allows the entity that follows to be discarded // without notifying the consumer... - if (mRawResponseListener) { - mRawResponseListener->Abort(); + if (mHTTPServerListener) { + mHTTPServerListener->Abort(); } // Null out pointers that are no longer needed... @@ -1687,21 +1668,8 @@ nsHTTPChannel::ProcessStatusCode(void) } nsCOMPtr listener = mResponseDataListener; - if (mLoadGroup) { - nsCOMPtr factory; - - // - // Create a load group "proxy" listener... - // - rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory)); - if (NS_SUCCEEDED(rv) && factory) { - factory->CreateLoadGroupListener(mResponseDataListener, - getter_AddRefs(listener)); - } - } statusClass = statusCode / 100; - switch (statusClass) { // // Informational: 1xx @@ -1744,10 +1712,11 @@ nsHTTPChannel::ProcessStatusCode(void) if (statusCode == 304) { rv = ProcessNotModifiedResponse(listener); if (NS_FAILED(rv)) return rv; + break; } // 300 (Multiple choices) or 301 (Redirect) results can be cached - else if ((statusCode == 300) || (statusCode == 301)) { + if ((statusCode == 300) || (statusCode == 301)) { nsCOMPtr listener2; CacheReceivedResponse(listener, getter_AddRefs(listener2)); if (listener2) { @@ -1792,8 +1761,8 @@ nsHTTPChannel::ProcessStatusCode(void) // If mResponseDataListener is null this means that the response has been // aborted... So, do not update the response listener because this // is being discarded... - if (mResponseDataListener && mRawResponseListener) { - mRawResponseListener->SetResponseDataListener(listener); + if (mResponseDataListener && mHTTPServerListener) { + mHTTPServerListener->SetListener(listener); } return rv; } @@ -1804,21 +1773,75 @@ nsHTTPChannel::ProcessNotModifiedResponse(nsIStreamListener *aListener) nsresult rv; NS_ASSERTION(!mCachedContentIsValid, "We should never have cached a 304 response"); - // Abort the current response... This will disconnect the consumer from - // the response listener... Thus allowing the entity that follows to - // be discarded without notifying the consumer... - Abort(); +#if defined(PR_LOGGING) + nsresult log_rv; + char *URLSpec; + + log_rv = mURI->GetSpec(&URLSpec); + if (NS_FAILED(log_rv)) { + URLSpec = nsCRT::strdup("?"); + } + + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("nsHTTPChannel::ProcessNotModifiedResponse [this=%x].\t" + "Using cache copy for: %s\n", + this, URLSpec)); + nsAllocator::Free(URLSpec); +#endif /* PR_LOGGING */ + + // Orphan the current nsHTTPServerListener instance... It will be + // replaced with a nsHTTPCacheListener instance. + NS_ASSERTION(mHTTPServerListener, "No nsHTTPServerResponse available!"); + if (mHTTPServerListener) { + mHTTPServerListener->Abort(); + } + + // Update the cached headers with any more recent ones from the + // server - see RFC2616 [13.5.3] + nsCOMPtr enumerator; + rv = mResponse->GetHeaderEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(rv)) return rv; + + mCachedResponse->UpdateHeaders(enumerator); + + // Store the updated HTTP headers in the cache + // XXX: Should the Expires value be recaluclated too? + nsCString allHeaders; + rv = mCachedResponse->EmitHeaders(allHeaders); + if (NS_FAILED(rv)) return rv; + + rv = mCacheEntry->SetAnnotation("HTTP headers", allHeaders.Length()+1, + allHeaders.GetBuffer()); + if (NS_FAILED(rv)) return rv; // Fake it so that HTTP headers come from cached versions SetResponse(mCachedResponse); - // We don't set a load group for the cache channel because the HTTP - // channel is handling the load group interactions - nsCOMPtr cacheChannel; - rv = mCacheEntry->NewChannel(mLoadGroup, this, getter_AddRefs(cacheChannel)); + // Create a cache transport to read the cached response... + nsCOMPtr cacheTransport; + rv = mCacheEntry->NewChannel(mLoadGroup, this, getter_AddRefs(cacheTransport)); if (NS_FAILED(rv)) return rv; - return cacheChannel->AsyncRead(0, -1, mResponseContext, aListener); + mRequest->SetTransport(cacheTransport); + + // Create a new HTTPCacheListener... + nsHTTPResponseListener *cacheListener; + cacheListener = new nsHTTPCacheListener(this); + if (!cacheListener) { + return NS_ERROR_OUT_OF_MEMORY; + } + NS_ADDREF(cacheListener); + + cacheListener->SetListener(aListener); + mResponseDataListener = aListener; + + rv = cacheTransport->AsyncRead(0, -1, mResponseContext, cacheListener); + if (NS_FAILED(rv)) { + ResponseCompleted(cacheListener, rv, nsnull); + } + NS_RELEASE(cacheListener); + + return rv; } nsresult diff --git a/netwerk/protocol/http/src/nsHTTPChannel.h b/netwerk/protocol/http/src/nsHTTPChannel.h index 088959aa311b..f03e7211c4b6 100644 --- a/netwerk/protocol/http/src/nsHTTPChannel.h +++ b/netwerk/protocol/http/src/nsHTTPChannel.h @@ -94,10 +94,10 @@ public: nsresult Redirect(const char *aURL, nsIChannel **aResult); - nsresult ResponseCompleted(nsIChannel* aTransport, - nsIStreamListener* aListener, + nsresult ResponseCompleted(nsIStreamListener* aListener, nsresult aStatus, const PRUnichar* aMsg); + nsresult ReleaseTransport(nsIChannel *aTransport); nsresult SetResponse(nsHTTPResponse* i_pResp); nsresult GetResponseContext(nsISupports** aContext); @@ -125,7 +125,7 @@ public: nsHTTPHandler* mHandler; nsHTTPRequest* mRequest; - nsHTTPResponseListener* mRawResponseListener; + nsHTTPResponseListener* mHTTPServerListener; nsCOMPtr mResponseContext; protected: diff --git a/netwerk/protocol/http/src/nsHTTPRequest.cpp b/netwerk/protocol/http/src/nsHTTPRequest.cpp index beb6bb6439f8..410d5651a564 100644 --- a/netwerk/protocol/http/src/nsHTTPRequest.cpp +++ b/netwerk/protocol/http/src/nsHTTPRequest.cpp @@ -50,23 +50,25 @@ static NS_DEFINE_CID(kHTTPHandlerCID, NS_IHTTPHANDLER_CID); extern nsresult DupString(char* *o_Dest, const char* i_Src); -nsHTTPRequest::nsHTTPRequest(nsIURI* i_URL, HTTPMethod i_Method, - nsIChannel* i_Transport): +nsHTTPRequest::nsHTTPRequest(nsIURI* i_URL, HTTPMethod i_Method): mMethod(i_Method), mVersion(HTTP_ONE_ZERO), - mUsingProxy(PR_FALSE), mRequestSpec(0) { NS_INIT_REFCNT(); + NS_ASSERTION(i_URL, "No URL for the request!!"); mURI = do_QueryInterface(i_URL); - PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("Creating nsHTTPRequest [this=%x].\n", this)); +#if defined(PR_LOGGING) + nsXPIDLCString urlCString; + mURI->GetSpec(getter_Copies(urlCString)); + + PR_LOG(gHTTPLog, PR_LOG_DEBUG, + ("Creating nsHTTPRequest [this=%x] for URI: %s.\n", + this, (const char *)urlCString)); +#endif - mTransport = i_Transport; - - NS_ASSERTION(mURI, "No URI for the request!!"); nsXPIDLCString host; mURI->GetHost(getter_Copies(host)); @@ -143,7 +145,7 @@ nsHTTPRequest::~nsHTTPRequest() PR_LOG(gHTTPLog, PR_LOG_ALWAYS, ("Deleting nsHTTPRequest [this=%x].\n", this)); - mTransport = null_nsCOMPtr(); + mTransport = 0; CRTFREEIF(mRequestSpec); } @@ -230,7 +232,7 @@ nsHTTPRequest::Resume(void) // Finally our own methods... -nsresult nsHTTPRequest::WriteRequest(nsIChannel *aTransport, PRBool aIsProxied) +nsresult nsHTTPRequest::WriteRequest(PRBool aIsProxied) { nsresult rv; if (!mURI) { @@ -238,10 +240,7 @@ nsresult nsHTTPRequest::WriteRequest(nsIChannel *aTransport, PRBool aIsProxied) return NS_ERROR_NULL_POINTER; } - NS_ASSERTION(!mTransport, "Transport being overwritten!"); - mTransport = aTransport; - - mUsingProxy = aIsProxied; + NS_ASSERTION(mTransport, "No transport has been set on this request."); PRUint32 loadAttributes; mConnection->GetLoadAttributes(&loadAttributes); @@ -287,7 +286,7 @@ nsresult nsHTTPRequest::WriteRequest(nsIChannel *aTransport, PRBool aIsProxied) mRequestBuffer.Append(mRequestSpec); else { - if (mUsingProxy) { + if (aIsProxied) { rv = mURI->GetSpec(getter_Copies(autoBuffer)); } else { rv = mURI->GetPath(getter_Copies(autoBuffer)); @@ -372,7 +371,7 @@ nsresult nsHTTPRequest::WriteRequest(nsIChannel *aTransport, PRBool aIsProxied) // // Write the request to the server. // - rv = aTransport->AsyncWrite(stream, 0, mRequestBuffer.Length(), + rv = mTransport->AsyncWrite(stream, 0, mRequestBuffer.Length(), (nsISupports*)(nsIRequest*)mConnection, this); return rv; } @@ -492,7 +491,7 @@ nsHTTPRequest::OnStopRequest(nsIChannel* channel, nsISupports* i_Context, "\tStatus: %x\n", this, iStatus)); - nsHTTPResponseListener* pListener = new nsHTTPResponseListener(mConnection); + nsHTTPResponseListener* pListener = new nsHTTPServerListener(mConnection); if (pListener) { NS_ADDREF(pListener); rv = mTransport->AsyncRead(0, -1, i_Context, pListener); @@ -511,16 +510,24 @@ nsHTTPRequest::OnStopRequest(nsIChannel* channel, nsISupports* i_Context, "\tStatus: %x\n", this, iStatus)); + rv = iStatus; + } + + // + // An error occurred... Finish the transaction and notify the consumer + // of the failure... + // + if (NS_FAILED(rv)) { // Notify the HTTPChannel that the request has finished nsCOMPtr consumer; mConnection->GetResponseDataListener(getter_AddRefs(consumer)); - mConnection->ResponseCompleted(mTransport, consumer, iStatus, i_Msg); + mConnection->ResponseCompleted(consumer, rv, i_Msg); + mConnection->ReleaseTransport(mTransport); - mTransport = null_nsCOMPtr(); - - rv = iStatus; + NS_ASSERTION(!mTransport, "nsHTTRequest::ReleaseTransport() " + "was not called!"); } // @@ -539,6 +546,19 @@ nsresult nsHTTPRequest::SetConnection(nsHTTPChannel* i_Connection) return NS_OK; } +nsresult nsHTTPRequest::SetTransport(nsIChannel *aTransport) +{ + mTransport = aTransport; + return NS_OK; +} + +nsresult nsHTTPRequest::ReleaseTransport(nsIChannel *aTransport) +{ + if (aTransport == mTransport) { + mTransport = 0; + } + return NS_OK; +} nsresult nsHTTPRequest::GetHeaderEnumerator(nsISimpleEnumerator** aResult) { diff --git a/netwerk/protocol/http/src/nsHTTPRequest.h b/netwerk/protocol/http/src/nsHTTPRequest.h index 1bc9d1cd38c5..58d45dba0193 100644 --- a/netwerk/protocol/http/src/nsHTTPRequest.h +++ b/netwerk/protocol/http/src/nsHTTPRequest.h @@ -64,7 +64,7 @@ class nsHTTPRequest : public nsIStreamObserver, public: // Constructor - nsHTTPRequest(nsIURI* i_URL=0, HTTPMethod i_Method=HM_GET, nsIChannel* i_Tranport = nsnull); + nsHTTPRequest(nsIURI* i_URL, HTTPMethod i_Method=HM_GET); NS_DECL_ISUPPORTS NS_DECL_NSISTREAMOBSERVER @@ -104,9 +104,11 @@ public: nsresult SetConnection(nsHTTPChannel* i_Connection); + nsresult SetTransport(nsIChannel *aTransport); + nsresult ReleaseTransport(nsIChannel *aTransport); + // Build the actual request string based on the settings. - nsresult WriteRequest(nsIChannel *aChannel, - PRBool aIsProxied = PR_FALSE); + nsresult WriteRequest(PRBool aIsProxied = PR_FALSE); nsresult GetPostDataStream(nsIInputStream* *aResult); nsresult SetPostDataStream(nsIInputStream* aStream); @@ -145,7 +147,6 @@ protected: nsHTTPChannel* mConnection; nsHTTPHeaderArray mHeaders; - PRBool mUsingProxy; nsCString mRequestBuffer; nsCOMPtr mPostDataStream; diff --git a/netwerk/protocol/http/src/nsHTTPResponse.cpp b/netwerk/protocol/http/src/nsHTTPResponse.cpp index 0607aa459554..11a3132a1bdf 100644 --- a/netwerk/protocol/http/src/nsHTTPResponse.cpp +++ b/netwerk/protocol/http/src/nsHTTPResponse.cpp @@ -715,6 +715,8 @@ nsresult nsHTTPResponse::EmitHeaders(nsCString& aResponseBuffer) headerAtomRaw == nsHTTPAtoms::Trailer || headerAtomRaw == nsHTTPAtoms::Transfer_Encoding || headerAtomRaw == nsHTTPAtoms::Upgrade || + // XXX: This seems wrong. See RFC 2109 [4.3.2] + // Should depend on Cache-control: no-cache="set-cookie" headerAtomRaw == nsHTTPAtoms::Set_Cookie) continue; @@ -753,3 +755,102 @@ nsresult nsHTTPResponse::ParseHeaders(nsCString& aAllHeaders) beginLineOffset = endLineOffset + 2; // Skip past CRLF } } + +// +// This routine is used to update the Response Headers after a 304 +// Response has been received. +// +// + The nsISimpleEnumerator contains the response headers from the +// 304 response. +// + These headers replace the cached headers. See RFC 2616 [13.5.3] +// + Wacky headers which are send by certain servers are also ignored. +// +nsresult nsHTTPResponse::UpdateHeaders(nsISimpleEnumerator *aEnumerator) +{ + nsresult rv; + + PRBool bMoreHeaders = PR_FALSE; + nsCOMPtr item; + nsCOMPtr header; + nsCOMPtr headerAtom; + nsXPIDLCString headerValue; + + PR_LOG(gHTTPLog, PR_LOG_DEBUG, + ("nsHTTPResponse::UpdateHeaders [this=%x].\n", this)); + + rv = aEnumerator->HasMoreElements(&bMoreHeaders); + + while (NS_SUCCEEDED(rv) && bMoreHeaders) { + rv = aEnumerator->GetNext(getter_AddRefs(item)); + if (NS_FAILED(rv)) return rv; + + header = do_QueryInterface(item, &rv); + + NS_ASSERTION(NS_SUCCEEDED(rv), "Bad HTTP header."); + if (NS_SUCCEEDED(rv)) { + rv = header->GetField(getter_AddRefs(headerAtom)); + if (NS_FAILED(rv)) return rv; + + nsIAtom *atom = headerAtom; + // Ignore any hop-by-hop headers... + if (atom == nsHTTPAtoms::Connection || + atom == nsHTTPAtoms::Keep_Alive || + atom == nsHTTPAtoms::Proxy_Authenticate || + atom == nsHTTPAtoms::Proxy_Authorization || + atom == nsHTTPAtoms::TE || + atom == nsHTTPAtoms::Trailer || + atom == nsHTTPAtoms::Transfer_Encoding || + atom == nsHTTPAtoms::Upgrade || + // Ignore any non-modifiable headers + atom == nsHTTPAtoms::Content_Location || + atom == nsHTTPAtoms::Content_MD5 || + atom == nsHTTPAtoms::ETag || + atom == nsHTTPAtoms::Last_Modified || + // Assume Cache-Control: "no-transform" + atom == nsHTTPAtoms::Content_Encoding || + atom == nsHTTPAtoms::Content_Range || + atom == nsHTTPAtoms::Content_Type || + // Ignore wacky headers too... + // This one is for MS Servers that send a Content-Length:0 + // on 304 responses... + atom == nsHTTPAtoms::Content_Length) { +#if defined(PR_LOGGING) + nsCAutoString nameBuffer; + const PRUnichar *name = nsnull; + + // Convert the atom name from unicode to ascii... + atom->GetUnicode(&name); + nameBuffer.Assign(name); + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("\tUpdateHeaders [this=%x]." + "\tIgnoring response header: \'%s\'\n", + this, nameBuffer.GetBuffer())); +#endif /* PR_LOGGING */ + } else { + // Delete the current header value (if any)... + mHeaders.SetHeader(headerAtom, nsnull); + + // Copy the new header value... + rv = header->GetValue(getter_Copies(headerValue)); + if (NS_SUCCEEDED(rv)) { + rv = mHeaders.SetHeader(headerAtom, headerValue); + } + if (NS_FAILED(rv)) return rv; + +#if defined(PR_LOGGING) + nsCAutoString nameBuffer; + const PRUnichar *name = nsnull; + + atom->GetUnicode(&name); + nameBuffer.Assign(name); + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("\tUpdateHeaders [this=%x].\tNew response header: \'%s: %s\'\n", + this, nameBuffer.GetBuffer(), (const char*)headerValue)); +#endif /* PR_LOGGING */ + } + } + rv = aEnumerator->HasMoreElements(&bMoreHeaders); + } + + return rv; +} diff --git a/netwerk/protocol/http/src/nsHTTPResponse.h b/netwerk/protocol/http/src/nsHTTPResponse.h index 657777a6d18e..7ae1d0279076 100644 --- a/netwerk/protocol/http/src/nsHTTPResponse.h +++ b/netwerk/protocol/http/src/nsHTTPResponse.h @@ -76,7 +76,9 @@ public: nsresult ParseHeaders(nsCString& aAllHeaders); nsresult ProcessHeader(nsIAtom* aHeader, nsCString& aValue); nsresult EmitHeaders(nsCString& aResult); - + + nsresult UpdateHeaders(nsISimpleEnumerator *aEnumerator); + PRBool IsStale(PRBool aUseHeuristicExpiration); nsresult ParseDateHeader(nsIAtom *aAtom, PRTime *aResultTime, PRBool *aHeaderIsPresent); diff --git a/netwerk/protocol/http/src/nsHTTPResponseListener.cpp b/netwerk/protocol/http/src/nsHTTPResponseListener.cpp index 60a585e0eb55..288f66f4cb70 100644 --- a/netwerk/protocol/http/src/nsHTTPResponseListener.cpp +++ b/netwerk/protocol/http/src/nsHTTPResponseListener.cpp @@ -55,35 +55,52 @@ extern PRLogModuleInfo* gHTTPLog; static const int kMAX_HEADER_SIZE = 60000; -nsHTTPResponseListener::nsHTTPResponseListener(nsHTTPChannel* aConnection): - mResponse(nsnull), - mFirstLineParsed(PR_FALSE), - mHeadersDone(PR_FALSE), - mBytesReceived(0) +/////////////////////////////////////////////////////////////////////////////// +// +// nsHTTPResponseListener Implementation (abstract base class) +// +/////////////////////////////////////////////////////////////////////////////// +nsHTTPResponseListener::nsHTTPResponseListener(nsHTTPChannel *aChannel) + : mChannel(aChannel) { - NS_INIT_REFCNT(); + NS_INIT_REFCNT(); - NS_ASSERTION(aConnection, "HTTPChannel is null."); - mChannel = aConnection; - NS_ADDREF(mChannel); - mChannel->mRawResponseListener = this; + // mChannel is not an interface pointer, so COMPtrs cannot be used :-( + NS_ASSERTION(aChannel, "HTTPChannel is null."); + NS_ADDREF(mChannel); + +#if defined(PR_LOGGING) + nsCOMPtr url; + nsXPIDLCString urlCString; + + aChannel->GetURI(getter_AddRefs(url)); + if (url) { + url->GetSpec(getter_Copies(urlCString)); + } + + PR_LOG(gHTTPLog, PR_LOG_DEBUG, + ("Creating nsHTTPResponseListener [this=%x] for URI: %s.\n", + this, (const char *)urlCString)); +#endif - PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("Creating nsHTTPResponseListener [this=%x].\n", this)); } nsHTTPResponseListener::~nsHTTPResponseListener() { - PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("Deleting nsHTTPResponseListener [this=%x].\n", this)); - - // These two should go away in the OnStopRequest() callback. - // But, just in case... - NS_IF_RELEASE(mChannel); - NS_IF_RELEASE(mResponse); + // mChannel is not an interface pointer, so COMPtrs cannot be used :-( + NS_IF_RELEASE(mChannel); } + +void nsHTTPResponseListener::SetListener(nsIStreamListener *aListener) +{ + mResponseDataListener = aListener; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports methods: + NS_IMPL_THREADSAFE_ADDREF(nsHTTPResponseListener) NS_IMPL_THREADSAFE_RELEASE(nsHTTPResponseListener) @@ -91,6 +108,135 @@ NS_IMPL_QUERY_INTERFACE2(nsHTTPResponseListener, nsIStreamListener, nsIStreamObserver); + + +/////////////////////////////////////////////////////////////////////////////// +// +// nsHTTPCacheListener Implementation +// +// This subclass of nsHTTPResponseListener processes responses from +// the cache. +// +/////////////////////////////////////////////////////////////////////////////// +nsHTTPCacheListener::nsHTTPCacheListener(nsHTTPChannel* aChannel) + : nsHTTPResponseListener(aChannel) +{ + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("Creating nsHTTPCacheListener [this=%x].\n", this)); + +} + +nsHTTPCacheListener::~nsHTTPCacheListener() +{ + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("Deleting nsHTTPCacheListener [this=%x].\n", this)); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIStreamObserver methods: + +NS_IMETHODIMP +nsHTTPCacheListener::OnStartRequest(nsIChannel *aChannel, + nsISupports *aContext) +{ + PR_LOG(gHTTPLog, PR_LOG_DEBUG, + ("nsHTTPCacheListener::OnStartRequest [this=%x].\n", + this)); + + return mResponseDataListener->OnStartRequest(mChannel, aContext); +} + +NS_IMETHODIMP +nsHTTPCacheListener::OnStopRequest(nsIChannel *aChannel, + nsISupports *aContext, + nsresult aStatus, + const PRUnichar *aErrorMsg) +{ + PR_LOG(gHTTPLog, PR_LOG_DEBUG, + ("nsHTTPCacheListener::OnStopRequest [this=%x]." + "\tStatus = %x\n", this, aStatus)); + + // + // Notify the channel that the response has finished. Since there + // is no socket transport involved nsnull is passed as the + // transport... + // + return mChannel->ResponseCompleted(mResponseDataListener, + aStatus, + aErrorMsg); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIStreamListener methods: + +NS_IMETHODIMP +nsHTTPCacheListener::OnDataAvailable(nsIChannel *aChannel, + nsISupports *aContext, + nsIInputStream *aStream, + PRUint32 aSourceOffset, + PRUint32 aCount) +{ + return mResponseDataListener->OnDataAvailable(mChannel, + aContext, + aStream, + aSourceOffset, + aCount); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsHTTPResponseListener methods: + +nsresult +nsHTTPCacheListener::FireSingleOnData(nsIStreamListener *aListener, + nsISupports *aContext) +{ + NS_ASSERTION(0, "nsHTTPCacheListener::FireSingleOnData(...) " + "should never be called."); + + return NS_ERROR_FAILURE; +} + +nsresult nsHTTPCacheListener::Abort() +{ + NS_ASSERTION(0, "nsHTTPCachedResponseListener::Abort() " + "should never be called."); + + return NS_ERROR_FAILURE; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// nsHTTPServerListener Implementation +// +// This subclass of nsHTTPResponseListener processes responses from +// HTTP servers. +// +//////////////////////////////////////////////////////////////////////////////// + +nsHTTPServerListener::nsHTTPServerListener(nsHTTPChannel* aChannel) + : nsHTTPResponseListener(aChannel), + mResponse(nsnull), + mFirstLineParsed(PR_FALSE), + mHeadersDone(PR_FALSE), + mBytesReceived(0) +{ + mChannel->mHTTPServerListener = this; + + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("Creating nsHTTPServerListener [this=%x].\n", this)); +} + +nsHTTPServerListener::~nsHTTPServerListener() +{ + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, + ("Deleting nsHTTPServerListener [this=%x].\n", this)); + + // These two should go away in the OnStopRequest() callback. + // But, just in case... + NS_IF_RELEASE(mResponse); +} + static NS_DEFINE_IID(kProxyObjectManagerIID, NS_IPROXYEVENT_MANAGER_IID); static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_CID(kNetModuleMgrCID, NS_NETMODULEMGR_CID); @@ -99,11 +245,11 @@ static NS_DEFINE_CID(kNetModuleMgrCID, NS_NETMODULEMGR_CID); // nsIStreamListener methods: NS_IMETHODIMP -nsHTTPResponseListener::OnDataAvailable(nsIChannel* channel, - nsISupports* context, - nsIInputStream *i_pStream, - PRUint32 i_SourceOffset, - PRUint32 i_Length) +nsHTTPServerListener::OnDataAvailable(nsIChannel* channel, + nsISupports* context, + nsIInputStream *i_pStream, + PRUint32 i_SourceOffset, + PRUint32 i_Length) { nsresult rv = NS_OK; PRUint32 actualBytesRead; @@ -111,7 +257,7 @@ nsHTTPResponseListener::OnDataAvailable(nsIChannel* channel, nsCOMPtr bufferInStream = do_QueryInterface(i_pStream); PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("nsHTTPResponseListener::OnDataAvailable [this=%x].\n" + ("nsHTTPServerListener::OnDataAvailable [this=%x].\n" "\tstream=%x. \toffset=%d. \tlength=%d.\n", this, i_pStream, i_SourceOffset, i_Length)); @@ -211,10 +357,10 @@ nsHTTPResponseListener::OnDataAvailable(nsIChannel* channel, NS_IMETHODIMP -nsHTTPResponseListener::OnStartRequest(nsIChannel* channel, nsISupports* i_pContext) +nsHTTPServerListener::OnStartRequest(nsIChannel* channel, nsISupports* i_pContext) { PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("nsHTTPResponseListener::OnStartRequest [this=%x].\n", this)); + ("nsHTTPServerListener::OnStartRequest [this=%x].\n", this)); // Initialize header varaibles... mHeadersDone = PR_FALSE; @@ -224,15 +370,15 @@ nsHTTPResponseListener::OnStartRequest(nsIChannel* channel, nsISupports* i_pCont } NS_IMETHODIMP -nsHTTPResponseListener::OnStopRequest(nsIChannel* channel, - nsISupports* i_pContext, - nsresult i_Status, - const PRUnichar* i_pMsg) +nsHTTPServerListener::OnStopRequest(nsIChannel* channel, + nsISupports* i_pContext, + nsresult i_Status, + const PRUnichar* i_pMsg) { nsresult rv = NS_OK; PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("nsHTTPResponseListener::OnStopRequest [this=%x]." + ("nsHTTPServerListener::OnStopRequest [this=%x]." "\tStatus = %x\n", this, i_Status)); if (NS_SUCCEEDED(rv) && !mHeadersDone) { @@ -253,11 +399,23 @@ nsHTTPResponseListener::OnStopRequest(nsIChannel* channel, // Notify the HTTPChannel that the response has completed... NS_ASSERTION(mChannel, "HTTPChannel is null."); if (mChannel) { - mChannel->ResponseCompleted(channel, mResponseDataListener, - i_Status, i_pMsg); + PRUint32 status = 0; - // The HTTPChannel is no longer needed... - mChannel->mRawResponseListener = 0; + if (mResponse) { + mResponse->GetStatus(&status); + } + + if (status != 304) { + mChannel->ResponseCompleted(mResponseDataListener, + i_Status, i_pMsg); + // The HTTPChannel no longer needs a reference to this object. + mChannel->mHTTPServerListener = 0; + } else { + PR_LOG(gHTTPLog, PR_LOG_DEBUG, + ("nsHTTPServerListener::OnStopRequest [this=%x]. " + "Discarding 304 response\n", this)); + } + mChannel->ReleaseTransport(channel); } NS_IF_RELEASE(mChannel); @@ -267,12 +425,12 @@ nsHTTPResponseListener::OnStopRequest(nsIChannel* channel, } //////////////////////////////////////////////////////////////////////////////// -// nsHTTPResponseListener methods: +// nsHTTPServerListener methods: -nsresult nsHTTPResponseListener::Abort() +nsresult nsHTTPServerListener::Abort() { PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("nsHTTPResponseListener::Abort [this=%x].", this)); + ("nsHTTPServerListener::Abort [this=%x].", this)); // // Clearing the data consumer will cause the response to abort. This @@ -284,7 +442,7 @@ nsresult nsHTTPResponseListener::Abort() } -nsresult nsHTTPResponseListener::FireSingleOnData(nsIStreamListener *aListener, nsISupports *aContext) +nsresult nsHTTPServerListener::FireSingleOnData(nsIStreamListener *aListener, nsISupports *aContext) { nsresult rv; @@ -318,9 +476,9 @@ nsWriteToString(void* closure, } -nsresult nsHTTPResponseListener::ParseStatusLine(nsIBufferInputStream* in, - PRUint32 aLength, - PRUint32 *aBytesRead) +nsresult nsHTTPServerListener::ParseStatusLine(nsIBufferInputStream* in, + PRUint32 aLength, + PRUint32 *aBytesRead) { nsresult rv = NS_OK; @@ -328,7 +486,7 @@ nsresult nsHTTPResponseListener::ParseStatusLine(nsIBufferInputStream* in, PRUint32 offsetOfEnd, totalBytesToRead, actualBytesRead; PR_LOG(gHTTPLog, PR_LOG_ALWAYS, - ("nsHTTPResponseListener::ParseStatusLine [this=%x].\taLength=%d\n", + ("nsHTTPServerListener::ParseStatusLine [this=%x].\taLength=%d\n", this, aLength)); *aBytesRead = 0; @@ -405,9 +563,9 @@ nsresult nsHTTPResponseListener::ParseStatusLine(nsIBufferInputStream* in, -nsresult nsHTTPResponseListener::ParseHTTPHeader(nsIBufferInputStream* in, - PRUint32 aLength, - PRUint32 *aBytesRead) +nsresult nsHTTPServerListener::ParseHTTPHeader(nsIBufferInputStream* in, + PRUint32 aLength, + PRUint32 *aBytesRead) { nsresult rv = NS_OK; @@ -508,7 +666,7 @@ nsresult nsHTTPResponseListener::ParseHTTPHeader(nsIBufferInputStream* in, return mResponse->ParseHeader(mHeaderBuffer); } -nsresult nsHTTPResponseListener::FinishedResponseHeaders(void) +nsresult nsHTTPServerListener::FinishedResponseHeaders(void) { nsresult rv; @@ -529,3 +687,6 @@ nsresult nsHTTPResponseListener::FinishedResponseHeaders(void) return rv; } + + + diff --git a/netwerk/protocol/http/src/nsHTTPResponseListener.h b/netwerk/protocol/http/src/nsHTTPResponseListener.h index 18e11eb0d752..d8bc69e35126 100644 --- a/netwerk/protocol/http/src/nsHTTPResponseListener.h +++ b/netwerk/protocol/http/src/nsHTTPResponseListener.h @@ -52,21 +52,48 @@ class nsHTTPChannel; */ class nsHTTPResponseListener : public nsIStreamListener { - public: - nsHTTPResponseListener(nsHTTPChannel* aConnection); virtual ~nsHTTPResponseListener(); + // nsISupport methods... NS_DECL_ISUPPORTS + + + // abstract methods implemented by the various ResponseListener + // subclasses... + virtual nsresult FireSingleOnData(nsIStreamListener *aListener, + nsISupports *aContext) = 0; + virtual nsresult Abort() = 0; + + void SetListener(nsIStreamListener *aListener); + +protected: + nsCOMPtr mResponseDataListener; + nsHTTPChannel* mChannel; +}; + + +/* + * This class pocesses responses from HTTP servers... + */ +class nsHTTPServerListener : public nsHTTPResponseListener +{ + +public: + + nsHTTPServerListener(nsHTTPChannel* aConnection); + virtual ~nsHTTPServerListener(); + NS_DECL_NSISTREAMOBSERVER NS_DECL_NSISTREAMLISTENER - nsresult FireSingleOnData(nsIStreamListener *aListener, nsISupports *aContext); - nsresult Abort(); - void SetResponseDataListener(nsIStreamListener *aListener) { - mResponseDataListener = aListener; - } + virtual nsresult FireSingleOnData(nsIStreamListener *aListener, + nsISupports *aContext); + virtual nsresult Abort(); + + nsresult Discard304Response(void); + protected: // nsHTTPResponseListener methods... @@ -80,15 +107,35 @@ protected: PRUint32* aBytesRead); protected: - nsCOMPtr mResponseDataListener; nsCString mHeaderBuffer; - nsHTTPChannel* mChannel; nsHTTPResponse* mResponse; - PRBool mFirstLineParsed; - PRBool mHeadersDone; + PRBool mFirstLineParsed; + PRBool mHeadersDone; nsCOMPtr mDataStream; PRUint32 mBytesReceived; }; + +/* + * This class processes responses from the cache... + */ +class nsHTTPCacheListener : public nsHTTPResponseListener +{ +public: + nsHTTPCacheListener(nsHTTPChannel* aChannel); + virtual ~nsHTTPCacheListener(); + + // nsIStreamObserver methods... + NS_DECL_NSISTREAMOBSERVER + + // nsIStreamListener methods... + NS_DECL_NSISTREAMLISTENER + + virtual nsresult FireSingleOnData(nsIStreamListener *aListener, + nsISupports *aContext); + virtual nsresult Abort(); +}; + + #endif /* _nsHTTPResponseListener_h_ */