fixes bug 325808 "HTTP code could use a HasToken function" r=biesi, sr=bzbarsky

This commit is contained in:
darin%meer.net 2006-02-17 22:52:40 +00:00
parent b804178071
commit 4982455809
11 changed files with 118 additions and 95 deletions

View File

@ -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;
}

View File

@ -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__

View File

@ -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;

View File

@ -42,6 +42,7 @@
#include "nsHttpConnectionInfo.h"
#include "nsHttpConnection.h"
#include "nsHttpTransaction.h"
#include "nsVoidArray.h"
#include "nsHashtable.h"
#include "prmon.h"

View File

@ -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

View File

@ -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; i<count; ++i) {
nsEntry *entry = (nsEntry *) mHeaders[i];
if (NS_FAILED(visitor->VisitHeader(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; i<count; ++i) {
nsEntry *entry = (nsEntry *) mHeaders[i];
PRUint32 i, count = mHeaders.Length();
for (i = 0; i < count; ++i) {
const nsEntry &entry = mHeaders[i];
// prune proxy headers if requested
if (pruneProxyHeaders && ((entry->header == 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; i<count; ++i)
delete (nsEntry *) mHeaders[i];
mHeaders.Clear();
}
@ -247,14 +232,10 @@ nsHttpHeaderArray::Clear()
PRInt32
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
{
PRInt32 i, count = mHeaders.Count();
for (i=0; i<count; ++i) {
*entry = (nsEntry *) mHeaders[i];
if ((*entry)->header == 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

View File

@ -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<nsEntry> mHeaders;
};
#endif

View File

@ -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:

View File

@ -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;
}

View File

@ -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);

View File

@ -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)