From 49824558097eac0362297cbfabe56186e25ae257 Mon Sep 17 00:00:00 2001 From: "darin%meer.net" Date: Fri, 17 Feb 2006 22:52:40 +0000 Subject: [PATCH] fixes bug 325808 "HTTP code could use a HasToken function" r=biesi, sr=bzbarsky --- netwerk/protocol/http/src/nsHttp.cpp | 27 ++++++++ netwerk/protocol/http/src/nsHttp.h | 36 +++++----- netwerk/protocol/http/src/nsHttpChannel.cpp | 27 ++++---- .../protocol/http/src/nsHttpConnectionMgr.h | 1 + netwerk/protocol/http/src/nsHttpHandler.cpp | 2 +- .../protocol/http/src/nsHttpHeaderArray.cpp | 67 +++++++------------ netwerk/protocol/http/src/nsHttpHeaderArray.h | 26 +++++-- netwerk/protocol/http/src/nsHttpRequestHead.h | 3 + .../protocol/http/src/nsHttpResponseHead.cpp | 18 ++--- .../protocol/http/src/nsHttpResponseHead.h | 3 + .../protocol/http/src/nsHttpTransaction.cpp | 3 +- 11 files changed, 118 insertions(+), 95 deletions(-) diff --git a/netwerk/protocol/http/src/nsHttp.cpp b/netwerk/protocol/http/src/nsHttp.cpp index c95f01280bb2..27dd6dc3c517 100644 --- a/netwerk/protocol/http/src/nsHttp.cpp +++ b/netwerk/protocol/http/src/nsHttp.cpp @@ -214,3 +214,30 @@ nsHttp::ResolveAtom(const char *str) stub->key = atom._val = heapAtom->value; return atom; } + +const char * +nsHttp::FindToken(const char *input, const char *token, const char *seps) +{ + if (!input) + return nsnull; + + int inputLen = strlen(input); + int tokenLen = strlen(token); + + if (inputLen < tokenLen) + return nsnull; + + const char *inputTop = input; + const char *inputEnd = input + inputLen - tokenLen; + for (; input <= inputEnd; ++input) { + if (PL_strncasecmp(input, token, tokenLen) == 0) { + if (input > inputTop && !strchr(seps, *(input - 1))) + continue; + if (input < inputEnd && !strchr(seps, *(input + tokenLen))) + continue; + return input; + } + } + + return nsnull; +} diff --git a/netwerk/protocol/http/src/nsHttp.h b/netwerk/protocol/http/src/nsHttp.h index 051604ea4dc0..26e06d0918e1 100644 --- a/netwerk/protocol/http/src/nsHttp.h +++ b/netwerk/protocol/http/src/nsHttp.h @@ -122,8 +122,8 @@ typedef PRUint8 nsHttpVersion; struct nsHttpAtom { - operator const char *() { return _val; } - const char *get() { return _val; } + operator const char *() const { return _val; } + const char *get() const { return _val; } void operator=(const char *v) { _val = v; } void operator=(const nsHttpAtom &a) { _val = a._val; } @@ -144,13 +144,20 @@ struct nsHttp return ResolveAtom(PromiseFlatCString(s).get()); } - /* Declare all atoms - * - * The atom names and values are stored in nsHttpAtomList.h and - * are brought to you by the magic of C preprocessing - * - * Add new atoms to nsHttpAtomList and all support logic will be auto-generated - */ + // find the first instance (case-insensitive comparison) of the given + // |token| in the |input| string. the |token| is bounded by elements of + // |separators| and may appear at the beginning or end of the |input| + // string. null is returned if the |token| is not found. |input| may be + // null, in which case null is returned. + static const char *FindToken(const char *input, const char *token, + const char *separators); + + // Declare all atoms + // + // The atom names and values are stored in nsHttpAtomList.h and are brought + // to you by the magic of C preprocessing. Add new atoms to nsHttpAtomList + // and all support logic will be auto-generated. + // #define HTTP_ATOM(_name, _value) static nsHttpAtom _name; #include "nsHttpAtomList.h" #undef HTTP_ATOM @@ -163,12 +170,7 @@ struct nsHttp static inline PRUint32 PRTimeToSeconds(PRTime t_usec) { - PRTime usec_per_sec; - PRUint32 t_sec; - LL_I2L(usec_per_sec, PR_USEC_PER_SEC); - LL_DIV(t_usec, t_usec, usec_per_sec); - LL_L2I(t_sec, t_usec); - return t_sec; + return PRUint32( t_usec / PR_USEC_PER_SEC ); } #define NowInSeconds() PRTimeToSeconds(PR_Now()) @@ -177,12 +179,10 @@ PRTimeToSeconds(PRTime t_usec) #undef CLAMP #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -// nsCRT::strdup likes to convert nsnull to "" -#define strdup_if(s) (s ? nsCRT::strdup(s) : nsnull) - // round q-value to one decimal place; return most significant digit as uint. #define QVAL_TO_UINT(q) ((unsigned int) ((q + 0.05) * 10.0)) #define HTTP_LWS " \t" +#define HTTP_HEADER_VALUE_SEPS HTTP_LWS "," #endif // nsHttp_h__ diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index 4e9ceb4d318b..c7dd3336194b 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -383,7 +383,8 @@ nsHttpChannel::Connect(PRBool firstTime) // out to net to validate it. this call sets mCachedContentIsValid // and may set request headers as required for cache validation. rv = CheckCache(); - NS_ASSERTION(NS_SUCCEEDED(rv), "cache check failed"); + if (NS_FAILED(rv)) + NS_WARNING("cache check failed"); // read straight from the cache if possible... if (mCachedContentIsValid) { @@ -877,15 +878,14 @@ nsHttpChannel::ProcessNormal() // must do this early on so as to prevent it from being seen up stream. // The same problem exists for Content-Encoding: compress in default // Apache installs. - const char *encoding = mResponseHead->PeekHeader(nsHttp::Content_Encoding); - if (encoding && PL_strcasestr(encoding, "gzip") && ( + if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "gzip") && ( mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP) || mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP2) || mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP3))) { // clear the Content-Encoding header mResponseHead->ClearHeader(nsHttp::Content_Encoding); } - else if (encoding && PL_strcasestr(encoding, "compress") && ( + else if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "compress") && ( mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS) || mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS2))) { // clear the Content-Encoding header @@ -1427,7 +1427,7 @@ nsHttpChannel::CheckCache() // Get the method that was used to generate the cached response rv = mCacheEntry->GetMetaDataElement("request-method", getter_Copies(buf)); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); nsHttpAtom method = nsHttp::ResolveAtom(buf); if (method == nsHttp::Head) { @@ -1441,7 +1441,7 @@ nsHttpChannel::CheckCache() // We'll need this value in later computations... PRUint32 lastModifiedTime; rv = mCacheEntry->GetLastModified(&lastModifiedTime); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); // Determine if this is the first time that this cache entry // has been accessed during this session. @@ -1450,7 +1450,7 @@ nsHttpChannel::CheckCache() // Get the cached HTTP response headers rv = mCacheEntry->GetMetaDataElement("response-head", getter_Copies(buf)); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); // Parse the cached HTTP response headers NS_ASSERTION(!mCachedResponseHead, "memory leak detected"); @@ -1458,7 +1458,7 @@ nsHttpChannel::CheckCache() if (!mCachedResponseHead) return NS_ERROR_OUT_OF_MEMORY; rv = mCachedResponseHead->Parse((char *) buf.get()); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); buf.Adopt(0); // If we were only granted read access, then assume the entry is valid. @@ -1480,7 +1480,7 @@ nsHttpChannel::CheckCache() if (contentLength != nsInt64(-1)) { PRUint32 size; rv = mCacheEntry->GetDataSize(&size); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); if (nsInt64(size) != contentLength) { LOG(("Cached data size does not match the Content-Length header " @@ -1488,7 +1488,7 @@ nsHttpChannel::CheckCache() if ((nsInt64(size) < contentLength) && mCachedResponseHead->IsResumable()) { // looks like a partial entry. rv = SetupByteRangeRequest(size); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); mCachedContentIsPartial = PR_TRUE; } return NS_OK; @@ -1544,7 +1544,7 @@ nsHttpChannel::CheckCache() PRUint32 time = 0; // a temporary variable for storing time values... rv = mCacheEntry->GetExpirationTime(&time); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); if (NowInSeconds() <= time) doValidation = PR_FALSE; @@ -1557,7 +1557,7 @@ nsHttpChannel::CheckCache() // is consistent with existing browsers and is generally expected // by web authors. rv = mCachedResponseHead->ComputeFreshnessLifetime(&time); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); if (time == 0) doValidation = PR_TRUE; @@ -3362,8 +3362,7 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) // Adjust mCaps according to our request headers: // - If "Connection: close" is set as a request header, then do not bother // trying to establish a keep-alive connection. - const char *connHeader = mRequestHead.PeekHeader(nsHttp::Connection); - if (PL_strcasestr(connHeader, "close")) + if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING); mIsPending = PR_TRUE; diff --git a/netwerk/protocol/http/src/nsHttpConnectionMgr.h b/netwerk/protocol/http/src/nsHttpConnectionMgr.h index 2eb12603d01b..50bdd154b1f6 100644 --- a/netwerk/protocol/http/src/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/src/nsHttpConnectionMgr.h @@ -42,6 +42,7 @@ #include "nsHttpConnectionInfo.h" #include "nsHttpConnection.h" #include "nsHttpTransaction.h" +#include "nsVoidArray.h" #include "nsHashtable.h" #include "prmon.h" diff --git a/netwerk/protocol/http/src/nsHttpHandler.cpp b/netwerk/protocol/http/src/nsHttpHandler.cpp index 13fcf746d980..a92205da2b60 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -390,7 +390,7 @@ nsHttpHandler::IsAcceptableEncoding(const char *enc) if (!PL_strncasecmp(enc, "x-", 2)) enc += 2; - return PL_strcasestr(mAcceptEncodings.get(), enc) != nsnull; + return nsHttp::FindToken(mAcceptEncodings.get(), enc, HTTP_LWS ",") != nsnull; } nsresult diff --git a/netwerk/protocol/http/src/nsHttpHeaderArray.cpp b/netwerk/protocol/http/src/nsHttpHeaderArray.cpp index 3f4e5442b03d..104749588636 100644 --- a/netwerk/protocol/http/src/nsHttpHeaderArray.cpp +++ b/netwerk/protocol/http/src/nsHttpHeaderArray.cpp @@ -56,22 +56,18 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header, // 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 (!merge && entry) { + if (!merge && entry) mHeaders.RemoveElementAt(index); - delete entry; - } return NS_OK; } // Create a new entry, or... if (!entry) { - entry = new nsEntry(header, value); + entry = mHeaders.AppendElement(); //new nsEntry(header, value); if (!entry) return NS_ERROR_OUT_OF_MEMORY; - if (!mHeaders.AppendElement(entry)) { - NS_WARNING("AppendElement failed"); - delete entry; - } + entry->header = header; + entry->value = value; } // Append the new value to the existing value iff... else if (merge && CanAppendToHeader(header)) { @@ -96,14 +92,7 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header, void nsHttpHeaderArray::ClearHeader(nsHttpAtom header) { - nsEntry *entry = nsnull; - PRInt32 index; - - index = LookupEntry(header, &entry); - if (entry) { - mHeaders.RemoveElementAt(index); - delete entry; - } + mHeaders.RemoveElement(header, nsEntry::MatchHeader()); } const char * @@ -129,10 +118,11 @@ nsresult nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor) { NS_ENSURE_ARG_POINTER(visitor); - PRInt32 i, count = mHeaders.Count(); - for (i=0; iVisitHeader(nsDependentCString(entry->header), entry->value))) + PRUint32 i, count = mHeaders.Length(); + for (i = 0; i < count; ++i) { + const nsEntry &entry = mHeaders[i]; + if (NS_FAILED(visitor->VisitHeader(nsDependentCString(entry.header), + entry.value))) break; } return NS_OK; @@ -206,16 +196,16 @@ nsHttpHeaderArray::ParseHeaderLine(char *line, nsHttpAtom *hdr, char **val) void nsHttpHeaderArray::Flatten(nsACString &buf, PRBool pruneProxyHeaders) { - PRInt32 i, count = mHeaders.Count(); - for (i=0; iheader == nsHttp::Proxy_Authorization) || - (entry->header == nsHttp::Proxy_Connection))) + if (pruneProxyHeaders && ((entry.header == nsHttp::Proxy_Authorization) || + (entry.header == nsHttp::Proxy_Connection))) continue; - buf.Append(entry->header); + buf.Append(entry.header); buf.AppendLiteral(": "); - buf.Append(entry->value); + buf.Append(entry.value); buf.AppendLiteral("\r\n"); } } @@ -223,20 +213,15 @@ nsHttpHeaderArray::Flatten(nsACString &buf, PRBool pruneProxyHeaders) const char * nsHttpHeaderArray::PeekHeaderAt(PRUint32 index, nsHttpAtom &header) { - nsEntry *entry = (nsEntry *) mHeaders[index]; - if (!entry) - return nsnull; + const nsEntry &entry = mHeaders[index]; - header = entry->header; - return entry->value.get(); + header = entry.header; + return entry.value.get(); } void nsHttpHeaderArray::Clear() { - PRInt32 i, count = mHeaders.Count(); - for (i=0; iheader == header) - return i; - } - *entry = nsnull; - return -1; + PRUint32 index = mHeaders.IndexOf(header, 0, nsEntry::MatchHeader()); + if (index != PR_UINT32_MAX) + *entry = &mHeaders[index]; + return index; } PRBool diff --git a/netwerk/protocol/http/src/nsHttpHeaderArray.h b/netwerk/protocol/http/src/nsHttpHeaderArray.h index 3c7131cb0817..ad52dfd6e30b 100644 --- a/netwerk/protocol/http/src/nsHttpHeaderArray.h +++ b/netwerk/protocol/http/src/nsHttpHeaderArray.h @@ -39,7 +39,7 @@ #ifndef nsHttpHeaderArray_h__ #define nsHttpHeaderArray_h__ -#include "nsVoidArray.h" +#include "nsTArray.h" #include "nsIHttpChannel.h" #include "nsIHttpHeaderVisitor.h" #include "nsCOMPtr.h" @@ -58,6 +58,17 @@ public: nsresult GetHeader(nsHttpAtom header, nsACString &value); void ClearHeader(nsHttpAtom h); + // Find the location of the given header value, or null if none exists. + const char *FindHeaderValue(nsHttpAtom header, const char *value) { + return nsHttp::FindToken(PeekHeader(header), value, + HTTP_HEADER_VALUE_SEPS); + } + + // Determine if the given header value exists. + PRBool HasHeaderValue(nsHttpAtom header, const char *value) { + return FindHeaderValue(header, value) != nsnull; + } + nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor); // parse a header line, return the header atom and a pointer to the @@ -66,7 +77,7 @@ public: void Flatten(nsACString &, PRBool pruneProxyHeaders=PR_FALSE); - PRUint32 Count() { return (PRUint32) mHeaders.Count(); } + PRUint32 Count() { return mHeaders.Length(); } const char *PeekHeaderAt(PRUint32 i, nsHttpAtom &header); @@ -75,18 +86,23 @@ public: private: struct nsEntry { - nsEntry(nsHttpAtom h, const nsACString &v) - : header(h) { value = v; } + nsEntry() {} nsHttpAtom header; nsCString value; + + struct MatchHeader { + PRBool Equals(const nsEntry &entry, const nsHttpAtom &header) const { + return entry.header == header; + } + }; }; PRInt32 LookupEntry(nsHttpAtom header, nsEntry **); PRBool CanAppendToHeader(nsHttpAtom header); private: - nsAutoVoidArray mHeaders; + nsTArray mHeaders; }; #endif diff --git a/netwerk/protocol/http/src/nsHttpRequestHead.h b/netwerk/protocol/http/src/nsHttpRequestHead.h index 8459828eaf72..6e256efd6e87 100644 --- a/netwerk/protocol/http/src/nsHttpRequestHead.h +++ b/netwerk/protocol/http/src/nsHttpRequestHead.h @@ -70,6 +70,9 @@ public: void ClearHeader(nsHttpAtom h) { mHeaders.ClearHeader(h); } void ClearHeaders() { mHeaders.Clear(); } + const char *FindHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.FindHeaderValue(h, v); } + PRBool HasHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.HasHeaderValue(h, v); } + void Flatten(nsACString &, PRBool pruneProxyHeaders = PR_FALSE); private: diff --git a/netwerk/protocol/http/src/nsHttpResponseHead.cpp b/netwerk/protocol/http/src/nsHttpResponseHead.cpp index 86b9f4dfd3d0..b6225915c85f 100644 --- a/netwerk/protocol/http/src/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/src/nsHttpResponseHead.cpp @@ -387,8 +387,7 @@ nsHttpResponseHead::MustValidateIfExpired() // cache, that cache MUST NOT use the entry after it becomes stale to respond to // a subsequent request without first revalidating it with the origin server. // - const char *val = PeekHeader(nsHttp::Cache_Control); - return val && PL_strcasestr(val, "must-revalidate"); + return HasHeaderValue(nsHttp::Cache_Control, "must-revalidate"); } PRBool @@ -399,7 +398,7 @@ nsHttpResponseHead::IsResumable() return mVersion >= NS_HTTP_VERSION_1_1 && PeekHeader(nsHttp::Content_Length) && (PeekHeader(nsHttp::ETag) || PeekHeader(nsHttp::Last_Modified)) && - PL_strcasestr(PeekHeader(nsHttp::Accept_Ranges), "bytes"); + HasHeaderValue(nsHttp::Accept_Ranges, "bytes"); } PRBool @@ -631,18 +630,13 @@ nsHttpResponseHead::ParseCacheControl(const char *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")) != nsnull) { - s += (sizeof("no-cache") - 1); - if (*s != '=') - mCacheControlNoCache = PR_TRUE; - } + if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS)) + mCacheControlNoCache = PR_TRUE; // search header value for occurance of "no-store" - if (PL_strcasestr(val, "no-store")) + if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS)) mCacheControlNoStore = PR_TRUE; } @@ -660,6 +654,6 @@ nsHttpResponseHead::ParsePragma(const char *val) // 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 (PL_strcasestr(val, "no-cache")) + if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS)) mPragmaNoCache = PR_TRUE; } diff --git a/netwerk/protocol/http/src/nsHttpResponseHead.h b/netwerk/protocol/http/src/nsHttpResponseHead.h index 537037d09965..f397c3e1b278 100644 --- a/netwerk/protocol/http/src/nsHttpResponseHead.h +++ b/netwerk/protocol/http/src/nsHttpResponseHead.h @@ -84,6 +84,9 @@ public: void ClearHeader(nsHttpAtom h) { mHeaders.ClearHeader(h); } void ClearHeaders() { mHeaders.Clear(); } + const char *FindHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.FindHeaderValue(h, v); } + PRBool HasHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.HasHeaderValue(h, v); } + void SetContentType(const nsACString &s) { mContentType = s; } void SetContentCharset(const nsACString &s) { mContentCharset = s; } void SetContentLength(PRInt64); diff --git a/netwerk/protocol/http/src/nsHttpTransaction.cpp b/netwerk/protocol/http/src/nsHttpTransaction.cpp index 656b9f09641f..5655e8e7ae7a 100644 --- a/netwerk/protocol/http/src/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/src/nsHttpTransaction.cpp @@ -775,8 +775,7 @@ nsHttpTransaction::HandleContentStart() // we're done with the socket. please note that _all_ other // decoding is done when the channel receives the content data // so as not to block the socket transport thread too much. - const char *val = mResponseHead->PeekHeader(nsHttp::Transfer_Encoding); - if (PL_strcasestr(val, "chunked")) { + if (mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) { // we only support the "chunked" transfer encoding right now. mChunkedDecoder = new nsHttpChunkedDecoder(); if (!mChunkedDecoder)