/* 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/. */
// HttpLog.h should generally be included first #include"mozilla/net/HttpBaseChannel.h"
// Subfields of unions cannot be targeted in an initializer list. #ifdef MOZ_VALGRIND // Zero the entire unions so that Valgrind doesn't complain when we send them // to another process.
memset(&mSelfAddr, 0, sizeof(NetAddr));
memset(&mPeerAddr, 0, sizeof(NetAddr)); #endif
mSelfAddr.raw.family = PR_AF_UNSPEC;
mPeerAddr.raw.family = PR_AF_UNSPEC;
}
void HttpBaseChannel::ReleaseMainThreadOnlyReferences() { if (NS_IsMainThread()) { // Already on main thread, let dtor to // take care of releasing references
RemoveAsNonTailRequest(); return;
}
if (LoadAddedAsNonTailRequest()) { // RemoveNonTailRequest() on our request context must be called on the main // thread
MOZ_RELEASE_ASSERT(mRequestContext, "Someone released rc or set flags w/o having it?");
NS_IMETHODIMP
HttpBaseChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { if (!LoadIsOCSP()) { return GetTRRModeImpl(aTRRMode);
}
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
nsIDNSService::ResolverMode trrMode = nsIDNSService::MODE_NATIVEONLY; // If this is an OCSP channel, and the global TRR mode is TRR_ONLY (3) // then we set the mode for this channel as TRR_DISABLED_MODE. // We do this to prevent a TRR service channel's OCSP validation from // blocking DNS resolution completely. if (dns && NS_SUCCEEDED(dns->GetCurrentTrrMode(&trrMode)) &&
trrMode == nsIDNSService::MODE_TRRONLY) {
*aTRRMode = nsIRequest::TRR_DISABLED_MODE; return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
if (!CanSetCallbacks(aCallbacks)) { return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
HttpBaseChannel::GetContentDisposition(uint32_t* aContentDisposition) { // See bug 1658877. If mContentDispositionHint is already // DISPOSITION_ATTACHMENT, it means this channel is created from a // download attribute. In this case, we should prefer the value from the // download attribute rather than the value in content disposition header. // DISPOSITION_FORCE_INLINE is used to explicitly set inline, used by // the pdf reader when loading a attachment pdf without having to // download it. if (mContentDispositionHint == nsIChannel::DISPOSITION_ATTACHMENT ||
mContentDispositionHint == nsIChannel::DISPOSITION_FORCE_INLINE) {
*aContentDisposition = mContentDispositionHint; return NS_OK;
}
nsresult rv;
nsCString header;
rv = GetContentDispositionHeader(header); if (NS_FAILED(rv)) { if (mContentDispositionHint == UINT32_MAX) return rv;
// If we failed to get the filename from header, we should use // mContentDispositionFilename, since mContentDispositionFilename is set from // the download attribute. if (NS_FAILED(rv)) { if (!mContentDispositionFilename) { return rv;
}
// For safety reasons ensure the filename doesn't contain null characters and // replace them with underscores. We may later pass the extension to system // MIME APIs that expect null terminated strings.
mContentDispositionFilename->ReplaceChar(char16_t(0), '_');
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetContentDispositionHeader(
nsACString& aContentDispositionHeader) { if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
NS_IMETHODIMP
HttpBaseChannel::SetUploadStream(nsIInputStream* stream, const nsACString& contentTypeArg,
int64_t contentLength) { // NOTE: for backwards compatibility and for compatibility with old style // plugins, |stream| may include headers, specifically Content-Type and // Content-Length headers. in this case, |contentType| and |contentLength| // would be unspecified. this is traditionally the case of a POST request, // and so we select POST as the request method if contentType and // contentLength are unspecified.
if (stream) {
nsAutoCString method; bool hasHeaders = false;
// This method and ExplicitSetUploadStream mean different things by "empty // content type string". This method means "no header", but // ExplicitSetUploadStream means "header with empty value". So we have to // massage the contentType argument into the form ExplicitSetUploadStream // expects.
nsCOMPtr<nsIMIMEInputStream> mimeStream;
nsCString contentType(contentTypeArg); if (contentType.IsEmpty()) {
contentType.SetIsVoid(true);
method = "POST"_ns;
// MIME streams are a special case, and include headers which need to be // copied to the channel.
mimeStream = do_QueryInterface(stream); if (mimeStream) { // Copy non-origin related headers to the channel.
nsCOMPtr<nsIHttpHeaderVisitor> visitor = new AddHeadersToChannelVisitor(this);
mimeStream->VisitHeaders(visitor);
MOZ_ASSERT(
NS_FAILED(CallQueryInterface(stream, getter_AddRefs(mimeStream))), "nsIMIMEInputStream should not be set with an explicit content type");
} return ExplicitSetUploadStream(stream, contentType, contentLength, method,
hasHeaders);
}
// if stream is null, ExplicitSetUploadStream returns error. // So we need special case for GET method.
StoreUploadStreamHasHeaders(false);
SetRequestMethod("GET"_ns); // revert to GET request
mUploadStream = nullptr; return NS_OK;
}
namespace {
class MIMEHeaderCopyVisitor final : public nsIHttpHeaderVisitor { public: explicit MIMEHeaderCopyVisitor(nsIMIMEInputStream* aDest) : mDest(aDest) {}
staticvoid NormalizeCopyComplete(void* aClosure, nsresult aStatus) { #ifdef DEBUG // Called on the STS thread by NS_AsyncCopy
nsCOMPtr<nsIEventTarget> sts =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); bool result = false;
sts->IsOnCurrentThread(&result);
MOZ_ASSERT(result, "Should only be called on the STS thread."); #endif
// Normalize the upload stream for a HTTP channel, so that is one of the // expected and compatible types. Components like WebExtensions and DevTools // expect that upload streams in the parent process are cloneable, seekable, and // synchronous to read, which this function helps guarantee somewhat efficiently // and without loss of information. // // If the replacement stream outparameter is not initialized to `nullptr`, the // returned stream should be used instead of `aUploadStream` as the upload // stream for the HTTP channel, and the previous stream should not be touched // again. // // If aReadyPromise is non-nullptr after the function is called, it is a promise // which should be awaited before continuing to `AsyncOpen` the HTTP channel, // as the replacement stream will not be ready until it is resolved. static nsresult NormalizeUploadStream(nsIInputStream* aUploadStream,
nsIInputStream** aReplacementStream,
GenericPromise** aReadyPromise) {
MOZ_ASSERT(XRE_IsParentProcess());
// Preserve MIME information on the stream when normalizing. if (nsCOMPtr<nsIMIMEInputStream> mime = do_QueryInterface(aUploadStream)) {
nsCOMPtr<nsIInputStream> data;
nsresult rv = mime->GetData(getter_AddRefs(data));
NS_ENSURE_SUCCESS(rv, rv);
// Preserve "real" buffered input streams which wrap data (i.e. are backed by // nsBufferedInputStream), but normalize the wrapped stream. if (nsCOMPtr<nsIBufferedInputStream> buffered =
do_QueryInterface(aUploadStream)) {
nsCOMPtr<nsIInputStream> data; if (NS_SUCCEEDED(buffered->GetData(getter_AddRefs(data)))) {
nsCOMPtr<nsIInputStream> replacement;
nsresult rv = NormalizeUploadStream(data, getter_AddRefs(replacement),
aReadyPromise);
NS_ENSURE_SUCCESS(rv, rv); if (replacement) { // This buffer size should be kept in sync with HTMLFormSubmission.
rv = NS_NewBufferedInputStream(aReplacementStream, replacement.forget(),
8192);
NS_ENSURE_SUCCESS(rv, rv);
} return NS_OK;
}
}
// Preserve multiplex input streams, normalizing each individual inner stream // to avoid unnecessary copying. if (nsCOMPtr<nsIMultiplexInputStream> multiplex =
do_QueryInterface(aUploadStream)) {
uint32_t count = multiplex->GetCount();
nsTArray<nsCOMPtr<nsIInputStream>> streams(count);
nsTArray<RefPtr<GenericPromise>> promises(count); bool replace = false; for (uint32_t i = 0; i < count; ++i) {
nsCOMPtr<nsIInputStream> inner;
nsresult rv = multiplex->GetStream(i, getter_AddRefs(inner));
NS_ENSURE_SUCCESS(rv, rv);
// If any of the inner streams needed to be replaced, replace the entire // nsIMultiplexInputStream. if (replace) {
nsresult rv;
nsCOMPtr<nsIMultiplexInputStream> replacement =
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
NS_ENSURE_SUCCESS(rv, rv); for (auto& stream : streams) {
rv = replacement->AppendStream(stream);
NS_ENSURE_SUCCESS(rv, rv);
}
// Wait for all inner promises to settle before resolving the final promise. if (!promises.IsEmpty()) {
RefPtr<GenericPromise> ready =
GenericPromise::AllSettled(GetCurrentSerialEventTarget(), promises)
->Then(GetCurrentSerialEventTarget(), __func__,
[](GenericPromise::AllSettledPromiseType::
ResolveOrRejectValue&& aResults)
-> RefPtr<GenericPromise> {
MOZ_ASSERT(aResults.IsResolve(), "AllSettled never rejects"); for (auto& result : aResults.ResolveValue()) { if (result.IsReject()) { return GenericPromise::CreateAndReject(
result.RejectValue(), __func__);
}
} return GenericPromise::CreateAndResolve(true, __func__);
});
ready.forget(aReadyPromise);
} return NS_OK;
}
// If the stream is cloneable, seekable and non-async, we can allow it. Async // input streams can cause issues, as various consumers of input streams // expect the payload to be synchronous and `Available()` to be the length of // the stream, which is not true for asynchronous streams.
nsCOMPtr<nsIAsyncInputStream> async = do_QueryInterface(aUploadStream);
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream); if (NS_InputStreamIsCloneable(aUploadStream) && seekable && !async) { return NS_OK;
}
// Asynchronously copy our non-normalized stream into a StorageStream so that // it is seekable, cloneable, and synchronous once the copy completes.
NS_WARNING("Upload Stream is being copied into StorageStream");
// Ensure the source stream is buffered before starting the copy so we can use // ReadSegments, as nsStorageStream doesn't implement WriteSegments.
nsCOMPtr<nsIInputStream> source = aUploadStream; if (!NS_InputStreamIsBuffered(aUploadStream)) {
nsCOMPtr<nsIInputStream> bufferedSource;
rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedSource),
source.forget(), 4096);
NS_ENSURE_SUCCESS(rv, rv);
source = bufferedSource.forget();
}
// Perform an AsyncCopy into the input stream on the STS.
nsCOMPtr<nsIEventTarget> target =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
RefPtr<GenericPromise::Private> ready = new GenericPromise::Private(__func__);
rv = NS_AsyncCopy(source, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
NormalizeCopyComplete, do_AddRef(ready).take()); if (NS_WARN_IF(NS_FAILED(rv))) {
ready.get()->Release(); return rv;
}
NS_IMETHODIMP
HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream* aStream, 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);
{
DebugOnly<nsCOMPtr<nsIMIMEInputStream>> mimeStream;
MOZ_ASSERT(
!aStreamHasHeaders || NS_FAILED(CallQueryInterface(
aStream, getter_AddRefs(mimeStream.value))), "nsIMIMEInputStream should not include headers");
}
nsresult HttpBaseChannel::InternalSetUploadStream(
nsIInputStream* aUploadStream, int64_t aContentLength, bool aSetContentLengthHeader) { // If we're not on the main thread, such as for TRR, the content length must // be provided, as we can't normalize our upload stream. if (!NS_IsMainThread()) { if (aContentLength < 0) {
MOZ_ASSERT_UNREACHABLE( "Upload content length must be explicit off-main-thread"); return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream); if (!NS_InputStreamIsCloneable(aUploadStream) || !seekable) {
MOZ_ASSERT_UNREACHABLE( "Upload stream must be cloneable & seekable off-main-thread"); return NS_ERROR_INVALID_ARG;
}
// Normalize the upload stream we're provided to ensure that it is cloneable, // seekable, and synchronous when in the parent process. // // This might be an async operation, in which case ready will be returned and // resolved when the operation is complete.
nsCOMPtr<nsIInputStream> replacement;
RefPtr<GenericPromise> ready; if (XRE_IsParentProcess()) {
nsresult rv = NormalizeUploadStream(
aUploadStream, getter_AddRefs(replacement), getter_AddRefs(ready));
NS_ENSURE_SUCCESS(rv, rv);
}
// Resolve onReady synchronously unless a promise is returned. if (ready) {
ready->Then(GetCurrentSerialEventTarget(), __func__,
[onReady = std::move(onReady)](
GenericPromise::ResolveOrRejectValue&&) { onReady(); });
} else {
onReady();
} return NS_OK;
}
void HttpBaseChannel::ExplicitSetUploadStreamLength(
uint64_t aContentLength, bool aSetContentLengthHeader) { // We already have the content length. We don't need to determinate it.
mReqContentLength = aContentLength;
// create a listener chain that looks like this // http-channel -> decompressor (n times) -> InterceptFailedOnSTop -> // channel-creator-listener // // we need to do this because not every decompressor has fully streamed output // so may need a call to OnStopRequest to identify its completion state.. and if // it creates an error there the channel status code needs to be updated before // calling the terminal listener. Having the decompress do it via cancel() means // channels cannot effectively be used in two contexts (specifically this one // and a peek context for sniffing) // class InterceptFailedOnStop : public nsIThreadRetargetableStreamListener { virtual ~InterceptFailedOnStop() = default;
nsCOMPtr<nsIStreamListener> mNext;
HttpBaseChannel* mChannel;
nsCOMPtr<nsIStreamListener> nextListener = new InterceptFailedOnStop(aNextListener, this);
// The encodings are listed in the order they were applied // (see rfc 2616 section 14.11), so they need to removed in reverse // order. This is accomplished because the converter chain ends up // being a stack with the last converter created being the first one // to accept the raw network data.
char* cePtr = contentEncoding.BeginWriting();
uint32_t count = 0; while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) { if (++count > 16) { // That's ridiculous. We only understand 2 different ones :) // but for compatibility with old code, we will just carry on without // removing the encodings
LOG(("Too many Content-Encodings. Ignoring remainder.\n")); break;
}
if (gHttpHandler->IsAcceptableEncoding(val,
isSecureOrTrustworthyURL(mURI))) {
RefPtr<nsHTTPCompressConv> converter = new nsHTTPCompressConv();
nsAutoCString from(val);
ToLowerCase(from);
rv = converter->AsyncConvertData(from.get(), "uncompressed", nextListener,
aCtxt); if (NS_FAILED(rv)) {
LOG(("Unexpected failure of AsyncConvertData %s\n", val)); return rv;
}
// At this point both mCurStart and mCurEnd point to somewhere // past the end of the next thing we want to return
while (mCurEnd != mEncodingHeader) {
--mCurEnd; if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd)) break;
} if (mCurEnd == mEncodingHeader) { return NS_ERROR_NOT_AVAILABLE; // no more encodings
}
++mCurEnd;
// At this point mCurEnd points to the first char _after_ the // header we want. Furthermore, mCurEnd - 1 != mEncodingHeader
mCurStart = mCurEnd - 1; while (mCurStart != mEncodingHeader && *mCurStart != ',' &&
!nsCRT::IsAsciiSpace(*mCurStart)) {
--mCurStart;
} if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart)) {
++mCurStart; // we stopped because of a weird char, so move up one
}
// At this point mCurStart and mCurEnd bracket the encoding string // we want. Check that it's not "identity" if (Substring(mCurStart, mCurEnd)
.Equals("identity", nsCaseInsensitiveCStringComparator)) {
mCurEnd = mCurStart; return PrepareForNext();
}
// Don't set referrerInfo if it has not been initialized. if (!referrerInfo->IsInitialized()) {
mReferrerInfo = nullptr; return NS_ERROR_NOT_INITIALIZED;
}
if (aClone) { // Record the telemetry once we set the referrer info to the channel // successfully.
referrerInfo->RecordTelemetry(this);
}
if (aCompute) {
rv = referrerInfo->ComputeReferrer(this); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
}
nsCOMPtr<nsIURI> computedReferrer = mReferrerInfo->GetComputedReferrer(); if (!computedReferrer) { return NS_OK;
}
// Return the channel's proxy URI, or if it doesn't exist, the // channel's main URI.
NS_IMETHODIMP
HttpBaseChannel::GetProxyURI(nsIURI** aOut) {
NS_ENSURE_ARG_POINTER(aOut);
nsCOMPtr<nsIURI> result(mProxyURI);
result.forget(aOut); return NS_OK;
}
// XXX might be better to search the header list directly instead of // hitting the http atom hash table.
nsHttpAtom atom = nsHttp::ResolveAtom(aHeader); if (!atom) return NS_ERROR_NOT_AVAILABLE;
// Verify header names are valid HTTP tokens and header values are reasonably // close to whats allowed in RFC 2616. if (!nsHttp::IsValidToken(flatHeader) ||
!nsHttp::IsReasonableHeaderValue(flatValue)) { return NS_ERROR_INVALID_ARG;
}
// Mark that the User-Agent header has been modified. if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) {
StoreIsUserAgentHeaderModified(true);
}
// Verify header names are valid HTTP tokens and header values are reasonably // close to whats allowed in RFC 2616. if (!nsHttp::IsValidToken(flatHeader)) { return NS_ERROR_INVALID_ARG;
}
// Mark that the User-Agent header has been modified. if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) {
StoreIsUserAgentHeaderModified(true);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.