diff --git a/mozilla/content/base/src/nsScriptLoader.cpp b/mozilla/content/base/src/nsScriptLoader.cpp index 2bead8b3139..9f2c640148e 100644 --- a/mozilla/content/base/src/nsScriptLoader.cpp +++ b/mozilla/content/base/src/nsScriptLoader.cpp @@ -398,9 +398,8 @@ nsScriptLoader::ProcessScriptElement(nsIDOMHTMLScriptElement *aElement, if (httpChannel) { // HTTP content negotation has little value in this context. httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("")); - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("*/*")); + NS_LITERAL_CSTRING("*/*"), + PR_FALSE); httpChannel->SetReferrer(documentURI); } rv = NS_NewStreamLoader(getter_AddRefs(loader), channel, this, reqsup); diff --git a/mozilla/content/html/document/src/nsHTMLContentSink.cpp b/mozilla/content/html/document/src/nsHTMLContentSink.cpp index aebc5dd77bf..3de9e58e1da 100644 --- a/mozilla/content/html/document/src/nsHTMLContentSink.cpp +++ b/mozilla/content/html/document/src/nsHTMLContentSink.cpp @@ -5319,7 +5319,8 @@ HTMLContentSink::ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue, (void)aHeader->GetUnicode(&header); (void)httpChannel->SetResponseHeader( NS_ConvertUCS2toUTF8(header), - NS_ConvertUCS2toUTF8(aValue)); + NS_ConvertUCS2toUTF8(aValue), + PR_TRUE); } } } diff --git a/mozilla/content/html/style/src/nsCSSLoader.cpp b/mozilla/content/html/style/src/nsCSSLoader.cpp index 9adafd5fe39..637e20fa71a 100644 --- a/mozilla/content/html/style/src/nsCSSLoader.cpp +++ b/mozilla/content/html/style/src/nsCSSLoader.cpp @@ -1594,9 +1594,8 @@ CSSLoaderImpl::LoadSheet(URLKey& aKey, SheetLoadData* aData) if (httpChannel) { // send a minimal Accept header for text/css httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("")); - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("text/css,*/*;q=0.1")); + NS_LITERAL_CSTRING("text/css,*/*;q=0.1"), + PR_FALSE); result = httpChannel->SetReferrer(document_uri); } } diff --git a/mozilla/content/xml/document/src/nsXMLContentSink.cpp b/mozilla/content/xml/document/src/nsXMLContentSink.cpp index 59f044e14c3..c200fa5b1d9 100644 --- a/mozilla/content/xml/document/src/nsXMLContentSink.cpp +++ b/mozilla/content/xml/document/src/nsXMLContentSink.cpp @@ -689,9 +689,8 @@ nsXMLContentSink::LoadXSLStyleSheet(nsIURI* aUrl) nsCOMPtr httpChannel(do_QueryInterface(channel)); if (httpChannel) { httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("")); - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1")); + NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"), + PR_FALSE); httpChannel->SetReferrer(mDocumentURL); } @@ -1094,7 +1093,8 @@ nsXMLContentSink::ProcessHeaderData(nsIAtom* aHeader,const nsAString& aValue,nsI (void)aHeader->GetUnicode(&header); (void)httpChannel->SetResponseHeader( NS_ConvertUCS2toUTF8(header), - NS_ConvertUCS2toUTF8(aValue)); + NS_ConvertUCS2toUTF8(aValue), + PR_TRUE); } } } diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index 711fd8f06c0..520807d0d0f 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -5447,7 +5447,7 @@ nsDocShell::AddHeadersToChannel(nsIInputStream * aHeadersData, // FINALLY: we can set the header! // - rv = aChannel->SetRequestHeader(headerName, headerValue); + rv = aChannel->SetRequestHeader(headerName, headerValue, PR_TRUE); if (NS_FAILED(rv)) { return NS_ERROR_NULL_POINTER; } diff --git a/mozilla/extensions/cookie/nsCookieHTTPNotify.cpp b/mozilla/extensions/cookie/nsCookieHTTPNotify.cpp index 7e1651ce07e..60e1279266c 100644 --- a/mozilla/extensions/cookie/nsCookieHTTPNotify.cpp +++ b/mozilla/extensions/cookie/nsCookieHTTPNotify.cpp @@ -191,13 +191,14 @@ nsCookieHTTPNotify::OnModifyRequest(nsIHttpChannel *aHttpChannel) rv = mCookieService->GetCookieStringFromHttp(pURL, pFirstURL, &cookie); if (NS_FAILED(rv)) return rv; - // Clear any existing Cookie request header - rv = aHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Cookie"), NS_LITERAL_CSTRING("")); - if (NS_FAILED(rv)) return rv; - - // Set the cookie into the request headers + const char *headerVal = ""; if (cookie && *cookie) - rv = aHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Cookie"), nsDependentCString(cookie)); + headerVal = cookie; + + // Set the cookie into the request headers overwriting any existing header. + rv = aHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Cookie"), + nsDependentCString(headerVal), + PR_FALSE); nsMemory::Free((void *)cookie); return rv; diff --git a/mozilla/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp b/mozilla/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp index 47ba7552037..ec29fc75baf 100644 --- a/mozilla/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp +++ b/mozilla/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp @@ -791,10 +791,13 @@ nsXMLHttpRequest::GetStreamForWString(const PRUnichar* aStr, nsCAutoString header; if( NS_FAILED(httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), header)) ) httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), - NS_LITERAL_CSTRING("text/xml") ); + NS_LITERAL_CSTRING("text/xml"), + PR_FALSE); // set the content length header - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), nsPrintfCString("%d", charLength) ); + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), + nsPrintfCString("%d", charLength), + PR_FALSE); // Shove in the trailing and leading CRLF postData[0] = nsCRT::CR; @@ -1320,15 +1323,10 @@ nsXMLHttpRequest::SetRequestHeader(const char *header, const char *value) nsCOMPtr httpChannel(do_QueryInterface(mChannel)); if (httpChannel) { - // We need to set, not add to, the header. Using empty value will - // clear existing header. - nsresult rv = httpChannel->SetRequestHeader(nsDependentCString(header), - nsCString()); - if (NS_FAILED(rv)) - return rv; - // Now set it for real + // We need to set, not add to, the header. return httpChannel->SetRequestHeader(nsDependentCString(header), - nsDependentCString(value)); + nsDependentCString(value), + PR_FALSE); } return NS_OK; diff --git a/mozilla/layout/style/nsCSSLoader.cpp b/mozilla/layout/style/nsCSSLoader.cpp index 9adafd5fe39..637e20fa71a 100644 --- a/mozilla/layout/style/nsCSSLoader.cpp +++ b/mozilla/layout/style/nsCSSLoader.cpp @@ -1594,9 +1594,8 @@ CSSLoaderImpl::LoadSheet(URLKey& aKey, SheetLoadData* aData) if (httpChannel) { // send a minimal Accept header for text/css httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("")); - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("text/css,*/*;q=0.1")); + NS_LITERAL_CSTRING("text/css,*/*;q=0.1"), + PR_FALSE); result = httpChannel->SetReferrer(document_uri); } } diff --git a/mozilla/modules/libpr0n/src/imgLoader.cpp b/mozilla/modules/libpr0n/src/imgLoader.cpp index f9c5eaeb1b8..a6061d71343 100644 --- a/mozilla/modules/libpr0n/src/imgLoader.cpp +++ b/mozilla/modules/libpr0n/src/imgLoader.cpp @@ -201,9 +201,8 @@ static nsresult NewImageChannel(nsIChannel **aResult, newHttpChannel = do_QueryInterface(*aResult); if (newHttpChannel) { newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("")); - newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1")); + NS_LITERAL_CSTRING("video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1"), + PR_FALSE); nsCOMPtr httpChannelInternal = do_QueryInterface(newHttpChannel); NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED); diff --git a/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp b/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp index df057985097..990e2f47089 100644 --- a/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp +++ b/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp @@ -1567,7 +1567,7 @@ nsPluginStreamInfo::RequestRead(nsByteRange* rangeList) if(!httpChannel) return NS_ERROR_FAILURE; - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString); + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE); mPluginStreamListenerPeer->mAbort = PR_TRUE; // instruct old stream listener to cancel // the request on the next ODA. @@ -5822,7 +5822,7 @@ nsPluginHostImpl::AddHeadersToChannel(const char *aHeadersData, // FINALLY: we can set the header! // - rv = aChannel->SetRequestHeader(headerName, headerValue); + rv = aChannel->SetRequestHeader(headerName, headerValue, PR_TRUE); if (NS_FAILED(rv)) { rv = NS_ERROR_NULL_POINTER; return rv; diff --git a/mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl b/mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl index 205b588cc59..05bdba784ee 100644 --- a/mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl +++ b/mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl @@ -25,62 +25,94 @@ #include "nsIChannel.idl" interface nsIHttpHeaderVisitor; -interface nsISimpleEnumerator; -[scriptable, uuid(d78b53c8-d03f-4fd8-b2ee-7b36fcd150d1)] +/** + * nsIHttpChannel + * + * This interface allows for the modification of HTTP request parameters and + * the inspection of the resulting HTTP response status and headers when they + * become available. + * + * @status FROZEN + */ +[scriptable, uuid(9277fe09-f0cc-4cd9-bbce-581dd94b0260)] interface nsIHttpChannel : nsIChannel { /************************************************************************** - * Request info... + * REQUEST CONFIGURATION + * + * Modifying request parameters after asyncOpen has been called is an error. */ /** - * The request method is case insensitive + * Set/get the HTTP request method (default is "GET"). Setter is case + * insensitive; getter returns an uppercase string. + * + * NOTE: The data for a "POST" or "PUT" request can be configured via + * nsIUploadChannel; however, after setting the upload data, it may be + * necessary to set the request method explicitly. The documentation + * for nsIUploadChannel has further details. */ attribute ACString requestMethod; /** - * Get/set the referrer URI on the request. This is the address (URI) of - * the resource from which this channel's URI was obtained (see RFC2616 - * section 14.36). + * Get/set the HTTP referrer URI. This is the address (URI) of the + * resource from which this channel's URI was obtained (see RFC2616 section + * 14.36). + * + * NOTE: The channel may silently refuse to set the Referer header if the + * URI does not pass certain security checks (e.g., a "https://" URL will + * never be sent as the referrer for a plaintext HTTP request). The + * implementation is not required to throw an exception when the referrer + * URI is rejected. */ attribute nsIURI referrer; /** - * Header strings are case insensitive. Setting a header is additive; to - * clear a header, assign an empty string. + * Get the value of a particular request header. + * + * @param aHeader + * The case-insensitive name of the request header to query (e.g., + * "Cache-Control"). + * + * @return the value of the request header. */ - ACString getRequestHeader(in ACString header); - void setRequestHeader(in ACString header, in ACString value); - void visitRequestHeaders(in nsIHttpHeaderVisitor visitor); - - /************************************************************************** - * Response info... - */ - - readonly attribute unsigned long responseStatus; - readonly attribute ACString responseStatusText; - readonly attribute boolean requestSucceeded; + ACString getRequestHeader(in ACString aHeader); /** - * Header strings are case insensitive. Setting a header is additive; to - * clear a header, assign an empty string. + * Set the value of a particular request header. + * + * This method allows, for example, the cookies module to add "Cookie" + * headers to the outgoing HTTP request. + * + * @param aHeader + * The case-insensitive name of the request header to set (e.g., + * "Cookie"). + * @param aValue + * The request header value to set (e.g., "X=1"). + * @param aMerge + * If true, the new header value will be merged with any existing + * values for the specified header. This flag is ignored if the + * specified header does not support merging (e.g., the "Content- + * Type" header can only have one value). The list of headers for + * which this flag is ignored is an implementation detail. If this + * flag is false, then the header value will be replaced with the + * contents of |aValue|. + * + * If aValue is empty and aMerge is false, the header will be cleared. */ - ACString getResponseHeader(in ACString header); - void setResponseHeader(in ACString header, in ACString value); - void visitResponseHeaders(in nsIHttpHeaderVisitor visitor); + void setRequestHeader(in ACString aHeader, + in ACString aValue, + in boolean aMerge); /** - * True if the server sent a "Cache-control: no-store" response header. + * Call this method to visit all request headers. Calling setRequestHeader + * while visiting request headers has undefined behavior. Don't do it! + * + * @param aVisitor + * the header visitor instance. */ - boolean isNoStoreResponse(); - - /** - * True if the server sent the equivalent of a "Cache-control: no-cache" - * response header. Other equivalent response headers include: "Pragma: - * no-cache" and "Expires" with a date-value in the past. - */ - boolean isNoCacheResponse(); + void visitRequestHeaders(in nsIHttpHeaderVisitor aVisitor); /** * This attribute is a hint to the channel to indicate whether or not @@ -89,7 +121,8 @@ interface nsIHttpChannel : nsIChannel * if the application knows that the corresponding document is likely * to be very large. * - * TRUE by default, though other factors may prevent pipelining. + * This attribute is true by default, though other factors may prevent + * pipelining. */ attribute boolean allowPipelining; @@ -97,6 +130,107 @@ interface nsIHttpChannel : nsIChannel * This attribute specifies the number of redirects this channel is allowed * to make. If zero, the channel will fail to redirect and will generate * a NS_ERROR_REDIRECT_LOOP failure status. + * + * NOTE: An HTTP redirect results in a new channel being created. If the + * new channel supports nsIHttpChannel, then it will be assigned a value + * to its |redirectionLimit| attribute one less than the value of the + * redirected channel's |redirectionLimit| attribute. The initial value + * for this attribute may be a configurable preference (depending on the + * implementation). */ attribute unsigned long redirectionLimit; + + + /************************************************************************** + * RESPONSE INFO + * + * Accessing response info before the onStartRequest event is an error. + */ + + /** + * Get the HTTP response code (e.g., 200). + */ + readonly attribute unsigned long responseStatus; + + /** + * Get the HTTP response status text (e.g., "OK"). + * + * NOTE: This returns the raw (possibly 8-bit) text from the server. There + * are no assumptions made about the charset of the returned text. You + * have been warned! + */ + readonly attribute ACString responseStatusText; + + /** + * Returns true if the HTTP response code indicates success. The value of + * nsIRequest::status will be NS_OK even when processing a 404 response + * because a 404 response may include a message body that (in some cases) + * should be shown to the user. + * + * Use this attribute to distinguish server error pages from normal pages, + * instead of comparing the response status manually against the set of + * valid response codes, if that is required by your application. + */ + readonly attribute boolean requestSucceeded; + + /** + * Get the value of a particular response header. + * + * @param aHeader + * The case-insensitive name of the response header to query (e.g., + * "Set-Cookie"). + * + * @return the value of the response header. + */ + ACString getResponseHeader(in ACString header); + + /** + * Set the value of a particular response header. + * + * This method allows, for example, the HTML content sink to inform the HTTP + * channel about HTTP-EQUIV headers found in HTML tags. + * + * @param aHeader + * The case-insensitive name of the response header to set (e.g., + * "Cache-control"). + * @param aValue + * The response header value to set (e.g., "no-cache"). + * @param aMerge + * If true, the new header value will be merged with any existing + * values for the specified header. This flag is ignored if the + * specified header does not support merging (e.g., the "Content- + * Type" header can only have one value). The list of headers for + * which this flag is ignored is an implementation detail. If this + * flag is false, then the header value will be replaced with the + * contents of |aValue|. + * + * If aValue is empty and aMerge is false, the header will be cleared. + */ + void setResponseHeader(in ACString header, + in ACString value, + in boolean merge); + + /** + * Call this method to visit all response headers. Calling + * setResponseHeader while visiting response headers has undefined + * behavior. Don't do it! + * + * @param aVisitor + * the header visitor instance. + */ + void visitResponseHeaders(in nsIHttpHeaderVisitor aVisitor); + + /** + * Returns true if the server sent a "Cache-Control: no-store" response + * header. + */ + boolean isNoStoreResponse(); + + /** + * Returns true if the server sent the equivalent of a "Cache-control: + * no-cache" response header. Equivalent response headers include: + * "Pragma: no-cache", "Expires: 0", and "Expires" with a date value + * in the past relative to the value of the "Date" header. + */ + boolean isNoCacheResponse(); }; diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp index 16b582c7cdd..73ecca04416 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -492,11 +492,11 @@ nsHttpChannel::SetupTransaction() // We need to send 'Pragma:no-cache' to inhibit proxy caching even if // no proxy is configured since we might be talking with a transparent // proxy, i.e. one that operates at the network level. See bug #14772. - mRequestHead.SetHeader(nsHttp::Pragma, NS_LITERAL_CSTRING("no-cache")); + mRequestHead.SetHeader(nsHttp::Pragma, NS_LITERAL_CSTRING("no-cache"), PR_TRUE); // If we're configured to speak HTTP/1.1 then also send 'Cache-control: // no-cache' if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1) - mRequestHead.SetHeader(nsHttp::Cache_Control, NS_LITERAL_CSTRING("no-cache")); + mRequestHead.SetHeader(nsHttp::Cache_Control, NS_LITERAL_CSTRING("no-cache"), PR_TRUE); } else if ((mLoadFlags & VALIDATE_ALWAYS) && (mCacheAccess & nsICache::ACCESS_READ)) { // We need to send 'Cache-Control: max-age=0' to force each cache along @@ -505,9 +505,9 @@ nsHttpChannel::SetupTransaction() // // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache' if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1) - mRequestHead.SetHeader(nsHttp::Cache_Control, NS_LITERAL_CSTRING("max-age=0")); + mRequestHead.SetHeader(nsHttp::Cache_Control, NS_LITERAL_CSTRING("max-age=0"), PR_TRUE); else - mRequestHead.SetHeader(nsHttp::Pragma, NS_LITERAL_CSTRING("no-cache")); + mRequestHead.SetHeader(nsHttp::Pragma, NS_LITERAL_CSTRING("no-cache"), PR_TRUE); } return mTransaction->SetupRequest(&mRequestHead, mUploadStream, @@ -688,13 +688,13 @@ nsHttpChannel::ProcessNormal() mResponseHead->ContentType().Equals(NS_LITERAL_CSTRING(APPLICATION_GZIP2)) || mResponseHead->ContentType().Equals(NS_LITERAL_CSTRING(APPLICATION_GZIP3)))) { // clear the Content-Encoding header - mResponseHead->SetHeader(nsHttp::Content_Encoding, NS_LITERAL_CSTRING("")); + mResponseHead->ClearHeader(nsHttp::Content_Encoding); } else if (encoding && PL_strcasestr(encoding, "compress") && ( mResponseHead->ContentType().Equals(NS_LITERAL_CSTRING(APPLICATION_COMPRESS)) || mResponseHead->ContentType().Equals(NS_LITERAL_CSTRING(APPLICATION_COMPRESS2)))) { // clear the Content-Encoding header - mResponseHead->SetHeader(nsHttp::Content_Encoding, NS_LITERAL_CSTRING("")); + mResponseHead->ClearHeader(nsHttp::Content_Encoding); } // this must be called before firing OnStartRequest, since http clients, @@ -1141,8 +1141,8 @@ nsHttpChannel::CheckCache() PRBool doValidation = PR_FALSE; // Be optimistic: assume that we won't need to do validation - mRequestHead.SetHeader(nsHttp::If_Modified_Since, NS_LITERAL_CSTRING("")); - mRequestHead.SetHeader(nsHttp::If_None_Match, NS_LITERAL_CSTRING("")); + mRequestHead.ClearHeader(nsHttp::If_Modified_Since); + mRequestHead.ClearHeader(nsHttp::If_None_Match); // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used. if (mLoadFlags & LOAD_FROM_CACHE) { @@ -2511,7 +2511,7 @@ nsHttpChannel::SetReferrer(nsIURI *referrer) // clear existing referrer, if any mReferrer = nsnull; - mRequestHead.SetHeader(nsHttp::Referer, NS_LITERAL_CSTRING("")); + mRequestHead.ClearHeader(nsHttp::Referer); if (!referrer) return NS_OK; @@ -2656,12 +2656,14 @@ nsHttpChannel::GetRequestHeader(const nsACString &header, nsACString &value) } NS_IMETHODIMP -nsHttpChannel::SetRequestHeader(const nsACString &header, const nsACString &value) +nsHttpChannel::SetRequestHeader(const nsACString &header, + const nsACString &value, + PRBool merge) { NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); - LOG(("nsHttpChannel::SetRequestHeader [this=%x header=%s value=%s]\n", - this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get())); + LOG(("nsHttpChannel::SetRequestHeader [this=%x header=\"%s\" value=\"%s\" merge=%u]\n", + this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), merge)); nsHttpAtom atom = nsHttp::ResolveAtom(header); if (!atom) { @@ -2669,7 +2671,7 @@ nsHttpChannel::SetRequestHeader(const nsACString &header, const nsACString &valu return NS_ERROR_NOT_AVAILABLE; } - return mRequestHead.SetHeader(atom, value); + return mRequestHead.SetHeader(atom, value, merge); } NS_IMETHODIMP @@ -2766,10 +2768,12 @@ nsHttpChannel::GetResponseHeader(const nsACString &header, nsACString &value) } NS_IMETHODIMP -nsHttpChannel::SetResponseHeader(const nsACString &header, const nsACString &value) +nsHttpChannel::SetResponseHeader(const nsACString &header, + const nsACString &value, + PRBool merge) { - LOG(("nsHttpChannel::SetResponseHeader [this=%x header=\"%s\" value=\"%s\"]\n", - this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get())); + 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; @@ -2785,7 +2789,7 @@ nsHttpChannel::SetResponseHeader(const nsACString &header, const nsACString &val atom == nsHttp::Transfer_Encoding) return NS_ERROR_ILLEGAL_VALUE; - nsresult rv = mResponseHead->SetHeader(atom, value); + nsresult rv = mResponseHead->SetHeader(atom, value, merge); // XXX temporary hack until http supports some form of a header change observer if ((atom == nsHttp::Set_Cookie) && NS_SUCCEEDED(rv)) diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp b/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp index 1e0a5ba67af..b32829d9d85 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -295,19 +295,22 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, // with HTTP/1.0 servers/proxies. We use "Proxy-Connection:" when // we're talking to an http proxy, and "Connection:" otherwise - const char* connectionType = "close"; + NS_NAMED_LITERAL_CSTRING(close, "close"); + NS_NAMED_LITERAL_CSTRING(keepAlive, "keep-alive"); + + const nsACString *connectionType = &close; if (caps & NS_HTTP_ALLOW_KEEPALIVE) { rv = request->SetHeader(nsHttp::Keep_Alive, nsPrintfCString("%u", mIdleTimeout)); if (NS_FAILED(rv)) return rv; - connectionType = "keep-alive"; + connectionType = &keepAlive; } else if (useProxy) { // Bug 92006 - request->SetHeader(nsHttp::Connection, NS_LITERAL_CSTRING("close")); + request->SetHeader(nsHttp::Connection, close); } - const nsHttpAtom &header = - useProxy ? nsHttp::Proxy_Connection : nsHttp::Connection; - return request->SetHeader(header, nsDependentCString(connectionType)); + const nsHttpAtom &header = useProxy ? nsHttp::Proxy_Connection + : nsHttp::Connection; + return request->SetHeader(header, *connectionType); } PRBool diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp index 1782b9af7b1..bbe4d5b31f5 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp @@ -29,22 +29,26 @@ //----------------------------------------------------------------------------- nsresult -nsHttpHeaderArray::SetHeader(nsHttpAtom header, const nsACString &value) +nsHttpHeaderArray::SetHeader(nsHttpAtom header, + const nsACString &value, + PRBool merge) { nsEntry *entry = nsnull; PRInt32 index; - // If an empty value is passed in, then delete the header entry... index = LookupEntry(header, &entry); + + // If an empty value is passed in, then delete the header entry... + // unless we are merging, in which case this function becomes a NOP. if (value.IsEmpty()) { - if (entry) { + if (!merge && entry) { mHeaders.RemoveElementAt(index); delete entry; } return NS_OK; } - // Create a new entry or... + // Create a new entry, or... if (!entry) { entry = new nsEntry(header, value); if (!entry) @@ -55,7 +59,7 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header, const nsACString &value) } } // Append the new value to the existing value iff... - else if (CanAppendToHeader(header)) { + else if (merge && CanAppendToHeader(header)) { if (header == nsHttp::Set_Cookie || header == nsHttp::WWW_Authenticate || header == nsHttp::Proxy_Authenticate) @@ -74,6 +78,19 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header, const nsACString &value) return NS_OK; } +void +nsHttpHeaderArray::ClearHeader(nsHttpAtom header) +{ + nsEntry *entry = nsnull; + PRInt32 index; + + index = LookupEntry(header, &entry); + if (entry) { + mHeaders.RemoveElementAt(index); + delete entry; + } +} + const char * nsHttpHeaderArray::PeekHeader(nsHttpAtom header) { @@ -219,18 +236,16 @@ nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry) PRBool nsHttpHeaderArray::CanAppendToHeader(nsHttpAtom header) { - return header == nsHttp::Accept_Charset || - header == nsHttp::Content_Type || - header == nsHttp::User_Agent || - header == nsHttp::Referer || - header == nsHttp::Host || - header == nsHttp::Authorization || - header == nsHttp::Proxy_Authorization || - header == nsHttp::If_Modified_Since || - header == nsHttp::If_Unmodified_Since || - header == nsHttp::From || - header == nsHttp::Location || - header == nsHttp::Max_Forwards - ? - PR_FALSE : PR_TRUE; + return header != nsHttp::Content_Type && + header != nsHttp::Content_Length && + header != nsHttp::User_Agent && + header != nsHttp::Referer && + header != nsHttp::Host && + header != nsHttp::Authorization && + header != nsHttp::Proxy_Authorization && + header != nsHttp::If_Modified_Since && + header != nsHttp::If_Unmodified_Since && + header != nsHttp::From && + header != nsHttp::Location && + header != nsHttp::Max_Forwards; } diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h index 3dd9a01bec0..3d1c6f2ea78 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h @@ -39,8 +39,9 @@ public: const char *PeekHeader(nsHttpAtom header); - nsresult SetHeader(nsHttpAtom header, const nsACString &value); + nsresult SetHeader(nsHttpAtom header, const nsACString &value, PRBool merge = PR_FALSE); nsresult GetHeader(nsHttpAtom header, nsACString &value); + void ClearHeader(nsHttpAtom h); nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor); diff --git a/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h b/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h index 5993414b306..6776d6f24ff 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h @@ -49,10 +49,11 @@ public: nsHttpVersion Version() { return mVersion; } const nsAFlatCString &RequestURI() { return mRequestURI; } - const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); } - nsresult SetHeader(nsHttpAtom h, const nsACString &v) { return mHeaders.SetHeader(h, v); } - nsresult GetHeader(nsHttpAtom h, nsACString &v) { return mHeaders.GetHeader(h, v); } - void ClearHeaders() { mHeaders.Clear(); } + const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); } + nsresult SetHeader(nsHttpAtom h, const nsACString &v, PRBool m=PR_FALSE) { return mHeaders.SetHeader(h, v, m); } + nsresult GetHeader(nsHttpAtom h, nsACString &v) { return mHeaders.GetHeader(h, v); } + void ClearHeader(nsHttpAtom h) { mHeaders.ClearHeader(h); } + void ClearHeaders() { mHeaders.Clear(); } void Flatten(nsACString &, PRBool pruneProxyHeaders = PR_FALSE); diff --git a/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp index 4f3cc0cc7fd..0847c216a1a 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp @@ -34,16 +34,19 @@ //----------------------------------------------------------------------------- nsresult -nsHttpResponseHead::SetHeader(nsHttpAtom hdr, const nsACString &val) +nsHttpResponseHead::SetHeader(nsHttpAtom hdr, + const nsACString &val, + PRBool merge) { - nsresult rv = mHeaders.SetHeader(hdr, val); + nsresult rv = mHeaders.SetHeader(hdr, val, merge); if (NS_FAILED(rv)) return rv; - // response to changes in these headers + // respond to changes in these headers. we need to reparse the entire + // header since the change may have merged in additional values. if (hdr == nsHttp::Cache_Control) - ParseCacheControl(PromiseFlatCString(val).get()); + ParseCacheControl(mHeaders.PeekHeader(hdr)); else if (hdr == nsHttp::Pragma) - ParsePragma(PromiseFlatCString(val).get()); + ParsePragma(mHeaders.PeekHeader(hdr)); return NS_OK; } @@ -53,9 +56,9 @@ nsHttpResponseHead::SetContentLength(PRInt32 len) { mContentLength = len; if (len < 0) - SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("")); + mHeaders.ClearHeader(nsHttp::Content_Length); else - SetHeader(nsHttp::Content_Length, nsPrintfCString("%d", len)); + mHeaders.SetHeader(nsHttp::Content_Length, nsPrintfCString("%d", len)); } void @@ -64,19 +67,16 @@ nsHttpResponseHead::Flatten(nsACString &buf, PRBool pruneTransients) if (mVersion == NS_HTTP_VERSION_0_9) return; - buf.Append("HTTP/"); + buf.Append(NS_LITERAL_CSTRING("HTTP/")); if (mVersion == NS_HTTP_VERSION_1_1) - buf.Append("1.1 "); + buf.Append(NS_LITERAL_CSTRING("1.1 ")); else - buf.Append("1.0 "); + buf.Append(NS_LITERAL_CSTRING("1.0 ")); - char b[32]; - PR_snprintf(b, sizeof(b), "%u", PRUintn(mStatus)); - - buf.Append(b); - buf.Append(' '); - buf.Append(mStatusText); - buf.Append("\r\n"); + buf.Append(nsPrintfCString("%u", PRUintn(mStatus)) + + NS_LITERAL_CSTRING(" ") + + mStatusText + + NS_LITERAL_CSTRING("\r\n")); if (!pruneTransients) { mHeaders.Flatten(buf, PR_FALSE); @@ -104,10 +104,10 @@ nsHttpResponseHead::Flatten(nsACString &buf, PRBool pruneTransients) continue; // otherwise, write out the "header: value\r\n" line - buf.Append(header.get()); - buf.Append(": "); - buf.Append(value); - buf.Append("\r\n"); + buf.Append(nsDependentCString(header.get()) + + NS_LITERAL_CSTRING(": ") + + nsDependentCString(value) + + NS_LITERAL_CSTRING("\r\n")); } } @@ -183,7 +183,7 @@ nsHttpResponseHead::ParseStatusLine(char *line) void nsHttpResponseHead::ParseHeaderLine(char *line) { - nsHttpAtom hdr; + nsHttpAtom hdr = {0}; char *val; mHeaders.ParseHeaderLine(line, &hdr, &val); @@ -427,11 +427,8 @@ nsHttpResponseHead::UpdateHeaders(nsHttpHeaderArray &headers) else { LOG(("new response header [%s: %s]\n", header.get(), val)); - // delete the current header value (if any) - mHeaders.SetHeader(header, NS_LITERAL_CSTRING("")); - - // copy the new header value... - mHeaders.SetHeader(header, nsDependentCString(val)); + // overwrite the current header value with the new value... + SetHeader(header, nsDependentCString(val)); } } @@ -633,19 +630,18 @@ nsHttpResponseHead::ParseContentType(char *type) void nsHttpResponseHead::ParseCacheControl(const char *val) { - if (!val) { - // clear no-cache flag + if (!(val && *val)) { + // clear flags mCacheControlNoCache = PR_FALSE; + mCacheControlNoStore = PR_FALSE; return; } - else if (!*val) - return; const char *s = val; // search header value for occurance(s) of "no-cache" but ignore // occurance(s) of "no-cache=blah" - while (s = PL_strcasestr(s, "no-cache")) { + while ((s = PL_strcasestr(s, "no-cache")) != nsnull) { s += (sizeof("no-cache") - 1); if (*s != '=') mCacheControlNoCache = PR_TRUE; @@ -661,15 +657,15 @@ nsHttpResponseHead::ParsePragma(const char *val) { LOG(("nsHttpResponseHead::ParsePragma [val=%s]\n", val)); - if (!val) { + if (!(val && *val)) { // clear no-cache flag mPragmaNoCache = PR_FALSE; return; } - // Although 'Pragma:no-cache' is not a standard HTTP response header (it's + // Although 'Pragma: no-cache' is not a standard HTTP response header (it's // a request header), caching is inhibited when this header is present so // as to match existing Navigator behavior. - if (*val && PL_strcasestr(val, "no-cache")) + if (PL_strcasestr(val, "no-cache")) mPragmaNoCache = PR_TRUE; } diff --git a/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h index a7b966d1016..d7ebee69fc9 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h @@ -58,8 +58,9 @@ public: PRBool NoCache() { return (mCacheControlNoCache || mPragmaNoCache); } const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); } - nsresult SetHeader(nsHttpAtom h, const nsACString &v); + nsresult SetHeader(nsHttpAtom h, const nsACString &v, PRBool m=PR_FALSE); nsresult GetHeader(nsHttpAtom h, nsACString &v) { return mHeaders.GetHeader(h, v); } + void ClearHeader(nsHttpAtom h) { mHeaders.ClearHeader(h); } void ClearHeaders() { mHeaders.Clear(); } void SetContentType(const nsACString &s) { mContentType = s; } diff --git a/mozilla/netwerk/protocol/viewsource/src/nsViewSourceChannel.cpp b/mozilla/netwerk/protocol/viewsource/src/nsViewSourceChannel.cpp index 68d364085a9..f0f679d607f 100644 --- a/mozilla/netwerk/protocol/viewsource/src/nsViewSourceChannel.cpp +++ b/mozilla/netwerk/protocol/viewsource/src/nsViewSourceChannel.cpp @@ -457,7 +457,7 @@ nsViewSourceChannel::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) if (mHttpChannel) { // we don't want view-source following Refresh: headers, so clear it mHttpChannel->SetResponseHeader(NS_LITERAL_CSTRING("Refresh"), - NS_LITERAL_CSTRING("")); + NS_LITERAL_CSTRING(""), PR_FALSE); } return mListener->OnStartRequest(NS_STATIC_CAST(nsIViewSourceChannel*, this), diff --git a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp index a7711cf9d46..ccf56a2e762 100644 --- a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -932,7 +932,7 @@ nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, // it's header observers. nsCOMPtr httpChannel = do_QueryInterface(aChannel); if (httpChannel) { - rv = httpChannel->SetResponseHeader(headerStr, headerVal); + rv = httpChannel->SetResponseHeader(headerStr, headerVal, PR_TRUE); if (NS_FAILED(rv)) return rv; } } else if (headerStr.EqualsIgnoreCase("content-range") || diff --git a/mozilla/netwerk/test/TestProtocols.cpp b/mozilla/netwerk/test/TestProtocols.cpp index 78680a0f364..85c4eb7520a 100644 --- a/mozilla/netwerk/test/TestProtocols.cpp +++ b/mozilla/netwerk/test/TestProtocols.cpp @@ -471,7 +471,8 @@ nsresult StartLoadingURL(const char* aUrlString) if (pHTTPCon) { // Setting a sample header. rv = pHTTPCon->SetRequestHeader(NS_LITERAL_CSTRING("sample-header"), - NS_LITERAL_CSTRING("Sample-Value")); + NS_LITERAL_CSTRING("Sample-Value"), + PR_FALSE); if (NS_FAILED(rv)) return rv; } InputTestConsumer* listener; diff --git a/mozilla/uriloader/prefetch/nsPrefetchService.cpp b/mozilla/uriloader/prefetch/nsPrefetchService.cpp index 7679b51e718..9a8d7329d80 100644 --- a/mozilla/uriloader/prefetch/nsPrefetchService.cpp +++ b/mozilla/uriloader/prefetch/nsPrefetchService.cpp @@ -293,7 +293,8 @@ nsPrefetchService::ProcessNextURI() if (httpChannel) { httpChannel->SetReferrer(referrer); httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), - NS_LITERAL_CSTRING("prefetch")); + NS_LITERAL_CSTRING("prefetch"), + PR_FALSE); } rv = mCurrentChannel->AsyncOpen(listener, nsnull); diff --git a/mozilla/xpfe/components/search/src/nsInternetSearchService.cpp b/mozilla/xpfe/components/search/src/nsInternetSearchService.cpp index a3c6ae5d23c..0057e6fc681 100755 --- a/mozilla/xpfe/components/search/src/nsInternetSearchService.cpp +++ b/mozilla/xpfe/components/search/src/nsInternetSearchService.cpp @@ -3895,7 +3895,8 @@ InternetSearchDataSource::DoSearch(nsIRDFResource *source, nsIRDFResource *engin if (httpMultiChannel) { httpMultiChannel->SetRequestHeader(NS_LITERAL_CSTRING("MultiSearch"), - NS_LITERAL_CSTRING("true")); + NS_LITERAL_CSTRING("true"), + PR_FALSE); } // get it just from the cache if we can (do not validate)