Do not apply content decoding if the url ends in .gz or .zip. The

content-encoding battle never ends...  Bug 128199, r=law, sr=darin,
a=asa
This commit is contained in:
bzbarsky%mit.edu 2002-03-14 00:51:28 +00:00
Родитель 2059c806a2
Коммит abe192dbf6
7 изменённых файлов: 297 добавлений и 13 удалений

Просмотреть файл

@ -61,6 +61,7 @@
#define APPLICATION_GZIP "application/x-gzip"
#define APPLICATION_GZIP2 "application/gzip"
#define APPLICATION_GZIP3 "application/x-gunzip"
#define APPLICATION_ZIP "application/zip"
#define APPLICATION_HTTP_INDEX_FORMAT "application/http-index-format"
#define APPLICATION_JAVASCRIPT "application/x-javascript"
#define APPLICATION_NETSCAPE_REVOCATION "application/x-netscape-revocation"

Просмотреть файл

@ -25,6 +25,7 @@
#include "nsIChannel.idl"
interface nsIHttpHeaderVisitor;
interface nsISimpleEnumerator;
[scriptable, uuid(d78b53c8-d03f-4fd8-b2ee-7b36fcd150d1)]
interface nsIHttpChannel : nsIChannel
@ -103,6 +104,22 @@ interface nsIHttpChannel : nsIChannel
*/
readonly attribute string charset;
/**
* This attribute holds the MIME types corresponding to the content
* encodings on the channel. The enumerator returns nsISupportsString
* objects. The first one corresponds to the outermost encoding on the
* channel and then we work our way inward. "identity" is skipped and not
* represented on the list. Unknown encodings make the enumeration stop.
* If you want the actual Content-Encoding value, use
* getResponseHeader("Content-Encoding").
*
* When there is no Content-Encoding header, this property is null.
*
* Modifying the Content-Encoding header on the channel will case
* this enumerator to have undefined behavior. Don't do it.
*/
readonly attribute nsISimpleEnumerator contentEncodings;
/**
* This attribute controls whether or not content conversion should be
* done per the Content-Encoding response header. Its value is true

Просмотреть файл

@ -2473,6 +2473,22 @@ nsHttpChannel::SetRedirectionLimit(PRUint32 value)
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetContentEncodings(nsISimpleEnumerator** aEncodings)
{
NS_PRECONDITION(aEncodings, "Null out param");
const char *encoding = mResponseHead->PeekHeader(nsHttp::Content_Encoding);
if (!encoding) {
*aEncodings = nsnull;
return NS_OK;
}
nsContentEncodings* enumerator = new nsContentEncodings(this, encoding);
if (!enumerator)
return NS_ERROR_OUT_OF_MEMORY;
return CallQueryInterface(enumerator, aEncodings);
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIRequestObserver
//-----------------------------------------------------------------------------
@ -2808,3 +2824,151 @@ nsHttpChannel::ClearPasswordManagerEntry(const char *host, PRInt32 port, const c
passWordManager->RemoveUser(domain.get(), user);
}
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsContentEncodings <public>
//-----------------------------------------------------------------------------
nsHttpChannel::nsContentEncodings::nsContentEncodings(nsIHttpChannel* aChannel,
const char* aEncodingHeader) :
mEncodingHeader(aEncodingHeader), mChannel(aChannel), mReady(PR_FALSE)
{
mCurEnd = aEncodingHeader + strlen(aEncodingHeader);
mCurStart = mCurEnd;
NS_INIT_ISUPPORTS();
}
nsHttpChannel::nsContentEncodings::~nsContentEncodings()
{
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsContentEncodings::nsISimpleEnumerator
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::nsContentEncodings::HasMoreElements(PRBool* aMoreEncodings)
{
if (mReady) {
*aMoreEncodings = PR_TRUE;
return NS_OK;
}
nsresult rv = PrepareForNext();
*aMoreEncodings = NS_SUCCEEDED(rv);
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::nsContentEncodings::GetNext(nsISupports** aNextEncoding)
{
*aNextEncoding = nsnull;
if (!mReady) {
nsresult rv = PrepareForNext();
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
}
const nsACString & encoding = Substring(mCurStart, mCurEnd);
nsACString::const_iterator start, end;
encoding.BeginReading(start);
encoding.EndReading(end);
nsCOMPtr<nsISupportsString> str;
str = do_CreateInstance("@mozilla.org/supports-string;1");
if (!str)
return NS_ERROR_FAILURE;
PRBool haveType = PR_FALSE;
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("gzip"),
start,
end)) {
str->SetDataWithLength(sizeof(APPLICATION_GZIP) - 1,
APPLICATION_GZIP);
haveType = PR_TRUE;
}
if (!haveType) {
encoding.BeginReading(start);
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("compress"),
start,
end)) {
str->SetDataWithLength(sizeof(APPLICATION_COMPRESS) - 1,
APPLICATION_COMPRESS);
haveType = PR_TRUE;
}
}
if (! haveType) {
encoding.BeginReading(start);
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("deflate"),
start,
end)) {
str->SetDataWithLength(sizeof(APPLICATION_ZIP) - 1,
APPLICATION_ZIP);
haveType = PR_TRUE;
}
}
// Prepare to fetch the next encoding
mCurEnd = mCurStart;
mReady = PR_FALSE;
if (haveType)
return CallQueryInterface(str, aNextEncoding);
NS_WARNING("Unknown encoding type");
return NS_ERROR_FAILURE;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsContentEncodings::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS1(nsHttpChannel::nsContentEncodings, nsISimpleEnumerator)
//-----------------------------------------------------------------------------
// nsHttpChannel::nsContentEncodings <private>
//-----------------------------------------------------------------------------
nsresult
nsHttpChannel::nsContentEncodings::PrepareForNext(void)
{
NS_PRECONDITION(mCurStart == mCurEnd, "Indeterminate state");
// At this point both mCurStart and mCurEnd point to somewhere
// past the end of the next thing we want to return
while (mCurEnd != mEncodingHeader) {
--mCurEnd;
if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd))
break;
}
if (mCurEnd == mEncodingHeader)
return NS_ERROR_NOT_AVAILABLE; // no more encodings
++mCurEnd;
// At this point mCurEnd points to the first char _after_ the
// header we want. Furthermore, mCurEnd - 1 != mEncodingHeader
mCurStart = mCurEnd - 1;
while (mCurStart != mEncodingHeader &&
*mCurStart != ',' && !nsCRT::IsAsciiSpace(*mCurStart))
--mCurStart;
if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart))
++mCurStart; // we stopped because of a weird char, so move up one
// At this point mCurStart and mCurEnd bracket the encoding string
// we want. Check that it's not "identity"
if (Substring(mCurStart, mCurEnd).Equals("identity",
nsCaseInsensitiveCStringComparator())) {
mCurEnd = mCurStart;
return PrepareForNext();
}
mReady = PR_TRUE;
return NS_OK;
}

Просмотреть файл

@ -43,6 +43,7 @@
#include "nsCOMPtr.h"
#include "nsXPIDLString.h"
#include "nsHttpConnection.h"
#include "nsISimpleEnumerator.h"
class nsHttpTransaction;
class nsHttpResponseHead;
@ -176,6 +177,30 @@ private:
PRPackedBool mResponseHeadersModified;
PRPackedBool mCanceled;
PRPackedBool mUploadStreamHasHeaders;
class nsContentEncodings : public nsISimpleEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
nsContentEncodings(nsIHttpChannel* aChannel, const char* aEncodingHeader);
virtual ~nsContentEncodings();
private:
nsresult PrepareForNext(void);
// We do not own the buffer. The channel owns it.
const char* mEncodingHeader;
const char* mCurStart; // points to start of current header
const char* mCurEnd; // points to end of current header
// Hold a ref to our channel so that it can't go away and take the
// header with it.
nsCOMPtr<nsIHttpChannel> mChannel;
PRPackedBool mReady;
};
};
#endif // nsHttpChannel_h__

Просмотреть файл

@ -143,6 +143,12 @@ static const char* const nonDecodableTypes [] = {
0
};
static const char* const nonDecodableExtensions [] = {
"gz",
"zip",
0
};
NS_IMPL_THREADSAFE_ADDREF(nsExternalHelperAppService)
NS_IMPL_THREADSAFE_RELEASE(nsExternalHelperAppService)
@ -319,6 +325,7 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const char *aMimeContentType
NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForType(const char *aMimeContentType, PRBool *aApplyDecoding)
{
NS_PRECONDITION(aMimeContentType, "Null MIME type");
*aApplyDecoding = PR_TRUE;
PRUint32 index;
for (index = 0; nonDecodableTypes[index]; ++index) {
@ -330,6 +337,20 @@ NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForType(const char *aMime
return NS_OK;
}
NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(const char *aExtension, PRBool *aApplyDecoding)
{
NS_PRECONDITION(aExtension, "Null Extension");
*aApplyDecoding = PR_TRUE;
PRUint32 index;
for(index = 0; nonDecodableExtensions[index]; ++index) {
if (!PL_strcasecmp(aExtension, nonDecodableExtensions[index])) {
*aApplyDecoding = PR_FALSE;
break;
}
}
return NS_OK;
}
NS_IMETHODIMP nsExternalHelperAppService::LaunchAppWithTempFile(nsIMIMEInfo * aMimeInfo, nsIFile * aTempFile)
{
// this method should only be implemented by each OS specific implementation of this service.
@ -1061,11 +1082,26 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
{
// Turn off content encoding conversions if needed
PRBool applyConversion = PR_TRUE;
nsXPIDLCString MIMEType;
mMimeInfo->GetMIMEType( getter_Copies( MIMEType ) );
nsCOMPtr<nsIExternalHelperAppService> extHandler = do_GetService("@mozilla.org/uriloader/external-helper-app-service;1");
if (extHandler)
if (extHandler) {
nsXPIDLCString MIMEType;
mMimeInfo->GetMIMEType( getter_Copies( MIMEType ) );
extHandler->ApplyDecodingForType(MIMEType, &applyConversion);
if (applyConversion) {
// Now we double-check that it's OK to decode this extension
nsCOMPtr<nsIURI> channelURI;
aChannel->GetURI(getter_AddRefs(channelURI));
nsCOMPtr<nsIURL> channelURL(do_QueryInterface(channelURI));
nsCAutoString extension;
if (channelURL) {
channelURL->GetFileExtension(extension);
if (!extension.IsEmpty())
extHandler->ApplyDecodingForExtension(extension.get(), &applyConversion);
}
}
}
httpChannel->SetApplyConversion( applyConversion );
}

Просмотреть файл

@ -60,6 +60,11 @@ interface nsIExternalHelperAppService : nsISupports
// prior to saving or passing off to helper apps,
// false otherwise
boolean applyDecodingForType (in string aMimeContentType);
// applyDecodingForExtension: returns false if files with this extension
// are not to be decoded prior to saving or
// passing off to helper apps, false otherwise
boolean applyDecodingForExtension(in string aExtension);
};
// this is a private interface shared between external app handlers and the platform specific

Просмотреть файл

@ -197,6 +197,23 @@ function saveInternal(aURL, aDocument,
function foundHeaderInfo(aSniffer, aData)
{
var contentType = aSniffer.contentType;
var contentEncodingType = aSniffer.contentEncodingType;
var shouldDecode = false;
// Are we allowed to decode?
try {
const helperAppService =
Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].
getService(Components.interfaces.nsIExternalHelperAppService);
var url = aSniffer.uri.QueryInterface(Components.interfaces.nsIURL);
var urlExt = url.fileExtension;
if (helperAppService.applyDecodingForType(contentType) &&
(!urlExt || helperAppService.applyDecodingForExtension(urlExt))) {
shouldDecode = true;
}
}
catch (e) {
}
var fp = makeFilePicker();
var titleKey = aData.filePickerTitle || "SaveLinkTitle";
@ -206,7 +223,16 @@ function foundHeaderInfo(aSniffer, aData)
var isDocument = aData.document != null && isDocumentType(contentType);
appendFiltersForContentType(fp, aSniffer.contentType,
if (!isDocument && !shouldDecode && contentEncodingType) {
// The data is encoded, we are not going to decode it, and this is not a
// document save so we won't be doing a "save as, complete" (which would
// break if we reset the type here). So just set our content type to
// correspond to the outermost encoding so we get extensions and the like
// right.
contentType = contentEncodingType;
}
appendFiltersForContentType(fp, contentType,
isDocument ? MODE_COMPLETE : MODE_FILEONLY);
const prefSvcContractID = "@mozilla.org/preferences-service;1";
@ -278,15 +304,9 @@ function foundHeaderInfo(aSniffer, aData)
else
persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
try {
const helperAppService =
Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].getService(Components.interfaces.nsIExternalHelperAppService);
if (helperAppService.applyDecodingForType(persistArgs.contentType)) {
persist.persistFlags &= ~nsIWBP.PERSIST_FLAGS_NO_CONVERSION;
}
} catch (e) {
}
if (shouldDecode)
persist.persistFlags &= ~nsIWBP.PERSIST_FLAGS_NO_CONVERSION;
if (isDocument && fp.filterIndex != 1) {
// Saving a Document, not a URI:
var filesFolder = null;
@ -403,6 +423,22 @@ nsHeaderSniffer.prototype = {
this.contentType = channel.contentType;
try {
var httpChannel = channel.QueryInterface(Components.interfaces.nsIHttpChannel);
this.contentEncodingType = null;
// There may be content-encodings on the channel. Multiple content
// encodings are allowed, eg "Content-Encoding: gzip, uuencode". This
// header would mean that the content was first gzipped and then
// uuencoded. The encoding enumerator returns MIME types
// corresponding to each encoding starting from the end, so the first
// thing it returns corresponds to the outermost encoding.
var encodingEnumerator = httpChannel.contentEncodings;
if (encodingEnumerator && encodingEnumerator.hasMoreElements()) {
try {
this.contentEncodingType =
encodingEnumerator.getNext().
QueryInterface(Components.interfaces.nsISupportsString).data;
} catch (e) {
}
}
this.mContentDisposition = httpChannel.getResponseHeader("content-disposition");
}
catch (e) {