mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 219556 necko support for resumable downloads via http
r=darin sr=bzbarsky
This commit is contained in:
parent
00ef7e8765
commit
91a47b5ddb
@ -42,18 +42,24 @@
|
||||
|
||||
[scriptable, uuid(c9df38d4-1dd1-11b2-81ae-c9e767256d1b)]
|
||||
interface nsIResumableEntityID : nsISupports {
|
||||
/** Size of the entity, -1 if unknown */
|
||||
/**
|
||||
* Size of the entity.
|
||||
* @throw NS_ERROR_NOT_AVAILABLE if the size is not known.
|
||||
*/
|
||||
attribute unsigned long size;
|
||||
|
||||
/** Last modified time, in seconds since epoch. Note that the timezone
|
||||
* these are in may not be known
|
||||
/**
|
||||
* An opaque, but human-readable ASCII string specifying the date when the
|
||||
* resource was last modified. May be empty.
|
||||
*/
|
||||
attribute PRTime lastModified;
|
||||
attribute ACString lastModified;
|
||||
|
||||
/** Entity, may be empty. This is meant to hold the E-Tag for http */
|
||||
// currently we only support ftp; add this in when its utility can be
|
||||
// checked
|
||||
//attribute string entity;
|
||||
/**
|
||||
* The entity tag is a strong identifier specified by the protocol. For
|
||||
* HTTP, this attribute corresponds to the value of the ETag response
|
||||
* header. This attribute may be empty. Otherwise it is an ASCII string.
|
||||
*/
|
||||
attribute ACString entityTag;
|
||||
|
||||
/** Compare to another nsIResumableEntityID */
|
||||
boolean equals(in nsIResumableEntityID other);
|
||||
|
@ -620,7 +620,8 @@ NS_GetURLSpecFromFile(nsIFile *aFile,
|
||||
inline nsresult
|
||||
NS_NewResumableEntityID(nsIResumableEntityID **aRes,
|
||||
PRUint32 size,
|
||||
PRTime lastModified)
|
||||
const nsACString &lastModified,
|
||||
const nsACString &entityTag)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIResumableEntityID> ent =
|
||||
@ -628,6 +629,7 @@ NS_NewResumableEntityID(nsIResumableEntityID **aRes,
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
ent->SetSize(size);
|
||||
ent->SetLastModified(lastModified);
|
||||
ent->SetEntityTag(entityTag);
|
||||
NS_ADDREF(*aRes = ent);
|
||||
}
|
||||
return rv;
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set et ts=4 sw=4 sts=4 cin: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
@ -17,10 +18,11 @@
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bradley Baetz
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
* Contributor(s):
|
||||
* Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
* Christian Biesinger <cbiesinger@web.de>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -37,18 +39,20 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsResumableEntityID.h"
|
||||
#include "nsReadableUtils.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsResumableEntityID, nsIResumableEntityID)
|
||||
|
||||
nsResumableEntityID::nsResumableEntityID() :
|
||||
mSize(PRUint32(-1)),
|
||||
mLastModified(PRTime(LL_INIT(-1,-1))) {
|
||||
mSize(PR_UINT32_MAX) {
|
||||
}
|
||||
|
||||
nsResumableEntityID::~nsResumableEntityID() {}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsResumableEntityID::GetSize(PRUint32 *aSize) {
|
||||
if (mSize == PR_UINT32_MAX)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
*aSize = mSize;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -60,27 +64,52 @@ nsResumableEntityID::SetSize(PRUint32 aSize) {
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsResumableEntityID::GetLastModified(PRTime *aLastModified) {
|
||||
*aLastModified = mLastModified;
|
||||
nsResumableEntityID::GetLastModified(nsACString& aLastModified) {
|
||||
aLastModified = mLastModified;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsResumableEntityID::SetLastModified(PRTime aLastModified) {
|
||||
nsResumableEntityID::SetLastModified(const nsACString& aLastModified) {
|
||||
mLastModified = aLastModified;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsResumableEntityID::SetEntityTag(const nsACString& aTag) {
|
||||
mEntityTag = aTag;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsResumableEntityID::GetEntityTag(nsACString& aTag) {
|
||||
aTag = mEntityTag;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsResumableEntityID::Equals(nsIResumableEntityID *other, PRBool *ret) {
|
||||
PRUint32 size;
|
||||
PRInt64 lastMod;
|
||||
nsCAutoString lastMod;
|
||||
nsCAutoString entityTag;
|
||||
|
||||
nsresult rv = other->GetSize(&size);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv))
|
||||
size = PR_UINT32_MAX;
|
||||
|
||||
rv = other->GetLastModified(&lastMod);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = other->GetLastModified(lastMod);
|
||||
if (NS_FAILED(rv))
|
||||
lastMod.Truncate();
|
||||
|
||||
rv = other->GetEntityTag(entityTag);
|
||||
if (NS_FAILED(rv))
|
||||
entityTag.Truncate();
|
||||
|
||||
// This assumes that the server generated the last modification time in
|
||||
// exactly the same way for both of these entity IDs (same timezone, same
|
||||
// format, etc).
|
||||
*ret = mEntityTag.Equals(entityTag) && lastMod.Equals(mLastModified) &&
|
||||
(mSize == size);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
*
|
||||
* The Initial Developer of the Original Code is Bradley Baetz
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
@ -36,6 +35,7 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIResumableEntityID.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsResumableEntityID : public nsIResumableEntityID {
|
||||
public:
|
||||
@ -43,9 +43,10 @@ public:
|
||||
NS_DECL_NSIRESUMABLEENTITYID
|
||||
|
||||
nsResumableEntityID();
|
||||
virtual ~nsResumableEntityID();
|
||||
~nsResumableEntityID();
|
||||
|
||||
private:
|
||||
PRUint32 mSize;
|
||||
PRTime mLastModified;
|
||||
nsCString mLastModified;
|
||||
nsCString mEntityTag;
|
||||
};
|
||||
|
@ -384,7 +384,6 @@ nsFtpState::nsFtpState()
|
||||
mControlConnection = nsnull;
|
||||
mDRequestForwarder = nsnull;
|
||||
mFileSize = PRUint32(-1);
|
||||
mModTime = -1;
|
||||
|
||||
// make sure handler stays around
|
||||
NS_ADDREF(gFtpHandler);
|
||||
@ -1357,26 +1356,12 @@ nsFtpState::R_mdtm() {
|
||||
if (mResponseMsg.Length() != 14) {
|
||||
NS_ASSERTION(mResponseMsg.Length() == 14, "Unknown MDTM response");
|
||||
} else {
|
||||
const char* date = mResponseMsg.get();
|
||||
PRExplodedTime exp;
|
||||
exp.tm_year = (date[0]-'0')*1000 + (date[1]-'0')*100 +
|
||||
(date[2]-'0')*10 + (date[3]-'0');
|
||||
exp.tm_month = (date[4]-'0')*10 + (date[5]-'0');
|
||||
exp.tm_mday = (date[6]-'0')*10 + (date[7]-'0');
|
||||
exp.tm_hour = (date[8]-'0')*10 + (date[9]-'0');
|
||||
exp.tm_min = (date[10]-'0')*10 + (date[11]-'0');
|
||||
exp.tm_sec = (date[12]-'0')*10 + (date[13]-'0');
|
||||
exp.tm_usec = 0;
|
||||
exp.tm_wday = 0;
|
||||
exp.tm_yday = 0;
|
||||
exp.tm_params.tp_gmt_offset = 0;
|
||||
exp.tm_params.tp_dst_offset = 0;
|
||||
mModTime = PR_ImplodeTime(&exp);
|
||||
mModTime = mResponseMsg;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = NS_NewResumableEntityID(getter_AddRefs(mEntityID),
|
||||
mFileSize, mModTime);
|
||||
mFileSize, mModTime, EmptyCString());
|
||||
if (NS_FAILED(rv)) return FTP_ERROR;
|
||||
mDRequestForwarder->SetEntityID(mEntityID);
|
||||
|
||||
|
@ -194,7 +194,7 @@ private:
|
||||
nsCOMPtr<nsIRequest> mDPipeRequest;
|
||||
DataRequestForwarder* mDRequestForwarder;
|
||||
PRUint32 mFileSize;
|
||||
PRTime mModTime;
|
||||
nsCString mModTime;
|
||||
|
||||
// ****** consumer vars
|
||||
nsCOMPtr<nsIFTPChannel> mChannel; // our owning FTP channel we pass through our events
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
// vim:expandtab:ts=4 sw=4:
|
||||
/* vim:set expandtab ts=4 sw=4 sts=4: */
|
||||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
@ -19,7 +19,8 @@
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com> (original author)
|
||||
* Darin Fisher <darin@meer.net> (original author)
|
||||
* Christian Biesinger <cbiesinger@web.de>
|
||||
*/
|
||||
|
||||
#include "nsHttpChannel.h"
|
||||
@ -52,6 +53,7 @@
|
||||
#include "prprf.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsICookieService.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
|
||||
static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
|
||||
|
||||
@ -83,6 +85,7 @@ nsHttpChannel::nsHttpChannel()
|
||||
, mPostID(0)
|
||||
, mRequestTime(0)
|
||||
, mAuthContinuationState(nsnull)
|
||||
, mStartPos(0)
|
||||
, mRedirectionLimit(gHttpHandler->RedirectionLimit())
|
||||
, mIsPending(PR_FALSE)
|
||||
, mApplyConversion(PR_TRUE)
|
||||
@ -94,6 +97,7 @@ nsHttpChannel::nsHttpChannel()
|
||||
, mTransactionReplaced(PR_FALSE)
|
||||
, mUploadStreamHasHeaders(PR_FALSE)
|
||||
, mAuthRetryPending(PR_FALSE)
|
||||
, mResuming(PR_FALSE)
|
||||
{
|
||||
LOG(("Creating nsHttpChannel @%x\n", this));
|
||||
|
||||
@ -288,6 +292,12 @@ nsHttpChannel::Connect(PRBool firstTime)
|
||||
if (offline)
|
||||
mLoadFlags |= LOAD_ONLY_FROM_CACHE;
|
||||
|
||||
// Don't allow resuming when cache must be used
|
||||
if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
|
||||
LOG(("Resuming from cache is not supported yet"));
|
||||
return NS_ERROR_DOCUMENT_NOT_CACHED;
|
||||
}
|
||||
|
||||
// open a cache entry for this channel...
|
||||
rv = OpenCacheEntry(offline, &delayed);
|
||||
|
||||
@ -518,6 +528,28 @@ nsHttpChannel::SetupTransaction()
|
||||
mRequestHead.SetHeader(nsHttp::Pragma, NS_LITERAL_CSTRING("no-cache"), PR_TRUE);
|
||||
}
|
||||
|
||||
if (mResuming) {
|
||||
char buf[32];
|
||||
PR_snprintf(buf, sizeof(buf), "bytes=%u-", mStartPos);
|
||||
mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
|
||||
|
||||
if (mEntityID) {
|
||||
// Also, we want an error if this resource changed in the meantime
|
||||
nsCAutoString entityTag;
|
||||
rv = mEntityID->GetEntityTag(entityTag);
|
||||
if (NS_SUCCEEDED(rv) && !entityTag.IsEmpty()) {
|
||||
mRequestHead.SetHeader(nsHttp::If_Match, entityTag);
|
||||
}
|
||||
|
||||
nsCAutoString lastMod;
|
||||
rv = mEntityID->GetLastModified(lastMod);
|
||||
if (NS_SUCCEEDED(rv) && !lastMod.IsEmpty()) {
|
||||
mRequestHead.SetHeader(nsHttp::If_Unmodified_Since, lastMod);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mEventQ) {
|
||||
// grab a reference to the calling thread's event queue.
|
||||
nsCOMPtr<nsIEventQueueService> eqs;
|
||||
@ -662,6 +694,14 @@ nsHttpChannel::ProcessResponse()
|
||||
switch (httpStatus) {
|
||||
case 200:
|
||||
case 203:
|
||||
// Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".
|
||||
// So if a server does that and sends 200 instead of 206 that we
|
||||
// expect, notify our caller.
|
||||
if (mResuming) {
|
||||
Cancel(NS_ERROR_NOT_RESUMABLE);
|
||||
rv = CallOnStartRequest();
|
||||
break;
|
||||
}
|
||||
// these can normally be cached
|
||||
rv = ProcessNormal();
|
||||
break;
|
||||
@ -713,6 +753,14 @@ nsHttpChannel::ProcessResponse()
|
||||
rv = ProcessNormal();
|
||||
}
|
||||
break;
|
||||
case 412: // Precondition failed
|
||||
case 416: // Invalid range
|
||||
if (mResuming) {
|
||||
Cancel(NS_ERROR_NOT_RESUMABLE);
|
||||
rv = CallOnStartRequest();
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
CloseCacheEntry(NS_ERROR_ABORT);
|
||||
rv = ProcessNormal();
|
||||
@ -763,6 +811,24 @@ nsHttpChannel::ProcessNormal()
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
// Check that the server sent us what we were asking for
|
||||
if (mResuming) {
|
||||
// Create an entity id from the response
|
||||
nsCOMPtr<nsIResumableEntityID> id;
|
||||
rv = GetEntityID(getter_AddRefs(id));
|
||||
if (NS_FAILED(rv)) {
|
||||
// If creating an entity id is not possible -> error
|
||||
Cancel(NS_ERROR_NOT_RESUMABLE);
|
||||
}
|
||||
// If we were passed an entity id, verify it's equal to the server's
|
||||
else if (mEntityID) {
|
||||
PRBool equal;
|
||||
rv = mEntityID->Equals(id, &equal);
|
||||
if (NS_FAILED(rv) || !equal)
|
||||
Cancel(NS_ERROR_NOT_RESUMABLE);
|
||||
}
|
||||
}
|
||||
|
||||
rv = CallOnStartRequest();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
@ -1095,7 +1161,7 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
|
||||
}
|
||||
else if (mRequestHead.PeekHeader(nsHttp::Range)) {
|
||||
// we don't support caching for byte range requests initiated
|
||||
// by our clients.
|
||||
// by our clients or via nsIResumableChannel.
|
||||
// XXX perhaps we could munge their byte range into the cache
|
||||
// key to make caching sort'a work.
|
||||
return NS_OK;
|
||||
@ -2509,6 +2575,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICacheListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
|
||||
NS_INTERFACE_MAP_END
|
||||
@ -3646,6 +3713,51 @@ nsHttpChannel::IsFromCache(PRBool *value)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsIResumableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::AsyncOpenAt(nsIStreamListener* aListener,
|
||||
nsISupports* aCtxt,
|
||||
PRUint32 aStartPos,
|
||||
nsIResumableEntityID* aEntityID)
|
||||
{
|
||||
mEntityID = aEntityID;
|
||||
mStartPos = aStartPos;
|
||||
mResuming = PR_TRUE;
|
||||
return AsyncOpen(aListener, aCtxt);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetEntityID(nsIResumableEntityID** aEntityID)
|
||||
{
|
||||
// Don't return an entity ID for HTTP/1.0 servers
|
||||
if (mResponseHead && (mResponseHead->Version() < NS_HTTP_VERSION_1_1)) {
|
||||
*aEntityID = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
// Neither return one for Non-GET requests which require additional data
|
||||
if (mRequestHead.Method() != nsHttp::Get) {
|
||||
*aEntityID = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint32 size = PR_UINT32_MAX;
|
||||
nsCAutoString etag, lastmod;
|
||||
if (mResponseHead) {
|
||||
size = mResponseHead->TotalEntitySize();
|
||||
const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified);
|
||||
if (cLastMod)
|
||||
lastmod = cLastMod;
|
||||
const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag);
|
||||
if (cEtag)
|
||||
etag = cEtag;
|
||||
}
|
||||
|
||||
return NS_NewResumableEntityID(aEntityID, size, lastmod, etag);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsICacheListener
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set et cin ts=4 sw=4 sts=4: */
|
||||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
@ -53,11 +54,13 @@
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIPrompt.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
|
||||
class nsHttpResponseHead;
|
||||
class nsAHttpConnection;
|
||||
class nsIHttpAuthenticator;
|
||||
class nsIProxyInfo;
|
||||
class nsIResumableEntityID;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel
|
||||
@ -71,6 +74,7 @@ class nsHttpChannel : public nsIHttpChannel
|
||||
, public nsICacheListener
|
||||
, public nsIEncodedChannel
|
||||
, public nsITransportEventSink
|
||||
, public nsIResumableChannel
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -85,6 +89,7 @@ public:
|
||||
NS_DECL_NSIENCODEDCHANNEL
|
||||
NS_DECL_NSIHTTPCHANNELINTERNAL
|
||||
NS_DECL_NSITRANSPORTEVENTSINK
|
||||
NS_DECL_NSIRESUMABLECHANNEL
|
||||
|
||||
nsHttpChannel();
|
||||
virtual ~nsHttpChannel();
|
||||
@ -208,6 +213,10 @@ private:
|
||||
nsHttpAuthIdentity mIdent;
|
||||
nsHttpAuthIdentity mProxyIdent;
|
||||
|
||||
// Resumable channel specific data
|
||||
nsCOMPtr<nsIResumableEntityID> mEntityID;
|
||||
PRUint32 mStartPos;
|
||||
|
||||
// redirection specific data.
|
||||
PRUint8 mRedirectionLimit;
|
||||
|
||||
@ -222,6 +231,7 @@ private:
|
||||
PRUint32 mTransactionReplaced : 1;
|
||||
PRUint32 mUploadStreamHasHeaders : 1;
|
||||
PRUint32 mAuthRetryPending : 1;
|
||||
PRUint32 mResuming : 1;
|
||||
|
||||
class nsContentEncodings : public nsIUTF8StringEnumerator
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et cin: */
|
||||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
@ -20,6 +21,7 @@
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com> (original author)
|
||||
* Andreas M. Schneider <clarence@clarence.de>
|
||||
* Christian Biesinger <cbiesinger@web.de>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -503,6 +505,25 @@ nsHttpResponseHead::GetExpiresValue(PRUint32 *result)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsHttpResponseHead::TotalEntitySize()
|
||||
{
|
||||
const char* contentRange = PeekHeader(nsHttp::Content_Range);
|
||||
if (!contentRange)
|
||||
return ContentLength();
|
||||
|
||||
// Total length is after a slash
|
||||
const char* slash = strrchr(contentRange, '/');
|
||||
if (!slash)
|
||||
return -1; // No idea what the length is
|
||||
|
||||
slash++;
|
||||
if (*slash == '*') // Server doesn't know the length
|
||||
return -1;
|
||||
|
||||
return atoi(slash);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpResponseHead <private>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -56,6 +56,12 @@ public:
|
||||
const nsAFlatCString &ContentCharset() { return mContentCharset; }
|
||||
PRBool NoStore() { return mCacheControlNoStore; }
|
||||
PRBool NoCache() { return (mCacheControlNoCache || mPragmaNoCache); }
|
||||
/**
|
||||
* Full length of the entity. For byte-range requests, this may be larger
|
||||
* than ContentLength(), which will only represent the requested part of the
|
||||
* entity.
|
||||
*/
|
||||
PRInt32 TotalEntitySize();
|
||||
|
||||
const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); }
|
||||
nsresult SetHeader(nsHttpAtom h, const nsACString &v, PRBool m=PR_FALSE);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=2 sw=2 et cindent: */
|
||||
/* vim: set ts=4 sw=4 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
@ -44,6 +44,7 @@
|
||||
-Gagan Saksena 04/29/99
|
||||
*/
|
||||
|
||||
#define FORCE_PR_LOG
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
@ -66,6 +67,7 @@
|
||||
#include "nsIResumableEntityID.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsIHttpEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
@ -79,6 +81,7 @@
|
||||
#include "nsXPIDLString.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prlog.h"
|
||||
#include "prtime.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
@ -99,6 +102,10 @@ static PRBool gAskUserForInput = PR_FALSE;
|
||||
static PRBool gResume = PR_FALSE;
|
||||
static PRUint32 gStartAt = 0;
|
||||
|
||||
static const char* gLastMod = NULL;
|
||||
static const char* gETag = NULL;
|
||||
static PRUint32 gTotalSize = PR_UINT32_MAX;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Set proxy preferences for testing
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -357,6 +364,7 @@ InputTestConsumer::OnStartRequest(nsIRequest *request, nsISupports* context)
|
||||
if (channel) {
|
||||
nsresult status;
|
||||
channel->GetStatus(&status);
|
||||
LOG(("Channel Status: %08x\n", status));
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
LOG(("Channel Info:\n"));
|
||||
|
||||
@ -381,6 +389,13 @@ InputTestConsumer::OnStartRequest(nsIRequest *request, nsISupports* context)
|
||||
LOG(("\tChannel Owner: %x\n", owner.get()));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannelInt(do_QueryInterface(request));
|
||||
if (httpChannelInt) {
|
||||
PRUint32 majorVer, minorVer;
|
||||
nsresult rv = httpChannelInt->GetResponseVersion(&majorVer, &minorVer);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
LOG(("HTTP Response version: %u.%u\n", majorVer, minorVer));
|
||||
}
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
|
||||
if (httpChannel) {
|
||||
HeaderVisitor *visitor = new HeaderVisitor();
|
||||
@ -409,18 +424,18 @@ InputTestConsumer::OnStartRequest(nsIRequest *request, nsISupports* context)
|
||||
LOG(("\tSize: %d\n", size));
|
||||
else
|
||||
LOG(("\tSize: Unknown\n"));
|
||||
PRTime lastModified;
|
||||
if (NS_SUCCEEDED(entityID->GetLastModified(&lastModified)) &&
|
||||
lastModified != -1) {
|
||||
PRExplodedTime exploded;
|
||||
PR_ExplodeTime(lastModified, PR_LocalTimeParameters, &exploded);
|
||||
|
||||
char buf[100];
|
||||
PR_FormatTime(buf, 100, "%c", &exploded);
|
||||
|
||||
LOG(("\tLast Modified: %s\n", buf));
|
||||
} else
|
||||
nsCAutoString lastModified;
|
||||
if (NS_SUCCEEDED(entityID->GetLastModified(lastModified)) &&
|
||||
!lastModified.IsEmpty())
|
||||
LOG(("\tLast Modified: %s\n", lastModified.get()));
|
||||
else
|
||||
LOG(("\tLast Modified: Unknown\n"));
|
||||
nsCAutoString etag;
|
||||
if (NS_SUCCEEDED(entityID->GetEntityTag(etag)) &&
|
||||
!etag.IsEmpty())
|
||||
LOG(("\tETag: %s\n", etag.get()));
|
||||
else
|
||||
LOG(("\tEtag: Unknown\n"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,10 +662,24 @@ nsresult StartLoadingURL(const char* aUrlString)
|
||||
NS_ERROR("Channel is not resumable!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
nsCOMPtr<nsIResumableEntityID> id;
|
||||
if (gETag || gLastMod || gTotalSize != PR_UINT32_MAX) {
|
||||
id = do_CreateInstance(NS_RESUMABLEENTITYID_CONTRACTID);
|
||||
if (!id) {
|
||||
fprintf(stderr, "Error creating entityid\n");
|
||||
}
|
||||
else {
|
||||
if (gETag)
|
||||
id->SetEntityTag(nsDependentCString(gETag));
|
||||
if (gLastMod)
|
||||
id->SetLastModified(nsDependentCString(gLastMod));
|
||||
id->SetSize(gTotalSize);
|
||||
}
|
||||
}
|
||||
rv = res->AsyncOpenAt(listener,
|
||||
info,
|
||||
gStartAt,
|
||||
nsnull);
|
||||
id);
|
||||
} else {
|
||||
rv = pChannel->AsyncOpen(listener, // IStreamListener consumer
|
||||
info);
|
||||
@ -732,7 +761,7 @@ main(int argc, char* argv[])
|
||||
{
|
||||
nsresult rv= (nsresult)-1;
|
||||
if (argc < 2) {
|
||||
printf("usage: %s [-verbose] [-file <name>] <url> <url> ... \n", argv[0]);
|
||||
printf("usage: %s [-verbose] [-file <name>] [-resume <startoffset> [-etag <etag>] [-lastmod <lastmod (string)>] [-totalsize <size>] [-proxy <proxy>] [-console] <url> <url> ... \n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -782,6 +811,21 @@ main(int argc, char* argv[])
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PL_strcasecmp(argv[i], "-etag") == 0) {
|
||||
gETag = argv[++i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PL_strcasecmp(argv[i], "-lastmod") == 0) {
|
||||
gLastMod = argv[++i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PL_strcasecmp(argv[i], "-totalsize") == 0) {
|
||||
gTotalSize = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PL_strcasecmp(argv[i], "-proxy") == 0) {
|
||||
SetHttpProxy(argv[++i]);
|
||||
continue;
|
||||
|
Loading…
Reference in New Issue
Block a user