зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1397635 - Support for non-seekable stream in HTTP connection, r=bagder
This commit is contained in:
Родитель
d8c84ef602
Коммит
3ccb9d2674
|
@ -0,0 +1,332 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PartiallySeekableInputStream.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsStreamUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ADDREF(PartiallySeekableInputStream);
|
||||
NS_IMPL_RELEASE(PartiallySeekableInputStream);
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(PartiallySeekableInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
|
||||
mWeakCloneableInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
|
||||
mWeakIPCSerializableInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
|
||||
mWeakAsyncInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
|
||||
mWeakAsyncInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
PartiallySeekableInputStream::PartiallySeekableInputStream(nsIInputStream* aInputStream,
|
||||
uint64_t aBufferSize)
|
||||
: mInputStream(aInputStream)
|
||||
, mWeakCloneableInputStream(nullptr)
|
||||
, mWeakIPCSerializableInputStream(nullptr)
|
||||
, mWeakAsyncInputStream(nullptr)
|
||||
, mBufferSize(aBufferSize)
|
||||
, mPos(0)
|
||||
, mClosed(false)
|
||||
{
|
||||
MOZ_ASSERT(aInputStream);
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aInputStream);
|
||||
MOZ_ASSERT(!seekableStream);
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsICloneableInputStream> cloneableStream =
|
||||
do_QueryInterface(aInputStream);
|
||||
if (cloneableStream && SameCOMIdentity(aInputStream, cloneableStream)) {
|
||||
mWeakCloneableInputStream = cloneableStream;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
|
||||
do_QueryInterface(aInputStream);
|
||||
if (serializableStream &&
|
||||
SameCOMIdentity(aInputStream, serializableStream)) {
|
||||
mWeakIPCSerializableInputStream = serializableStream;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
|
||||
do_QueryInterface(aInputStream);
|
||||
if (asyncInputStream && SameCOMIdentity(aInputStream, asyncInputStream)) {
|
||||
mWeakAsyncInputStream = asyncInputStream;
|
||||
}
|
||||
}
|
||||
|
||||
PartiallySeekableInputStream::~PartiallySeekableInputStream()
|
||||
{}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::Close()
|
||||
{
|
||||
mInputStream->Close();
|
||||
mCachedBuffer.Clear();
|
||||
mPos = 0;
|
||||
mClosed = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIInputStream interface
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::Available(uint64_t* aLength)
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
nsresult rv = mInputStream->Available(aLength);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mPos < mCachedBuffer.Length()) {
|
||||
*aLength += mCachedBuffer.Length() - mPos;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::Read(char* aBuffer, uint32_t aCount,
|
||||
uint32_t* aReadCount)
|
||||
{
|
||||
*aReadCount = 0;
|
||||
|
||||
if (mClosed) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t byteRead = 0;
|
||||
|
||||
if (mPos < mCachedBuffer.Length()) {
|
||||
// We are reading from the cached buffer.
|
||||
byteRead = XPCOM_MIN(mCachedBuffer.Length() - mPos, (uint64_t)aCount);
|
||||
memcpy(aBuffer, mCachedBuffer.Elements() + mPos, byteRead);
|
||||
*aReadCount = byteRead;
|
||||
mPos += byteRead;
|
||||
}
|
||||
|
||||
if (byteRead < aCount) {
|
||||
MOZ_ASSERT(mPos >= mCachedBuffer.Length());
|
||||
MOZ_ASSERT_IF(mPos > mCachedBuffer.Length(),
|
||||
mCachedBuffer.Length() == mBufferSize);
|
||||
|
||||
// We can read from the stream.
|
||||
uint32_t byteWritten;
|
||||
nsresult rv = mInputStream->Read(aBuffer + byteRead, aCount - byteRead,
|
||||
&byteWritten);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || byteWritten == 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*aReadCount += byteWritten;
|
||||
|
||||
// Maybe we have to cache something.
|
||||
if (mPos < mBufferSize) {
|
||||
uint32_t size = XPCOM_MIN(mPos + byteWritten, mBufferSize);
|
||||
mCachedBuffer.SetLength(size);
|
||||
memcpy(mCachedBuffer.Elements() + mPos, aBuffer + byteRead, size - mPos);
|
||||
}
|
||||
|
||||
mPos += byteWritten;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t *aResult)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::IsNonBlocking(bool* aNonBlocking)
|
||||
{
|
||||
return mInputStream->IsNonBlocking(aNonBlocking);
|
||||
}
|
||||
|
||||
// nsICloneableInputStream interface
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::GetCloneable(bool* aCloneable)
|
||||
{
|
||||
NS_ENSURE_STATE(mWeakCloneableInputStream);
|
||||
|
||||
*aCloneable = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::Clone(nsIInputStream** aResult)
|
||||
{
|
||||
NS_ENSURE_STATE(mWeakCloneableInputStream);
|
||||
|
||||
nsCOMPtr<nsIInputStream> clonedStream;
|
||||
nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream =
|
||||
new PartiallySeekableInputStream(clonedStream, mBufferSize);
|
||||
|
||||
stream.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAsyncInputStream interface
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::CloseWithStatus(nsresult aStatus)
|
||||
{
|
||||
NS_ENSURE_STATE(mWeakAsyncInputStream);
|
||||
|
||||
return mWeakAsyncInputStream->CloseWithStatus(aStatus);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
|
||||
uint32_t aFlags,
|
||||
uint32_t aRequestedCount,
|
||||
nsIEventTarget* aEventTarget)
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
NS_ENSURE_STATE(mWeakAsyncInputStream);
|
||||
|
||||
if (mAsyncWaitCallback && aCallback) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mAsyncWaitCallback = aCallback;
|
||||
|
||||
if (!mAsyncWaitCallback) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mWeakAsyncInputStream->AsyncWait(this, aFlags, aRequestedCount,
|
||||
aEventTarget);
|
||||
}
|
||||
|
||||
// nsIInputStreamCallback
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(mWeakAsyncInputStream);
|
||||
MOZ_ASSERT(mWeakAsyncInputStream == aStream);
|
||||
|
||||
// We have been canceled in the meanwhile.
|
||||
if (!mAsyncWaitCallback) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> callback = mAsyncWaitCallback;
|
||||
|
||||
mAsyncWaitCallback = nullptr;
|
||||
|
||||
return callback->OnInputStreamReady(this);
|
||||
}
|
||||
|
||||
// nsIIPCSerializableInputStream
|
||||
|
||||
void
|
||||
PartiallySeekableInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors)
|
||||
{
|
||||
MOZ_ASSERT(mWeakIPCSerializableInputStream);
|
||||
mozilla::ipc::InputStreamHelper::SerializeInputStream(mInputStream, aParams,
|
||||
aFileDescriptors);
|
||||
}
|
||||
|
||||
bool
|
||||
PartiallySeekableInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
|
||||
const FileDescriptorArray& aFileDescriptors)
|
||||
{
|
||||
MOZ_CRASH("This method should never be called!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::Maybe<uint64_t>
|
||||
PartiallySeekableInputStream::ExpectedSerializedLength()
|
||||
{
|
||||
if (!mWeakIPCSerializableInputStream) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
|
||||
return mWeakIPCSerializableInputStream->ExpectedSerializedLength();
|
||||
}
|
||||
|
||||
// nsISeekableStream
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::Seek(int32_t aWhence, int64_t aOffset)
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
int64_t offset;
|
||||
|
||||
switch (aWhence) {
|
||||
case NS_SEEK_SET:
|
||||
offset = aOffset;
|
||||
break;
|
||||
case NS_SEEK_CUR:
|
||||
offset = mPos + aOffset;
|
||||
break;
|
||||
case NS_SEEK_END: {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
default:
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
if ((uint64_t)offset >= mCachedBuffer.Length() || mPos > mBufferSize) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
mPos = offset;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::Tell(int64_t *aResult)
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
*aResult = mPos;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartiallySeekableInputStream::SetEOF()
|
||||
{
|
||||
return Close();
|
||||
}
|
||||
|
||||
} // net namespace
|
||||
} // mozilla namespace
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PartiallySeekableInputStream_h
|
||||
#define PartiallySeekableInputStream_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsICloneableInputStream.h"
|
||||
#include "nsIIPCSerializableInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// A wrapper for making a stream seekable for the first |aBufferSize| bytes.
|
||||
|
||||
class PartiallySeekableInputStream final : public nsISeekableStream
|
||||
, public nsIAsyncInputStream
|
||||
, public nsICloneableInputStream
|
||||
, public nsIIPCSerializableInputStream
|
||||
, public nsIInputStreamCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSISEEKABLESTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
NS_DECL_NSICLONEABLEINPUTSTREAM
|
||||
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
|
||||
explicit PartiallySeekableInputStream(nsIInputStream* aInputStream,
|
||||
uint64_t aBufferSize = 4096);
|
||||
|
||||
private:
|
||||
~PartiallySeekableInputStream();
|
||||
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
|
||||
// Raw pointers because these are just QI of mInputStream.
|
||||
nsICloneableInputStream* mWeakCloneableInputStream;
|
||||
nsIIPCSerializableInputStream* mWeakIPCSerializableInputStream;
|
||||
nsIAsyncInputStream* mWeakAsyncInputStream;
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
|
||||
|
||||
nsTArray<char> mCachedBuffer;
|
||||
|
||||
uint64_t mBufferSize;
|
||||
uint64_t mPos;
|
||||
bool mClosed;
|
||||
};
|
||||
|
||||
} // net namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // PartiallySeekableInputStream_h
|
|
@ -5,6 +5,9 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDocumentLoader.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
@ -16,6 +19,8 @@
|
|||
#include "mozilla/Services.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "mozilla/net/PSpdyPush.h"
|
||||
|
||||
#include "../protocol/http/nsHttpHandler.h"
|
||||
|
|
|
@ -175,6 +175,7 @@ EXPORTS.mozilla.net += [
|
|||
'Dashboard.h',
|
||||
'DashboardTypes.h',
|
||||
'MemoryDownloader.h',
|
||||
'PartiallySeekableInputStream.h',
|
||||
'Predictor.h',
|
||||
'ReferrerPolicy.h',
|
||||
'SimpleChannelParent.h',
|
||||
|
@ -241,6 +242,7 @@ UNIFIED_SOURCES += [
|
|||
'nsUnicharStreamLoader.cpp',
|
||||
'nsURLHelper.cpp',
|
||||
'nsURLParsers.cpp',
|
||||
'PartiallySeekableInputStream.cpp',
|
||||
'PollableEvent.cpp',
|
||||
'Predictor.cpp',
|
||||
'ProxyAutoConfig.cpp',
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "ASpdySession.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsAHttpConnection.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/net/PartiallySeekableInputStream.h"
|
||||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsIMIMEInputStream.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
|
@ -212,6 +213,8 @@ HttpBaseChannel::HttpBaseChannel()
|
|||
, mForceMainDocumentChannel(false)
|
||||
, mIsTrackingResource(false)
|
||||
, mLastRedirectFlags(0)
|
||||
, mReqContentLength(0U)
|
||||
, mReqContentLengthDetermined(false)
|
||||
{
|
||||
LOG(("Creating HttpBaseChannel @%p\n", this));
|
||||
|
||||
|
@ -1012,10 +1015,10 @@ HttpBaseChannel::CloneUploadStream(nsIInputStream** aClonedStream)
|
|||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream,
|
||||
const nsACString &aContentType,
|
||||
int64_t aContentLength,
|
||||
const nsACString &aMethod,
|
||||
bool aStreamHasHeaders)
|
||||
const nsACString &aContentType,
|
||||
int64_t aContentLength,
|
||||
const nsACString &aMethod,
|
||||
bool aStreamHasHeaders)
|
||||
{
|
||||
// Ensure stream is set and method is valid
|
||||
NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);
|
||||
|
@ -1055,6 +1058,18 @@ HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream,
|
|||
}
|
||||
|
||||
mUploadStreamHasHeaders = aStreamHasHeaders;
|
||||
|
||||
// We already have the content length. We don't need to determinate it.
|
||||
if (aContentLength > 0) {
|
||||
mReqContentLength = aContentLength;
|
||||
mReqContentLengthDetermined = true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
|
||||
if (!seekable) {
|
||||
aStream = new PartiallySeekableInputStream(aStream);
|
||||
}
|
||||
|
||||
mUploadStream = aStream;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -3411,8 +3426,9 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
|
|||
if (mUploadStream && (uploadChannel2 || uploadChannel)) {
|
||||
// rewind upload stream
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
|
||||
if (seekable)
|
||||
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
MOZ_ASSERT(seekable);
|
||||
|
||||
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
|
||||
// replicate original call to SetUploadStream...
|
||||
if (uploadChannel2) {
|
||||
|
|
|
@ -656,6 +656,9 @@ protected:
|
|||
// method.
|
||||
uint32_t mLastRedirectFlags;
|
||||
|
||||
uint64_t mReqContentLength;
|
||||
bool mReqContentLengthDetermined;
|
||||
|
||||
nsString mIntegrityMetadata;
|
||||
|
||||
// Classified channel's matched information
|
||||
|
|
|
@ -337,8 +337,6 @@ nsHttpChannel::nsHttpChannel()
|
|||
, mStronglyFramed(false)
|
||||
, mUsedNetwork(0)
|
||||
, mAuthConnectionRestartable(0)
|
||||
, mReqContentLengthDetermined(0)
|
||||
, mReqContentLength(0U)
|
||||
, mPushedStream(nullptr)
|
||||
, mLocalBlocklist(false)
|
||||
, mOnTailUnblock(nullptr)
|
||||
|
|
|
@ -661,10 +661,6 @@ private:
|
|||
// the next authentication request can be sent on a whole new connection
|
||||
uint32_t mAuthConnectionRestartable : 1;
|
||||
|
||||
uint32_t mReqContentLengthDetermined : 1;
|
||||
|
||||
uint64_t mReqContentLength;
|
||||
|
||||
nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
|
||||
|
||||
// Needed for accurate DNS timing
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
#include "gtest/gtest.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "mozilla/net/PartiallySeekableInputStream.h"
|
||||
|
||||
using mozilla::net::PartiallySeekableInputStream;
|
||||
|
||||
class NonSeekableStream final : public nsIInputStream
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
explicit NonSeekableStream(const nsACString& aBuffer)
|
||||
{
|
||||
NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Available(uint64_t* aLength) override
|
||||
{
|
||||
return mStream->Available(aLength);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
|
||||
{
|
||||
return mStream->Read(aBuffer, aCount, aReadCount);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t *aResult) override
|
||||
{
|
||||
return mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Close() override
|
||||
{
|
||||
return mStream->Close();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
IsNonBlocking(bool* aNonBlocking) override
|
||||
{
|
||||
return mStream->IsNonBlocking(aNonBlocking);
|
||||
}
|
||||
|
||||
private:
|
||||
~NonSeekableStream() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(NonSeekableStream, nsIInputStream)
|
||||
|
||||
// Helper function for creating a non-seekable nsIInputStream + a
|
||||
// PartiallySeekableInputStream.
|
||||
PartiallySeekableInputStream*
|
||||
CreateStream(uint32_t aSize, uint64_t aStreamSize, nsCString& aBuffer)
|
||||
{
|
||||
aBuffer.SetLength(aSize);
|
||||
for (uint32_t i = 0; i < aSize; ++i) {
|
||||
aBuffer.BeginWriting()[i] = i % 10;
|
||||
}
|
||||
|
||||
RefPtr<NonSeekableStream> stream = new NonSeekableStream(aBuffer);
|
||||
return new PartiallySeekableInputStream(stream, aStreamSize);
|
||||
}
|
||||
|
||||
// Simple reading.
|
||||
TEST(TestPartiallySeekableInputStream, SimpleRead) {
|
||||
const size_t kBufSize = 10;
|
||||
|
||||
nsCString buf;
|
||||
RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 5, buf);
|
||||
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize, length);
|
||||
|
||||
char buf2[kBufSize];
|
||||
uint32_t count;
|
||||
ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ(count, buf.Length());
|
||||
ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
|
||||
|
||||
// At this point, after reading more than the buffer size, seek is not
|
||||
// allowed.
|
||||
ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED,
|
||||
psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
|
||||
|
||||
ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED,
|
||||
psi->Seek(nsISeekableStream::NS_SEEK_END, 0));
|
||||
|
||||
ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED,
|
||||
psi->Seek(nsISeekableStream::NS_SEEK_CUR, 0));
|
||||
|
||||
// Position is at the end of the stream.
|
||||
int64_t pos;
|
||||
ASSERT_EQ(NS_OK, psi->Tell(&pos));
|
||||
ASSERT_EQ((int64_t)kBufSize, pos);
|
||||
}
|
||||
|
||||
// Simple seek
|
||||
TEST(TestPartiallySeekableInputStream, SimpleSeek) {
|
||||
const size_t kBufSize = 10;
|
||||
|
||||
nsCString buf;
|
||||
RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 5, buf);
|
||||
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize, length);
|
||||
|
||||
uint32_t count;
|
||||
|
||||
{
|
||||
char buf2[3];
|
||||
ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ(count, sizeof(buf2));
|
||||
ASSERT_TRUE(nsCString(buf.get(), sizeof(buf2)).Equals(nsCString(buf2, sizeof(buf2))));
|
||||
|
||||
int64_t pos;
|
||||
ASSERT_EQ(NS_OK, psi->Tell(&pos));
|
||||
ASSERT_EQ((int64_t)sizeof(buf2), pos);
|
||||
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2), length);
|
||||
}
|
||||
|
||||
// Let's seek back to the beginning using NS_SEEK_SET
|
||||
ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
|
||||
|
||||
{
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize, length);
|
||||
|
||||
char buf2[3];
|
||||
ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ(count, sizeof(buf2));
|
||||
ASSERT_TRUE(nsCString(buf.get(), sizeof(buf2)).Equals(nsCString(buf2, sizeof(buf2))));
|
||||
|
||||
int64_t pos;
|
||||
ASSERT_EQ(NS_OK, psi->Tell(&pos));
|
||||
ASSERT_EQ((int64_t)sizeof(buf2), pos);
|
||||
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2), length);
|
||||
}
|
||||
|
||||
// Let's seek back of 2 bytes using NS_SEEK_CUR
|
||||
ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_CUR, -2));
|
||||
|
||||
{
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize - 1, length);
|
||||
|
||||
char buf2[3];
|
||||
ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ(count, sizeof(buf2));
|
||||
ASSERT_TRUE(nsCString(buf.get() + 1, sizeof(buf2)).Equals(nsCString(buf2, sizeof(buf2))));
|
||||
|
||||
int64_t pos;
|
||||
ASSERT_EQ(NS_OK, psi->Tell(&pos));
|
||||
ASSERT_EQ((int64_t)sizeof(buf2) + 1, pos);
|
||||
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2) - 1, length);
|
||||
}
|
||||
|
||||
// Let's seek back to the beginning using NS_SEEK_SET
|
||||
ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
|
||||
|
||||
{
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize, length);
|
||||
|
||||
char buf2[kBufSize];
|
||||
ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ(count, buf.Length());
|
||||
ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
|
||||
}
|
||||
}
|
||||
|
||||
// Full in cache
|
||||
TEST(TestPartiallySeekableInputStream, FullCachedSeek) {
|
||||
const size_t kBufSize = 10;
|
||||
|
||||
nsCString buf;
|
||||
RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 4096, buf);
|
||||
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize, length);
|
||||
|
||||
char buf2[kBufSize];
|
||||
uint32_t count;
|
||||
ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ(count, buf.Length());
|
||||
ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
|
||||
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)0, length);
|
||||
|
||||
ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
|
||||
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)kBufSize, length);
|
||||
|
||||
ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ(count, buf.Length());
|
||||
ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
|
||||
|
||||
ASSERT_EQ(NS_OK, psi->Available(&length));
|
||||
ASSERT_EQ((uint64_t)0, length);
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
UNIFIED_SOURCES += [
|
||||
'TestHeaders.cpp',
|
||||
'TestHttpAuthUtils.cpp',
|
||||
'TestPartiallySeekableInputStream.cpp',
|
||||
'TestProtocolProxyService.cpp',
|
||||
'TestStandardURL.cpp',
|
||||
]
|
||||
|
|
Загрузка…
Ссылка в новой задаче