fixes bug 116365 (second attempt) "[RFE] Cache partial documents; automatically

issue byte range requests" r=gagan sr=mscott,rpotts,brendan a=asa
This commit is contained in:
darin%netscape.com 2002-03-26 23:33:19 +00:00
Родитель f5ef8ca045
Коммит edab6546f5
26 изменённых файлов: 398 добавлений и 87 удалений

Просмотреть файл

@ -539,7 +539,11 @@ NS_IMETHODIMP nsMsgDBFolder::GetOfflineFileTransport(nsMsgKey msgKey, PRUint32 *
if (NS_FAILED(rv))
return rv;
rv = fts->CreateTransport(localStore, PR_RDWR | PR_CREATE_FILE, 0664, aFileChannel);
rv = fts->CreateTransport(localStore,
PR_RDWR | PR_CREATE_FILE,
0664,
PR_TRUE,
aFileChannel);
if (NS_SUCCEEDED(rv))
{

Просмотреть файл

@ -234,7 +234,7 @@ nsresult nsMsgProtocol::OpenFileSocket(nsIURI * aURL, PRUint32 aStartPosition, P
if (NS_FAILED(rv)) return rv;
//we are always using this file socket to read data from the mailbox.
rv = fts->CreateTransport(file, PR_RDONLY,
0664, getter_AddRefs(m_transport));
0664, PR_TRUE, getter_AddRefs(m_transport));
m_socketIsOpen = PR_FALSE;
return rv;
@ -880,7 +880,7 @@ nsresult nsMsgFilePostHelper::Init(nsIOutputStream * aOutStream, nsMsgAsyncWrite
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsITransport> transport;
rv = fts->CreateTransport(aFileToPost, PR_RDONLY, 0664, getter_AddRefs(transport));
rv = fts->CreateTransport(aFileToPost, PR_RDONLY, 0664, PR_TRUE, getter_AddRefs(transport));
if (transport)
{
rv = transport->AsyncRead(this, nsnull, 0, PRUint32(-1), 0, getter_AddRefs(mPostFileRequest));

Просмотреть файл

@ -339,7 +339,7 @@ nsJARChannel::AsyncReadJARElement()
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsITransport> jarTransport;
rv = fts->CreateTransportFromStreamIO(this, getter_AddRefs(jarTransport));
rv = fts->CreateTransportFromStreamIO(this, PR_TRUE, getter_AddRefs(jarTransport));
if (NS_FAILED(rv)) return rv;
if (mCallbacks) {

Просмотреть файл

@ -49,7 +49,8 @@ interface nsIFileTransportService : nsISupports
{
nsITransport createTransport(in nsIFile file,
in long ioFlags,
in long perm);
in long perm,
in boolean closeStreamWhenDone);
// This version can be used with an existing input stream to serve
// as a data pump:
@ -60,7 +61,8 @@ interface nsIFileTransportService : nsISupports
in long contentLength,
in boolean closeStreamWhenDone);
nsITransport createTransportFromStreamIO(in nsIStreamIO io);
nsITransport createTransportFromStreamIO(in nsIStreamIO io,
in boolean closeStreamWhenDone);
void dispatchRequest(in nsIRunnable runnable);
void processPendingRequests();

Просмотреть файл

@ -124,6 +124,10 @@ nsFileIO::nsFileIO()
nsFileIO::~nsFileIO()
{
(void)Close(NS_OK);
if (mFD) {
PR_Close(mFD);
mFD = nsnull;
}
#ifdef PR_LOGGING
if (mSpec) nsCRT::free(mSpec);
#endif
@ -185,11 +189,18 @@ nsFileIO::Open()
rv = localFile->OpenNSPRFileDesc(mIOFlags, mPerm, &mFD);
if (NS_FAILED(rv)) {
mFD = nsnull; // just in case
#ifdef PR_LOGGING
nsresult openError = rv;
#endif
// maybe we can't open this because it is a directory...
PRBool isDir;
rv = localFile->IsDirectory(&isDir);
if (NS_SUCCEEDED(rv) && isDir) {
return NS_OK;
}
PR_LOG(gFileIOLog, PR_LOG_DEBUG,
("nsFileIO: OpenNSPRFileDesc failed [rv=%x]\n", openError));
return NS_ERROR_FILE_NOT_FOUND;
}
@ -294,8 +305,10 @@ nsFileIO::GetInputStream(nsIInputStream * *aInputStream)
return rv;
if (isDir) {
if (mFD)
if (mFD) {
PR_Close(mFD);
mFD = nsnull;
}
rv = nsDirectoryIndexStream::Create(mFile, aInputStream);
PR_LOG(gFileIOLog, PR_LOG_DEBUG,
("nsFileIO: opening local dir %s for input (%x)",
@ -307,7 +320,7 @@ nsFileIO::GetInputStream(nsIInputStream * *aInputStream)
if (fileIn == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(fileIn);
rv = fileIn->InitWithFileDescriptor(mFD, mFile, PR_FALSE);
rv = fileIn->InitWithFileDescriptor(mFD, this);
if (NS_SUCCEEDED(rv)) {
#ifdef NS_NO_INPUT_BUFFERING
*aInputStream = fileIn;
@ -351,7 +364,7 @@ nsFileIO::GetOutputStream(nsIOutputStream * *aOutputStream)
if (fileOut == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(fileOut);
rv = fileOut->InitWithFileDescriptor(mFD, mFile);
rv = fileOut->InitWithFileDescriptor(mFD, this);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIOutputStream> bufStr;
#ifdef NS_NO_OUTPUT_BUFFERING
@ -391,22 +404,40 @@ nsFileIO::GetName(nsACString &aName)
nsFileStream::nsFileStream()
: mFD(nsnull)
, mCloseFD(PR_TRUE)
{
NS_INIT_REFCNT();
}
nsFileStream::~nsFileStream()
{
Close();
if (mCloseFD)
Close();
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStream, nsISeekableStream)
nsresult
nsFileStream::InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent)
{
NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
//
// this file stream is dependent on its parent to keep the
// file descriptor valid. an owning reference to the parent
// prevents the file descriptor from going away prematurely.
//
mFD = fd;
mCloseFD = PR_FALSE;
mParent = parent;
return NS_OK;
}
nsresult
nsFileStream::Close()
{
if (mFD) {
PR_Close(mFD);
if (mCloseFD)
PR_Close(mFD);
mFD = nsnull;
}
return NS_OK;
@ -505,6 +536,8 @@ nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
NS_IMETHODIMP
nsFileInputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRBool deleteOnClose)
{
NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
nsresult rv = NS_OK;
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
if (NS_FAILED(rv)) return rv;
@ -516,18 +549,7 @@ nsFileInputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRBool del
PRFileDesc* fd;
rv = localFile->OpenNSPRFileDesc(ioFlags, perm, &fd);
if (NS_FAILED(rv)) return rv;
return InitWithFileDescriptor(fd, file, deleteOnClose);
}
nsresult
nsFileInputStream::InitWithFileDescriptor(PRFileDesc* fd, nsIFile* file, PRBool deleteOnClose)
{
NS_ASSERTION(mFD == nsnull, "already inited");
if (mFD || !fd)
return NS_ERROR_FAILURE;
mLineBuffer = nsnull;
mFD = fd;
if (deleteOnClose) {
@ -609,6 +631,7 @@ nsFileInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint
if (NS_SUCCEEDED(rv)) {
rv = writer(this, closure, readBuf, 0, nBytes, _retval);
NS_ASSERTION(NS_SUCCEEDED(rv) ? nBytes == *_retval : PR_TRUE, "Didn't write all Data.");
// XXX this assertion is invalid!
}
nsMemory::Free(readBuf);
@ -647,6 +670,8 @@ nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
NS_IMETHODIMP
nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm)
{
NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
nsresult rv;
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
if (NS_FAILED(rv)) return rv;
@ -654,20 +679,11 @@ nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm)
ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
if (perm <= 0)
perm = 0664;
PRFileDesc* fd;
rv = localFile->OpenNSPRFileDesc(ioFlags, perm, &fd);
if (NS_FAILED(rv)) return rv;
return InitWithFileDescriptor(fd, file);
}
nsresult
nsFileOutputStream::InitWithFileDescriptor(PRFileDesc* fd, nsIFile* file)
{
NS_ASSERTION(mFD == nsnull, "already inited");
if (mFD || !fd)
return NS_ERROR_FAILURE;
mFD = fd;
return NS_OK;
}

Просмотреть файл

@ -88,9 +88,13 @@ public:
virtual ~nsFileStream();
nsresult Close();
nsresult InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent);
protected:
PRFileDesc* mFD;
PRFileDesc* mFD;
nsCOMPtr<nsISupports> mParent; // strong reference to parent nsFileIO,
// which ensures mFD remains valid.
PRBool mCloseFD;
};
////////////////////////////////////////////////////////////////////////////////
@ -116,7 +120,7 @@ public:
static NS_METHOD
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
nsresult InitWithFileDescriptor(PRFileDesc* fd, nsIFile* file, PRBool deleteOnClose);
protected:
nsLineBuffer *mLineBuffer;
nsCOMPtr<nsIFile> mFileToDelete;
@ -137,7 +141,6 @@ public:
static NS_METHOD
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
nsresult InitWithFileDescriptor(PRFileDesc* fd, nsIFile* file);
};
////////////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -234,14 +234,18 @@ nsFileTransport::nsFileTransport()
}
nsresult
nsFileTransport::Init(nsFileTransportService *aService, nsIFile* file, PRInt32 ioFlags, PRInt32 perm)
nsFileTransport::Init(nsFileTransportService *aService,
nsIFile *file,
PRInt32 ioFlags,
PRInt32 perm,
PRBool closeStreamWhenDone)
{
nsresult rv;
nsCOMPtr<nsIFileIO> io;
rv = NS_NewFileIO(getter_AddRefs(io), file, ioFlags, perm);
if (NS_FAILED(rv)) return rv;
return Init(aService, io);
return Init(aService, io, closeStreamWhenDone);
}
nsresult
@ -258,12 +262,13 @@ nsFileTransport::Init(nsFileTransportService *aService,
rv = NS_NewInputStreamIO(getter_AddRefs(io), name, inStr,
contentType, contentCharset, contentLength);
if (NS_FAILED(rv)) return rv;
mCloseStreamWhenDone = closeStreamWhenDone;
return Init(aService, io);
return Init(aService, io, closeStreamWhenDone);
}
nsresult
nsFileTransport::Init(nsFileTransportService *aService, nsIStreamIO* io)
nsFileTransport::Init(nsFileTransportService *aService,
nsIStreamIO* io,
PRBool closeStreamWhenDone)
{
nsresult rv = NS_OK;
if (mLock == nsnull) {
@ -275,6 +280,8 @@ nsFileTransport::Init(nsFileTransportService *aService, nsIStreamIO* io)
rv = mStreamIO->GetName(mStreamName);
NS_ASSERTION(NS_SUCCEEDED(rv), "GetName failed");
mCloseStreamWhenDone = closeStreamWhenDone;
NS_ADDREF(mService = aService);
PR_AtomicIncrement(&mService->mTotalTransports);

Просмотреть файл

@ -88,14 +88,17 @@ public:
nsresult Init(nsFileTransportService *aService, nsIFile* file,
PRInt32 ioFlags,
PRInt32 perm);
PRInt32 perm,
PRBool closeStreamWhenDone);
nsresult Init(nsFileTransportService *aService, const nsACString &name,
nsIInputStream *fromStream,
const nsACString &contentType,
const nsACString &contentCharset,
PRInt32 contentLength,
PRBool closeStreamWhenDone);
nsresult Init(nsFileTransportService *aService, nsIStreamIO* io);
nsresult Init(nsFileTransportService *aService,
nsIStreamIO* io,
PRBool closeStreamWhenDone);
void Process(nsIProgressEventSink *);
void DoClose(void);

Просмотреть файл

@ -127,6 +127,7 @@ NS_IMETHODIMP
nsFileTransportService::CreateTransport(nsIFile* file,
PRInt32 ioFlags,
PRInt32 perm,
PRBool closeStreamWhenDone,
nsITransport** result)
{
nsresult rv;
@ -134,7 +135,7 @@ nsFileTransportService::CreateTransport(nsIFile* file,
if (trans == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(trans);
rv = trans->Init(this, file, ioFlags, perm);
rv = trans->Init(this, file, ioFlags, perm, closeStreamWhenDone);
if (NS_FAILED(rv)) {
NS_RELEASE(trans);
return rv;
@ -170,6 +171,7 @@ nsFileTransportService::CreateTransportFromStream(const nsACString &name,
NS_IMETHODIMP
nsFileTransportService::CreateTransportFromStreamIO(nsIStreamIO *io,
PRBool closeStreamWhenDone,
nsITransport **result)
{
nsresult rv;
@ -177,7 +179,7 @@ nsFileTransportService::CreateTransportFromStreamIO(nsIStreamIO *io,
if (trans == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(trans);
rv = trans->Init(this, io);
rv = trans->Init(this, io, closeStreamWhenDone);
if (NS_FAILED(rv)) {
NS_RELEASE(trans);
return rv;

Просмотреть файл

@ -311,9 +311,10 @@ nsStreamIOChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
do_GetService(kFileTransportServiceCID, &rv);
if (NS_FAILED(rv)) goto done;
rv = fts->CreateTransportFromStreamIO(mStreamIO, getter_AddRefs(mFileTransport));
if (NS_FAILED(rv)) goto done;
}
rv = fts->CreateTransportFromStreamIO(mStreamIO, PR_TRUE,
getter_AddRefs(mFileTransport));
if (NS_FAILED(rv)) goto done;
}
// Hook up the notification callbacks InterfaceRequestor...
{

2
netwerk/cache/src/nsDiskCacheDevice.cpp поставляемый
Просмотреть файл

@ -580,7 +580,7 @@ nsDiskCacheDevice::GetTransportForEntry(nsCacheEntry * entry,
break;
}
rv = gFileTransportService->CreateTransport(file, ioFlags, PR_IRUSR | PR_IWUSR, result);
rv = gFileTransportService->CreateTransport(file, ioFlags, PR_IRUSR | PR_IWUSR, PR_FALSE, result);
return rv;
}

Просмотреть файл

@ -308,7 +308,11 @@ nsXMLMIMEDataSource::Serialize() {
do_GetService(kFileTransportServiceCID, &rv) ;
if(NS_FAILED(rv)) return rv ;
rv = fts->CreateTransport(mFile, PR_WRONLY|PR_CREATE_FILE, PR_IRWXU, getter_AddRefs(transport)) ;
rv = fts->CreateTransport(mFile,
PR_WRONLY|PR_CREATE_FILE,
PR_IRWXU,
PR_TRUE,
getter_AddRefs(transport)) ;
if(NS_FAILED(rv))
return rv ;
@ -726,7 +730,11 @@ nsXMLMIMEDataSource::InitFromFile( nsIFile* aFile )
do_GetService(kFileTransportServiceCID, &rv) ;
if(NS_FAILED(rv)) return rv ;
// Made second parameter 0 since I really don't know what it is used for
rv = fts->CreateTransport(aFile, PR_RDONLY, PR_IRWXU, getter_AddRefs(transport)) ;
rv = fts->CreateTransport(aFile,
PR_RDONLY,
PR_IRWXU,
PR_TRUE,
getter_AddRefs(transport)) ;
if(NS_FAILED(rv))
return rv ;

Просмотреть файл

@ -231,7 +231,7 @@ nsFileChannel::EnsureTransport()
do_GetService(kFileTransportServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = fts->CreateTransport(mFile, mIOFlags, mPerm,
rv = fts->CreateTransport(mFile, mIOFlags, mPerm, PR_TRUE,
getter_AddRefs(mFileTransport));
if (NS_FAILED(rv)) return rv;

Просмотреть файл

@ -59,6 +59,7 @@ nsHttpChannel::nsHttpChannel()
, mConnectionInfo(nsnull)
, mLoadFlags(LOAD_NORMAL)
, mStatus(NS_OK)
, mLogicalOffset(0)
, mCapabilities(0)
, mReferrerType(REFERRER_NONE)
, mCachedResponseHead(nsnull)
@ -70,6 +71,7 @@ nsHttpChannel::nsHttpChannel()
, mApplyConversion(PR_TRUE)
, mFromCacheOnly(PR_FALSE)
, mCachedContentIsValid(PR_FALSE)
, mCachedContentIsPartial(PR_FALSE)
, mResponseHeadersModified(PR_FALSE)
, mCanceled(PR_FALSE)
, mUploadStreamHasHeaders(PR_FALSE)
@ -389,7 +391,7 @@ nsHttpChannel::SetupTransaction()
if (mConnectionInfo->UsingSSL() || !mConnectionInfo->UsingHttpProxy()) {
rv = mURI->GetPath(path);
if (NS_FAILED(rv)) return rv;
// path may contain UTF-8 characters, so ensure that their escaped.
// path may contain UTF-8 characters, so ensure that they're escaped.
if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf))
requestURI = buf.get();
else
@ -489,10 +491,15 @@ nsHttpChannel::ProcessResponse()
switch (httpStatus) {
case 200:
case 203:
case 206:
// these can normally be cached
rv = ProcessNormal();
break;
case 206:
if (mCachedContentIsPartial) // an internal byte range request...
rv = ProcessPartialContent();
else
rv = ProcessNormal();
break;
case 300:
case 301:
case 302:
@ -596,6 +603,160 @@ nsHttpChannel::ProcessNormal()
return rv;
}
//-----------------------------------------------------------------------------
// nsHttpChannel <byte-range>
//-----------------------------------------------------------------------------
nsresult
nsHttpChannel::SetupByteRangeRequest(PRUint32 partialLen)
{
// cached content has been found to be partial, add necessary request
// headers to complete cache entry.
// use strongest validator available...
const char *val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
if (!val)
val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified);
if (!val) {
// if we hit this code it means mCachedResponseHead->IsResumable() is
// either broken or not being called.
NS_NOTREACHED("no cache validator");
return NS_ERROR_FAILURE;
}
char buf[32];
PR_snprintf(buf, sizeof(buf), "bytes=%u-", partialLen);
mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
mRequestHead.SetHeader(nsHttp::If_Range, nsDependentCString(val));
return NS_OK;
}
nsresult
nsHttpChannel::ProcessPartialContent()
{
nsresult rv;
// ok, we've just received a 206
//
// we need to stream whatever data is in the cache out first, and then
// pick up whatever data is on the wire, writing it into the cache.
LOG(("nsHttpChannel::ProcessPartialContent [this=%x]\n", this));
NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
// suspend the current transaction (may still get an OnDataAvailable)
rv = mTransaction->Suspend();
if (NS_FAILED(rv)) return rv;
// merge any new headers with the cached response headers
rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
if (NS_FAILED(rv)) return rv;
// update the cached response head
nsCAutoString head;
mCachedResponseHead->Flatten(head, PR_TRUE);
rv = mCacheEntry->SetMetaDataElement("response-head", head.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;
// the cached content is valid, although incomplete.
mCachedContentIsValid = PR_TRUE;
return ReadFromCache();
}
nsresult
nsHttpChannel::BufferPartialContent(nsIInputStream *input, PRUint32 count)
{
nsresult rv;
LOG(("nsHttpChannel::BufferPartialContent [this=%x count=%u]\n", this, count));
if (!mBufferOut) {
LOG(("creating pipe...\n"));
//
// create a pipe for buffering network data (the size of this
// pipe must be equal to or greater than the size of the pipe
// used to proxy data from the socket transport thread).
//
rv = NS_NewPipe(getter_AddRefs(mBufferIn),
getter_AddRefs(mBufferOut),
NS_HTTP_SEGMENT_SIZE,
NS_HTTP_BUFFER_SIZE,
PR_TRUE,
PR_TRUE);
if (NS_FAILED(rv)) return rv;
}
PRUint32 bytesWritten = 0;
rv = mBufferOut->WriteFrom(input, count, &bytesWritten);
if (NS_FAILED(rv) || (bytesWritten != count)) {
LOG(("writing to pipe failed [rv=%s bytes-written=%u]\n", rv, bytesWritten));
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
nsresult
nsHttpChannel::OnDoneReadingPartialCacheEntry(PRBool *streamDone)
{
nsresult rv;
LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%x]", this));
// by default, assume we would have streamed all data or failed...
*streamDone = PR_TRUE;
// setup cache listener to append to cache entry
PRUint32 size;
rv = mCacheEntry->GetDataSize(&size);
if (NS_FAILED(rv)) return rv;
rv = InstallCacheListener(size);
if (NS_FAILED(rv)) return rv;
// process any buffered data
if (mBufferIn) {
PRUint32 avail;
rv = mBufferIn->Available(&avail);
if (NS_FAILED(rv)) return rv;
rv = mListener->OnDataAvailable(this, mListenerContext, mBufferIn, size, avail);
if (NS_FAILED(rv)) return rv;
// done with the pipe
mBufferIn = 0;
mBufferOut = 0;
}
// need to track the logical offset of the data being sent to our listener
mLogicalOffset = size;
// we're now completing the cached content, so we can clear this flag.
// this puts us in the state of a regular download.
mCachedContentIsPartial = PR_FALSE;
// resume the transaction if it exists, otherwise the pipe contained the
// remaining part of the document and we've now streamed all of the data.
if (mTransaction) {
rv = mTransaction->Resume();
if (NS_SUCCEEDED(rv))
*streamDone = PR_FALSE;
}
return rv;
}
//-----------------------------------------------------------------------------
// nsHttpChannel <cache>
//-----------------------------------------------------------------------------
@ -664,7 +825,10 @@ nsHttpChannel::OpenCacheEntry(PRBool *delayed)
return NS_OK;
}
else if (mRequestHead.PeekHeader(nsHttp::Range)) {
// we don't support caching for byte range requests
// we don't support caching for byte range requests initiated
// by our clients.
// XXX perhaps we could munge their byte range into the cache
// key to make caching sort'a work.
return NS_OK;
}
@ -850,18 +1014,24 @@ nsHttpChannel::CheckCache()
}
// If the cached content-length is set and it does not match the data size
// of the cached content, then refetch.
PRInt32 contentLength = mCachedResponseHead->ContentLength();
if (contentLength != -1) {
// of the cached content, then the cached response is partial...
// either we need to issue a byte range request or we need to refetch the
// entire document.
PRUint32 contentLength = (PRUint32) mCachedResponseHead->ContentLength();
if (contentLength != PRUint32(-1)) {
PRUint32 size;
rv = mCacheEntry->GetDataSize(&size);
if (NS_FAILED(rv)) return rv;
if (size != (PRUint32) contentLength) {
if (size != contentLength) {
LOG(("Cached data size does not match the Content-Length header "
"[content-length=%u size=%u]\n", contentLength, size));
// looks like a partial entry.
// XXX must re-fetch until we learn how to do byte range requests.
if ((size < contentLength) && mCachedResponseHead->IsResumable()) {
// looks like a partial entry.
rv = SetupByteRangeRequest(size);
if (NS_FAILED(rv)) return rv;
mCachedContentIsPartial = PR_TRUE;
}
return NS_OK;
}
}
@ -1002,7 +1172,7 @@ nsHttpChannel::ReadFromCache()
if (!mSecurityInfo)
mCacheEntry->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
if (mCacheAccess & nsICache::ACCESS_WRITE) {
if ((mCacheAccess & nsICache::ACCESS_WRITE) && !mCachedContentIsPartial) {
// 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
// valid in order to allow others access to this cache entry.
@ -1150,17 +1320,22 @@ nsHttpChannel::FinalizeCacheEntry()
// Open an output stream to the cache entry and insert a listener tee into
// the chain of response listeners.
nsresult
nsHttpChannel::InstallCacheListener()
nsHttpChannel::InstallCacheListener(PRUint32 offset)
{
nsresult rv;
LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
rv = mCacheEntry->GetTransport(getter_AddRefs(mCacheTransport));
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(mCacheEntry, "no cache entry");
NS_ASSERTION(mListener, "no listener");
if (!mCacheTransport) {
rv = mCacheEntry->GetTransport(getter_AddRefs(mCacheTransport));
if (NS_FAILED(rv)) return rv;
}
nsCOMPtr<nsIOutputStream> out;
rv = mCacheTransport->OpenOutputStream(0, PRUint32(-1), 0, getter_AddRefs(out));
rv = mCacheTransport->OpenOutputStream(offset, PRUint32(-1), 0, getter_AddRefs(out));
if (NS_FAILED(rv)) return rv;
// XXX disk cache does not support overlapped i/o yet
@ -2536,14 +2711,17 @@ nsHttpChannel::GetContentEncodings(nsISimpleEnumerator** aEncodings)
NS_IMETHODIMP
nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
// capture the request's status, so our consumers will know ASAP of any
// connection failures, etc - bug 93581
request->GetStatus(&mStatus);
if (!(mCanceled || NS_FAILED(mStatus))) {
// capture the request's status, so our consumers will know ASAP of any
// connection failures, etc - bug 93581
request->GetStatus(&mStatus);
}
LOG(("nsHttpChannel::OnStartRequest [this=%x request=%x status=%x]\n",
this, request, mStatus));
if (mTransaction) {
// don't enter this block if we're reading from the cache...
if (NS_SUCCEEDED(mStatus) && !mCacheReadRequest && mTransaction) {
// grab the security info from the connection object; the transaction
// is guaranteed to own a reference to the connection.
mSecurityInfo = mTransaction->SecurityInfo();
@ -2578,6 +2756,23 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
mPrevTransaction = nsnull;
}
if (mCachedContentIsPartial && NS_SUCCEEDED(status)) {
if (request == mTransaction) {
// byte-range transaction finished before we got around to streaming it.
NS_ASSERTION(mCacheReadRequest, "should be reading from cache right now");
NS_RELEASE(mTransaction);
mTransaction = nsnull;
return NS_OK;
}
if (request == mCacheReadRequest) {
PRBool streamDone;
status = OnDoneReadingPartialCacheEntry(&streamDone);
if (NS_SUCCEEDED(status) && !streamDone)
return status;
// otherwise, fall through and fire OnStopRequest...
}
}
// if the request is for something we no longer reference, then simply
// drop this event.
if ((request != mTransaction) && (request != mCacheReadRequest))
@ -2586,8 +2781,11 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
mIsPending = PR_FALSE;
mStatus = status;
// at this point, we're done with the transaction
PRBool isPartial = PR_FALSE;
if (mTransaction) {
// find out if the transaction ran to completion...
isPartial = !mTransaction->ResponseIsComplete();
// at this point, we're done with the transaction
NS_RELEASE(mTransaction);
mTransaction = nsnull;
}
@ -2609,12 +2807,21 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
}
if (mCacheEntry) {
// we don't want to discard the cache entry if canceled and
// reading from the cache.
if (mCanceled && (request == mCacheReadRequest))
CloseCacheEntry(NS_OK);
else
CloseCacheEntry(status);
nsresult closeStatus = status;
if (mCanceled) {
// we don't want to discard the cache entry if canceled and
// reading from the cache.
if (request == mCacheReadRequest)
closeStatus = NS_OK;
// we also don't want to discard the cache entry if the
// server supports byte range requests, because we could always
// complete the download at a later time.
else if (isPartial && mResponseHead && mResponseHead->IsResumable()) {
LOG(("keeping partial response that is resumable!\n"));
closeStatus = NS_OK;
}
}
CloseCacheEntry(closeStatus);
}
if (mLoadGroup)
@ -2635,6 +2842,11 @@ nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
LOG(("nsHttpChannel::OnDataAvailable [this=%x request=%x offset=%u count=%u]\n",
this, request, offset, count));
if (mCachedContentIsPartial && (request == mTransaction)) {
// XXX we can eliminate this buffer once bug 93055 is resolved.
return BufferPartialContent(input, count);
}
// if the request is for something we no longer reference, then simply
// drop this event.
if ((request != mTransaction) && (request != mCacheReadRequest)) {
@ -2642,8 +2854,21 @@ nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
return NS_BASE_STREAM_CLOSED;
}
if (mListener)
return mListener->OnDataAvailable(this, mListenerContext, input, offset, count);
if (mListener) {
//
// we have to manually keep the logical offset of the stream up-to-date.
// we cannot depend soley on the offset provided, since we may have
// already streamed some data from another source (see, for example,
// OnDoneReadingPartialCacheEntry).
//
nsresult rv = mListener->OnDataAvailable(this,
mListenerContext,
input,
mLogicalOffset,
count);
mLogicalOffset += count;
return rv;
}
return NS_BASE_STREAM_CLOSED;
}

Просмотреть файл

@ -40,10 +40,12 @@
#include "nsICacheListener.h"
#include "nsITransport.h"
#include "nsIUploadChannel.h"
#include "nsISimpleEnumerator.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsCOMPtr.h"
#include "nsXPIDLString.h"
#include "nsHttpConnection.h"
#include "nsISimpleEnumerator.h"
class nsHttpTransaction;
class nsHttpResponseHead;
@ -106,7 +108,13 @@ private:
nsresult InitCacheEntry();
nsresult StoreAuthorizationMetaData();
nsresult FinalizeCacheEntry();
nsresult InstallCacheListener();
nsresult InstallCacheListener(PRUint32 offset = 0);
// byte range request specific methods
nsresult SetupByteRangeRequest(PRUint32 partialLen);
nsresult ProcessPartialContent();
nsresult BufferPartialContent(nsIInputStream *, PRUint32 count);
nsresult OnDoneReadingPartialCacheEntry(PRBool *streamDone);
// auth specific methods
nsresult GetCredentials(const char *challenges, PRBool proxyAuth, nsAFlatCString &creds);
@ -149,6 +157,7 @@ private:
PRUint32 mLoadFlags;
PRUint32 mStatus;
PRUint32 mLogicalOffset;
PRUint8 mCapabilities;
PRUint8 mReferrerType;
@ -161,6 +170,10 @@ private:
PRUint32 mPostID;
PRUint32 mRequestTime;
// byte-range specific data
nsCOMPtr<nsIInputStream> mBufferIn;
nsCOMPtr<nsIOutputStream> mBufferOut;
// auth specific data
nsXPIDLString mUser;
nsXPIDLString mPass;
@ -174,6 +187,7 @@ private:
PRPackedBool mApplyConversion;
PRPackedBool mFromCacheOnly;
PRPackedBool mCachedContentIsValid;
PRPackedBool mCachedContentIsPartial;
PRPackedBool mResponseHeadersModified;
PRPackedBool mCanceled;
PRPackedBool mUploadStreamHasHeaders;

Просмотреть файл

@ -363,6 +363,17 @@ nsHttpResponseHead::MustValidateIfExpired()
return val && PL_strcasestr(val, "must-revalidate");
}
PRBool
nsHttpResponseHead::IsResumable()
{
// even though some HTTP/1.0 servers may support byte range requests, we're not
// going to bother with them, since those servers wouldn't understand If-Range.
return mVersion >= NS_HTTP_VERSION_1_1 &&
PeekHeader(nsHttp::Content_Length) &&
(PeekHeader(nsHttp::ETag) || PeekHeader(nsHttp::Last_Modified)) &&
PL_strcasestr(PeekHeader(nsHttp::Accept_Ranges), "bytes");
}
PRBool
nsHttpResponseHead::ExpiresInPast()
{

Просмотреть файл

@ -87,6 +87,9 @@ public:
PRBool MustValidate();
PRBool MustValidateIfExpired();
// returns true if the server appears to support byte range requests.
PRBool IsResumable();
// returns true if the Expires header has a value in the past relative to the
// value of the Date header.
PRBool ExpiresInPast();

Просмотреть файл

@ -95,6 +95,7 @@ nsHttpTransaction::nsHttpTransaction(nsIStreamListener *listener,
, mCapabilities(caps)
, mHaveStatusLine(PR_FALSE)
, mHaveAllHeaders(PR_FALSE)
, mResponseIsComplete(PR_FALSE)
, mFiredOnStart(PR_FALSE)
, mNoContent(PR_FALSE)
, mPrematureEOF(PR_FALSE)
@ -617,6 +618,7 @@ nsHttpTransaction::HandleContent(char *buf,
// OnTransactionComplete is fired only once!
PRInt32 priorVal = PR_AtomicSet(&mTransactionDone, 1);
if (priorVal == 0) {
mResponseIsComplete = PR_TRUE;
// let the connection know that we are done with it; this should
// result in OnStopTransaction being fired.
return mConnection->OnTransactionComplete(this, NS_OK);

Просмотреть файл

@ -78,6 +78,9 @@ public:
// will drop any reference to the response headers after this call.
nsHttpResponseHead *TakeResponseHead();
// Called to find out if the transaction generated a complete response.
PRBool ResponseIsComplete() { return mResponseIsComplete; }
// nsAHttpTransaction methods:
void SetConnection(nsAHttpConnection *conn) { NS_IF_ADDREF(mConnection = conn); }
void SetSecurityInfo(nsISupports *info) { mSecurityInfo = info; }
@ -133,6 +136,7 @@ private:
PRPackedBool mHaveStatusLine;
PRPackedBool mHaveAllHeaders;
PRPackedBool mResponseIsComplete;
PRPackedBool mFiredOnStart;
PRPackedBool mNoContent; // expecting an empty entity body?
PRPackedBool mPrematureEOF;

Просмотреть файл

@ -339,7 +339,7 @@ nsJARChannel::AsyncReadJARElement()
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsITransport> jarTransport;
rv = fts->CreateTransportFromStreamIO(this, getter_AddRefs(jarTransport));
rv = fts->CreateTransportFromStreamIO(this, PR_TRUE, getter_AddRefs(jarTransport));
if (NS_FAILED(rv)) return rv;
if (mCallbacks) {

Просмотреть файл

@ -366,7 +366,7 @@ ParallelReadTest(char* dirName, nsIFileTransportService* fts)
NS_ASSERTION(listener, "QI failed");
nsITransport* trans;
rv = fts->CreateTransport(file, PR_RDONLY, 0, &trans);
rv = fts->CreateTransport(file, PR_RDONLY, 0, PR_TRUE, &trans);
NS_ASSERTION(NS_SUCCEEDED(rv), "create failed");
nsCOMPtr<nsIRequest> request;
rv = trans->AsyncRead(nsnull, listener, 0, -1, 0, getter_AddRefs(request));

Просмотреть файл

@ -204,7 +204,7 @@ TestAsyncRead(const char* fileName, PRUint32 offset, PRInt32 length)
nsCOMPtr<nsILocalFile> file;
rv = NS_NewLocalFile(fileName, PR_FALSE, getter_AddRefs(file));
if (NS_FAILED(rv)) return rv;
rv = fts->CreateTransport(file, PR_RDONLY, 0, &fileTrans);
rv = fts->CreateTransport(file, PR_RDONLY, 0, PR_TRUE, &fileTrans);
if (NS_FAILED(rv)) return rv;
MyListener* listener = new MyListener();
@ -260,7 +260,7 @@ TestAsyncWrite(const char* fileName, PRUint32 offset, PRInt32 length)
if (NS_FAILED(rv)) return rv;
rv = fts->CreateTransport(file,
PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
0664, &fileTrans);
0664, PR_TRUE, &fileTrans);
if (NS_FAILED(rv)) return rv;
MyListener* listener = new MyListener();

Просмотреть файл

@ -133,7 +133,7 @@ TestSyncWrite(char* filename, PRUint32 startPosition, PRInt32 length)
if (NS_FAILED(rv)) return rv ;
nsCOMPtr<nsITransport> transport;
rv = fts->CreateTransport(fs, PR_RDWR | PR_CREATE_FILE, 0664,
rv = fts->CreateTransport(fs, PR_RDWR | PR_CREATE_FILE, 0664, PR_TRUE,
getter_AddRefs(transport)) ;
if (NS_FAILED(rv)) return rv ;

Просмотреть файл

@ -1030,10 +1030,14 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel * aChannel)
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsITransport> fileTransport;
rv = fts->CreateTransport(mTempFile, PR_WRONLY | PR_CREATE_FILE, 0600, getter_AddRefs(fileTransport));
rv = fts->CreateTransport(mTempFile,
PR_WRONLY | PR_CREATE_FILE,
0600,
PR_TRUE,
getter_AddRefs(fileTransport));
if (NS_FAILED(rv)) return rv;
rv = fileTransport->OpenOutputStream(0, -1, 0, getter_AddRefs(mOutStream));
rv = fileTransport->OpenOutputStream(0, PRUint32(-1), 0, getter_AddRefs(mOutStream));
#ifdef XP_MAC
nsXPIDLCString contentType;

Просмотреть файл

@ -168,6 +168,7 @@ nsStreamXferOp::Start( void ) {
rv = fts->CreateTransport( mOutputFile,
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0664,
PR_TRUE,
getter_AddRefs( mOutputTransport ) );
if ( NS_SUCCEEDED( rv ) ) {

Просмотреть файл

@ -777,6 +777,7 @@ nsXPInstallManager::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
rv = fts->CreateTransport(mItem->mFile,
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0664,
PR_TRUE,
getter_AddRefs( outTransport));
if (NS_SUCCEEDED(rv))