304 support

git-svn-id: svn://10.0.0.236/branches/HttpConnectionMagic_20010415_BRANCH@94090 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
darin%netscape.com
2001-05-06 19:42:55 +00:00
parent f7deb0fa0b
commit cd099d2cbc
6 changed files with 183 additions and 43 deletions

View File

@@ -270,6 +270,40 @@ nsHttpChannel::SetupTransaction()
return mTransaction->SetupRequest(&mRequestHead, mUploadStream);
}
nsresult
nsHttpChannel::ApplyContentConversions()
{
LOG(("nsHttpChannel::ApplyContentConversions [this=%x]\n", this));
if (!mApplyConversion) {
LOG(("not applying conversion per mApplyConversion\n"));
return NS_OK;
}
const char *val = mResponseHead->PeekHeader(nsHttp::Content_Encoding);
if (val) {
nsCOMPtr<nsIStreamConverterService> serv;
nsresult rv = nsHttpHandler::get()->
GetStreamConverterService(getter_AddRefs(serv));
// we won't fail to load the page just because we couldn't load the
// stream converter service.. carry on..
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIStreamListener> converter;
nsAutoString from = NS_ConvertASCIItoUCS2(val);
rv = serv->AsyncConvertData(from.get(),
NS_LITERAL_STRING("uncompressed").get(),
mListener,
mListenerContext,
getter_AddRefs(converter));
if (NS_SUCCEEDED(rv)) {
LOG(("converter installed from \'%s\' to \'uncompressed\'\n", val));
mListener = converter;
}
}
}
return NS_OK;
}
nsresult
nsHttpChannel::ProcessResponse()
{
@@ -303,7 +337,6 @@ nsHttpChannel::ProcessResponse()
rv = ProcessNormal();
break;
case 304:
CloseCacheEntry(NS_ERROR_ABORT);
rv = ProcessNotModified();
break;
case 401:
@@ -329,28 +362,8 @@ nsHttpChannel::ProcessNormal()
LOG(("nsHttpChannel::ProcessNormal [this=%x]\n", this));
// install stream converter(s) if required
if (mApplyConversion) {
const char *val = mResponseHead->PeekHeader(nsHttp::Content_Encoding);
if (val) {
nsCOMPtr<nsIStreamConverterService> serv;
rv = nsHttpHandler::get()->
GetStreamConverterService(getter_AddRefs(serv));
// we won't fail to load the page just because we couldn't load the
// stream converter service.. carry on..
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIStreamListener> converter;
nsAutoString from = NS_ConvertASCIItoUCS2(val);
rv = serv->AsyncConvertData(from.get(),
NS_LITERAL_STRING("uncompressed").get(),
mListener,
mListenerContext,
getter_AddRefs(converter));
if (NS_SUCCEEDED(rv))
mListener = converter;
}
}
}
// install stream converter if required
ApplyContentConversions();
// install cache listener if we still have a cache entry open
if (mCacheEntry) {
@@ -358,6 +371,9 @@ nsHttpChannel::ProcessNormal()
if (NS_FAILED(rv)) return rv;
}
// notify nsIHttpNotify implementations
nsHttpHandler::get()->OnExamineResponse(this);
return mListener->OnStartRequest(this, mListenerContext);
}
@@ -368,8 +384,45 @@ nsHttpChannel::ProcessNormal()
nsresult
nsHttpChannel::ProcessNotModified()
{
NS_NOTREACHED("not implemented");
return NS_ERROR_NOT_IMPLEMENTED;
nsresult rv;
LOG(("nsHttpChannel::ProcessNotModified [this=%x]\n", this));
NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
// merge any new headers with the cached response headers
rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
if (NS_FAILED(rv)) return rv;
// update the cached response headers
nsCAutoString headers;
rv = mCachedResponseHead->Flatten(headers);
if (NS_FAILED(rv)) return rv;
rv = mCacheEntry->SetMetaDataElement("http-headers", headers.get());
if (NS_FAILED(rv)) return rv;
// make the cached response be the current response
delete mResponseHead;
mResponseHead = mCachedResponseHead;
mCachedResponseHead = 0;
rv = UpdateExpirationTime();
if (NS_FAILED(rv)) return rv;
// drop our reference to the current transaction... ie. let it finish
// in the background.
// XXX we shouldn't have to cancel the transaction
mTransaction->Cancel(NS_BINDING_ABORTED);
NS_RELEASE(mTransaction);
mTransaction = 0;
// notify nsIHttpNotify implementations as response headers may have changed
nsHttpHandler::get()->OnExamineResponse(this);
mCachedContentIsValid = PR_TRUE;
return ReadFromCache();
}
nsresult
@@ -705,6 +758,15 @@ nsHttpChannel::ReadFromCache()
LOG(("nsHttpChannel::ReadFromCache [this=%x] "
"Using cached copy of: %s\n", this, mSpec.get()));
if (mCachedResponseHead) {
NS_ASSERTION(!mResponseHead, "memory leak");
mResponseHead = mCachedResponseHead;
mCachedResponseHead = 0;
}
// install stream converter if required
ApplyContentConversions();
if (mCacheAccess & nsICache::ACCESS_WRITE) {
// We have write access to the cache, but we don't need to go to the
// server to validate at this time, so just mark the cache entry as
@@ -734,6 +796,11 @@ nsHttpChannel::CloseCacheEntry(nsresult status)
rv = mCacheEntry->Doom();
}
if (mCachedResponseHead) {
delete mCachedResponseHead;
mCachedResponseHead = 0;
}
mCacheReadRequest = 0;
mCacheTransport = 0;
mCacheEntry = 0;
@@ -841,6 +908,9 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType)
if (!location)
return NS_ERROR_FAILURE;
// notify nsIHttpNotify implementations before this channel goes away
nsHttpHandler::get()->OnExamineResponse(this);
LOG(("redirecting to: %s\n", location));
nsresult rv;
@@ -1764,21 +1834,14 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
LOG(("nsHttpChannel::OnStartRequest [this=%x]\n", this));
// all of the response headers have been acquired, so we can take ownership
// of them from the transaction.
if (mTransaction)
if (mTransaction) {
// all of the response headers have been acquired, so we can take ownership
// of them from the transaction.
mResponseHead = mTransaction->TakeResponseHead();
else {
NS_ASSERTION(!mResponseHead, "memory leak");
mResponseHead = mCachedResponseHead;
mCachedResponseHead = 0;
}
if (mResponseHead && mTransaction) {
// notify nsIHttpNotify implementations
nsHttpHandler::get()->OnExamineResponse(this);
return ProcessResponse();
// the response head may be null if the transaction was cancelled. in
// which case we just need to call OnStartRequest/OnStopRequest.
if (mResponseHead)
return ProcessResponse();
}
// there won't be a response head if we've been cancelled
@@ -1829,8 +1892,10 @@ nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
{
// if the request is for something we no longer reference, then simply
// drop this event.
if ((request != mTransaction) && (request != mCacheReadRequest))
return NS_OK;
if ((request != mTransaction) && (request != mCacheReadRequest)) {
NS_WARNING("got stale request... why wasn't it cancelled?");
return NS_BASE_STREAM_CLOSED;
}
LOG(("nsHttpChannel::OnDataAvailable [this=%x offset=%u count=%u]\n",
this, offset, count));

View File

@@ -81,7 +81,7 @@ private:
nsresult Connect(PRBool firstTime = PR_TRUE);
nsresult AsyncAbort(nsresult status);
nsresult SetupTransaction();
nsresult BuildConnectionInfo(nsHttpConnectionInfo **);
nsresult ApplyContentConversions();
nsresult ProcessResponse();
nsresult ProcessNormal();
nsresult ProcessNotModified();

View File

@@ -25,7 +25,7 @@
#include "nsHttp.h"
//-----------------------------------------------------------------------------
// nsHttpHeaderArray
// nsHttpHeaderArray <public>
//-----------------------------------------------------------------------------
nsresult
@@ -120,6 +120,21 @@ nsHttpHeaderArray::Flatten(nsACString &buf)
return NS_OK;
}
const char *
nsHttpHeaderArray::PeekHeaderAt(PRUint32 index, nsHttpAtom &header)
{
nsEntry *entry = (nsEntry *) mHeaders[index];
if (!entry)
return nsnull;
header = entry->header;
return entry->value.get();
}
//-----------------------------------------------------------------------------
// nsHttpHeaderArray <private>
//-----------------------------------------------------------------------------
PRInt32
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
{

View File

@@ -45,6 +45,10 @@ public:
nsresult Flatten(nsACString &);
PRUint32 Count() { return (PRUint32) mHeaders.Count(); }
const char *PeekHeaderAt(PRUint32 i, nsHttpAtom &header);
private:
struct nsEntry
{

View File

@@ -264,6 +264,59 @@ nsHttpResponseHead::ComputeFreshnessLifetime(PRUint32 *result)
return NS_OK;
}
nsresult
nsHttpResponseHead::UpdateHeaders(nsHttpHeaderArray &headers)
{
LOG(("nsHttpResponseHead::UpdateHeaders [this=%x]\n", this));
PRUint32 i, count = headers.Count();
for (i=0; i<count; ++i) {
nsHttpAtom header;
const char *val = headers.PeekHeaderAt(i, header);
if (!val) {
NS_NOTREACHED("null header value");
continue;
}
// Ignore any hop-by-hop headers...
if (header == nsHttp::Connection ||
header == nsHttp::Keep_Alive ||
header == nsHttp::Proxy_Authenticate ||
header == nsHttp::Proxy_Authorization || // not a response header!
header == nsHttp::TE ||
header == nsHttp::Trailer ||
header == nsHttp::Transfer_Encoding ||
header == nsHttp::Upgrade ||
// Ignore any non-modifiable headers...
header == nsHttp::Content_Location ||
header == nsHttp::Content_MD5 ||
header == nsHttp::ETag ||
header == nsHttp::Last_Modified ||
// Assume Cache-Control: "no-transform"
header == nsHttp::Content_Encoding ||
header == nsHttp::Content_Range ||
header == nsHttp::Content_Type ||
// Ignore wacky headers too...
// this one is for MS servers that send "Content-Length: 0"
// on 304 responses
header == nsHttp::Content_Length) {
LOG(("ignoring response header [%s: %s]\n", header.get(), val));
}
else {
LOG(("new response header [%s: %s]\n", header.get(), val));
// delete the current header value (if any)
mHeaders.SetHeader(header, nsnull);
// copy the new header value...
mHeaders.SetHeader(header, val);
}
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpResponseHead <private>
//-----------------------------------------------------------------------------

View File

@@ -67,6 +67,9 @@ public:
nsresult ComputeFreshnessLifetime(PRUint32 *);
nsresult ComputeCurrentAge(PRUint32 now, PRUint32 requestTime, PRUint32 *result);
// update headers...
nsresult UpdateHeaders(nsHttpHeaderArray &headers);
private:
nsresult ParseVersion(const char *);
nsresult ParseContentType(char *);