зеркало из https://github.com/mozilla/gecko-dev.git
Make our content-type header parsing closer to spec (it wasn't allowing some
perfectly valid things), and have only one function for doing said parsing instead of two separate functions. Bug 244964, r+sr=darin, a=bsmedberg
This commit is contained in:
Родитель
2071f5468a
Коммит
6c59f965fd
|
@ -114,6 +114,7 @@ XPIDLSRCS = \
|
|||
nsPIProtocolProxyService.idl \
|
||||
nsPISocketTransportService.idl \
|
||||
nsIChannelEventSink.idl \
|
||||
nsINetUtil.idl \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS = \
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** 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) 2005
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Boris Zbarsky <bzbarsky@mit.edu> (original author)
|
||||
*
|
||||
* 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 "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* nsINetUtil provides various network-related utility methods.
|
||||
*/
|
||||
[scriptable, uuid(f0c5dddb-4713-4603-af2d-bf671838996b)]
|
||||
interface nsINetUtil : nsISupports
|
||||
{
|
||||
/**
|
||||
* Parse a content-type header and return the content type and
|
||||
* charset (if any).
|
||||
*
|
||||
* @param aTypeHeader the header string to parse
|
||||
* @param [out] aCharset the charset parameter specified in the
|
||||
* header, if any.
|
||||
* @param [out] aHadCharset whether a charset was explicitly specified.
|
||||
* @return the MIME type specified in the header, in lower-case.
|
||||
*/
|
||||
AUTF8String parseContentType(in AUTF8String aTypeHeader,
|
||||
out AUTF8String aCharset,
|
||||
out boolean aHadCharset);
|
||||
};
|
|
@ -86,6 +86,7 @@
|
|||
#include "nsISyncStreamListener.h"
|
||||
#include "nsInterfaceRequestorAgg.h"
|
||||
#include "nsInt64.h"
|
||||
#include "nsINetUtil.h"
|
||||
|
||||
// Helper, to simplify getting the I/O service.
|
||||
inline const nsGetServiceByCIDWithError
|
||||
|
@ -682,24 +683,16 @@ NS_ParseContentType(const nsACString &rawContentType,
|
|||
nsCString &contentCharset)
|
||||
{
|
||||
// contentCharset is left untouched if not present in rawContentType
|
||||
nsACString::const_iterator begin, it, end;
|
||||
it = rawContentType.BeginReading(begin);
|
||||
rawContentType.EndReading(end);
|
||||
if (FindCharInReadable(';', it, end)) {
|
||||
contentType = Substring(begin, it);
|
||||
// now look for "charset=FOO" and extract "FOO"
|
||||
begin = ++it;
|
||||
if (FindInReadable(NS_LITERAL_CSTRING("charset="), begin, it = end,
|
||||
nsCaseInsensitiveCStringComparator())) {
|
||||
contentCharset = Substring(it, end);
|
||||
contentCharset.StripWhitespace();
|
||||
}
|
||||
}
|
||||
else
|
||||
contentType = rawContentType;
|
||||
ToLowerCase(contentType);
|
||||
contentType.StripWhitespace();
|
||||
return NS_OK;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsINetUtil> util = do_GetIOService(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCString charset;
|
||||
PRBool hadCharset;
|
||||
rv = util->ParseContentType(rawContentType, charset, &hadCharset,
|
||||
contentType);
|
||||
if (NS_SUCCEEDED(rv) && hadCharset)
|
||||
contentCharset = charset;
|
||||
return rv;
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
|
|
|
@ -237,8 +237,9 @@ nsIOService::~nsIOService()
|
|||
{
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(nsIOService,
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS4(nsIOService,
|
||||
nsIIOService,
|
||||
nsINetUtil,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
|
@ -707,3 +708,14 @@ nsIOService::Observe(nsISupports *subject,
|
|||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsINetUtil interface
|
||||
NS_IMETHODIMP
|
||||
nsIOService::ParseContentType(const nsACString &aTypeHeader,
|
||||
nsACString &aCharset,
|
||||
PRBool *aHadCharset,
|
||||
nsACString &aContentType)
|
||||
{
|
||||
net_ParseContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "nsSupportsArray.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsINetUtil.h"
|
||||
|
||||
#define NS_N(x) (sizeof(x)/sizeof(*x))
|
||||
|
||||
|
@ -71,12 +72,14 @@ class nsIPrefBranch2;
|
|||
|
||||
class nsIOService : public nsIIOService
|
||||
, public nsIObserver
|
||||
, public nsINetUtil
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIIOSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSINETUTIL
|
||||
|
||||
nsIOService() NS_HIDDEN;
|
||||
~nsIOService() NS_HIDDEN;
|
||||
|
|
|
@ -675,3 +675,220 @@ repeat:
|
|||
}
|
||||
return (char *) iter;
|
||||
}
|
||||
|
||||
#define HTTP_LWS " \t"
|
||||
|
||||
// Return the index of the closing quote of the string, if any
|
||||
static PRUint32
|
||||
net_FindStringEnd(const nsCString& flatStr,
|
||||
PRUint32 stringStart,
|
||||
char stringDelim)
|
||||
{
|
||||
NS_ASSERTION(stringStart < flatStr.Length() &&
|
||||
flatStr.CharAt(stringStart) == stringDelim &&
|
||||
(stringDelim == '"' || stringDelim == '\''),
|
||||
"Invalid stringStart");
|
||||
|
||||
const char set[] = { stringDelim, '\\', '\0' };
|
||||
do {
|
||||
// stringStart points to either the start quote or the last
|
||||
// escaped char (the char following a '\\')
|
||||
|
||||
// Write to searchStart here, so that when we get back to the
|
||||
// top of the loop right outside this one we search from the
|
||||
// right place.
|
||||
PRUint32 stringEnd = flatStr.FindCharInSet(set, stringStart + 1);
|
||||
if (stringEnd == PRUint32(kNotFound))
|
||||
return flatStr.Length();
|
||||
|
||||
if (flatStr.CharAt(stringEnd) == '\\') {
|
||||
// Hit a backslash-escaped char. Need to skip over it.
|
||||
stringStart = stringEnd + 1;
|
||||
if (stringStart == flatStr.Length())
|
||||
return stringStart;
|
||||
|
||||
// Go back to looking for the next escape or the string end
|
||||
continue;
|
||||
}
|
||||
|
||||
return stringEnd;
|
||||
|
||||
} while (PR_TRUE);
|
||||
|
||||
NS_NOTREACHED("How did we get here?");
|
||||
return flatStr.Length();
|
||||
}
|
||||
|
||||
|
||||
static PRUint32
|
||||
net_FindMediaDelimiter(const nsCString& flatStr,
|
||||
PRUint32 searchStart,
|
||||
char delimiter)
|
||||
{
|
||||
do {
|
||||
// searchStart points to the spot from which we should start looking
|
||||
// for the delimiter.
|
||||
const char delimStr[] = { delimiter, '"', '\'', '\0' };
|
||||
PRUint32 curDelimPos = flatStr.FindCharInSet(delimStr, searchStart);
|
||||
if (curDelimPos == PRUint32(kNotFound))
|
||||
return flatStr.Length();
|
||||
|
||||
char ch = flatStr.CharAt(curDelimPos);
|
||||
if (ch == delimiter) {
|
||||
// Found delimiter
|
||||
return curDelimPos;
|
||||
}
|
||||
|
||||
// We hit the start of a quoted string. Look for its end.
|
||||
searchStart = net_FindStringEnd(flatStr, curDelimPos, ch);
|
||||
if (searchStart == flatStr.Length())
|
||||
return searchStart;
|
||||
|
||||
++searchStart;
|
||||
|
||||
// searchStart now points to the first char after the end of the
|
||||
// string, so just go back to the top of the loop and look for
|
||||
// |delimiter| again.
|
||||
} while (PR_TRUE);
|
||||
|
||||
NS_NOTREACHED("How did we get here?");
|
||||
return flatStr.Length();
|
||||
}
|
||||
|
||||
static void
|
||||
net_ParseMediaType(const nsACString &aMediaTypeStr,
|
||||
nsACString &aContentType,
|
||||
nsACString &aContentCharset,
|
||||
PRBool *aHadCharset)
|
||||
{
|
||||
const nsCString& flatStr = PromiseFlatCString(aMediaTypeStr);
|
||||
const char* start = flatStr.get();
|
||||
const char* end = start + flatStr.Length();
|
||||
|
||||
// Trim LWS leading and trailing whitespace from type. We include '(' in
|
||||
// the trailing trim set to catch media-type comments, which are not at all
|
||||
// standard, but may occur in rare cases.
|
||||
const char* type = net_FindCharNotInSet(start, end, HTTP_LWS);
|
||||
const char* typeEnd = net_FindCharInSet(type, end, HTTP_LWS ";(");
|
||||
|
||||
const char* charset = "";
|
||||
const char* charsetEnd = charset;
|
||||
|
||||
// Iterate over parameters
|
||||
PRBool typeHasCharset = PR_FALSE;
|
||||
PRUint32 paramStart = flatStr.FindChar(';', typeEnd - start);
|
||||
if (paramStart != PRUint32(kNotFound)) {
|
||||
// We have parameters. Iterate over them.
|
||||
PRUint32 curParamStart = paramStart + 1;
|
||||
do {
|
||||
PRUint32 curParamEnd =
|
||||
net_FindMediaDelimiter(flatStr, curParamStart, ';');
|
||||
|
||||
const char* paramName = net_FindCharNotInSet(start + curParamStart,
|
||||
start + curParamEnd,
|
||||
HTTP_LWS);
|
||||
static const char charsetStr[] = "charset=";
|
||||
if (PL_strncasecmp(paramName, charsetStr,
|
||||
sizeof(charsetStr) - 1) == 0) {
|
||||
charset = paramName + sizeof(charsetStr) - 1;
|
||||
charsetEnd = start + curParamEnd;
|
||||
typeHasCharset = PR_TRUE;
|
||||
}
|
||||
|
||||
curParamStart = curParamEnd + 1;
|
||||
} while (curParamStart < flatStr.Length());
|
||||
}
|
||||
|
||||
if (typeHasCharset) {
|
||||
// Trim LWS leading and trailing whitespace from charset. We include
|
||||
// '(' in the trailing trim set to catch media-type comments, which are
|
||||
// not at all standard, but may occur in rare cases.
|
||||
charset = net_FindCharNotInSet(charset, charsetEnd, HTTP_LWS);
|
||||
if (*charset == '"' || *charset == '\'') {
|
||||
charsetEnd =
|
||||
start + net_FindStringEnd(flatStr, charset - start, *charset);
|
||||
charset++;
|
||||
NS_ASSERTION(charsetEnd >= charset, "Bad charset parsing");
|
||||
} else {
|
||||
charsetEnd = net_FindCharInSet(charset, charsetEnd, HTTP_LWS ";(");
|
||||
}
|
||||
}
|
||||
|
||||
// if the server sent "*/*", it is meaningless, so do not store it.
|
||||
// also, if type is the same as aContentType, then just update the
|
||||
// charset. however, if charset is empty and aContentType hasn't
|
||||
// changed, then don't wipe-out an existing aContentCharset. We
|
||||
// also want to reject a mime-type if it does not include a slash.
|
||||
// some servers give junk after the charset parameter, which may
|
||||
// include a comma, so this check makes us a bit more tolerant.
|
||||
|
||||
if (type != typeEnd && strncmp(type, "*/*", typeEnd - type) != 0 &&
|
||||
memchr(type, '/', typeEnd - type) != NULL) {
|
||||
// Common case here is that aContentType is empty
|
||||
PRBool eq = !aContentType.IsEmpty() &&
|
||||
aContentType.Equals(Substring(type, typeEnd),
|
||||
nsCaseInsensitiveCStringComparator());
|
||||
if (!eq) {
|
||||
aContentType.Assign(type, typeEnd - type);
|
||||
ToLowerCase(aContentType);
|
||||
}
|
||||
if ((!eq && *aHadCharset) || typeHasCharset) {
|
||||
*aHadCharset = PR_TRUE;
|
||||
aContentCharset.Assign(charset, charsetEnd - charset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef HTTP_LWS
|
||||
|
||||
void
|
||||
net_ParseContentType(const nsACString &aHeaderStr,
|
||||
nsACString &aContentType,
|
||||
nsACString &aContentCharset,
|
||||
PRBool *aHadCharset)
|
||||
{
|
||||
//
|
||||
// Augmented BNF (from RFC 2616 section 3.7):
|
||||
//
|
||||
// header-value = media-type *( LWS "," LWS media-type )
|
||||
// media-type = type "/" subtype *( LWS ";" LWS parameter )
|
||||
// type = token
|
||||
// subtype = token
|
||||
// parameter = attribute "=" value
|
||||
// attribute = token
|
||||
// value = token | quoted-string
|
||||
//
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// text/html
|
||||
// text/html, text/html
|
||||
// text/html,text/html; charset=ISO-8859-1
|
||||
// text/html,text/html; charset="ISO-8859-1"
|
||||
// text/html;charset=ISO-8859-1, text/html
|
||||
// text/html;charset='ISO-8859-1', text/html
|
||||
// application/octet-stream
|
||||
//
|
||||
|
||||
*aHadCharset = PR_FALSE;
|
||||
const nsCString& flatStr = PromiseFlatCString(aHeaderStr);
|
||||
|
||||
// iterate over media-types. Note that ',' characters can happen
|
||||
// inside quoted strings, so we need to watch out for that.
|
||||
PRUint32 curTypeStart = 0;
|
||||
do {
|
||||
// curTypeStart points to the start of the current media-type. We want
|
||||
// to look for its end.
|
||||
PRUint32 curTypeEnd =
|
||||
net_FindMediaDelimiter(flatStr, curTypeStart, ',');
|
||||
|
||||
// At this point curTypeEnd points to the spot where the media-type
|
||||
// starting at curTypeEnd ends. Time to parse that!
|
||||
net_ParseMediaType(Substring(flatStr, curTypeStart,
|
||||
curTypeEnd - curTypeStart),
|
||||
aContentType, aContentCharset, aHadCharset);
|
||||
|
||||
// And let's move on to the next media-type
|
||||
curTypeStart = curTypeEnd + 1;
|
||||
} while (curTypeStart < flatStr.Length());
|
||||
}
|
||||
|
|
|
@ -194,6 +194,19 @@ NS_HIDDEN_(char *) net_RFindCharInSet(const char *str, const char *end, const ch
|
|||
*/
|
||||
NS_HIDDEN_(char *) net_RFindCharNotInSet(const char *str, const char *end, const char *set);
|
||||
|
||||
/**
|
||||
* Parses a content-type header and returns the content type and
|
||||
* charset (if any). aCharset is not modified if no charset is
|
||||
* specified in anywhere in aHeaderStr. In that case (no charset
|
||||
* specified), aHadCharset is set to false. Otherwise, it's set to
|
||||
* true. Note that aContentCharset can be empty even if aHadCharset
|
||||
* is true.
|
||||
*/
|
||||
NS_HIDDEN_(void) net_ParseContentType(const nsACString &aHeaderStr,
|
||||
nsACString &aContentType,
|
||||
nsACString &aContentCharset,
|
||||
PRBool* aHadCharset);
|
||||
|
||||
/* inline versions */
|
||||
|
||||
/* remember the 64-bit platforms ;-) */
|
||||
|
|
|
@ -57,6 +57,10 @@
|
|||
{0x93, 0x37, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
|
||||
}
|
||||
|
||||
// service implementing nsINetUtil
|
||||
#define NS_NETUTIL_CONTRACTID \
|
||||
"@mozilla.org/network/util;1"
|
||||
|
||||
// service implementing nsIEventTarget. events dispatched to this event
|
||||
// target will be executed on one of necko's background i/o threads.
|
||||
#define NS_IOTHREADPOOL_CLASSNAME \
|
||||
|
|
|
@ -621,6 +621,10 @@ static const nsModuleComponentInfo gNetModuleInfo[] = {
|
|||
NS_IOSERVICE_CID,
|
||||
NS_IOSERVICE_CONTRACTID,
|
||||
nsIOServiceConstructor },
|
||||
{ NS_IOSERVICE_CLASSNAME,
|
||||
NS_IOSERVICE_CID,
|
||||
NS_NETUTIL_CONTRACTID,
|
||||
nsIOServiceConstructor },
|
||||
{ NS_IOTHREADPOOL_CLASSNAME,
|
||||
NS_IOTHREADPOOL_CID,
|
||||
NS_IOTHREADPOOL_CONTRACTID,
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "nsIMIMEService.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsURLHelper.h"
|
||||
|
||||
static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
|
||||
static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
|
||||
|
@ -313,8 +314,8 @@ nsFileChannel::SetContentType(const nsACString &aContentType)
|
|||
// If someone gives us a type hint we should just use that type instead of
|
||||
// doing our guessing. So we don't care when this is being called.
|
||||
|
||||
// mContentCharset is unchanged if not parsed
|
||||
NS_ParseContentType(aContentType, mContentType, mContentCharset);
|
||||
PRBool dummy;
|
||||
net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,11 @@ MODULE_OPTIMIZE_FLAGS=-O -g
|
|||
endif
|
||||
endif
|
||||
|
||||
LOCAL_INCLUDES=-I$(topsrcdir)/xpcom/ds
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
$(NULL)
|
||||
|
||||
|
||||
# we don't want the shared lib, but we want to force the creation of a
|
||||
# static lib.
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "nsIPrefBranch.h"
|
||||
#include "nsIStreamConverterService.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsURLHelper.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
extern PRLogModuleInfo* gFTPLog;
|
||||
|
@ -432,7 +433,8 @@ nsFTPChannel::GetContentType(nsACString &aContentType)
|
|||
NS_IMETHODIMP
|
||||
nsFTPChannel::SetContentType(const nsACString &aContentType)
|
||||
{
|
||||
NS_ParseContentType(aContentType, mContentType, mContentCharset);
|
||||
PRBool dummy;
|
||||
net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -423,6 +423,7 @@ nsGopherChannel::SetContentType(const nsACString &aContentType)
|
|||
// from the charset part of this.
|
||||
|
||||
nsCString *contentType = mIsPending ? &mContentType : &mContentTypeHint;
|
||||
// Not in libnecko, so can't use net_ParseContentType
|
||||
NS_ParseContentType(aContentType, *contentType, mContentCharset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -3161,16 +3161,19 @@ nsHttpChannel::SetContentType(const nsACString &value)
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
nsCAutoString contentTypeBuf, charsetBuf;
|
||||
NS_ParseContentType(value, contentTypeBuf, charsetBuf);
|
||||
PRBool hadCharset;
|
||||
net_ParseContentType(value, contentTypeBuf, charsetBuf, &hadCharset);
|
||||
|
||||
mResponseHead->SetContentType(contentTypeBuf);
|
||||
|
||||
// take care not to stomp on an existing charset
|
||||
if (!charsetBuf.IsEmpty())
|
||||
if (hadCharset)
|
||||
mResponseHead->SetContentCharset(charsetBuf);
|
||||
} else {
|
||||
// We are being given a content-type hint.
|
||||
NS_ParseContentType(value, mContentTypeHint, mContentCharsetHint);
|
||||
PRBool dummy;
|
||||
net_ParseContentType(value, mContentTypeHint, mContentCharsetHint,
|
||||
&dummy);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -208,8 +208,12 @@ nsHttpResponseHead::ParseHeaderLine(char *line)
|
|||
// handle some special case headers...
|
||||
if (hdr == nsHttp::Content_Length)
|
||||
PR_sscanf(val, "%lld", &mContentLength);
|
||||
else if (hdr == nsHttp::Content_Type)
|
||||
ParseContentType(val);
|
||||
else if (hdr == nsHttp::Content_Type) {
|
||||
LOG(("ParseContentType [type=%s]\n", val));
|
||||
PRBool dummy;
|
||||
net_ParseContentType(nsDependentCString(val),
|
||||
mContentType, mContentCharset, &dummy);
|
||||
}
|
||||
else if (hdr == nsHttp::Cache_Control)
|
||||
ParseCacheControl(val);
|
||||
else if (hdr == nsHttp::Pragma)
|
||||
|
@ -617,96 +621,6 @@ nsHttpResponseHead::ParseVersion(const char *str)
|
|||
mVersion = NS_HTTP_VERSION_1_0;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpResponseHead::ParseContentType(char *type)
|
||||
{
|
||||
LOG(("nsHttpResponseHead::ParseContentType [type=%s]\n", type));
|
||||
|
||||
//
|
||||
// Augmented BNF (from RFC 2616 section 3.7):
|
||||
//
|
||||
// header-value = media-type *( LWS "," LWS media-type )
|
||||
// media-type = type "/" subtype *( LWS ";" LWS parameter )
|
||||
// type = token
|
||||
// subtype = token
|
||||
// parameter = attribute "=" value
|
||||
// attribute = token
|
||||
// value = token | quoted-string
|
||||
//
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// text/html
|
||||
// text/html, text/html
|
||||
// text/html,text/html; charset=ISO-8859-1
|
||||
// text/html;charset=ISO-8859-1, text/html
|
||||
// application/octet-stream
|
||||
//
|
||||
|
||||
// iterate over media-types
|
||||
char *nextType;
|
||||
do {
|
||||
nextType = (char *) strchr(type, ',');
|
||||
if (nextType) {
|
||||
*nextType = '\0';
|
||||
++nextType;
|
||||
}
|
||||
// type points at this media-type; locate first parameter if any
|
||||
char *charset = "";
|
||||
char *param = (char *) strchr(type, ';');
|
||||
if (param) {
|
||||
*param = '\0';
|
||||
++param;
|
||||
|
||||
// iterate over parameters
|
||||
char *nextParam;
|
||||
do {
|
||||
nextParam = (char *) strchr(param, ';');
|
||||
if (nextParam) {
|
||||
*nextParam = '\0';
|
||||
++nextParam;
|
||||
}
|
||||
// param points at this parameter
|
||||
|
||||
param = net_FindCharNotInSet(param, HTTP_LWS);
|
||||
if (PL_strncasecmp(param, "charset=", 8) == 0)
|
||||
charset = param + 8;
|
||||
|
||||
} while ((param = nextParam) != nsnull);
|
||||
}
|
||||
|
||||
// trim LWS leading and trailing whitespace from type and charset.
|
||||
// charset cannot have leading whitespace. we include '(' in the
|
||||
// trailing trim set to catch media-type comments, which are not
|
||||
// at all standard, but may occur in rare cases.
|
||||
|
||||
type = net_FindCharNotInSet(type, HTTP_LWS);
|
||||
|
||||
char *typeEnd = net_FindCharInSet(type, HTTP_LWS "(");
|
||||
char *charsetEnd = net_FindCharInSet(charset, HTTP_LWS "(");
|
||||
|
||||
// force content-type to be lowercase
|
||||
net_ToLowerCase(type, typeEnd - type);
|
||||
|
||||
// if the server sent "*/*", it is meaningless, so do not store it.
|
||||
// also, if type is the same as mContentType, then just update the
|
||||
// charset. however, if charset is empty and mContentType hasn't
|
||||
// changed, then don't wipe-out an existing mContentCharset. we
|
||||
// also want to reject a mime-type if it does not include a slash.
|
||||
// some servers give junk after the charset parameter, which may
|
||||
// include a comma, so this check makes us a bit more tolerant.
|
||||
|
||||
if (*type && strcmp(type, "*/*") != 0 && strchr(type, '/')) {
|
||||
PRBool eq = mContentType.Equals(Substring(type, typeEnd));
|
||||
if (!eq)
|
||||
mContentType.Assign(type, typeEnd - type);
|
||||
if (!eq || *charset)
|
||||
mContentCharset.Assign(charset, charsetEnd - charset);
|
||||
}
|
||||
|
||||
} while ((type = nextType) != nsnull);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpResponseHead::ParseCacheControl(const char *val)
|
||||
{
|
||||
|
|
|
@ -132,7 +132,6 @@ public:
|
|||
|
||||
private:
|
||||
void ParseVersion(const char *);
|
||||
void ParseContentType(char *);
|
||||
void ParseCacheControl(const char *);
|
||||
void ParsePragma(const char *);
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "nsIMultiPartChannel.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsURLHelper.h"
|
||||
|
||||
//
|
||||
// Helper function for determining the length of data bytes up to
|
||||
|
@ -325,7 +326,8 @@ nsPartChannel::GetContentType(nsACString &aContentType)
|
|||
NS_IMETHODIMP
|
||||
nsPartChannel::SetContentType(const nsACString &aContentType)
|
||||
{
|
||||
NS_ParseContentType(aContentType, mContentType, mContentCharset);
|
||||
PRBool dummy;
|
||||
net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ _UNIT_FILES = unit/test_all.sh \
|
|||
unit/tail.js \
|
||||
unit/test_protocolproxyservice.js \
|
||||
unit/test_http_headers.js \
|
||||
unit/test_parse_content_type.js \
|
||||
$(NULL)
|
||||
libs:: $(_UNIT_FILES)
|
||||
$(INSTALL) $^ $(DIST)/bin/necko_unit_tests
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** 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) 2005
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Boris Zbarsky <bzbarsky@mit.edu>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
var charset = {};
|
||||
var hadCharset = {};
|
||||
var type;
|
||||
|
||||
function reset() {
|
||||
delete charset.value;
|
||||
delete hadCharset.value;
|
||||
type = undefined;
|
||||
}
|
||||
|
||||
function check(aType, aCharset, aHadCharset) {
|
||||
do_check_eq(type, aType);
|
||||
do_check_eq(aCharset, charset.value);
|
||||
do_check_eq(aHadCharset, hadCharset.value);
|
||||
reset();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
var netutil = Components.classes["@mozilla.org/network/util;1"]
|
||||
.getService(Components.interfaces.nsINetUtil);
|
||||
type = netutil.parseContentType("text/html", charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
type = netutil.parseContentType("TEXT/HTML", charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
type = netutil.parseContentType("text/html, text/html", charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
type = netutil.parseContentType("text/html, text/plain",
|
||||
charset, hadCharset);
|
||||
check("text/plain", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/html, ', charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/html, */*', charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/html, foo', charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
type = netutil.parseContentType("text/html; charset=ISO-8859-1",
|
||||
charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType('text/html; charset="ISO-8859-1"',
|
||||
charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType("text/html; charset='ISO-8859-1'",
|
||||
charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType("text/html; charset='ISO-8859-1', text/html",
|
||||
charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType("text/html; charset='ISO-8859-1', text/html; charset=UTF8",
|
||||
charset, hadCharset);
|
||||
check("text/html", "UTF8", true);
|
||||
|
||||
type = netutil.parseContentType("text/html; charset=ISO-8859-1, TEXT/HTML", charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType("text/html; charset=ISO-8859-1, TEXT/plain", charset, hadCharset);
|
||||
check("text/plain", "", true);
|
||||
|
||||
type = netutil.parseContentType("text/plain, TEXT/HTML; charset='ISO-8859-1', text/html, TEXT/HTML", charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType('text/plain, TEXT/HTML; param="charset=UTF8"; charset=\'ISO-8859-1\'; param2="charset=UTF16", text/html, TEXT/HTML', charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType('text/plain, TEXT/HTML; param=charset=UTF8; charset=\'ISO-8859-1\'; param2=charset=UTF16, text/html, TEXT/HTML', charset, hadCharset);
|
||||
check("text/html", "ISO-8859-1", true);
|
||||
|
||||
type = netutil.parseContentType("text/plain; param= , text/html", charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/plain; param=", text/html"', charset, hadCharset);
|
||||
check("text/plain", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/plain; param=", \\" , text/html"', charset, hadCharset);
|
||||
check("text/plain", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/plain; param=", \\" , text/html , "', charset, hadCharset);
|
||||
check("text/plain", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/plain param=", \\" , text/html , "', charset, hadCharset);
|
||||
check("text/plain", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/plain charset=UTF8', charset, hadCharset);
|
||||
check("text/plain", "", false);
|
||||
|
||||
type = netutil.parseContentType('text/plain, TEXT/HTML; param="charset=UTF8"; ; param2="charset=UTF16", text/html, TEXT/HTML', charset, hadCharset);
|
||||
check("text/html", "", false);
|
||||
|
||||
}
|
Загрузка…
Ссылка в новой задаче