From 70617ee7a84d74be660c2b4cf92e66b66cfce82e Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Wed, 7 Apr 2010 01:43:09 -0700 Subject: [PATCH] Bug 546581: e10s HTTP: create common base class for HttpChannelChild and nsHttpChannel. a=dwitte, r=jae-seong, r=jduell --- netwerk/protocol/http/src/HttpBaseChannel.cpp | 816 ++++++++++++++++++ netwerk/protocol/http/src/HttpBaseChannel.h | 186 ++++ .../protocol/http/src/HttpChannelChild.cpp | 653 +------------- netwerk/protocol/http/src/HttpChannelChild.h | 79 +- .../protocol/http/src/HttpChannelParent.cpp | 34 +- netwerk/protocol/http/src/HttpChannelParent.h | 6 +- netwerk/protocol/http/src/Makefile.in | 7 +- netwerk/protocol/http/src/PHttpChannel.ipdl | 7 +- .../protocol/http/src/PHttpChannelParams.h | 1 + netwerk/protocol/http/src/nsHttpChannel.cpp | 808 +---------------- netwerk/protocol/http/src/nsHttpChannel.h | 89 +- netwerk/protocol/http/src/nsHttpHandler.cpp | 41 +- netwerk/test/unit_ipc/test_bug528292_wrap.js | 3 + 13 files changed, 1160 insertions(+), 1570 deletions(-) create mode 100644 netwerk/protocol/http/src/HttpBaseChannel.cpp create mode 100644 netwerk/protocol/http/src/HttpBaseChannel.h create mode 100644 netwerk/test/unit_ipc/test_bug528292_wrap.js diff --git a/netwerk/protocol/http/src/HttpBaseChannel.cpp b/netwerk/protocol/http/src/HttpBaseChannel.cpp new file mode 100644 index 000000000000..68ae6aa00e01 --- /dev/null +++ b/netwerk/protocol/http/src/HttpBaseChannel.cpp @@ -0,0 +1,816 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Daniel Witte + * + * 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 + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/net/HttpBaseChannel.h" + +#include "nsHttpHandler.h" +#include "nsMimeTypes.h" +#include "nsNetUtil.h" + +#define DROP_DEAD() \ + do { \ + fprintf(stderr, \ + "*&*&*&*&*&*&*&**&*&&*& FATAL ERROR: '%s' UNIMPLEMENTED: %s +%d", \ + __FUNCTION__, __FILE__, __LINE__); \ + NS_ABORT(); \ + return NS_ERROR_NOT_IMPLEMENTED; \ + } while (0) + +namespace mozilla { +namespace net { + +HttpBaseChannel::HttpBaseChannel() + : mStatus(NS_OK) + , mLoadFlags(LOAD_NORMAL) + , mCaps(0) + , mRedirectionLimit(gHttpHandler->RedirectionLimit()) + , mIsPending(PR_FALSE) + , mWasOpened(PR_FALSE) + , mResponseHeadersModified(PR_FALSE) + , mAllowPipelining(PR_TRUE) + , mForceAllowThirdPartyCookie(PR_FALSE) +{ + LOG(("Creating HttpBaseChannel @%x\n", this)); + + // grab a reference to the handler to ensure that it doesn't go away. + NS_ADDREF(gHttpHandler); +} + +HttpBaseChannel::~HttpBaseChannel() +{ + LOG(("Destroying HttpBaseChannel @%x\n", this)); + + nsHttpHandler* handler = gHttpHandler; + NS_RELEASE(handler); +} + +nsresult +HttpBaseChannel::Init(nsIURI *aURI, + PRUint8 aCaps, + nsProxyInfo *aProxyInfo) +{ + LOG(("HttpBaseChannel::Init [this=%p]\n", this)); + + NS_PRECONDITION(aURI, "null uri"); + + nsresult rv = nsHashPropertyBag::Init(); + if (NS_FAILED(rv)) return rv; + + mURI = aURI; + mOriginalURI = aURI; + mDocumentURI = nsnull; + mCaps = aCaps; + + // Construct connection info object + nsCAutoString host; + PRInt32 port = -1; + PRBool usingSSL = PR_FALSE; + + rv = mURI->SchemeIs("https", &usingSSL); + if (NS_FAILED(rv)) return rv; + + rv = mURI->GetAsciiHost(host); + if (NS_FAILED(rv)) return rv; + + // Reject the URL if it doesn't specify a host + if (host.IsEmpty()) + return NS_ERROR_MALFORMED_URI; + + rv = mURI->GetPort(&port); + if (NS_FAILED(rv)) return rv; + + LOG(("host=%s port=%d\n", host.get(), port)); + + rv = mURI->GetAsciiSpec(mSpec); + if (NS_FAILED(rv)) return rv; + LOG(("uri=%s\n", mSpec.get())); + + mConnectionInfo = new nsHttpConnectionInfo(host, port, + aProxyInfo, usingSSL); + if (!mConnectionInfo) + return NS_ERROR_OUT_OF_MEMORY; + + // Set default request method + mRequestHead.SetMethod(nsHttp::Get); + + // Set request headers + nsCAutoString hostLine; + rv = nsHttpHandler::GenerateHostPort(host, port, hostLine); + if (NS_FAILED(rv)) return rv; + + rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); + if (NS_FAILED(rv)) return rv; + + rv = gHttpHandler-> + AddStandardRequestHeaders(&mRequestHead.Headers(), aCaps, + !mConnectionInfo->UsingSSL() && + mConnectionInfo->UsingHttpProxy()); + + return rv; +} + +//----------------------------------------------------------------------------- +// HttpBaseChannel::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS_INHERITED4(HttpBaseChannel, + nsHashPropertyBag, + nsIRequest, + nsIChannel, + nsIHttpChannel, + nsIHttpChannelInternal) + +//----------------------------------------------------------------------------- +// HttpBaseChannel::nsIRequest +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpBaseChannel::GetName(nsACString& aName) +{ + aName = mSpec; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::IsPending(PRBool *aIsPending) +{ + NS_ENSURE_ARG_POINTER(aIsPending); + *aIsPending = mIsPending; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetStatus(nsresult *aStatus) +{ + NS_ENSURE_ARG_POINTER(aStatus); + *aStatus = mStatus; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) +{ + NS_ENSURE_ARG_POINTER(aLoadGroup); + *aLoadGroup = mLoadGroup; + NS_IF_ADDREF(*aLoadGroup); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) +{ + mLoadGroup = aLoadGroup; + mProgressSink = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) +{ + NS_ENSURE_ARG_POINTER(aLoadFlags); + *aLoadFlags = mLoadFlags; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) +{ + mLoadFlags = aLoadFlags; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// HttpBaseChannel::nsIChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpBaseChannel::GetOriginalURI(nsIURI **aOriginalURI) +{ + NS_ENSURE_ARG_POINTER(aOriginalURI); + *aOriginalURI = mOriginalURI; + NS_ADDREF(*aOriginalURI); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetOriginalURI(nsIURI *aOriginalURI) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + NS_ENSURE_ARG_POINTER(aOriginalURI); + mOriginalURI = aOriginalURI; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetURI(nsIURI **aURI) +{ + NS_ENSURE_ARG_POINTER(aURI); + *aURI = mURI; + NS_ADDREF(*aURI); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) +{ + *aCallbacks = mCallbacks; + NS_IF_ADDREF(*aCallbacks); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) +{ + mCallbacks = aCallbacks; + mProgressSink = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetContentType(nsACString& aContentType) +{ + if (!mResponseHead) { + aContentType.Truncate(); + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mResponseHead->ContentType().IsEmpty()) { + aContentType = mResponseHead->ContentType(); + return NS_OK; + } + + aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetContentType(const nsACString& aContentType) +{ + if (mListener || mWasOpened) { + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + nsCAutoString contentTypeBuf, charsetBuf; + PRBool hadCharset; + net_ParseContentType(aContentType, contentTypeBuf, charsetBuf, &hadCharset); + + mResponseHead->SetContentType(contentTypeBuf); + + // take care not to stomp on an existing charset + if (hadCharset) + mResponseHead->SetContentCharset(charsetBuf); + + } else { + // We are being given a content-type hint. + PRBool dummy; + net_ParseContentType(aContentType, mContentTypeHint, mContentCharsetHint, + &dummy); + } + + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetContentCharset(nsACString& aContentCharset) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + aContentCharset = mResponseHead->ContentCharset(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset) +{ + if (mListener) { + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + mResponseHead->SetContentCharset(aContentCharset); + } else { + // Charset hint + mContentCharsetHint = aContentCharset; + } + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetContentLength(PRInt32 *aContentLength) +{ + NS_ENSURE_ARG_POINTER(aContentLength); + + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + // XXX truncates to 32 bit + *aContentLength = mResponseHead->ContentLength(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetContentLength(PRInt32 value) +{ + NS_NOTYETIMPLEMENTED("nsHttpChannel::SetContentLength"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +HttpBaseChannel::Open(nsIInputStream **aResult) +{ + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); + return NS_ImplementChannelOpen(this, aResult); +} + +//----------------------------------------------------------------------------- +// HttpBaseChannel::nsIHttpChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpBaseChannel::GetRequestMethod(nsACString& aMethod) +{ + aMethod = mRequestHead.Method(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetRequestMethod(const nsACString& aMethod) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + const nsCString& flatMethod = PromiseFlatCString(aMethod); + + // Method names are restricted to valid HTTP tokens. + if (!nsHttp::IsValidToken(flatMethod)) + return NS_ERROR_INVALID_ARG; + + nsHttpAtom atom = nsHttp::ResolveAtom(flatMethod.get()); + if (!atom) + return NS_ERROR_FAILURE; + + mRequestHead.SetMethod(atom); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetReferrer(nsIURI **referrer) +{ + NS_ENSURE_ARG_POINTER(referrer); + *referrer = mReferrer; + NS_IF_ADDREF(*referrer); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetReferrer(nsIURI *referrer) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + // clear existing referrer, if any + mReferrer = nsnull; + mRequestHead.ClearHeader(nsHttp::Referer); + + if (!referrer) + return NS_OK; + + // check referrer blocking pref + PRUint32 referrerLevel; + if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) + referrerLevel = 1; // user action + else + referrerLevel = 2; // inline content + if (gHttpHandler->ReferrerLevel() < referrerLevel) + return NS_OK; + + nsCOMPtr referrerGrip; + nsresult rv; + PRBool match; + + // + // Strip off "wyciwyg://123/" from wyciwyg referrers. + // + // XXX this really belongs elsewhere since wyciwyg URLs aren't part of necko. + // perhaps some sort of generic nsINestedURI could be used. then, if an URI + // fails the whitelist test, then we could check for an inner URI and try + // that instead. though, that might be too automatic. + // + rv = referrer->SchemeIs("wyciwyg", &match); + if (NS_FAILED(rv)) return rv; + if (match) { + nsCAutoString path; + rv = referrer->GetPath(path); + if (NS_FAILED(rv)) return rv; + + PRUint32 pathLength = path.Length(); + if (pathLength <= 2) return NS_ERROR_FAILURE; + + // Path is of the form "//123/http://foo/bar", with a variable number of digits. + // To figure out where the "real" URL starts, search path for a '/', starting at + // the third character. + PRInt32 slashIndex = path.FindChar('/', 2); + if (slashIndex == kNotFound) return NS_ERROR_FAILURE; + + // Get the charset of the original URI so we can pass it to our fixed up URI. + nsCAutoString charset; + referrer->GetOriginCharset(charset); + + // Replace |referrer| with a URI without wyciwyg://123/. + rv = NS_NewURI(getter_AddRefs(referrerGrip), + Substring(path, slashIndex + 1, pathLength - slashIndex - 1), + charset.get()); + if (NS_FAILED(rv)) return rv; + + referrer = referrerGrip.get(); + } + + // + // block referrer if not on our white list... + // + static const char *const referrerWhiteList[] = { + "http", + "https", + "ftp", + "gopher", + nsnull + }; + match = PR_FALSE; + const char *const *scheme = referrerWhiteList; + for (; *scheme && !match; ++scheme) { + rv = referrer->SchemeIs(*scheme, &match); + if (NS_FAILED(rv)) return rv; + } + if (!match) + return NS_OK; // kick out.... + + // + // Handle secure referrals. + // + // Support referrals from a secure server if this is a secure site + // and (optionally) if the host names are the same. + // + rv = referrer->SchemeIs("https", &match); + if (NS_FAILED(rv)) return rv; + if (match) { + rv = mURI->SchemeIs("https", &match); + if (NS_FAILED(rv)) return rv; + if (!match) + return NS_OK; + + if (!gHttpHandler->SendSecureXSiteReferrer()) { + nsCAutoString referrerHost; + nsCAutoString host; + + rv = referrer->GetAsciiHost(referrerHost); + if (NS_FAILED(rv)) return rv; + + rv = mURI->GetAsciiHost(host); + if (NS_FAILED(rv)) return rv; + + // GetAsciiHost returns lowercase hostname. + if (!referrerHost.Equals(host)) + return NS_OK; + } + } + + nsCOMPtr clone; + // + // we need to clone the referrer, so we can: + // (1) modify it + // (2) keep a reference to it after returning from this function + // + rv = referrer->Clone(getter_AddRefs(clone)); + if (NS_FAILED(rv)) return rv; + + // strip away any userpass; we don't want to be giving out passwords ;-) + clone->SetUserPass(EmptyCString()); + + // strip away any fragment per RFC 2616 section 14.36 + nsCOMPtr url = do_QueryInterface(clone); + if (url) + url->SetRef(EmptyCString()); + + nsCAutoString spec; + rv = clone->GetAsciiSpec(spec); + if (NS_FAILED(rv)) return rv; + + // finally, remember the referrer URI and set the Referer header. + mReferrer = clone; + mRequestHead.SetHeader(nsHttp::Referer, spec); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetRequestHeader(const nsACString& aHeader, + nsACString& aValue) +{ + // 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; + + return mRequestHead.GetHeader(atom, aValue); +} + +NS_IMETHODIMP +HttpBaseChannel::SetRequestHeader(const nsACString& aHeader, + const nsACString& aValue, + PRBool aMerge) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + const nsCString &flatHeader = PromiseFlatCString(aHeader); + const nsCString &flatValue = PromiseFlatCString(aValue); + + LOG(("HttpBaseChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", + this, flatHeader.get(), flatValue.get(), aMerge)); + + // Header names are restricted to valid HTTP tokens. + if (!nsHttp::IsValidToken(flatHeader)) + return NS_ERROR_INVALID_ARG; + + // Header values MUST NOT contain line-breaks. RFC 2616 technically + // permits CTL characters, including CR and LF, in header values provided + // they are quoted. However, this can lead to problems if servers do not + // interpret quoted strings properly. Disallowing CR and LF here seems + // reasonable and keeps things simple. We also disallow a null byte. + if (flatValue.FindCharInSet("\r\n") != kNotFound || + flatValue.Length() != strlen(flatValue.get())) + return NS_ERROR_INVALID_ARG; + + nsHttpAtom atom = nsHttp::ResolveAtom(flatHeader.get()); + if (!atom) { + NS_WARNING("failed to resolve atom"); + return NS_ERROR_NOT_AVAILABLE; + } + + return mRequestHead.SetHeader(atom, flatValue, aMerge); +} + +NS_IMETHODIMP +HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) +{ + return mRequestHead.Headers().VisitHeaders(visitor); +} + +NS_IMETHODIMP +HttpBaseChannel::GetResponseHeader(const nsACString &header, nsACString &value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + nsHttpAtom atom = nsHttp::ResolveAtom(header); + if (!atom) + return NS_ERROR_NOT_AVAILABLE; + + return mResponseHead->GetHeader(atom, value); +} + +NS_IMETHODIMP +HttpBaseChannel::SetResponseHeader(const nsACString& header, + const nsACString& value, + PRBool merge) +{ + LOG(("HttpBaseChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", + this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), merge)); + + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + nsHttpAtom atom = nsHttp::ResolveAtom(header); + if (!atom) + return NS_ERROR_NOT_AVAILABLE; + + // these response headers must not be changed + if (atom == nsHttp::Content_Type || + atom == nsHttp::Content_Length || + atom == nsHttp::Content_Encoding || + atom == nsHttp::Trailer || + atom == nsHttp::Transfer_Encoding) + return NS_ERROR_ILLEGAL_VALUE; + + mResponseHeadersModified = PR_TRUE; + + return mResponseHead->SetHeader(atom, value, merge); +} + +NS_IMETHODIMP +HttpBaseChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + return mResponseHead->Headers().VisitHeaders(visitor); +} + +NS_IMETHODIMP +HttpBaseChannel::GetAllowPipelining(PRBool *value) +{ + NS_ENSURE_ARG_POINTER(value); + *value = mAllowPipelining; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetAllowPipelining(PRBool value) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + mAllowPipelining = value; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetRedirectionLimit(PRUint32 *value) +{ + NS_ENSURE_ARG_POINTER(value); + *value = mRedirectionLimit; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetRedirectionLimit(PRUint32 value) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + mRedirectionLimit = PR_MIN(value, 0xff); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::IsNoStoreResponse(PRBool *value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + *value = mResponseHead->NoStore(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::IsNoCacheResponse(PRBool *value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + *value = mResponseHead->NoCache(); + if (!*value) + *value = mResponseHead->ExpiresInPast(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetResponseStatus(PRUint32 *aValue) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + *aValue = mResponseHead->Status(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetResponseStatusText(nsACString& aValue) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + aValue = mResponseHead->StatusText(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetRequestSucceeded(PRBool *aValue) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + PRUint32 status = mResponseHead->Status(); + *aValue = (status / 100 == 2); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// HttpBaseChannel::nsIHttpChannelInternal +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpBaseChannel::GetDocumentURI(nsIURI **aDocumentURI) +{ + NS_ENSURE_ARG_POINTER(aDocumentURI); + *aDocumentURI = mDocumentURI; + NS_IF_ADDREF(*aDocumentURI); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetDocumentURI(nsIURI *aDocumentURI) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + mDocumentURI = aDocumentURI; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetRequestVersion(PRUint32 *major, PRUint32 *minor) +{ + nsHttpVersion version = mRequestHead.Version(); + + if (major) { *major = version / 10; } + if (minor) { *minor = version % 10; } + + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetResponseVersion(PRUint32 *major, PRUint32 *minor) +{ + if (!mResponseHead) + { + *major = *minor = 0; // we should at least be kind about it + return NS_ERROR_NOT_AVAILABLE; + } + + nsHttpVersion version = mResponseHead->Version(); + + if (major) { *major = version / 10; } + if (minor) { *minor = version % 10; } + + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetCookie(const char *aCookieHeader) +{ + if (mLoadFlags & LOAD_ANONYMOUS) + return NS_OK; + + // empty header isn't an error + if (!(aCookieHeader && *aCookieHeader)) + return NS_OK; + + nsICookieService *cs = gHttpHandler->GetCookieService(); + NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE); + + return cs->SetCookieStringFromHttp(mURI, + nsnull, + nsnull, + aCookieHeader, + mResponseHead->PeekHeader(nsHttp::Date), + this); +} + +NS_IMETHODIMP +HttpBaseChannel::GetForceAllowThirdPartyCookie(PRBool *aForce) +{ + *aForce = mForceAllowThirdPartyCookie; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetForceAllowThirdPartyCookie(PRBool aForce) +{ + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + mForceAllowThirdPartyCookie = aForce; + return NS_OK; +} + +//------------------------------------------------------------------------------ + +} +} + diff --git a/netwerk/protocol/http/src/HttpBaseChannel.h b/netwerk/protocol/http/src/HttpBaseChannel.h new file mode 100644 index 000000000000..c023c58bfe29 --- /dev/null +++ b/netwerk/protocol/http/src/HttpBaseChannel.h @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Daniel Witte + * + * 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 + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_net_HttpBaseChannel_h +#define mozilla_net_HttpBaseChannel_h + +#include "nsHttp.h" +#include "nsAutoPtr.h" +#include "nsHashPropertyBag.h" +#include "nsProxyInfo.h" +#include "nsHttpRequestHead.h" +#include "nsHttpResponseHead.h" +#include "nsHttpConnectionInfo.h" +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsIProgressEventSink.h" +#include "nsIURI.h" + +#define DIE_WITH_ASYNC_OPEN_MSG() \ + do { \ + fprintf(stderr, \ + "*&*&*&*&*&*&*&**&*&&*& FATAL ERROR: '%s' " \ + "called after AsyncOpen: %s +%d", \ + __FUNCTION__, __FILE__, __LINE__); \ + NS_ABORT(); \ + return NS_ERROR_NOT_IMPLEMENTED; \ + } while (0) + +#define ENSURE_CALLED_BEFORE_ASYNC_OPEN() \ + if (mIsPending) \ + DIE_WITH_ASYNC_OPEN_MSG(); \ + if (mWasOpened) \ + DIE_WITH_ASYNC_OPEN_MSG(); \ + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); \ + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); + +namespace mozilla { +namespace net { + +/* + * This class is a partial implementation of nsIHttpChannel. It contains code + * shared by nsHttpChannel and HttpChannelChild. + * - Note that this class has nothing to do with nsBaseChannel, which is an + * earlier effort at a base class for channels that somehow never made it all + * the way to the HTTP channel. + */ +class HttpBaseChannel : public nsHashPropertyBag + , public nsIHttpChannel + , public nsIHttpChannelInternal +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + HttpBaseChannel(); + virtual ~HttpBaseChannel(); + + nsresult Init(nsIURI *aURI, PRUint8 aCaps, nsProxyInfo *aProxyInfo); + + // nsIRequest + NS_IMETHOD GetName(nsACString& aName); + NS_IMETHOD IsPending(PRBool *aIsPending); + NS_IMETHOD GetStatus(nsresult *aStatus); + NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup); + NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup); + NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags); + NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags); + + // nsIChannel + NS_IMETHOD GetOriginalURI(nsIURI **aOriginalURI); + NS_IMETHOD SetOriginalURI(nsIURI *aOriginalURI); + NS_IMETHOD GetURI(nsIURI **aURI); + NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks); + NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks); + NS_IMETHOD GetContentType(nsACString& aContentType); + NS_IMETHOD SetContentType(const nsACString& aContentType); + NS_IMETHOD GetContentCharset(nsACString& aContentCharset); + NS_IMETHOD SetContentCharset(const nsACString& aContentCharset); + NS_IMETHOD GetContentLength(PRInt32 *aContentLength); + NS_IMETHOD SetContentLength(PRInt32 aContentLength); + NS_IMETHOD Open(nsIInputStream **aResult); + + // HttpBaseChannel::nsIHttpChannel + NS_IMETHOD GetRequestMethod(nsACString& aMethod); + NS_IMETHOD SetRequestMethod(const nsACString& aMethod); + NS_IMETHOD GetReferrer(nsIURI **referrer); + NS_IMETHOD SetReferrer(nsIURI *referrer); + NS_IMETHOD GetRequestHeader(const nsACString& aHeader, nsACString& aValue); + NS_IMETHOD SetRequestHeader(const nsACString& aHeader, + const nsACString& aValue, PRBool aMerge); + NS_IMETHOD VisitRequestHeaders(nsIHttpHeaderVisitor *visitor); + NS_IMETHOD GetResponseHeader(const nsACString &header, nsACString &value); + NS_IMETHOD SetResponseHeader(const nsACString& header, + const nsACString& value, PRBool merge); + NS_IMETHOD VisitResponseHeaders(nsIHttpHeaderVisitor *visitor); + NS_IMETHOD GetAllowPipelining(PRBool *value); + NS_IMETHOD SetAllowPipelining(PRBool value); + NS_IMETHOD GetRedirectionLimit(PRUint32 *value); + NS_IMETHOD SetRedirectionLimit(PRUint32 value); + NS_IMETHOD IsNoStoreResponse(PRBool *value); + NS_IMETHOD IsNoCacheResponse(PRBool *value); + NS_IMETHOD GetResponseStatus(PRUint32 *aValue); + NS_IMETHOD GetResponseStatusText(nsACString& aValue); + NS_IMETHOD GetRequestSucceeded(PRBool *aValue); + + // nsIHttpChannelInternal + NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI); + NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI); + NS_IMETHOD GetRequestVersion(PRUint32 *major, PRUint32 *minor); + NS_IMETHOD GetResponseVersion(PRUint32 *major, PRUint32 *minor); + NS_IMETHOD SetCookie(const char *aCookieHeader); + NS_IMETHOD GetForceAllowThirdPartyCookie(PRBool *aForce); + NS_IMETHOD SetForceAllowThirdPartyCookie(PRBool aForce); + +protected: + nsCOMPtr mURI; + nsCOMPtr mOriginalURI; + nsCOMPtr mDocumentURI; + nsCOMPtr mListener; + nsCOMPtr mListenerContext; + nsCOMPtr mLoadGroup; + nsCOMPtr mCallbacks; + nsCOMPtr mProgressSink; + nsCOMPtr mReferrer; + + nsHttpRequestHead mRequestHead; + nsAutoPtr mResponseHead; + nsRefPtr mConnectionInfo; + + nsCString mSpec; // ASCII encoded URL spec + nsCString mContentTypeHint; + nsCString mContentCharsetHint; + + nsresult mStatus; + PRUint32 mLoadFlags; + PRUint8 mCaps; + PRUint8 mRedirectionLimit; + + PRUint8 mIsPending : 1; + PRUint8 mWasOpened : 1; + PRUint8 mResponseHeadersModified : 1; + PRUint8 mAllowPipelining : 1; + PRUint8 mForceAllowThirdPartyCookie : 1; +}; + + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_HttpBaseChannel_h diff --git a/netwerk/protocol/http/src/HttpChannelChild.cpp b/netwerk/protocol/http/src/HttpChannelChild.cpp index ebf9e2cf96c5..b232f125b123 100644 --- a/netwerk/protocol/http/src/HttpChannelChild.cpp +++ b/netwerk/protocol/http/src/HttpChannelChild.cpp @@ -23,6 +23,7 @@ * * Contributor(s): * Jason Duell + * Daniel Witte * * 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 @@ -47,146 +48,27 @@ #include "nsMimeTypes.h" #include "nsNetUtil.h" -// - TODO: Can we add these checks to nsHttpChannel.cpp too? -#define ENSURE_CALLED_BEFORE_ASYNC_OPEN() \ - if (mIsPending) \ - DROP_DEAD(); \ - if (mWasOpened) \ - DROP_DEAD(); \ - NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); \ - NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); - - namespace mozilla { namespace net { // C++ file contents HttpChannelChild::HttpChannelChild() : mState(HCC_NEW) - // FIELDS COPIED FROM nsHttpChannel.h - , mLoadFlags(LOAD_NORMAL) - , mStatus(NS_OK) - , mIsPending(PR_FALSE) - , mWasOpened(PR_FALSE) { LOG(("Creating HttpChannelChild @%x\n", this)); - - // grab a reference to the handler to ensure that it doesn't go away. - NS_ADDREF(gHttpHandler); } HttpChannelChild::~HttpChannelChild() { LOG(("Destroying HttpChannelChild @%x\n", this)); - - // release our reference to the handler - NS_RELEASE(gHttpHandler); -} - -nsresult -HttpChannelChild::Init(nsIURI *uri) -{ - /** - * COPIED from nsHttpChannel and tweaked: merge into base class? - */ - LOG(("HttpChannelChild::Init [this=%x]\n", this)); - - NS_PRECONDITION(uri, "null uri"); - - nsresult rv = nsHashPropertyBag::Init(); - if (NS_FAILED(rv)) - return rv; - - mURI = uri; - mOriginalURI = uri; - mDocumentURI = nsnull; -// mCaps = caps; - - // - // Construct connection info object - // - nsCAutoString host; - PRInt32 port = -1; - PRBool usingSSL = PR_FALSE; - - rv = mURI->SchemeIs("https", &usingSSL); - if (NS_FAILED(rv)) return rv; - - rv = mURI->GetAsciiHost(host); - if (NS_FAILED(rv)) return rv; - - // reject the URL if it doesn't specify a host - if (host.IsEmpty()) - return NS_ERROR_MALFORMED_URI; - - rv = mURI->GetPort(&port); - if (NS_FAILED(rv)) return rv; - - LOG(("host=%s port=%d\n", host.get(), port)); - - rv = mURI->GetAsciiSpec(mSpec); - if (NS_FAILED(rv)) return rv; - LOG(("uri=%s\n", mSpec.get())); - -#if 0 - // Not yet clear that we need this in child - mConnectionInfo = new nsHttpConnectionInfo(host, port, - proxyInfo, usingSSL); - if (!mConnectionInfo) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mConnectionInfo); -#endif - - // Set default request method - mRequestHead.SetMethod(nsHttp::Get); - -#if 0 - // FIXME (bug 541017): split this out into a separate function so we can share - // with nsHttpChannel. - // - Make sure not to set any headers twice on parent. - - // - // Set request headers - // - nsCAutoString hostLine; - if (strchr(host.get(), ':')) { - // host is an IPv6 address literal and must be encapsulated in []'s - hostLine.Assign('['); - // scope id is not needed for Host header. - int scopeIdPos = host.FindChar('%'); - if (scopeIdPos == kNotFound) - hostLine.Append(host); - else if (scopeIdPos > 0) - hostLine.Append(Substring(host, 0, scopeIdPos)); - else - return NS_ERROR_MALFORMED_URI; - hostLine.Append(']'); - } - else - hostLine.Assign(host); - if (port != -1) { - hostLine.Append(':'); - hostLine.AppendInt(port); - } - - rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); - if (NS_FAILED(rv)) return rv; - - rv = gHttpHandler-> - AddStandardRequestHeaders(&mRequestHead.Headers(), caps, - !mConnectionInfo->UsingSSL() && - mConnectionInfo->UsingHttpProxy()); -#endif /* 0 */ - - return rv; } //----------------------------------------------------------------------------- // HttpChannelChild::nsISupports //----------------------------------------------------------------------------- -NS_IMPL_ADDREF_INHERITED(HttpChannelChild, nsHashPropertyBag) -NS_IMPL_RELEASE_INHERITED(HttpChannelChild, nsHashPropertyBag) +NS_IMPL_ADDREF_INHERITED(HttpChannelChild, HttpBaseChannel) +NS_IMPL_RELEASE_INHERITED(HttpChannelChild, HttpBaseChannel) NS_INTERFACE_MAP_BEGIN(HttpChannelChild) NS_INTERFACE_MAP_ENTRY(nsIRequest) @@ -203,7 +85,7 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelChild) NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel) -NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) +NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) //----------------------------------------------------------------------------- // HttpChannelChild::PHttpChannelChild @@ -218,7 +100,7 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead) mResponseHead = new nsHttpResponseHead(responseHead); - nsresult rv = mChildListener->OnStartRequest(this, mChildListenerContext); + nsresult rv = mListener->OnStartRequest(this, mListenerContext); if (!NS_SUCCEEDED(rv)) { // TODO: Cancel request: // - Send Cancel msg to parent @@ -253,8 +135,8 @@ HttpChannelChild::RecvOnDataAvailable(const nsCString& data, // TODO: what to do here? Cancel request? Very unlikely to fail. return false; } - rv = mChildListener->OnDataAvailable(this, mChildListenerContext, - stringStream, offset, count); + rv = mListener->OnDataAvailable(this, mListenerContext, + stringStream, offset, count); stringStream->Close(); if (!NS_SUCCEEDED(rv)) { // TODO: Cancel request: see notes in OnStartRequest @@ -273,10 +155,9 @@ HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode) mIsPending = PR_FALSE; mStatus = statusCode; - nsresult rv = mChildListener->OnStopRequest(this, mChildListenerContext, - statusCode); - mChildListener = 0; - mChildListenerContext = 0; + nsresult rv = mListener->OnStopRequest(this, mListenerContext, statusCode); + mListener = 0; + mListenerContext = 0; if (!NS_SUCCEEDED(rv)) { // TODO: Cancel request: see notes in OnStartRequest return false; @@ -289,35 +170,7 @@ HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode) //----------------------------------------------------------------------------- NS_IMETHODIMP -HttpChannelChild::GetName(nsACString& aName) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::IsPending(PRBool *retval) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_ENSURE_ARG_POINTER(retval); - *retval = mIsPending; - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetStatus(nsresult *aStatus) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_ENSURE_ARG_POINTER(aStatus); - *aStatus = mStatus; - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::Cancel(nsresult aStatus) +HttpChannelChild::Cancel(nsresult status) { DROP_DEAD(); } @@ -334,211 +187,27 @@ HttpChannelChild::Resume() DROP_DEAD(); } -NS_IMETHODIMP -HttpChannelChild::GetLoadGroup(nsILoadGroup **aLoadGroup) -{ - DROP_DEAD(); -} -NS_IMETHODIMP -HttpChannelChild::SetLoadGroup(nsILoadGroup *aLoadGroup) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::GetLoadFlags(nsLoadFlags *aLoadFlags) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_ENSURE_ARG_POINTER(aLoadFlags); - *aLoadFlags = mLoadFlags; - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::SetLoadFlags(nsLoadFlags aLoadFlags) -{ - ENSURE_CALLED_BEFORE_ASYNC_OPEN(); - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - mLoadFlags = aLoadFlags; - return NS_OK; -} - //----------------------------------------------------------------------------- // HttpChannelChild::nsIChannel //----------------------------------------------------------------------------- - -NS_IMETHODIMP -HttpChannelChild::GetOriginalURI(nsIURI **originalURI) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_ENSURE_ARG_POINTER(originalURI); - *originalURI = mOriginalURI; - NS_ADDREF(*originalURI); - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::SetOriginalURI(nsIURI *originalURI) -{ - ENSURE_CALLED_BEFORE_ASYNC_OPEN(); - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_ENSURE_ARG_POINTER(originalURI); - mOriginalURI = originalURI; - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetURI(nsIURI **URI) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_ENSURE_ARG_POINTER(URI); - *URI = mURI; - NS_IF_ADDREF(*URI); - return NS_OK; -} - NS_IMETHODIMP HttpChannelChild::GetOwner(nsISupports **aOwner) { DROP_DEAD(); } + NS_IMETHODIMP HttpChannelChild::SetOwner(nsISupports *aOwner) { DROP_DEAD(); } -NS_IMETHODIMP -HttpChannelChild::GetNotificationCallbacks(nsIInterfaceRequestor **callbacks) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_IF_ADDREF(*callbacks = mCallbacks); - return NS_OK; -} -NS_IMETHODIMP -HttpChannelChild::SetNotificationCallbacks(nsIInterfaceRequestor *callbacks) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - mCallbacks = callbacks; - mProgressSink = nsnull; - return NS_OK; -} - NS_IMETHODIMP HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo) { DROP_DEAD(); } -NS_IMETHODIMP -HttpChannelChild::GetContentType(nsACString& value) -{ - if (!mResponseHead) { - value.Truncate(); - return NS_ERROR_NOT_AVAILABLE; - } - if (mResponseHead->ContentType().IsEmpty()) { - value.AssignLiteral(UNKNOWN_CONTENT_TYPE); - } else { - value = mResponseHead->ContentType(); - } - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::SetContentType(const nsACString& aContentType) -{ - return BaseClassSetContentType_HACK(aContentType); -} - -nsresult -HttpChannelChild::BaseClassSetContentType_HACK(const nsACString &value) -{ - if (mChildListener || mWasOpened) { - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - - nsCAutoString contentTypeBuf, charsetBuf; - PRBool hadCharset; - net_ParseContentType(value, contentTypeBuf, charsetBuf, &hadCharset); - - mResponseHead->SetContentType(contentTypeBuf); - - // take care not to stomp on an existing charset - if (hadCharset) - mResponseHead->SetContentCharset(charsetBuf); - } - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetContentCharset(nsACString& aContentCharset) -{ - return BaseClassGetContentCharset_HACK(aContentCharset); -} - -nsresult -HttpChannelChild::BaseClassGetContentCharset_HACK(nsACString &value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - - value = mResponseHead->ContentCharset(); - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::SetContentCharset(const nsACString& aContentCharset) -{ - return BaseClassSetContentCharset_HACK(aContentCharset); -} - -nsresult -HttpChannelChild::BaseClassSetContentCharset_HACK(const nsACString &value) -{ - if (mChildListener) { - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - - mResponseHead->SetContentCharset(value); - } - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetContentLength(PRInt32 *aContentLength) -{ - *aContentLength = mResponseHead->ContentLength(); - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::SetContentLength(PRInt32 aContentLength) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::Open(nsIInputStream **retval) -{ - NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); - return NS_ImplementChannelOpen(this, retval); -} - NS_IMETHODIMP HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) { @@ -564,8 +233,14 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) // TODO: currently means "this" has been deleted! bug 529693 DROP_DEAD(); } - mChildListener = listener; - mChildListenerContext = aContext; + mListener = listener; + mListenerContext = aContext; + + // TODO: serialize mConnectionInfo across to the parent, and set it on + // the new channel somehow? + + // TODO: serialize mCaps across to the parent, and set it on + // the new channel somehow? // TODO: need to dupe cookies logic from nsHttpChannel.cpp? @@ -589,11 +264,13 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) } if (!SendAsyncOpen(mSpec, charset, originalSpec, originalCharset, - docSpec, docCharset, mLoadFlags, mRequestHeaders)) { + docSpec, docCharset, mLoadFlags, mRequestHeaders, + mRequestHead.Method(), mRedirectionLimit, mAllowPipelining, + mForceAllowThirdPartyCookie)) { // IPDL error: our destructor will be called automatically // -- TODO: verify that that's the case :) - mChildListener = 0; - mChildListenerContext = 0; + mListener = 0; + mListenerContext = 0; return NS_ERROR_FAILURE; } @@ -608,77 +285,12 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) // HttpChannelChild::nsIHttpChannel //----------------------------------------------------------------------------- -NS_IMETHODIMP -HttpChannelChild::GetRequestMethod(nsACString& method) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - method = mRequestHead.Method(); - return NS_OK; -} -NS_IMETHODIMP -HttpChannelChild::SetRequestMethod(const nsACString& method) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - * - TODO: pass along to parent in AsyncOpen - */ - NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); - - const nsCString& flatMethod = PromiseFlatCString(method); - - // Method names are restricted to valid HTTP tokens. - if (!nsHttp::IsValidToken(flatMethod)) - return NS_ERROR_INVALID_ARG; - - nsHttpAtom atom = nsHttp::ResolveAtom(flatMethod.get()); - if (!atom) - return NS_ERROR_FAILURE; - - mRequestHead.SetMethod(atom); - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetReferrer(nsIURI **aReferrer) -{ - DROP_DEAD(); -} -NS_IMETHODIMP -HttpChannelChild::SetReferrer(nsIURI *aReferrer) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::GetRequestHeader(const nsACString& hdr, nsACString& val) -{ - return BaseClassGetRequestHeader_HACK(hdr, val); -} - -nsresult -HttpChannelChild::BaseClassGetRequestHeader_HACK(const nsACString &header, - nsACString &value) -{ - // XXX might be better to search the header list directly instead of - // hitting the http atom hash table. - - nsHttpAtom atom = nsHttp::ResolveAtom(header); - if (!atom) - return NS_ERROR_NOT_AVAILABLE; - - return mRequestHead.GetHeader(atom, value); -} - NS_IMETHODIMP HttpChannelChild::SetRequestHeader(const nsACString& aHeader, const nsACString& aValue, PRBool aMerge) { - ENSURE_CALLED_BEFORE_ASYNC_OPEN(); - - nsresult rv = BaseClassSetRequestHeader_HACK(aHeader, aValue, aMerge); + nsresult rv = HttpBaseChannel::SetRequestHeader(aHeader, aValue, aMerge); if (NS_FAILED(rv)) return rv; @@ -692,229 +304,16 @@ HttpChannelChild::SetRequestHeader(const nsACString& aHeader, return NS_OK; } -nsresult -HttpChannelChild::BaseClassSetRequestHeader_HACK(const nsACString &header, - const nsACString &value, - PRBool merge) -{ - NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); - - const nsCString &flatHeader = PromiseFlatCString(header); - const nsCString &flatValue = PromiseFlatCString(value); - - LOG(("nsHttpChannel::SetRequestHeader [this=%x header=\"%s\" value=\"%s\" merge=%u]\n", - this, flatHeader.get(), flatValue.get(), merge)); - - // Header names are restricted to valid HTTP tokens. - if (!nsHttp::IsValidToken(flatHeader)) - return NS_ERROR_INVALID_ARG; - - // Header values MUST NOT contain line-breaks. RFC 2616 technically - // permits CTL characters, including CR and LF, in header values provided - // they are quoted. However, this can lead to problems if servers do not - // interpret quoted strings properly. Disallowing CR and LF here seems - // reasonable and keeps things simple. We also disallow a null byte. - if (flatValue.FindCharInSet("\r\n") != kNotFound || - flatValue.Length() != strlen(flatValue.get())) - return NS_ERROR_INVALID_ARG; - - nsHttpAtom atom = nsHttp::ResolveAtom(flatHeader.get()); - if (!atom) { - NS_WARNING("failed to resolve atom"); - return NS_ERROR_NOT_AVAILABLE; - } - - return mRequestHead.SetHeader(atom, flatValue, merge); -} - -NS_IMETHODIMP -HttpChannelChild::VisitRequestHeaders(nsIHttpHeaderVisitor *aVisitor) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::GetAllowPipelining(PRBool *aAllowPipelining) -{ - DROP_DEAD(); -} -NS_IMETHODIMP -HttpChannelChild::SetAllowPipelining(PRBool aAllowPipelining) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::GetRedirectionLimit(PRUint32 *aRedirectionLimit) -{ - DROP_DEAD(); -} -NS_IMETHODIMP -HttpChannelChild::SetRedirectionLimit(PRUint32 aRedirectionLimit) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::GetResponseStatus(PRUint32 *value) -{ - NS_ENSURE_ARG_POINTER(value); - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - *value = mResponseHead->Status(); - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetResponseStatusText(nsACString& value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - value = mResponseHead->StatusText(); - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetRequestSucceeded(PRBool *value) -{ - NS_PRECONDITION(value, "Don't ever pass a null arg to this function"); - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - PRUint32 status = mResponseHead->Status(); - *value = (status / 100 == 2); - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetResponseHeader(const nsACString& header, nsACString& val) -{ - return BaseClassGetResponseHeader_HACK(header, val); -} - -nsresult -HttpChannelChild::BaseClassGetResponseHeader_HACK(const nsACString &header, - nsACString &value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - nsHttpAtom atom = nsHttp::ResolveAtom(header); - if (!atom) - return NS_ERROR_NOT_AVAILABLE; - return mResponseHead->GetHeader(atom, value); -} - -NS_IMETHODIMP -HttpChannelChild::SetResponseHeader(const nsACString& header, - const nsACString& value, - PRBool merge) -{ - return BaseClassSetResponseHeader_HACK(header, value, merge); -} - -nsresult -HttpChannelChild::BaseClassSetResponseHeader_HACK(const nsACString &header, - const nsACString &value, - PRBool merge) -{ - LOG(("nsHttpChannel::SetResponseHeader [this=%x header=\"%s\" value=\"%s\" merge=%u]\n", - this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), merge)); - - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - nsHttpAtom atom = nsHttp::ResolveAtom(header); - if (!atom) - return NS_ERROR_NOT_AVAILABLE; - - // these response headers must not be changed - if (atom == nsHttp::Content_Type || - atom == nsHttp::Content_Length || - atom == nsHttp::Content_Encoding || - atom == nsHttp::Trailer || - atom == nsHttp::Transfer_Encoding) - return NS_ERROR_ILLEGAL_VALUE; - - return mResponseHead->SetHeader(atom, value, merge); -} - -NS_IMETHODIMP -HttpChannelChild::VisitResponseHeaders(nsIHttpHeaderVisitor *aVisitor) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::IsNoStoreResponse(PRBool *retval) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::IsNoCacheResponse(PRBool *retval) -{ - DROP_DEAD(); -} - //----------------------------------------------------------------------------- // HttpChannelChild::nsIHttpChannelInternal //----------------------------------------------------------------------------- -NS_IMETHODIMP -HttpChannelChild::GetDocumentURI(nsIURI **aDocumentURI) -{ - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - NS_ENSURE_ARG_POINTER(aDocumentURI); - *aDocumentURI = mDocumentURI; - NS_IF_ADDREF(*aDocumentURI); - return NS_OK; -} -NS_IMETHODIMP -HttpChannelChild::SetDocumentURI(nsIURI *aDocumentURI) -{ - ENSURE_CALLED_BEFORE_ASYNC_OPEN(); - /** - * COPIED from nsHttpChannel.cpp: move to shared base class? - */ - mDocumentURI = aDocumentURI; - return NS_OK; -} - -NS_IMETHODIMP -HttpChannelChild::GetRequestVersion(PRUint32 *major, PRUint32 *minor) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::GetResponseVersion(PRUint32 *major, PRUint32 *minor) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::SetCookie(const char *aCookieHeader) -{ - DROP_DEAD(); -} - NS_IMETHODIMP HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey) { DROP_DEAD(); } -NS_IMETHODIMP -HttpChannelChild::GetForceAllowThirdPartyCookie(PRBool *force) -{ - DROP_DEAD(); -} - -NS_IMETHODIMP -HttpChannelChild::SetForceAllowThirdPartyCookie(PRBool force) -{ - DROP_DEAD(); -} //----------------------------------------------------------------------------- // HttpChannelChild::nsICachingChannel diff --git a/netwerk/protocol/http/src/HttpChannelChild.h b/netwerk/protocol/http/src/HttpChannelChild.h index 884bf9e8c2b7..07668b90186d 100644 --- a/netwerk/protocol/http/src/HttpChannelChild.h +++ b/netwerk/protocol/http/src/HttpChannelChild.h @@ -23,6 +23,7 @@ * * Contributor(s): * Jason Duell + * Daniel Witte * * 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 @@ -41,15 +42,10 @@ #ifndef mozilla_net_HttpChannelChild_h #define mozilla_net_HttpChannelChild_h +#include "mozilla/net/HttpBaseChannel.h" #include "mozilla/net/PHttpChannelChild.h" -#include "mozilla/net/NeckoCommon.h" -#include "nsHttpRequestHead.h" -#include "nsHashPropertyBag.h" -#include "nsIHttpChannel.h" -#include "nsIHttpChannelInternal.h" #include "nsIStreamListener.h" -#include "nsIURI.h" #include "nsILoadGroup.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" @@ -65,7 +61,6 @@ #include "nsIProxiedChannel.h" #include "nsITraceableChannel.h" - namespace mozilla { namespace net { @@ -80,9 +75,7 @@ enum HttpChannelChildState { // Header file contents class HttpChannelChild : public PHttpChannelChild - , public nsIHttpChannel - , public nsHashPropertyBag - , public nsIHttpChannelInternal + , public HttpBaseChannel , public nsICachingChannel , public nsIUploadChannel , public nsIUploadChannel2 @@ -95,10 +88,6 @@ class HttpChannelChild : public PHttpChannelChild { public: NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIREQUEST - NS_DECL_NSICHANNEL - NS_DECL_NSIHTTPCHANNEL - NS_DECL_NSIHTTPCHANNELINTERNAL NS_DECL_NSICACHINGCHANNEL NS_DECL_NSIUPLOADCHANNEL NS_DECL_NSIUPLOADCHANNEL2 @@ -115,6 +104,24 @@ public: nsresult Init(nsIURI *uri); + // Methods HttpBaseChannel didn't implement for us or that we override. + // + // nsIRequest + NS_IMETHOD Cancel(nsresult status); + NS_IMETHOD Suspend(); + NS_IMETHOD Resume(); + // nsIChannel + NS_IMETHOD GetOwner(nsISupports **aOwner); + NS_IMETHOD SetOwner(nsISupports *aOwner); + NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo); + NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext); + // HttpBaseChannel::nsIHttpChannel + NS_IMETHOD SetRequestHeader(const nsACString& aHeader, + const nsACString& aValue, + PRBool aMerge); + // nsIHttpChannelInternal + NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); + protected: bool RecvOnStartRequest(const nsHttpResponseHead& responseHead); bool RecvOnDataAvailable(const nsCString& data, @@ -123,54 +130,12 @@ protected: bool RecvOnStopRequest(const nsresult& statusCode); private: - nsresult BaseClassSetContentType_HACK(const nsACString &value); - nsresult BaseClassGetContentCharset_HACK(nsACString &value); - nsresult BaseClassSetContentCharset_HACK(const nsACString &value); - nsresult BaseClassSetRequestHeader_HACK(const nsACString &header, - const nsACString &value, - PRBool merge); - nsresult BaseClassGetRequestHeader_HACK(const nsACString &header, - nsACString &value); - nsresult BaseClassGetResponseHeader_HACK(const nsACString &header, - nsACString &value); - nsresult BaseClassSetResponseHeader_HACK(const nsACString &header, - const nsACString &value, - PRBool merge); - - nsCOMPtr mChildListener; - nsCOMPtr mChildListenerContext; - - RequestHeaderTuples mRequestHeaders; - - nsAutoPtr mResponseHead; + RequestHeaderTuples mRequestHeaders; // FIXME: replace with IPDL states (bug 536319) enum HttpChannelChildState mState; - - /** - * fields copied from nsHttpChannel.h - */ - nsCOMPtr mOriginalURI; - nsCOMPtr mURI; - nsCOMPtr mDocumentURI; - - nsCOMPtr mCallbacks; - nsCOMPtr mProgressSink; - - nsHttpRequestHead mRequestHead; - - nsCString mSpec; // ASCII encoded URL spec - - PRUint32 mLoadFlags; - PRUint32 mStatus; - - // state flags - PRUint32 mIsPending : 1; - PRUint32 mWasOpened : 1; - }; - } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/http/src/HttpChannelParent.cpp b/netwerk/protocol/http/src/HttpChannelParent.cpp index ae80406eb80a..2bc49d2a086c 100644 --- a/netwerk/protocol/http/src/HttpChannelParent.cpp +++ b/netwerk/protocol/http/src/HttpChannelParent.cpp @@ -38,9 +38,9 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsHttp.h" #include "mozilla/net/HttpChannelParent.h" #include "nsHttpChannel.h" +#include "nsHttpHandler.h" #include "nsNetUtil.h" namespace mozilla { @@ -49,10 +49,15 @@ namespace net { // C++ file contents HttpChannelParent::HttpChannelParent() { + // Ensure gHttpHandler is initialized: we need the atom table up and running. + nsIHttpProtocolHandler* handler; + CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &handler); + NS_ASSERTION(handler, "no http handler"); } HttpChannelParent::~HttpChannelParent() { + NS_RELEASE(gHttpHandler); } //----------------------------------------------------------------------------- @@ -76,8 +81,15 @@ HttpChannelParent::RecvAsyncOpen(const nsCString& uriSpec, const nsCString& docUriSpec, const nsCString& docCharset, const PRUint32& loadFlags, - const RequestHeaderTuples& requestHeaders) + const RequestHeaderTuples& requestHeaders, + const nsHttpAtom& requestMethod, + const PRUint8& redirectionLimit, + const PRBool& allowPipelining, + const PRBool& forceAllowThirdPartyCookie) { + LOG(("HttpChannelParent RecvAsyncOpen [this=%x uri=%s (%s)]\n", + this, uriSpec.get(), charset.get())); + nsresult rv; nsCOMPtr ios(do_GetIOService(&rv)); @@ -89,16 +101,14 @@ HttpChannelParent::RecvAsyncOpen(const nsCString& uriSpec, if (NS_FAILED(rv)) return false; // TODO: send fail msg to child, return true - // Delay log to here, as gHttpLog may not exist in parent until we init - // gHttpHandler via above call to NS_NewURI. Avoids segfault :) - LOG(("HttpChannelParent RecvAsyncOpen [this=%x uri=%s (%s)]\n", - this, uriSpec.get(), charset.get())); - nsCOMPtr chan; rv = NS_NewChannel(getter_AddRefs(chan), uri, ios, nsnull, nsnull, loadFlags); if (NS_FAILED(rv)) return false; // TODO: send fail msg to child, return true + nsCOMPtr httpChan(do_QueryInterface(chan)); + nsCOMPtr httpChanInt(do_QueryInterface(chan)); + if (!originalUriSpec.IsEmpty()) { nsCOMPtr originalUri; rv = NS_NewURI(getter_AddRefs(originalUri), originalUriSpec, @@ -111,15 +121,12 @@ HttpChannelParent::RecvAsyncOpen(const nsCString& uriSpec, rv = NS_NewURI(getter_AddRefs(docUri), docUriSpec, docCharset.get(), nsnull, ios); if (!NS_FAILED(rv)) { - nsCOMPtr iChan(do_QueryInterface(chan)); - if (iChan) - iChan->SetDocumentURI(docUri); + httpChanInt->SetDocumentURI(docUri); } } if (loadFlags != nsIRequest::LOAD_NORMAL) chan->SetLoadFlags(loadFlags); - nsCOMPtr httpChan(do_QueryInterface(chan)); for (PRUint32 i = 0; i < requestHeaders.Length(); i++) httpChan->SetRequestHeader(requestHeaders[i].mHeader, requestHeaders[i].mValue, @@ -129,6 +136,11 @@ HttpChannelParent::RecvAsyncOpen(const nsCString& uriSpec, // process, or rig up appropriate hacks. // chan->SetNotificationCallbacks(this); + httpChan->SetRequestMethod(nsDependentCString(requestMethod.get())); + httpChan->SetRedirectionLimit(redirectionLimit); + httpChan->SetAllowPipelining(allowPipelining); + httpChanInt->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie); + rv = chan->AsyncOpen(this, nsnull); if (NS_FAILED(rv)) return false; // TODO: send fail msg to child, return true diff --git a/netwerk/protocol/http/src/HttpChannelParent.h b/netwerk/protocol/http/src/HttpChannelParent.h index f178920e3316..c4332531c614 100644 --- a/netwerk/protocol/http/src/HttpChannelParent.h +++ b/netwerk/protocol/http/src/HttpChannelParent.h @@ -72,7 +72,11 @@ protected: const nsCString& docUriSpec, const nsCString& docCharset, const PRUint32& loadFlags, - const RequestHeaderTuples& requestHeaders); + const RequestHeaderTuples& requestHeaders, + const nsHttpAtom& requestMethod, + const PRUint8& redirectionLimit, + const PRBool& allowPipelining, + const PRBool& forceAllowThirdPartyCookie); }; } // namespace net diff --git a/netwerk/protocol/http/src/Makefile.in b/netwerk/protocol/http/src/Makefile.in index cc948d7d0995..1c4e2bd85ada 100644 --- a/netwerk/protocol/http/src/Makefile.in +++ b/netwerk/protocol/http/src/Makefile.in @@ -46,10 +46,14 @@ MODULE = necko LIBRARY_NAME = nkhttp_s LIBXUL_LIBRARY = 1 -ifdef MOZ_IPC EXPORTS_NAMESPACES = mozilla/net EXPORTS_mozilla/net = \ + HttpBaseChannel.h \ + $(NULL) + +ifdef MOZ_IPC +EXPORTS_mozilla/net += \ HttpChannelParent.h \ HttpChannelChild.h \ PHttpChannelParams.h \ @@ -79,6 +83,7 @@ CPPSRCS = \ nsHttpNTLMAuth.cpp \ nsHttpTransaction.cpp \ nsHttpHandler.cpp \ + HttpBaseChannel.cpp \ nsHttpChannel.cpp \ nsHttpPipeline.cpp \ nsHttpActivityDistributor.cpp \ diff --git a/netwerk/protocol/http/src/PHttpChannel.ipdl b/netwerk/protocol/http/src/PHttpChannel.ipdl index 7160f349e921..644509231074 100644 --- a/netwerk/protocol/http/src/PHttpChannel.ipdl +++ b/netwerk/protocol/http/src/PHttpChannel.ipdl @@ -44,6 +44,7 @@ include "mozilla/net/PHttpChannelParams.h"; using RequestHeaderTuples; using nsHttpResponseHead; +using nsHttpAtom; namespace mozilla { namespace net { @@ -67,7 +68,11 @@ parent: nsCString docUriSpec, nsCString docCharset, PRUint32 loadFlags, - RequestHeaderTuples requestHeaders); + RequestHeaderTuples requestHeaders, + nsHttpAtom requestMethod, + PRUint8 redirectionLimit, + PRBool allowPipelining, + PRBool forceAllowThirdPartyCookie); child: OnStartRequest(nsHttpResponseHead responseHead); diff --git a/netwerk/protocol/http/src/PHttpChannelParams.h b/netwerk/protocol/http/src/PHttpChannelParams.h index c3db427f66a6..4f9443b8336b 100644 --- a/netwerk/protocol/http/src/PHttpChannelParams.h +++ b/netwerk/protocol/http/src/PHttpChannelParams.h @@ -108,6 +108,7 @@ struct ParamTraits return false; *aResult = nsHttp::ResolveAtom(value.get()); + NS_ASSERTION(aResult->get(), "atom table not initialized"); return true; } }; diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index 10f08ecc2fa2..5f5f9cab5634 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -28,6 +28,7 @@ * Jan Odvarko * Dave Camp * Honza Bambas + * Daniel Witte * * 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 @@ -44,12 +45,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsHttpChannel.h" -#include "nsHttpTransaction.h" -#include "nsHttpConnection.h" #include "nsHttpHandler.h" -#include "nsHttpAuthCache.h" -#include "nsHttpResponseHead.h" -#include "nsHttp.h" #include "nsIHttpAuthenticator.h" #include "nsIApplicationCacheService.h" #include "nsIApplicationCacheContainer.h" @@ -57,29 +53,16 @@ #include "nsIAuthPrompt2.h" #include "nsIAuthPromptProvider.h" #include "nsIStringBundle.h" -#include "nsXPCOM.h" -#include "nsISupportsPrimitives.h" -#include "nsIURL.h" #include "nsIIDNService.h" #include "nsIStreamListenerTee.h" #include "nsISeekableStream.h" #include "nsMimeTypes.h" -#include "nsNetUtil.h" -#include "nsString.h" #include "nsPrintfCString.h" -#include "nsReadableUtils.h" -#include "nsUnicharUtils.h" -#include "nsAutoPtr.h" -#include "plstr.h" +#include "nsNetUtil.h" #include "prprf.h" #include "nsEscape.h" -#include "nsICookieService.h" -#include "nsIResumableChannel.h" #include "nsInt64.h" -#include "nsIVariant.h" -#include "nsChannelProperties.h" #include "nsStreamUtils.h" -#include "nsIOService.h" #include "nsAuthInformationHolder.h" #include "nsICacheService.h" #include "nsDNSPrefetch.h" @@ -97,15 +80,8 @@ static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); //----------------------------------------------------------------------------- nsHttpChannel::nsHttpChannel() - : mResponseHead(nsnull) - , mTransaction(nsnull) - , mConnectionInfo(nsnull) - , mLoadFlags(LOAD_NORMAL) - , mStatus(NS_OK) - , mLogicalOffset(0) - , mCaps(0) + : mLogicalOffset(0) , mPriority(PRIORITY_NORMAL) - , mCachedResponseHead(nsnull) , mCacheAccess(0) , mPostID(0) , mRequestTime(0) @@ -114,14 +90,9 @@ nsHttpChannel::nsHttpChannel() , mStartPos(LL_MAXUINT) , mPendingAsyncCallOnResume(nsnull) , mSuspendCount(0) - , mRedirectionLimit(gHttpHandler->RedirectionLimit()) - , mIsPending(PR_FALSE) - , mWasOpened(PR_FALSE) , mApplyConversion(PR_TRUE) - , mAllowPipelining(PR_TRUE) , mCachedContentIsValid(PR_FALSE) , mCachedContentIsPartial(PR_FALSE) - , mResponseHeadersModified(PR_FALSE) , mCanceled(PR_FALSE) , mTransactionReplaced(PR_FALSE) , mUploadStreamHasHeaders(PR_FALSE) @@ -139,105 +110,17 @@ nsHttpChannel::nsHttpChannel() , mChooseApplicationCache(PR_FALSE) , mLoadedFromApplicationCache(PR_FALSE) , mTracingEnabled(PR_TRUE) - , mForceAllowThirdPartyCookie(PR_FALSE) , mCustomConditionalRequest(PR_FALSE) { LOG(("Creating nsHttpChannel [this=%p]\n", this)); - - // grab a reference to the handler to ensure that it doesn't go away. - nsHttpHandler *handler = gHttpHandler; - NS_ADDREF(handler); } nsHttpChannel::~nsHttpChannel() { LOG(("Destroying nsHttpChannel [this=%p]\n", this)); - NS_IF_RELEASE(mConnectionInfo); - NS_IF_RELEASE(mTransaction); - NS_IF_RELEASE(mProxyAuthContinuationState); NS_IF_RELEASE(mAuthContinuationState); - - delete mResponseHead; - delete mCachedResponseHead; - - // release our reference to the handler - nsHttpHandler *handler = gHttpHandler; - NS_RELEASE(handler); -} - -nsresult -nsHttpChannel::Init(nsIURI *uri, - PRUint8 caps, - nsProxyInfo *proxyInfo) -{ - LOG(("nsHttpChannel::Init [this=%p]\n", this)); - - NS_PRECONDITION(uri, "null uri"); - - nsresult rv = nsHashPropertyBag::Init(); - if (NS_FAILED(rv)) - return rv; - - mURI = uri; - mOriginalURI = uri; - mDocumentURI = nsnull; - mCaps = caps; - - // - // Construct connection info object - // - nsCAutoString host; - PRInt32 port = -1; - PRBool usingSSL = PR_FALSE; - - rv = mURI->SchemeIs("https", &usingSSL); - if (NS_FAILED(rv)) return rv; - - rv = mURI->GetAsciiHost(host); - if (NS_FAILED(rv)) return rv; - - // reject the URL if it doesn't specify a host - if (host.IsEmpty()) - return NS_ERROR_MALFORMED_URI; - - rv = mURI->GetPort(&port); - if (NS_FAILED(rv)) return rv; - - LOG(("host=%s port=%d\n", host.get(), port)); - - rv = mURI->GetAsciiSpec(mSpec); - if (NS_FAILED(rv)) return rv; - - LOG(("uri=%s\n", mSpec.get())); - - mConnectionInfo = new nsHttpConnectionInfo(host, port, - proxyInfo, usingSSL); - if (!mConnectionInfo) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mConnectionInfo); - - // Set default request method - mRequestHead.SetMethod(nsHttp::Get); - - // - // Set request headers - // - nsCAutoString hostLine; - rv = nsHttpHandler::GenerateHostPort(host, port, hostLine); - if (NS_FAILED(rv)) - return rv; - - rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); - if (NS_FAILED(rv)) return rv; - - rv = gHttpHandler-> - AddStandardRequestHeaders(&mRequestHead.Headers(), caps, - !mConnectionInfo->UsingSSL() && - mConnectionInfo->UsingHttpProxy()); - - return rv; } //----------------------------------------------------------------------------- @@ -650,7 +533,6 @@ nsHttpChannel::SetupTransaction() mTransaction = new nsHttpTransaction(); if (!mTransaction) return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mTransaction); // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. if (mLoadFlags & LOAD_ANONYMOUS) @@ -664,7 +546,7 @@ nsHttpChannel::SetupTransaction() NS_GetCurrentThread(), callbacks, this, getter_AddRefs(responseStream)); if (NS_FAILED(rv)) { - NS_RELEASE(mTransaction); + mTransaction = nsnull; return rv; } @@ -1414,9 +1296,7 @@ nsHttpChannel::ProcessPartialContent() 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; @@ -1497,9 +1377,7 @@ nsHttpChannel::ProcessNotModified() 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; @@ -2027,7 +1905,6 @@ nsHttpChannel::CheckCache() NS_ENSURE_SUCCESS(rv, rv); // Parse the cached HTTP response headers - NS_ASSERTION(!mCachedResponseHead, "memory leak detected"); mCachedResponseHead = new nsHttpResponseHead(); if (!mCachedResponseHead) return NS_ERROR_OUT_OF_MEMORY; @@ -2322,11 +2199,8 @@ nsHttpChannel::ReadFromCache() LOG(("nsHttpChannel::ReadFromCache [this=%p] " "Using cached copy of: %s\n", this, mSpec.get())); - if (mCachedResponseHead) { - NS_ASSERTION(!mResponseHead, "memory leak"); + if (mCachedResponseHead) mResponseHead = mCachedResponseHead; - mCachedResponseHead = 0; - } // if we don't already have security info, try to get it from the cache // entry. there are two cases to consider here: 1) we are just reading @@ -2408,10 +2282,7 @@ nsHttpChannel::CloseCacheEntry(PRBool doomOnFailure) mCacheEntry->Doom(); } - if (mCachedResponseHead) { - delete mCachedResponseHead; - mCachedResponseHead = 0; - } + mCachedResponseHead = nsnull; mCachePump = 0; mCacheEntry = 0; @@ -4117,8 +3988,8 @@ nsHttpChannel::GetCurrentPath(nsACString &path) // nsHttpChannel::nsISupports //----------------------------------------------------------------------------- -NS_IMPL_ADDREF_INHERITED(nsHttpChannel, nsHashPropertyBag) -NS_IMPL_RELEASE_INHERITED(nsHttpChannel, nsHashPropertyBag) +NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel) +NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel) NS_INTERFACE_MAP_BEGIN(nsHttpChannel) NS_INTERFACE_MAP_ENTRY(nsIRequest) @@ -4141,35 +4012,12 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel) NS_INTERFACE_MAP_ENTRY(nsIAuthPromptCallback) -NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) +NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) //----------------------------------------------------------------------------- // nsHttpChannel::nsIRequest //----------------------------------------------------------------------------- -NS_IMETHODIMP -nsHttpChannel::GetName(nsACString &aName) -{ - aName = mSpec; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::IsPending(PRBool *value) -{ - NS_ENSURE_ARG_POINTER(value); - *value = mIsPending; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetStatus(nsresult *aStatus) -{ - NS_ENSURE_ARG_POINTER(aStatus); - *aStatus = mStatus; - return NS_OK; -} - NS_IMETHODIMP nsHttpChannel::Cancel(nsresult status) { @@ -4231,65 +4079,10 @@ nsHttpChannel::Resume() return NS_OK; } -NS_IMETHODIMP -nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) -{ - NS_ENSURE_ARG_POINTER(aLoadGroup); - *aLoadGroup = mLoadGroup; - NS_IF_ADDREF(*aLoadGroup); - return NS_OK; -} -NS_IMETHODIMP -nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) -{ - mLoadGroup = aLoadGroup; - mProgressSink = nsnull; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) -{ - NS_ENSURE_ARG_POINTER(aLoadFlags); - *aLoadFlags = mLoadFlags; - return NS_OK; -} -NS_IMETHODIMP -nsHttpChannel::SetLoadFlags(nsLoadFlags aLoadFlags) -{ - mLoadFlags = aLoadFlags; - return NS_OK; -} - //----------------------------------------------------------------------------- // nsHttpChannel::nsIChannel //----------------------------------------------------------------------------- -NS_IMETHODIMP -nsHttpChannel::GetOriginalURI(nsIURI **originalURI) -{ - NS_ENSURE_ARG_POINTER(originalURI); - *originalURI = mOriginalURI; - NS_ADDREF(*originalURI); - return NS_OK; -} -NS_IMETHODIMP -nsHttpChannel::SetOriginalURI(nsIURI *originalURI) -{ - NS_ENSURE_ARG_POINTER(originalURI); - mOriginalURI = originalURI; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetURI(nsIURI **URI) -{ - NS_ENSURE_ARG_POINTER(URI); - *URI = mURI; - NS_IF_ADDREF(*URI); - return NS_OK; -} - NS_IMETHODIMP nsHttpChannel::GetOwner(nsISupports **owner) { @@ -4298,6 +4091,7 @@ nsHttpChannel::GetOwner(nsISupports **owner) NS_IF_ADDREF(*owner); return NS_OK; } + NS_IMETHODIMP nsHttpChannel::SetOwner(nsISupports *owner) { @@ -4305,20 +4099,6 @@ nsHttpChannel::SetOwner(nsISupports *owner) return NS_OK; } -NS_IMETHODIMP -nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **callbacks) -{ - NS_IF_ADDREF(*callbacks = mCallbacks); - return NS_OK; -} -NS_IMETHODIMP -nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *callbacks) -{ - mCallbacks = callbacks; - mProgressSink = nsnull; - return NS_OK; -} - NS_IMETHODIMP nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo) { @@ -4328,103 +4108,6 @@ nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo) return NS_OK; } -NS_IMETHODIMP -nsHttpChannel::GetContentType(nsACString &value) -{ - if (!mResponseHead) { - // We got no data, we got no headers, we got nothing - value.Truncate(); - return NS_ERROR_NOT_AVAILABLE; - } - - if (!mResponseHead->ContentType().IsEmpty()) { - value = mResponseHead->ContentType(); - return NS_OK; - } - - - value.AssignLiteral(UNKNOWN_CONTENT_TYPE); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetContentType(const nsACString &value) -{ - if (mListener || mWasOpened) { - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - - nsCAutoString contentTypeBuf, charsetBuf; - PRBool hadCharset; - net_ParseContentType(value, contentTypeBuf, charsetBuf, &hadCharset); - - mResponseHead->SetContentType(contentTypeBuf); - - // take care not to stomp on an existing charset - if (hadCharset) - mResponseHead->SetContentCharset(charsetBuf); - } else { - // We are being given a content-type hint. - PRBool dummy; - net_ParseContentType(value, mContentTypeHint, mContentCharsetHint, - &dummy); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetContentCharset(nsACString &value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - - value = mResponseHead->ContentCharset(); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetContentCharset(const nsACString &value) -{ - if (mListener) { - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - - mResponseHead->SetContentCharset(value); - } else { - // Charset hint - mContentCharsetHint = value; - } - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetContentLength(PRInt32 *value) -{ - NS_ENSURE_ARG_POINTER(value); - - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - - // XXX truncates to 32 bit - LL_L2I(*value, mResponseHead->ContentLength()); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetContentLength(PRInt32 value) -{ - NS_NOTYETIMPLEMENTED("nsHttpChannel::SetContentLength"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsHttpChannel::Open(nsIInputStream **_retval) -{ - NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); - return NS_ImplementChannelOpen(this, _retval); -} - NS_IMETHODIMP nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) { @@ -4496,235 +4179,25 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) } return NS_OK; } + //----------------------------------------------------------------------------- -// nsHttpChannel::nsIHttpChannel +// nsHttpChannel::nsIHttpChannelInternal //----------------------------------------------------------------------------- NS_IMETHODIMP -nsHttpChannel::GetRequestMethod(nsACString &method) +nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey) { - method = mRequestHead.Method(); + LOG(("nsHttpChannel::SetupFallbackChannel [this=%x, key=%s]", + this, aFallbackKey)); + mFallbackChannel = PR_TRUE; + mFallbackKey = aFallbackKey; + return NS_OK; } -NS_IMETHODIMP -nsHttpChannel::SetRequestMethod(const nsACString &method) -{ - NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); - - const nsCString &flatMethod = PromiseFlatCString(method); - - // Method names are restricted to valid HTTP tokens. - if (!nsHttp::IsValidToken(flatMethod)) - return NS_ERROR_INVALID_ARG; - - nsHttpAtom atom = nsHttp::ResolveAtom(flatMethod.get()); - if (!atom) - return NS_ERROR_FAILURE; - - mRequestHead.SetMethod(atom); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetReferrer(nsIURI **referrer) -{ - NS_ENSURE_ARG_POINTER(referrer); - *referrer = mReferrer; - NS_IF_ADDREF(*referrer); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetReferrer(nsIURI *referrer) -{ - NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); - - // clear existing referrer, if any - mReferrer = nsnull; - mRequestHead.ClearHeader(nsHttp::Referer); - - if (!referrer) - return NS_OK; - - // check referrer blocking pref - PRUint32 referrerLevel; - if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) - referrerLevel = 1; // user action - else - referrerLevel = 2; // inline content - if (gHttpHandler->ReferrerLevel() < referrerLevel) - return NS_OK; - - nsCOMPtr referrerGrip; - nsresult rv; - PRBool match; - - // - // Strip off "wyciwyg://123/" from wyciwyg referrers. - // - // XXX this really belongs elsewhere since wyciwyg URLs aren't part of necko. - // perhaps some sort of generic nsINestedURI could be used. then, if an URI - // fails the whitelist test, then we could check for an inner URI and try - // that instead. though, that might be too automatic. - // - rv = referrer->SchemeIs("wyciwyg", &match); - if (NS_FAILED(rv)) return rv; - if (match) { - nsCAutoString path; - rv = referrer->GetPath(path); - if (NS_FAILED(rv)) return rv; - - PRUint32 pathLength = path.Length(); - if (pathLength <= 2) return NS_ERROR_FAILURE; - - // Path is of the form "//123/http://foo/bar", with a variable number of digits. - // To figure out where the "real" URL starts, search path for a '/', starting at - // the third character. - PRInt32 slashIndex = path.FindChar('/', 2); - if (slashIndex == kNotFound) return NS_ERROR_FAILURE; - - // Get the charset of the original URI so we can pass it to our fixed up URI. - nsCAutoString charset; - referrer->GetOriginCharset(charset); - - // Replace |referrer| with a URI without wyciwyg://123/. - rv = NS_NewURI(getter_AddRefs(referrerGrip), - Substring(path, slashIndex + 1, pathLength - slashIndex - 1), - charset.get()); - if (NS_FAILED(rv)) return rv; - - referrer = referrerGrip.get(); - } - - // - // block referrer if not on our white list... - // - static const char *const referrerWhiteList[] = { - "http", - "https", - "ftp", - "gopher", - nsnull - }; - match = PR_FALSE; - const char *const *scheme = referrerWhiteList; - for (; *scheme && !match; ++scheme) { - rv = referrer->SchemeIs(*scheme, &match); - if (NS_FAILED(rv)) return rv; - } - if (!match) - return NS_OK; // kick out.... - - // - // Handle secure referrals. - // - // Support referrals from a secure server if this is a secure site - // and (optionally) if the host names are the same. - // - rv = referrer->SchemeIs("https", &match); - if (NS_FAILED(rv)) return rv; - if (match) { - rv = mURI->SchemeIs("https", &match); - if (NS_FAILED(rv)) return rv; - if (!match) - return NS_OK; - - if (!gHttpHandler->SendSecureXSiteReferrer()) { - nsCAutoString referrerHost; - nsCAutoString host; - - rv = referrer->GetAsciiHost(referrerHost); - if (NS_FAILED(rv)) return rv; - - rv = mURI->GetAsciiHost(host); - if (NS_FAILED(rv)) return rv; - - // GetAsciiHost returns lowercase hostname. - if (!referrerHost.Equals(host)) - return NS_OK; - } - } - - nsCOMPtr clone; - // - // we need to clone the referrer, so we can: - // (1) modify it - // (2) keep a reference to it after returning from this function - // - rv = referrer->Clone(getter_AddRefs(clone)); - if (NS_FAILED(rv)) return rv; - - // strip away any userpass; we don't want to be giving out passwords ;-) - clone->SetUserPass(EmptyCString()); - - // strip away any fragment per RFC 2616 section 14.36 - nsCOMPtr url = do_QueryInterface(clone); - if (url) - url->SetRef(EmptyCString()); - - nsCAutoString spec; - rv = clone->GetAsciiSpec(spec); - if (NS_FAILED(rv)) return rv; - - // finally, remember the referrer URI and set the Referer header. - mReferrer = clone; - mRequestHead.SetHeader(nsHttp::Referer, spec); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetRequestHeader(const nsACString &header, nsACString &value) -{ - // XXX might be better to search the header list directly instead of - // hitting the http atom hash table. - - nsHttpAtom atom = nsHttp::ResolveAtom(header); - if (!atom) - return NS_ERROR_NOT_AVAILABLE; - - return mRequestHead.GetHeader(atom, value); -} - -NS_IMETHODIMP -nsHttpChannel::SetRequestHeader(const nsACString &header, - const nsACString &value, - PRBool merge) -{ - NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); - - const nsCString &flatHeader = PromiseFlatCString(header); - const nsCString &flatValue = PromiseFlatCString(value); - - LOG(("nsHttpChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", - this, flatHeader.get(), flatValue.get(), merge)); - - // Header names are restricted to valid HTTP tokens. - if (!nsHttp::IsValidToken(flatHeader)) - return NS_ERROR_INVALID_ARG; - - // Header values MUST NOT contain line-breaks. RFC 2616 technically - // permits CTL characters, including CR and LF, in header values provided - // they are quoted. However, this can lead to problems if servers do not - // interpret quoted strings properly. Disallowing CR and LF here seems - // reasonable and keeps things simple. We also disallow a null byte. - if (flatValue.FindCharInSet("\r\n") != kNotFound || - flatValue.Length() != strlen(flatValue.get())) - return NS_ERROR_INVALID_ARG; - - nsHttpAtom atom = nsHttp::ResolveAtom(flatHeader.get()); - if (!atom) { - NS_WARNING("failed to resolve atom"); - return NS_ERROR_NOT_AVAILABLE; - } - - return mRequestHead.SetHeader(atom, flatValue, merge); -} - -NS_IMETHODIMP -nsHttpChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) -{ - return mRequestHead.Headers().VisitHeaders(visitor); -} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIUploadChannel +//----------------------------------------------------------------------------- NS_IMETHODIMP nsHttpChannel::GetUploadStream(nsIInputStream **stream) @@ -4775,6 +4248,10 @@ nsHttpChannel::SetUploadStream(nsIInputStream *stream, return NS_OK; } +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIUploadChannel2 +//----------------------------------------------------------------------------- + NS_IMETHODIMP nsHttpChannel::ExplicitSetUploadStream(nsIInputStream *aStream, const nsACString &aContentType, @@ -4809,101 +4286,9 @@ nsHttpChannel::ExplicitSetUploadStream(nsIInputStream *aStream, return NS_OK; } -NS_IMETHODIMP -nsHttpChannel::GetResponseStatus(PRUint32 *value) -{ - NS_ENSURE_ARG_POINTER(value); - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - *value = mResponseHead->Status(); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetResponseStatusText(nsACString &value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - value = mResponseHead->StatusText(); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetRequestSucceeded(PRBool *value) -{ - NS_PRECONDITION(value, "Don't ever pass a null arg to this function"); - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - PRUint32 status = mResponseHead->Status(); - *value = (status / 100 == 2); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetResponseHeader(const nsACString &header, nsACString &value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - nsHttpAtom atom = nsHttp::ResolveAtom(header); - if (!atom) - return NS_ERROR_NOT_AVAILABLE; - return mResponseHead->GetHeader(atom, value); -} - -NS_IMETHODIMP -nsHttpChannel::SetResponseHeader(const nsACString &header, - const nsACString &value, - PRBool merge) -{ - LOG(("nsHttpChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", - this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), merge)); - - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - nsHttpAtom atom = nsHttp::ResolveAtom(header); - if (!atom) - return NS_ERROR_NOT_AVAILABLE; - - // these response headers must not be changed - if (atom == nsHttp::Content_Type || - atom == nsHttp::Content_Length || - atom == nsHttp::Content_Encoding || - atom == nsHttp::Trailer || - atom == nsHttp::Transfer_Encoding) - return NS_ERROR_ILLEGAL_VALUE; - - mResponseHeadersModified = PR_TRUE; - - return mResponseHead->SetHeader(atom, value, merge); -} - -NS_IMETHODIMP -nsHttpChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - return mResponseHead->Headers().VisitHeaders(visitor); -} - -NS_IMETHODIMP -nsHttpChannel::IsNoStoreResponse(PRBool *value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - *value = mResponseHead->NoStore(); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::IsNoCacheResponse(PRBool *value) -{ - if (!mResponseHead) - return NS_ERROR_NOT_AVAILABLE; - *value = mResponseHead->NoCache(); - if (!*value) - *value = mResponseHead->ExpiresInPast(); - return NS_OK; -} +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIEncodedChannel +//----------------------------------------------------------------------------- NS_IMETHODIMP nsHttpChannel::GetApplyConversion(PRBool *value) @@ -4921,38 +4306,6 @@ nsHttpChannel::SetApplyConversion(PRBool value) return NS_OK; } -NS_IMETHODIMP -nsHttpChannel::GetAllowPipelining(PRBool *value) -{ - NS_ENSURE_ARG_POINTER(value); - *value = mAllowPipelining; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetAllowPipelining(PRBool value) -{ - if (mIsPending) - return NS_ERROR_FAILURE; - mAllowPipelining = value; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetRedirectionLimit(PRUint32 *value) -{ - NS_ENSURE_ARG_POINTER(value); - *value = PRUint32(mRedirectionLimit); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetRedirectionLimit(PRUint32 value) -{ - mRedirectionLimit = PR_MIN(value, 0xff); - return NS_OK; -} - NS_IMETHODIMP nsHttpChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) { @@ -4975,104 +4328,6 @@ nsHttpChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) return NS_OK; } -//----------------------------------------------------------------------------- -// nsHttpChannel::nsIHttpChannelInternal -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -nsHttpChannel::GetDocumentURI(nsIURI **aDocumentURI) -{ - NS_ENSURE_ARG_POINTER(aDocumentURI); - *aDocumentURI = mDocumentURI; - NS_IF_ADDREF(*aDocumentURI); - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetDocumentURI(nsIURI *aDocumentURI) -{ - mDocumentURI = aDocumentURI; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetForceAllowThirdPartyCookie(PRBool *aForceAllowThirdPartyCookie) -{ - *aForceAllowThirdPartyCookie = mForceAllowThirdPartyCookie; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetForceAllowThirdPartyCookie(PRBool aForceAllowThirdPartyCookie) -{ - mForceAllowThirdPartyCookie = aForceAllowThirdPartyCookie; - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetRequestVersion(PRUint32 *major, PRUint32 *minor) -{ - int version = mRequestHead.Version(); - - if (major) { *major = version / 10; } - if (minor) { *minor = version % 10; } - - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::GetResponseVersion(PRUint32 *major, PRUint32 *minor) -{ - if (!mResponseHead) - { - *major = *minor = 0; // we should at least be kind about it - return NS_ERROR_NOT_AVAILABLE; - } - - int version = mResponseHead->Version(); - - if (major) { *major = version / 10; } - if (minor) { *minor = version % 10; } - - return NS_OK; -} - -NS_IMETHODIMP -nsHttpChannel::SetCookie(const char *aCookieHeader) -{ - if (mLoadFlags & LOAD_ANONYMOUS) { - return NS_OK; - } - - // empty header isn't an error - if (!(aCookieHeader && *aCookieHeader)) - return NS_OK; - - nsICookieService *cs = gHttpHandler->GetCookieService(); - NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE); - - nsCOMPtr prompt; - GetCallback(prompt); - - return cs->SetCookieStringFromHttp(mURI, - mDocumentURI ? mDocumentURI : mOriginalURI, - prompt, - aCookieHeader, - mResponseHead->PeekHeader(nsHttp::Date), - this); -} - -NS_IMETHODIMP -nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey) -{ - LOG(("nsHttpChannel::SetupFallbackChannel [this=%p, key=%s]", - this, aFallbackKey)); - mFallbackChannel = PR_TRUE; - mFallbackKey = aFallbackKey; - - return NS_OK; -} - //----------------------------------------------------------------------------- // nsHttpChannel::nsISupportsPriority //----------------------------------------------------------------------------- @@ -5171,8 +4426,6 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) // don't enter this block if we're reading from the cache... if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) { - NS_ASSERTION(mResponseHead == nsnull, "leaking mResponseHead"); - // all of the response headers have been acquired, so we can take ownership // of them from the transaction. mResponseHead = mTransaction->TakeResponseHead(); @@ -5267,7 +4520,7 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st } // at this point, we're done with the transaction - NS_RELEASE(mTransaction); + mTransaction = nsnull; mTransactionPump = 0; // handle auth retry... @@ -5713,7 +4966,6 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn) mIsPending = PR_TRUE; // get rid of the old response headers - delete mResponseHead; mResponseHead = nsnull; // set sticky connection flag and disable pipelining. diff --git a/netwerk/protocol/http/src/nsHttpChannel.h b/netwerk/protocol/http/src/nsHttpChannel.h index 3786540f4336..f1c903f80c51 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.h +++ b/netwerk/protocol/http/src/nsHttpChannel.h @@ -23,6 +23,7 @@ * Contributor(s): * Darin Fisher (original author) * Christian Biesinger + * Daniel Witte * * 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 @@ -41,43 +42,23 @@ #ifndef nsHttpChannel_h__ #define nsHttpChannel_h__ +#include "HttpBaseChannel.h" + #include "nsHttpTransaction.h" -#include "nsHttpRequestHead.h" #include "nsHttpAuthCache.h" -#include "nsHashPropertyBag.h" #include "nsInputStreamPump.h" #include "nsThreadUtils.h" -#include "nsString.h" -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsInt64.h" -#include "nsIHttpChannel.h" -#include "nsIHttpChannelInternal.h" -#include "nsIHttpHeaderVisitor.h" #include "nsIHttpEventSink.h" -#include "nsIChannelEventSink.h" -#include "nsIStreamListener.h" -#include "nsIIOService.h" -#include "nsIURI.h" -#include "nsILoadGroup.h" -#include "nsIInterfaceRequestor.h" -#include "nsIInterfaceRequestorUtils.h" -#include "nsIInputStream.h" -#include "nsIProgressEventSink.h" #include "nsICachingChannel.h" -#include "nsICacheSession.h" #include "nsICacheEntryDescriptor.h" #include "nsICacheListener.h" #include "nsIApplicationCache.h" #include "nsIApplicationCacheChannel.h" #include "nsIEncodedChannel.h" -#include "nsITransport.h" #include "nsIUploadChannel.h" #include "nsIUploadChannel2.h" #include "nsIStringEnumerator.h" -#include "nsIOutputStream.h" -#include "nsIAsyncInputStream.h" #include "nsIPrompt.h" #include "nsIResumableChannel.h" #include "nsISupportsPriority.h" @@ -87,18 +68,16 @@ #include "nsITraceableChannel.h" #include "nsIAuthPromptCallback.h" -class nsHttpResponseHead; class nsAHttpConnection; class nsIHttpAuthenticator; -class nsProxyInfo; + +using namespace mozilla::net; //----------------------------------------------------------------------------- // nsHttpChannel //----------------------------------------------------------------------------- -class nsHttpChannel : public nsHashPropertyBag - , public nsIHttpChannel - , public nsIHttpChannelInternal +class nsHttpChannel : public HttpBaseChannel , public nsIStreamListener , public nsICachingChannel , public nsIUploadChannel @@ -116,9 +95,6 @@ class nsHttpChannel : public nsHashPropertyBag { public: NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIREQUEST - NS_DECL_NSICHANNEL - NS_DECL_NSIHTTPCHANNEL NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSICACHINGCHANNEL @@ -126,7 +102,6 @@ public: NS_DECL_NSIUPLOADCHANNEL2 NS_DECL_NSICACHELISTENER NS_DECL_NSIENCODEDCHANNEL - NS_DECL_NSIHTTPCHANNELINTERNAL NS_DECL_NSITRANSPORTEVENTSINK NS_DECL_NSIRESUMABLECHANNEL NS_DECL_NSISUPPORTSPRIORITY @@ -140,11 +115,23 @@ public: nsHttpChannel(); virtual ~nsHttpChannel(); - nsresult Init(nsIURI *uri, - PRUint8 capabilities, - nsProxyInfo* proxyInfo); + // Methods HttpBaseChannel didn't implement for us or that we override. + // + // nsIRequest + NS_IMETHOD Cancel(nsresult status); + NS_IMETHOD Suspend(); + NS_IMETHOD Resume(); + // nsIChannel + NS_IMETHOD GetOwner(nsISupports **aOwner); + NS_IMETHOD SetOwner(nsISupports *aOwner); + NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo); + NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext); + // nsIHttpChannelInternal + NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); -public: /* internal; workaround lame compilers */ + + +public: /* internal necko use only */ typedef void (nsHttpChannel:: *nsAsyncCallback)(void); nsHttpResponseHead * GetResponseHead() const { return mResponseHead; } private: @@ -263,43 +250,23 @@ private: nsresult ContinueOnAuthAvailable(const nsCSubstring& creds); private: - nsCOMPtr mOriginalURI; - nsCOMPtr mURI; - nsCOMPtr mDocumentURI; - nsCOMPtr mListener; - nsCOMPtr mListenerContext; - nsCOMPtr mLoadGroup; nsCOMPtr mOwner; - nsCOMPtr mCallbacks; - nsCOMPtr mProgressSink; nsCOMPtr mUploadStream; - nsCOMPtr mReferrer; nsCOMPtr mSecurityInfo; nsCOMPtr mProxyRequest; - nsHttpRequestHead mRequestHead; - nsHttpResponseHead *mResponseHead; - nsRefPtr mTransactionPump; - nsHttpTransaction *mTransaction; // hard ref - nsHttpConnectionInfo *mConnectionInfo; // hard ref + nsRefPtr mTransaction; - nsCString mSpec; // ASCII encoded URL spec - - PRUint32 mLoadFlags; - PRUint32 mStatus; PRUint64 mLogicalOffset; - PRUint8 mCaps; PRInt16 mPriority; - nsCString mContentTypeHint; - nsCString mContentCharsetHint; nsCString mUserSetCookieHeader; // cache specific data nsCOMPtr mCacheEntry; nsRefPtr mCachePump; - nsHttpResponseHead *mCachedResponseHead; + nsAutoPtr mCachedResponseHead; nsCacheAccessMode mCacheAccess; PRUint32 mPostID; PRUint32 mRequestTime; @@ -347,22 +314,15 @@ private: // before we have either a cache pump or a transaction pump. PRUint32 mSuspendCount; - // redirection specific data. - PRUint8 mRedirectionLimit; - // If the channel is associated with a cache, and the URI matched // a fallback namespace, this will hold the key for the fallback // cache entry. nsCString mFallbackKey; // state flags - PRUint32 mIsPending : 1; - PRUint32 mWasOpened : 1; PRUint32 mApplyConversion : 1; - PRUint32 mAllowPipelining : 1; PRUint32 mCachedContentIsValid : 1; PRUint32 mCachedContentIsPartial : 1; - PRUint32 mResponseHeadersModified : 1; PRUint32 mCanceled : 1; PRUint32 mTransactionReplaced : 1; PRUint32 mUploadStreamHasHeaders : 1; @@ -386,7 +346,6 @@ private: PRUint32 mChooseApplicationCache : 1; PRUint32 mLoadedFromApplicationCache : 1; PRUint32 mTracingEnabled : 1; - PRUint32 mForceAllowThirdPartyCookie : 1; // True if consumer added its own If-None-Match or If-Modified-Since // headers. In such a case we must not override them in the cache code // and also we want to pass possible 304 code response through. diff --git a/netwerk/protocol/http/src/nsHttpHandler.cpp b/netwerk/protocol/http/src/nsHttpHandler.cpp index 2d5cc17c40ee..2ba33ef58cc1 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -99,8 +99,8 @@ #endif //----------------------------------------------------------------------------- -#ifdef MOZ_IPC using namespace mozilla::net; +#ifdef MOZ_IPC #include "mozilla/net/HttpChannelChild.h" #endif @@ -1524,7 +1524,7 @@ nsHttpHandler::NewProxiedChannel(nsIURI *uri, nsIProxyInfo* givenProxyInfo, nsIChannel **result) { - nsHttpChannel *httpChannel = nsnull; + nsRefPtr httpChannel; LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n", givenProxyInfo)); @@ -1540,36 +1540,22 @@ nsHttpHandler::NewProxiedChannel(nsIURI *uri, if (NS_FAILED(rv)) return rv; -#if MOZ_IPC +#ifdef MOZ_IPC if (IsNeckoChild()) { LOG(("NECKO_E10S_HTTP set: using experimental interprocess HTTP\n")); - // TODO_JCD: - // - Create a common BaseHttpChannel so can share logic? - HttpChannelChild *childChannel = nsnull; - NS_NEWXPCOM(childChannel, HttpChannelChild); - if (!childChannel) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(childChannel); + // TODO: Just ignore HTTPS and proxying for now if (https) DROP_DEAD(); if (givenProxyInfo) DROP_DEAD(); - // TODO: Init caps, etc, as below? - rv = childChannel->Init(uri); - if (NS_FAILED(rv)) { - NS_RELEASE(childChannel); - return rv; - } - *result = childChannel; - return NS_OK; - } -#endif - NS_NEWXPCOM(httpChannel, nsHttpChannel); - if (!httpChannel) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(httpChannel); + httpChannel = new HttpChannelChild(); + } else +#endif + { + httpChannel = new nsHttpChannel(); + } // select proxy caps if using a non-transparent proxy. SSL tunneling // should not use proxy settings. @@ -1594,13 +1580,10 @@ nsHttpHandler::NewProxiedChannel(nsIURI *uri, } rv = httpChannel->Init(uri, caps, proxyInfo); - - if (NS_FAILED(rv)) { - NS_RELEASE(httpChannel); + if (NS_FAILED(rv)) return rv; - } - *result = httpChannel; + httpChannel.forget(result); return NS_OK; } diff --git a/netwerk/test/unit_ipc/test_bug528292_wrap.js b/netwerk/test/unit_ipc/test_bug528292_wrap.js new file mode 100644 index 000000000000..f81abcdbdbef --- /dev/null +++ b/netwerk/test/unit_ipc/test_bug528292_wrap.js @@ -0,0 +1,3 @@ +function run_test() { + run_test_in_child("../unit/test_bug528292.js"); +}