/* 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/. */
// Because nsSupportsWeakReference isn't thread-safe we must ensure that // TRRServiceChannel is destroyed on the target thread. Any Release() called // on a different thread is dispatched to the target thread. bool TRRServiceChannel::DispatchRelease() { if (mCurrentEventTarget->IsOnCurrentThread()) { returnfalse;
}
if (mCanceled) {
ReleaseListeners(); return mStatus;
}
// HttpBaseChannel::MaybeWaitForUploadStreamNormalization can only be used on // main thread, so we can only return an error here. #ifdef NIGHTLY_BUILD
MOZ_ASSERT(!LoadPendingUploadStreamNormalization()); #endif if (LoadPendingUploadStreamNormalization()) { return NS_ERROR_FAILURE;
}
if (!gHttpHandler->Active()) {
LOG((" after HTTP shutdown..."));
ReleaseListeners(); return NS_ERROR_NOT_AVAILABLE;
}
// The common case for HTTP channels is to begin proxy resolution and return // at this point. The only time we know mProxyInfo already is if we're // proxying a non-http protocol like ftp. We don't need to discover proxy // settings if we are never going to make a network connection. // If mConnectionInfo is already supplied, we don't need to do proxy // resolution again. if (!mProxyInfo && !mConnectionInfo &&
!(mLoadFlags & (nsICachingChannel::LOAD_ONLY_FROM_CACHE |
nsICachingChannel::LOAD_NO_NETWORK_IO)) &&
NS_SUCCEEDED(ResolveProxy())) { return NS_OK;
}
rv = BeginConnect(); if (NS_FAILED(rv)) {
Unused << AsyncAbort(rv);
}
if (NS_FAILED(rv)) { if (!mCurrentEventTarget->IsOnCurrentThread()) { return mCurrentEventTarget->Dispatch(
NewRunnableMethod<nsresult>("TRRServiceChannel::AsyncAbort", this,
&TRRServiceChannel::AsyncAbort, rv),
NS_DISPATCH_NORMAL);
}
}
{ auto req = mProxyRequest.Lock(); // We only set mProxyRequest if the channel hasn't already been cancelled // on another thread. if (!mCanceled) {
*req = proxyRequest.forget();
}
}
// If the channel has been cancelled, we go ahead and cancel the proxy // request right here. if (proxyRequest) {
proxyRequest->Cancel(mStatus);
}
{ auto proxyRequest = mProxyRequest.Lock();
*proxyRequest = nullptr;
}
nsresult rv;
// If status is a failure code, then it means that we failed to resolve // proxy info. That is a non-fatal error assuming it wasn't because the // request was canceled. We just failover to DIRECT when proxy resolution // fails (failure can mean that the PAC URL could not be loaded).
// Construct connection info object
nsAutoCString host;
nsAutoCString scheme;
int32_t port = -1; bool isHttps = mURI->SchemeIs("https");
rv = mURI->GetScheme(scheme); if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiHost(host); if (NS_SUCCEEDED(rv)) rv = mURI->GetPort(&port); if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiSpec(mSpec); if (NS_FAILED(rv)) { return rv;
}
// Just a warning here because some nsIURIs do not implement this method.
Unused << NS_WARN_IF(NS_FAILED(mURI->GetUsername(mUsername)));
// Reject the URL if it doesn't specify a host if (host.IsEmpty()) {
rv = NS_ERROR_MALFORMED_URI; return rv;
}
LOG(("host=%s port=%d\n", host.get(), port));
LOG(("uri=%s\n", mSpec.get()));
nsCOMPtr<nsProxyInfo> proxyInfo; if (mProxyInfo) proxyInfo = do_QueryInterface(mProxyInfo);
// Need to re-ask the handler, since mConnectionInfo may not be the connInfo // we used earlier if (gHttpHandler->IsHttp2Excluded(mConnectionInfo)) {
StoreAllowSpdy(0);
mCaps |= NS_HTTP_DISALLOW_SPDY;
mConnectionInfo->SetNoSpdy(true);
}
// if this somehow fails we can go on without it
Unused << gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps);
// Adjust mCaps according to our request headers: // - If "Connection: close" is set as a request header, then do not bother // trying to establish a keep-alive connection. if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) {
mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE);
}
if (gHttpHandler->CriticalRequestPrioritization()) { if (mClassOfService.Flags() & nsIClassOfService::Leader) {
mCaps |= NS_HTTP_LOAD_AS_BLOCKING;
} if (mClassOfService.Flags() & nsIClassOfService::Unblocked) {
mCaps |= NS_HTTP_LOAD_UNBLOCKED;
} if (mClassOfService.Flags() & nsIClassOfService::UrgentStart &&
gHttpHandler->IsUrgentStartEnabled()) {
mCaps |= NS_HTTP_URGENT_START;
SetPriority(nsISupportsPriority::PRIORITY_HIGHEST);
}
}
if (mCanceled) { return mStatus;
}
MaybeStartDNSPrefetch();
rv = ContinueOnBeforeConnect(); if (NS_FAILED(rv)) { return rv;
}
if (!LoadAllowSpdy()) {
mCaps |= NS_HTTP_DISALLOW_SPDY;
} // Check a proxy info from mConnectionInfo. TRR channel may use a proxy that // is set in mConnectionInfo but acutally the channel do not have mProxyInfo // set. This can happend when network.trr.async_connInfo is true. bool useNonDirectProxy = mConnectionInfo->ProxyInfo()
? !mConnectionInfo->ProxyInfo()->IsDirect()
: false; if (!Http3Allowed() || useNonDirectProxy) {
mCaps |= NS_HTTP_DISALLOW_HTTP3;
} if (LoadBeConservative()) {
mCaps |= NS_HTTP_BE_CONSERVATIVE;
}
// Use the URI path if not proxying (transparent proxying such as proxy // CONNECT does not count here). Also figure out what HTTP version to use.
nsAutoCString buf, path;
nsCString* requestURI;
// This is the normal e2e H1 path syntax "/index.html"
rv = mURI->GetPathQueryRef(path); if (NS_FAILED(rv)) { return rv;
}
// path may contain UTF-8 characters, so ensure that they're escaped. if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces,
buf)) {
requestURI = &buf;
} else {
requestURI = &path;
}
// trim off the #ref portion if any...
int32_t ref1 = requestURI->FindChar('#'); if (ref1 != kNotFound) {
requestURI->SetLength(ref1);
}
if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
mRequestHead.SetVersion(gHttpHandler->HttpVersion());
} else {
mRequestHead.SetPath(*requestURI);
// RequestURI should be the absolute uri H1 proxy syntax // "http://foo/index.html" so we will overwrite the relative version in // requestURI
rv = mURI->GetUserPass(buf); if (NS_FAILED(rv)) return rv; if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
strncmp(mSpec.get(), "https:", 6) == 0)) {
nsCOMPtr<nsIURI> tempURI = nsIOService::CreateExposableURI(mURI);
rv = tempURI->GetAsciiSpec(path); if (NS_FAILED(rv)) return rv;
requestURI = &path;
} else {
requestURI = &mSpec;
}
// trim off the #ref portion if any...
int32_t ref2 = requestURI->FindChar('#'); if (ref2 != kNotFound) {
requestURI->SetLength(ref2);
}
// Force setting no-cache header for TRRServiceChannel. // We need to send 'Pragma:no-cache' to inhibit proxy caching even if // no proxy is configured since we might be talking with a transparent // proxy, i.e. one that operates at the network level. See bug #14772.
rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
MOZ_ASSERT(NS_SUCCEEDED(rv)); // If we're configured to speak HTTP/1.1 then also send 'Cache-control: // no-cache' if (mRequestHead.Version() >= HttpVersion::v1_1) {
rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// create wrapper for this channel's notification callbacks
nsCOMPtr<nsIInterfaceRequestor> callbacks;
NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
getter_AddRefs(callbacks));
// create the transaction object
mTransaction = new nsHttpTransaction();
LOG1(("TRRServiceChannel %p created nsHttpTransaction %p\n", this,
mTransaction.get()));
// See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS;
// new channel needs mrqeuesthead and headers from pushedStream
channel->mRequestHead.ParseHeaderSet(aRequestString.BeginReading());
channel->mLoadGroup = mLoadGroup;
channel->mCallbacks = mCallbacks;
// Link the pushed stream with the new channel and call listener
channel->SetPushedStreamTransactionAndId(aTransaction, aPushedStreamId);
rv = pushListener->OnPush(this, channel); return rv;
}
if (LoadOnStartRequestCalled()) {
LOG(("CallOnStartRequest already invoked before")); return mStatus;
}
nsresult rv = NS_OK;
StoreTracingEnabled(false);
// Ensure mListener->OnStartRequest will be invoked before exiting // this function. auto onStartGuard = MakeScopeExit([&] {
LOG(
(" calling mListener->OnStartRequest by ScopeExit [this=%p, " "listener=%p]\n", this, mListener.get()));
MOZ_ASSERT(!LoadOnStartRequestCalled());
if (mListener) {
nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
StoreOnStartRequestCalled(true);
deleteProtector->OnStartRequest(this);
}
StoreOnStartRequestCalled(true);
});
if (mResponseHead && !mResponseHead->HasContentCharset()) {
mResponseHead->SetContentCharset(mContentCharsetHint);
}
// DoApplyContentConversions can only be called on the main thread. if (NS_IsMainThread()) {
nsCOMPtr<nsIStreamListener> listener;
rv =
DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr); if (NS_FAILED(rv)) { return rv;
}
StoreAfterOnStartRequestBegun(true); if (mTransaction) { if (!mSecurityInfo) { // grab the security info from the connection object; the transaction // is guaranteed to own a reference to the connection.
mSecurityInfo = mTransaction->SecurityInfo();
}
}
if (NS_SUCCEEDED(mStatus) && mTransaction) { // mTransactionPump doesn't hit OnInputStreamReady and call this until // all of the response headers have been acquired, so we can take // ownership of them from the transaction.
mResponseHead = mTransaction->TakeResponseHead(); if (mResponseHead) {
uint32_t httpStatus = mResponseHead->Status(); if (mTransaction->ProxyConnectFailed()) {
LOG(("TRRServiceChannel proxy connect failed httpStatus: %d",
httpStatus));
MOZ_ASSERT(mConnectionInfo->UsingConnect(), "proxy connect failed but not using CONNECT?");
nsresult rv = HttpProxyResponseToErrorCode(httpStatus);
mTransaction->DontReuseConnection();
Cancel(rv); return CallOnStartRequest();
}
// if a location header was not given, then we can't perform the redirect, // so just carry on as though this were a normal response. if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location))) { return NS_ERROR_FAILURE;
}
// make sure non-ASCII characters in the location header are escaped.
nsAutoCString locationBuf; if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
locationBuf)) {
location = locationBuf;
}
LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(),
uint32_t(mRedirectionLimit)));
// Let's not rewrite the method to GET for TRR requests. if (rewriteToGET) { return NS_ERROR_FAILURE;
}
// If the method is not safe (such as POST, PUT, DELETE, ...) if (!mRequestHead.IsSafeMethod()) {
LOG(("TRRServiceChannel: unsafe redirect to:%s\n", location.get()));
}
if (mListener) {
LOG(("TRRServiceChannel %p calling OnStopRequest\n", this));
MOZ_ASSERT(LoadOnStartRequestCalled(), "OnStartRequest should be called before OnStopRequest");
MOZ_ASSERT(!LoadOnStopRequestCalled(), "We should not call OnStopRequest twice");
StoreOnStopRequestCalled(true);
mListener->OnStopRequest(this, status);
}
StoreOnStopRequestCalled(true);
mDNSPrefetch = nullptr;
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, status);
}
// We no longer need the dns prefetch object. Note: mDNSPrefetch could be // validly null if OnStopRequest has already been called. // We only need the domainLookup timestamps when not loading from cache if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) {
TimeStamp connectStart = mTransaction->GetConnectStart();
TimeStamp requestStart = mTransaction->GetRequestStart(); // We only set the domainLookup timestamps if we're not using a // persistent connection. if (requestStart.IsNull() && connectStart.IsNull()) {
mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());
mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());
}
}
mDNSPrefetch = nullptr;
// Unset DNS cache refresh if it was requested, if (mCaps & NS_HTTP_REFRESH_DNS) {
mCaps &= ~NS_HTTP_REFRESH_DNS; if (mTransaction) {
mTransaction->SetDNSWasRefreshed();
}
}
// In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag // LOAD_ONLY_IF_MODIFIED) we want to set AfterOnStartRequestBegun to true // before notifying listener. if (!LoadAfterOnStartRequestBegun()) {
StoreAfterOnStartRequestBegun(true);
}
// Make sure IsPending is set to false. At this moment we are done from // the point of view of our consumer and we have to report our self // as not-pending.
StoreIsPending(false);
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.