зеркало из https://github.com/mozilla/gecko-dev.git
Bug 669259 - Expose original header received from a peer. r=mcmanus
This commit is contained in:
Родитель
730c3b339c
Коммит
7cf5152088
|
@ -1511,6 +1511,12 @@ pref("network.http.enable-packaged-apps", false);
|
|||
// Set to false if you don't need the signed packaged web app support (i.e. NSec).
|
||||
pref("network.http.signed-packages.enabled", false);
|
||||
|
||||
// If it is set to false, headers with empty value will not appear in the header
|
||||
// array - behavior as it used to be. If it is true: empty headers coming from
|
||||
// the network will exits in header array as empty string. Call SetHeader with
|
||||
// an empty value will still delete the header.(Bug 6699259)
|
||||
pref("network.http.keep_empty_response_headers_as_empty_string", false);
|
||||
|
||||
// default values for FTP
|
||||
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
|
||||
// Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
|
||||
|
|
|
@ -1660,9 +1660,38 @@ HttpBaseChannel::SetResponseHeader(const nsACString& header,
|
|||
NS_IMETHODIMP
|
||||
HttpBaseChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor)
|
||||
{
|
||||
if (!mResponseHead)
|
||||
if (!mResponseHead) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
return mResponseHead->Headers().VisitHeaders(visitor);
|
||||
}
|
||||
return mResponseHead->Headers().VisitHeaders(visitor,
|
||||
nsHttpHeaderArray::eFilterResponse);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetOriginalResponseHeader(const nsACString& aHeader,
|
||||
nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
if (!mResponseHead) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
|
||||
if (!atom) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return mResponseHead->Headers().GetOriginalHeader(atom, aVisitor);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::VisitOriginalResponseHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
if (!mResponseHead) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return mResponseHead->Headers().VisitHeaders(aVisitor,
|
||||
nsHttpHeaderArray::eFilterResponseOriginal);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -155,6 +155,9 @@ public:
|
|||
NS_IMETHOD SetResponseHeader(const nsACString& header,
|
||||
const nsACString& value, bool merge) override;
|
||||
NS_IMETHOD VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) override;
|
||||
NS_IMETHOD GetOriginalResponseHeader(const nsACString &aHeader,
|
||||
nsIHttpHeaderVisitor *aVisitor) override;
|
||||
NS_IMETHOD VisitOriginalResponseHeaders(nsIHttpHeaderVisitor *aVisitor) override;
|
||||
NS_IMETHOD GetAllowPipelining(bool *value) override;
|
||||
NS_IMETHOD SetAllowPipelining(bool value) override;
|
||||
NS_IMETHOD GetAllowSTS(bool *value) override;
|
||||
|
|
|
@ -209,6 +209,19 @@ NullHttpChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *aVisitor)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetOriginalResponseHeader(const nsACString & header,
|
||||
nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::VisitOriginalResponseHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::IsNoStoreResponse(bool *_retval)
|
||||
{
|
||||
|
|
|
@ -100,14 +100,58 @@ struct ParamTraits<mozilla::net::nsHttpHeaderArray::nsEntry>
|
|||
{
|
||||
WriteParam(aMsg, aParam.header);
|
||||
WriteParam(aMsg, aParam.value);
|
||||
switch (aParam.variety) {
|
||||
case mozilla::net::nsHttpHeaderArray::eVarietyUnknown:
|
||||
WriteParam(aMsg, (uint8_t)0);
|
||||
break;
|
||||
case mozilla::net::nsHttpHeaderArray::eVarietyRequestOverride:
|
||||
WriteParam(aMsg, (uint8_t)1);
|
||||
break;
|
||||
case mozilla::net::nsHttpHeaderArray::eVarietyRequestDefault:
|
||||
WriteParam(aMsg, (uint8_t)2);
|
||||
break;
|
||||
case mozilla::net::nsHttpHeaderArray::eVarietyResponseNetOriginalAndResponse:
|
||||
WriteParam(aMsg, (uint8_t)3);
|
||||
break;
|
||||
case mozilla::net::nsHttpHeaderArray::eVarietyResponseNetOriginal:
|
||||
WriteParam(aMsg, (uint8_t)4);
|
||||
break;
|
||||
case mozilla::net::nsHttpHeaderArray::eVarietyResponse:
|
||||
WriteParam(aMsg, (uint8_t)5);
|
||||
}
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
uint8_t variety;
|
||||
if (!ReadParam(aMsg, aIter, &aResult->header) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->value))
|
||||
!ReadParam(aMsg, aIter, &aResult->value) ||
|
||||
!ReadParam(aMsg, aIter, &variety))
|
||||
return false;
|
||||
|
||||
switch (variety) {
|
||||
case 0:
|
||||
aResult->variety = mozilla::net::nsHttpHeaderArray::eVarietyUnknown;
|
||||
break;
|
||||
case 1:
|
||||
aResult->variety = mozilla::net::nsHttpHeaderArray::eVarietyRequestOverride;
|
||||
break;
|
||||
case 2:
|
||||
aResult->variety = mozilla::net::nsHttpHeaderArray::eVarietyRequestDefault;
|
||||
break;
|
||||
case 3:
|
||||
aResult->variety = mozilla::net::nsHttpHeaderArray::eVarietyResponseNetOriginalAndResponse;
|
||||
break;
|
||||
case 4:
|
||||
aResult->variety = mozilla::net::nsHttpHeaderArray::eVarietyResponseNetOriginal;
|
||||
break;
|
||||
case 5:
|
||||
aResult->variety = mozilla::net::nsHttpHeaderArray::eVarietyResponse;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -238,6 +238,7 @@ nsHttpHandler::nsHttpHandler()
|
|||
, mTCPKeepaliveLongLivedEnabled(false)
|
||||
, mTCPKeepaliveLongLivedIdleTimeS(600)
|
||||
, mEnforceH1Framing(FRAMECHECK_BARELY)
|
||||
, mKeepEmptyResponseHeadersAsEmtpyString(false)
|
||||
{
|
||||
LOG(("Creating nsHttpHandler [this=%p].\n", this));
|
||||
|
||||
|
@ -455,7 +456,7 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpRequestHead *request, bool isSecu
|
|||
|
||||
// Add the "User-Agent" header
|
||||
rv = request->SetHeader(nsHttp::User_Agent, UserAgent(),
|
||||
false, nsHttpHeaderArray::eVarietyDefault);
|
||||
false, nsHttpHeaderArray::eVarietyRequestDefault);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// MIME based content negotiation lives!
|
||||
|
@ -463,7 +464,7 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpRequestHead *request, bool isSecu
|
|||
// service worker expects to see it. The other "default" headers are
|
||||
// hidden from service worker interception.
|
||||
rv = request->SetHeader(nsHttp::Accept, mAccept,
|
||||
false, nsHttpHeaderArray::eVarietyOverride);
|
||||
false, nsHttpHeaderArray::eVarietyRequestOverride);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Add the "Accept-Language" header. This header is also exposed to the
|
||||
|
@ -471,31 +472,36 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpRequestHead *request, bool isSecu
|
|||
if (!mAcceptLanguages.IsEmpty()) {
|
||||
// Add the "Accept-Language" header
|
||||
rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages,
|
||||
false, nsHttpHeaderArray::eVarietyOverride);
|
||||
false,
|
||||
nsHttpHeaderArray::eVarietyRequestOverride);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
// Add the "Accept-Encoding" header
|
||||
if (isSecure) {
|
||||
rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings,
|
||||
false, nsHttpHeaderArray::eVarietyDefault);
|
||||
false,
|
||||
nsHttpHeaderArray::eVarietyRequestDefault);
|
||||
} else {
|
||||
rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings,
|
||||
false, nsHttpHeaderArray::eVarietyDefault);
|
||||
false,
|
||||
nsHttpHeaderArray::eVarietyRequestDefault);
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Add the "Do-Not-Track" header
|
||||
if (mDoNotTrackEnabled) {
|
||||
rv = request->SetHeader(nsHttp::DoNotTrack, NS_LITERAL_CSTRING("1"),
|
||||
false, nsHttpHeaderArray::eVarietyDefault);
|
||||
false,
|
||||
nsHttpHeaderArray::eVarietyRequestDefault);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
// add the "Send Hint" header
|
||||
if (mSafeHintEnabled || mParentalControlEnabled) {
|
||||
rv = request->SetHeader(nsHttp::Prefer, NS_LITERAL_CSTRING("safe"),
|
||||
false, nsHttpHeaderArray::eVarietyDefault);
|
||||
false,
|
||||
nsHttpHeaderArray::eVarietyRequestDefault);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -1695,6 +1701,14 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
|||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("keep_empty_response_headers_as_empty_string"))) {
|
||||
rv = prefs->GetBoolPref(HTTP_PREF("keep_empty_response_headers_as_empty_string"),
|
||||
&cVar);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mKeepEmptyResponseHeadersAsEmtpyString = cVar;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable HTTP response timeout if TCP Keepalives are disabled.
|
||||
mResponseTimeoutEnabled = !mTCPKeepaliveShortLivedEnabled &&
|
||||
!mTCPKeepaliveLongLivedEnabled;
|
||||
|
|
|
@ -359,6 +359,11 @@ public:
|
|||
|
||||
void ShutdownConnectionManager();
|
||||
|
||||
bool KeepEmptyResponseHeadersAsEmtpyString() const
|
||||
{
|
||||
return mKeepEmptyResponseHeadersAsEmtpyString;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsHttpHandler();
|
||||
|
||||
|
@ -572,6 +577,13 @@ private:
|
|||
// True if remote newtab content-signature disabled because of the channel.
|
||||
bool mNewTabContentSignaturesDisabled;
|
||||
|
||||
// If it is set to false, headers with empty value will not appear in the
|
||||
// header array - behavior as it used to be. If it is true: empty headers
|
||||
// coming from the network will exits in header array as empty string.
|
||||
// Call SetHeader with an empty value will still delete the header.
|
||||
// (Bug 6699259)
|
||||
bool mKeepEmptyResponseHeadersAsEmtpyString;
|
||||
|
||||
private:
|
||||
// For Rate Pacing Certain Network Events. Only assign this pointer on
|
||||
// socket thread.
|
||||
|
|
|
@ -23,6 +23,11 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
|||
bool merge,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
MOZ_ASSERT((variety == eVarietyResponse) ||
|
||||
(variety == eVarietyRequestDefault) ||
|
||||
(variety == eVarietyRequestOverride),
|
||||
"Net original headers can only be set using SetHeader_internal().");
|
||||
|
||||
nsEntry *entry = nullptr;
|
||||
int32_t index;
|
||||
|
||||
|
@ -31,71 +36,120 @@ 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)
|
||||
mHeaders.RemoveElementAt(index);
|
||||
if (!merge && entry) {
|
||||
if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
|
||||
MOZ_ASSERT(variety == eVarietyResponse);
|
||||
entry->variety = eVarietyResponseNetOriginal;
|
||||
} else {
|
||||
mHeaders.RemoveElementAt(index);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!entry || variety != eVarietyDefault,
|
||||
MOZ_ASSERT(!entry || variety != eVarietyRequestDefault,
|
||||
"Cannot set default entry which overrides existing entry!");
|
||||
if (!entry) {
|
||||
entry = mHeaders.AppendElement(); // new nsEntry()
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
entry->header = header;
|
||||
entry->value = value;
|
||||
entry->variety = variety;
|
||||
return SetHeader_internal(header, value, variety);
|
||||
} else if (merge && !IsSingletonHeader(header)) {
|
||||
MergeHeader(header, entry, value);
|
||||
return MergeHeader(header, entry, value, variety);
|
||||
} else {
|
||||
// Replace the existing string with the new value
|
||||
entry->value = value;
|
||||
entry->variety = eVarietyOverride;
|
||||
if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
|
||||
MOZ_ASSERT(variety == eVarietyResponse);
|
||||
entry->variety = eVarietyResponseNetOriginal;
|
||||
return SetHeader_internal(header, value, variety);
|
||||
} else {
|
||||
entry->value = value;
|
||||
entry->variety = variety;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetEmptyHeader(nsHttpAtom header)
|
||||
nsHttpHeaderArray::SetHeader_internal(nsHttpAtom header,
|
||||
const nsACString &value,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
nsEntry *entry = mHeaders.AppendElement();
|
||||
if (!entry) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
entry->header = header;
|
||||
entry->value = value;
|
||||
entry->variety = variety;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetEmptyHeader(nsHttpAtom header, HeaderVariety variety)
|
||||
{
|
||||
MOZ_ASSERT((variety == eVarietyResponse) ||
|
||||
(variety == eVarietyRequestDefault) ||
|
||||
(variety == eVarietyRequestOverride),
|
||||
"Original headers can only be set using SetHeader_internal().");
|
||||
nsEntry *entry = nullptr;
|
||||
|
||||
LookupEntry(header, &entry);
|
||||
|
||||
if (!entry) {
|
||||
entry = mHeaders.AppendElement(); // new nsEntry()
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
entry->header = header;
|
||||
} else {
|
||||
if (entry &&
|
||||
entry->variety != eVarietyResponseNetOriginalAndResponse) {
|
||||
entry->value.Truncate();
|
||||
return NS_OK;
|
||||
} else if (entry) {
|
||||
MOZ_ASSERT(variety == eVarietyResponse);
|
||||
entry->variety = eVarietyResponseNetOriginal;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return SetHeader_internal(header, EmptyCString(), variety);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value)
|
||||
nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
|
||||
const nsACString &value,
|
||||
bool response)
|
||||
{
|
||||
// mHeader holds the consolidated (merged or updated) headers.
|
||||
// mHeader for response header will keep the original heades as well.
|
||||
nsEntry *entry = nullptr;
|
||||
|
||||
LookupEntry(header, &entry);
|
||||
|
||||
if (!entry) {
|
||||
if (value.IsEmpty()) {
|
||||
if (!TrackEmptyHeader(header)) {
|
||||
if (!gHttpHandler->KeepEmptyResponseHeadersAsEmtpyString() &&
|
||||
!TrackEmptyHeader(header)) {
|
||||
LOG(("Ignoring Empty Header: %s\n", header.get()));
|
||||
if (response) {
|
||||
// Set header as original but not as response header.
|
||||
return SetHeader_internal(header, value,
|
||||
eVarietyResponseNetOriginal);
|
||||
}
|
||||
return NS_OK; // ignore empty headers by default
|
||||
}
|
||||
}
|
||||
entry = mHeaders.AppendElement(); //new nsEntry(header, value);
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
entry->header = header;
|
||||
entry->value = value;
|
||||
HeaderVariety variety = eVarietyRequestOverride;
|
||||
if (response) {
|
||||
variety = eVarietyResponseNetOriginalAndResponse;
|
||||
}
|
||||
return SetHeader_internal(header, value, variety);
|
||||
|
||||
} else if (!IsSingletonHeader(header)) {
|
||||
MergeHeader(header, entry, value);
|
||||
HeaderVariety variety = eVarietyRequestOverride;
|
||||
if (response) {
|
||||
variety = eVarietyResponse;
|
||||
}
|
||||
nsresult rv = MergeHeader(header, entry, value, variety);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (response) {
|
||||
rv = SetHeader_internal(header, value,
|
||||
eVarietyResponseNetOriginal);
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
// Multiple instances of non-mergeable header received from network
|
||||
// - ignore if same value
|
||||
|
@ -106,6 +160,11 @@ nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value)
|
|||
} // else silently drop value: keep value from 1st header seen
|
||||
LOG(("Header %s silently dropped as non mergeable header\n",
|
||||
header.get()));
|
||||
|
||||
}
|
||||
if (response) {
|
||||
return SetHeader_internal(header, value,
|
||||
eVarietyResponseNetOriginal);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +174,15 @@ nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value)
|
|||
void
|
||||
nsHttpHeaderArray::ClearHeader(nsHttpAtom header)
|
||||
{
|
||||
mHeaders.RemoveElement(header, nsEntry::MatchHeader());
|
||||
nsEntry *entry = nullptr;
|
||||
int32_t index = LookupEntry(header, &entry);
|
||||
if (entry) {
|
||||
if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
|
||||
entry->variety = eVarietyResponseNetOriginal;
|
||||
} else {
|
||||
mHeaders.RemoveElementAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
|
@ -137,6 +204,40 @@ nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result) const
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::GetOriginalHeader(nsHttpAtom aHeader,
|
||||
nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aVisitor);
|
||||
uint32_t index = 0;
|
||||
nsresult rv = NS_ERROR_NOT_AVAILABLE;
|
||||
while (true) {
|
||||
index = mHeaders.IndexOf(aHeader, index, nsEntry::MatchHeader());
|
||||
if (index != UINT32_MAX) {
|
||||
const nsEntry &entry = mHeaders[index];
|
||||
|
||||
MOZ_ASSERT((entry.variety == eVarietyResponseNetOriginalAndResponse) ||
|
||||
(entry.variety == eVarietyResponseNetOriginal) ||
|
||||
(entry.variety == eVarietyResponse),
|
||||
"This must be a response header.");
|
||||
index++;
|
||||
if (entry.variety == eVarietyResponse) {
|
||||
continue;
|
||||
}
|
||||
rv = NS_OK;
|
||||
if (NS_FAILED(aVisitor->VisitHeader(nsDependentCString(entry.header),
|
||||
entry.value))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// if there is no such a header, it will return
|
||||
// NS_ERROR_NOT_AVAILABLE or NS_OK otherwise.
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpHeaderArray::HasHeader(nsHttpAtom header) const
|
||||
{
|
||||
|
@ -152,17 +253,22 @@ nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor, nsHttpHeaderArray
|
|||
uint32_t i, count = mHeaders.Length();
|
||||
for (i = 0; i < count; ++i) {
|
||||
const nsEntry &entry = mHeaders[i];
|
||||
if (filter == eFilterSkipDefault && entry.variety == eVarietyDefault) {
|
||||
if (filter == eFilterSkipDefault && entry.variety == eVarietyRequestDefault) {
|
||||
continue;
|
||||
} else if (filter == eFilterResponse && entry.variety == eVarietyResponseNetOriginal) {
|
||||
continue;
|
||||
} else if (filter == eFilterResponseOriginal && entry.variety == eVarietyResponse) {
|
||||
continue;
|
||||
}
|
||||
if (NS_FAILED(visitor->VisitHeader(nsDependentCString(entry.header),
|
||||
entry.value)))
|
||||
entry.value))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
/*static*/ nsresult
|
||||
nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
||||
nsHttpAtom *hdr,
|
||||
char **val)
|
||||
|
@ -180,17 +286,16 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
|
||||
// We skip over mal-formed headers in the hope that we'll still be able to
|
||||
// do something useful with the response.
|
||||
|
||||
char *p = (char *) strchr(line, ':');
|
||||
if (!p) {
|
||||
LOG(("malformed header [%s]: no colon\n", line));
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// make sure we have a valid token for the field-name
|
||||
if (!nsHttp::IsValidToken(line, p)) {
|
||||
LOG(("malformed header [%s]: field-name not a token\n", line));
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*p = 0; // null terminate field-name
|
||||
|
@ -198,7 +303,7 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
nsHttpAtom atom = nsHttp::ResolveAtom(line);
|
||||
if (!atom) {
|
||||
LOG(("failed to resolve atom [%s]\n", line));
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// skip over whitespace
|
||||
|
@ -215,39 +320,60 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
if (hdr) *hdr = atom;
|
||||
if (val) *val = p;
|
||||
|
||||
// assign response header
|
||||
return SetHeaderFromNet(atom, nsDependentCString(p, p2 - p));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHeaderArray::ParseHeaderSet(char *buffer)
|
||||
{
|
||||
nsHttpAtom hdr;
|
||||
char *val;
|
||||
while (buffer) {
|
||||
char *eof = strchr(buffer, '\r');
|
||||
if (!eof) {
|
||||
break;
|
||||
}
|
||||
*eof = '\0';
|
||||
ParseHeaderLine(buffer, &hdr, &val);
|
||||
buffer = eof + 1;
|
||||
if (*buffer == '\n') {
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders)
|
||||
nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders,
|
||||
bool pruneTransients)
|
||||
{
|
||||
uint32_t 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)))
|
||||
// Skip original header.
|
||||
if (entry.variety == eVarietyResponseNetOriginal) {
|
||||
continue;
|
||||
}
|
||||
// prune proxy headers if requested
|
||||
if (pruneProxyHeaders &&
|
||||
((entry.header == nsHttp::Proxy_Authorization) ||
|
||||
(entry.header == nsHttp::Proxy_Connection))) {
|
||||
continue;
|
||||
}
|
||||
if (pruneTransients &&
|
||||
(entry.value.IsEmpty() ||
|
||||
entry.header == nsHttp::Connection ||
|
||||
entry.header == nsHttp::Proxy_Connection ||
|
||||
entry.header == nsHttp::Keep_Alive ||
|
||||
entry.header == nsHttp::WWW_Authenticate ||
|
||||
entry.header == nsHttp::Proxy_Authenticate ||
|
||||
entry.header == nsHttp::Trailer ||
|
||||
entry.header == nsHttp::Transfer_Encoding ||
|
||||
entry.header == nsHttp::Upgrade ||
|
||||
// XXX this will cause problems when we start honoring
|
||||
// Cache-Control: no-cache="set-cookie", what to do?
|
||||
entry.header == nsHttp::Set_Cookie)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.Append(entry.header);
|
||||
buf.AppendLiteral(": ");
|
||||
buf.Append(entry.value);
|
||||
buf.AppendLiteral("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHeaderArray::FlattenOriginalHeader(nsACString &buf)
|
||||
{
|
||||
uint32_t i, count = mHeaders.Length();
|
||||
for (i = 0; i < count; ++i) {
|
||||
const nsEntry &entry = mHeaders[i];
|
||||
// Skip changed header.
|
||||
if (entry.variety == eVarietyResponse) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.Append(entry.header);
|
||||
buf.AppendLiteral(": ");
|
||||
buf.Append(entry.value);
|
||||
|
|
|
@ -26,24 +26,44 @@ class nsHttpHeaderArray
|
|||
public:
|
||||
const char *PeekHeader(nsHttpAtom header) const;
|
||||
|
||||
// For nsHttpResponseHead nsHttpHeaderArray will keep track of the original
|
||||
// headers as they come from the network and the parse headers used in
|
||||
// firefox.
|
||||
// If the original and the firefox header are the same, we will keep just
|
||||
// one copy and marked it as eVarietyResponseNetOriginalAndResponse.
|
||||
// If firefox header representation changes a header coming from the
|
||||
// network (e.g. merged it) or a eVarietyResponseNetOriginalAndResponse
|
||||
// header has been changed by SetHeader method, we will keep the original
|
||||
// header as eVarietyResponseNetOriginal and make a copy for the new header
|
||||
// and mark it as eVarietyResponse.
|
||||
enum HeaderVariety
|
||||
{
|
||||
eVarietyOverride,
|
||||
eVarietyDefault,
|
||||
eVarietyUnknown,
|
||||
// Used only for request header.
|
||||
eVarietyRequestOverride,
|
||||
eVarietyRequestDefault,
|
||||
// Used only for response header.
|
||||
eVarietyResponseNetOriginalAndResponse,
|
||||
eVarietyResponseNetOriginal,
|
||||
eVarietyResponse
|
||||
};
|
||||
|
||||
// Used by internal setters: to set header from network use SetHeaderFromNet
|
||||
nsresult SetHeader(nsHttpAtom header, const nsACString &value,
|
||||
bool merge = false, HeaderVariety variety = eVarietyOverride);
|
||||
bool merge, HeaderVariety variety);
|
||||
|
||||
// Used by internal setters to set an empty header
|
||||
nsresult SetEmptyHeader(nsHttpAtom header);
|
||||
nsresult SetEmptyHeader(nsHttpAtom header, HeaderVariety variety);
|
||||
|
||||
// Merges supported headers. For other duplicate values, determines if error
|
||||
// needs to be thrown or 1st value kept.
|
||||
nsresult SetHeaderFromNet(nsHttpAtom header, const nsACString &value);
|
||||
// For the response header we keep the original headers as well.
|
||||
nsresult SetHeaderFromNet(nsHttpAtom header, const nsACString &value,
|
||||
bool response);
|
||||
|
||||
nsresult GetHeader(nsHttpAtom header, nsACString &value) const;
|
||||
nsresult GetOriginalHeader(nsHttpAtom aHeader,
|
||||
nsIHttpHeaderVisitor *aVisitor);
|
||||
void ClearHeader(nsHttpAtom h);
|
||||
|
||||
// Find the location of the given header value, or null if none exists.
|
||||
|
@ -65,19 +85,20 @@ public:
|
|||
{
|
||||
eFilterAll,
|
||||
eFilterSkipDefault,
|
||||
eFilterResponse,
|
||||
eFilterResponseOriginal
|
||||
};
|
||||
|
||||
nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor, VisitorFilter filter = eFilterAll);
|
||||
|
||||
// parse a header line, return the header atom and a pointer to the
|
||||
// header value (the substring of the header line -- do not free).
|
||||
nsresult ParseHeaderLine(const char *line,
|
||||
nsHttpAtom *header=nullptr,
|
||||
char **value=nullptr);
|
||||
static nsresult ParseHeaderLine(const char *line,
|
||||
nsHttpAtom *header=nullptr,
|
||||
char **value=nullptr);
|
||||
|
||||
void Flatten(nsACString &, bool pruneProxyHeaders=false);
|
||||
|
||||
void ParseHeaderSet(char *buffer);
|
||||
void Flatten(nsACString &, bool pruneProxyHeaders, bool pruneTransients);
|
||||
void FlattenOriginalHeader(nsACString &);
|
||||
|
||||
uint32_t Count() const { return mHeaders.Length(); }
|
||||
|
||||
|
@ -90,7 +111,7 @@ public:
|
|||
{
|
||||
nsHttpAtom header;
|
||||
nsCString value;
|
||||
HeaderVariety variety = eVarietyOverride;
|
||||
HeaderVariety variety = eVarietyUnknown;
|
||||
|
||||
struct MatchHeader {
|
||||
bool Equals(const nsEntry &entry, const nsHttpAtom &header) const {
|
||||
|
@ -110,9 +131,14 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
// LookupEntry function will never return eVarietyResponseNetOriginal.
|
||||
// It will ignore original headers from the network.
|
||||
int32_t LookupEntry(nsHttpAtom header, const nsEntry **) const;
|
||||
int32_t LookupEntry(nsHttpAtom header, nsEntry **);
|
||||
void MergeHeader(nsHttpAtom header, nsEntry *entry, const nsACString &value);
|
||||
nsresult MergeHeader(nsHttpAtom header, nsEntry *entry,
|
||||
const nsACString &value, HeaderVariety variety);
|
||||
nsresult SetHeader_internal(nsHttpAtom header, const nsACString &value,
|
||||
HeaderVariety variety);
|
||||
|
||||
// Header cannot be merged: only one value possible
|
||||
bool IsSingletonHeader(nsHttpAtom header);
|
||||
|
@ -139,18 +165,35 @@ private:
|
|||
inline int32_t
|
||||
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, const nsEntry **entry) const
|
||||
{
|
||||
uint32_t index = mHeaders.IndexOf(header, 0, nsEntry::MatchHeader());
|
||||
if (index != UINT32_MAX)
|
||||
*entry = &mHeaders[index];
|
||||
uint32_t index = 0;
|
||||
while (index != UINT32_MAX) {
|
||||
index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
|
||||
if (index != UINT32_MAX) {
|
||||
if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
|
||||
*entry = &mHeaders[index];
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
inline int32_t
|
||||
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
|
||||
{
|
||||
uint32_t index = mHeaders.IndexOf(header, 0, nsEntry::MatchHeader());
|
||||
if (index != UINT32_MAX)
|
||||
*entry = &mHeaders[index];
|
||||
uint32_t index = 0;
|
||||
while (index != UINT32_MAX) {
|
||||
index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
|
||||
if (index != UINT32_MAX) {
|
||||
if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
|
||||
*entry = &mHeaders[index];
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -179,15 +222,17 @@ nsHttpHeaderArray::TrackEmptyHeader(nsHttpAtom header)
|
|||
header == nsHttp::Location;
|
||||
}
|
||||
|
||||
inline void
|
||||
inline nsresult
|
||||
nsHttpHeaderArray::MergeHeader(nsHttpAtom header,
|
||||
nsEntry *entry,
|
||||
const nsACString &value)
|
||||
const nsACString &value,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
if (value.IsEmpty())
|
||||
return; // merge of empty header = no-op
|
||||
return NS_OK; // merge of empty header = no-op
|
||||
|
||||
if (!entry->value.IsEmpty()) {
|
||||
nsCString newValue = entry->value;
|
||||
if (!newValue.IsEmpty()) {
|
||||
// Append the new value to the existing value
|
||||
if (header == nsHttp::Set_Cookie ||
|
||||
header == nsHttp::WWW_Authenticate ||
|
||||
|
@ -196,14 +241,26 @@ nsHttpHeaderArray::MergeHeader(nsHttpAtom header,
|
|||
// Special case these headers and use a newline delimiter to
|
||||
// delimit the values from one another as commas may appear
|
||||
// in the values of these headers contrary to what the spec says.
|
||||
entry->value.Append('\n');
|
||||
newValue.Append('\n');
|
||||
} else {
|
||||
// Delimit each value from the others using a comma (per HTTP spec)
|
||||
entry->value.AppendLiteral(", ");
|
||||
newValue.AppendLiteral(", ");
|
||||
}
|
||||
}
|
||||
entry->value.Append(value);
|
||||
entry->variety = eVarietyOverride;
|
||||
|
||||
newValue.Append(value);
|
||||
if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
|
||||
MOZ_ASSERT(variety == eVarietyResponse);
|
||||
entry->variety = eVarietyResponseNetOriginal;
|
||||
nsresult rv = SetHeader_internal(header, newValue, eVarietyResponse);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
entry->value = newValue;
|
||||
entry->variety = variety;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -130,7 +130,8 @@ nsHttpRequestHead::SetHeader(nsHttpAtom h, const nsACString &v,
|
|||
bool m /*= false*/)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
return mHeaders.SetHeader(h, v, m);
|
||||
return mHeaders.SetHeader(h, v, m,
|
||||
nsHttpHeaderArray::eVarietyRequestOverride);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -145,7 +146,8 @@ nsresult
|
|||
nsHttpRequestHead::SetEmptyHeader(nsHttpAtom h)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
return mHeaders.SetEmptyHeader(h);
|
||||
return mHeaders.SetEmptyHeader(h,
|
||||
nsHttpHeaderArray::eVarietyRequestOverride);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -190,7 +192,8 @@ nsHttpRequestHead::SetHeaderOnce(nsHttpAtom h, const char *v,
|
|||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
if (!merge || !mHeaders.HasHeaderValue(h, v)) {
|
||||
return mHeaders.SetHeader(h, nsDependentCString(v), merge);
|
||||
return mHeaders.SetHeader(h, nsDependentCString(v), merge,
|
||||
nsHttpHeaderArray::eVarietyRequestOverride);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -213,7 +216,24 @@ void
|
|||
nsHttpRequestHead::ParseHeaderSet(char *buffer)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mHeaders.ParseHeaderSet(buffer);
|
||||
nsHttpAtom hdr;
|
||||
char *val;
|
||||
while (buffer) {
|
||||
char *eof = strchr(buffer, '\r');
|
||||
if (!eof) {
|
||||
break;
|
||||
}
|
||||
*eof = '\0';
|
||||
if (NS_SUCCEEDED(nsHttpHeaderArray::ParseHeaderLine(buffer,
|
||||
&hdr,
|
||||
&val))) {
|
||||
mHeaders.SetHeaderFromNet(hdr, nsDependentCString(val), false);
|
||||
}
|
||||
buffer = eof + 1;
|
||||
if (*buffer == '\n') {
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -305,7 +325,7 @@ nsHttpRequestHead::Flatten(nsACString &buf, bool pruneProxyHeaders)
|
|||
|
||||
buf.AppendLiteral("\r\n");
|
||||
|
||||
mHeaders.Flatten(buf, pruneProxyHeaders);
|
||||
mHeaders.Flatten(buf, pruneProxyHeaders, false);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -26,7 +26,8 @@ nsHttpResponseHead::SetHeader(nsHttpAtom hdr,
|
|||
const nsACString &val,
|
||||
bool merge)
|
||||
{
|
||||
nsresult rv = mHeaders.SetHeader(hdr, val, merge);
|
||||
nsresult rv = mHeaders.SetHeader(hdr, val, merge,
|
||||
nsHttpHeaderArray::eVarietyResponse);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// respond to changes in these headers. we need to reparse the entire
|
||||
|
@ -46,7 +47,10 @@ nsHttpResponseHead::SetContentLength(int64_t len)
|
|||
if (len < 0)
|
||||
mHeaders.ClearHeader(nsHttp::Content_Length);
|
||||
else
|
||||
mHeaders.SetHeader(nsHttp::Content_Length, nsPrintfCString("%lld", len));
|
||||
mHeaders.SetHeader(nsHttp::Content_Length,
|
||||
nsPrintfCString("%lld", len),
|
||||
false,
|
||||
nsHttpHeaderArray::eVarietyResponse);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -68,37 +72,21 @@ nsHttpResponseHead::Flatten(nsACString &buf, bool pruneTransients)
|
|||
mStatusText +
|
||||
NS_LITERAL_CSTRING("\r\n"));
|
||||
|
||||
if (!pruneTransients) {
|
||||
mHeaders.Flatten(buf, false);
|
||||
|
||||
mHeaders.Flatten(buf, false, pruneTransients);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpResponseHead::FlattenOriginalHeader(nsACString &buf)
|
||||
{
|
||||
if (mVersion == NS_HTTP_VERSION_0_9) {
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, we need to iterate over the headers and only flatten
|
||||
// those that are appropriate.
|
||||
uint32_t i, count = mHeaders.Count();
|
||||
for (i=0; i<count; ++i) {
|
||||
nsHttpAtom header;
|
||||
const char *value = mHeaders.PeekHeaderAt(i, header);
|
||||
buf.AppendLiteral(" OriginalHeaders");
|
||||
buf.AppendLiteral("\r\n");
|
||||
|
||||
if (!value || header == nsHttp::Connection
|
||||
|| header == nsHttp::Proxy_Connection
|
||||
|| header == nsHttp::Keep_Alive
|
||||
|| header == nsHttp::WWW_Authenticate
|
||||
|| header == nsHttp::Proxy_Authenticate
|
||||
|| header == nsHttp::Trailer
|
||||
|| header == nsHttp::Transfer_Encoding
|
||||
|| header == nsHttp::Upgrade
|
||||
// XXX this will cause problems when we start honoring
|
||||
// Cache-Control: no-cache="set-cookie", what to do?
|
||||
|| header == nsHttp::Set_Cookie)
|
||||
continue;
|
||||
|
||||
// otherwise, write out the "header: value\r\n" line
|
||||
buf.Append(nsDependentCString(header.get()) +
|
||||
NS_LITERAL_CSTRING(": ") +
|
||||
nsDependentCString(value) +
|
||||
NS_LITERAL_CSTRING("\r\n"));
|
||||
}
|
||||
mHeaders.FlattenOriginalHeader(buf);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -120,8 +108,8 @@ nsHttpResponseHead::Parse(char *block)
|
|||
do {
|
||||
block = p + 2;
|
||||
|
||||
if (*block == 0)
|
||||
break;
|
||||
if (*block == 0)
|
||||
break;
|
||||
|
||||
p = PL_strstr(block, "\r\n");
|
||||
if (!p)
|
||||
|
@ -328,11 +316,16 @@ nsHttpResponseHead::ParseHeaderLine(const char *line)
|
|||
{
|
||||
nsHttpAtom hdr = {0};
|
||||
char *val;
|
||||
nsresult rv;
|
||||
|
||||
rv = mHeaders.ParseHeaderLine(line, &hdr, &val);
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(line, &hdr, &val))) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult rv = mHeaders.SetHeaderFromNet(hdr,
|
||||
nsDependentCString(val),
|
||||
true);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// leading and trailing LWS has been removed from |val|
|
||||
|
||||
|
|
|
@ -78,7 +78,9 @@ public:
|
|||
// write out the response status line and headers as a single text block,
|
||||
// optionally pruning out transient headers (ie. headers that only make
|
||||
// sense the first time the response is handled).
|
||||
// Both functions append to the string supplied string.
|
||||
void Flatten(nsACString &, bool pruneTransients);
|
||||
void FlattenOriginalHeader(nsACString &buf);
|
||||
|
||||
// parse flattened response head. block must be null terminated. parsing is
|
||||
// destructive.
|
||||
|
|
|
@ -1564,6 +1564,7 @@ nsHttpTransaction::HandleContentStart()
|
|||
LOG3(("http response [\n"));
|
||||
nsAutoCString headers;
|
||||
mResponseHead->Flatten(headers, false);
|
||||
mResponseHead->FlattenOriginalHeader(headers);
|
||||
LogHeaders(headers.get());
|
||||
LOG3(("]\n"));
|
||||
}
|
||||
|
|
|
@ -344,6 +344,39 @@ interface nsIHttpChannel : nsIChannel
|
|||
*/
|
||||
void visitResponseHeaders(in nsIHttpHeaderVisitor aVisitor);
|
||||
|
||||
/**
|
||||
* Get the value(s) of a particular response header in the form and order
|
||||
* it has been received from the remote peer. There can be multiple headers
|
||||
* with the same name.
|
||||
*
|
||||
* @param aHeader
|
||||
* The case-insensitive name of the response header to query (e.g.,
|
||||
* "Set-Cookie").
|
||||
*
|
||||
* @param aVisitor
|
||||
* the header visitor instance.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if called before the response
|
||||
* has been received (before onStartRequest) or if the header is
|
||||
* not set in the response.
|
||||
*/
|
||||
void getOriginalResponseHeader(in ACString aHeader,
|
||||
in nsIHttpHeaderVisitor aVisitor);
|
||||
|
||||
/**
|
||||
* Call this method to visit all response headers in the form and order as
|
||||
* they have been received from the remote peer.
|
||||
* Calling setResponseHeader while visiting response headers has undefined
|
||||
* behavior. Don't do it!
|
||||
*
|
||||
* @param aVisitor
|
||||
* the header visitor instance.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if called before the response
|
||||
* has been received (before onStartRequest).
|
||||
*/
|
||||
void visitOriginalResponseHeaders(in nsIHttpHeaderVisitor aVisitor);
|
||||
|
||||
/**
|
||||
* Returns true if the server sent a "Cache-Control: no-store" response
|
||||
* header.
|
||||
|
|
|
@ -905,6 +905,25 @@ nsViewSourceChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *aVisitor)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::GetOriginalResponseHeader(const nsACString & aHeader,
|
||||
nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
nsAutoCString value;
|
||||
nsresult rv = GetResponseHeader(aHeader, value);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
aVisitor->VisitHeader(aHeader, value);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::VisitOriginalResponseHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return VisitResponseHeaders(aVisitor);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::IsNoStoreResponse(bool *_retval)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nsHttp.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsHttpHeaderArray.h"
|
||||
|
||||
//
|
||||
// Helper function for determining the length of data bytes up to
|
||||
|
@ -420,7 +421,8 @@ nsPartChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor)
|
|||
{
|
||||
if (!mResponseHead)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
return mResponseHead->Headers().VisitHeaders(visitor);
|
||||
return mResponseHead->Headers().VisitHeaders(visitor,
|
||||
mozilla::net::nsHttpHeaderArray::eFilterResponse);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -3701,6 +3701,15 @@ Response.prototype =
|
|||
this._headers.setHeader(name, value, merge);
|
||||
},
|
||||
|
||||
setHeaderNoCheck: function(name, value)
|
||||
{
|
||||
if (!this._headers || this._finished || this._powerSeized)
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
this._ensureAlive();
|
||||
|
||||
this._headers.setHeaderNoCheck(name, value);
|
||||
},
|
||||
|
||||
//
|
||||
// see nsIHttpResponse.processAsync
|
||||
//
|
||||
|
@ -4989,6 +4998,17 @@ nsHttpHeaders.prototype =
|
|||
}
|
||||
},
|
||||
|
||||
setHeaderNoCheck: function(fieldName, fieldValue)
|
||||
{
|
||||
var name = headerUtils.normalizeFieldName(fieldName);
|
||||
var value = headerUtils.normalizeFieldValue(fieldValue);
|
||||
if (name in this._headers) {
|
||||
this._headers[name].push(fieldValue);
|
||||
} else {
|
||||
this._headers[name] = [fieldValue];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the value for the header specified by this.
|
||||
*
|
||||
|
|
|
@ -529,6 +529,12 @@ interface nsIHttpResponse : nsISupports
|
|||
*/
|
||||
void setHeader(in string name, in string value, in boolean merge);
|
||||
|
||||
/**
|
||||
* This is used for testing our header handling, so header will be sent out
|
||||
* without transformation. There can be multiple headers.
|
||||
*/
|
||||
void setHeaderNoCheck(in string name, in string value);
|
||||
|
||||
/**
|
||||
* A stream to which data appearing in the body of this response (or in the
|
||||
* totality of the response if seizePower() is called) should be written.
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
//
|
||||
// HTTP headers test
|
||||
// Response headers can be changed after they have been received, e.g. empty
|
||||
// headers are deleted, some duplicate header are merged (if no error is
|
||||
// thrown), etc.
|
||||
//
|
||||
// The "original header" is introduced to hold the header array in the order
|
||||
// and the form as they have been received from the network.
|
||||
// Here, the "original headers" are tested.
|
||||
//
|
||||
|
||||
// Note: sets Cc and Ci variables
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "URL", function() {
|
||||
return "http://localhost:" + httpserver.identity.primaryPort;
|
||||
});
|
||||
|
||||
var httpserver = new HttpServer();
|
||||
var testpath = "/simple";
|
||||
var httpbody = "0123456789";
|
||||
var channel;
|
||||
var ios;
|
||||
|
||||
var dbg=1
|
||||
if (dbg) { print("============== START =========="); }
|
||||
|
||||
function run_test() {
|
||||
setup_test();
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function setup_test() {
|
||||
if (dbg) { print("============== setup_test: in"); }
|
||||
|
||||
httpserver.registerPathHandler(testpath, serverHandler);
|
||||
httpserver.start(-1);
|
||||
|
||||
channel = setupChannel(testpath);
|
||||
|
||||
// ChannelListener defined in head_channels.js
|
||||
channel.asyncOpen(new ChannelListener(checkResponse, channel), null);
|
||||
|
||||
if (dbg) { print("============== setup_test: out"); }
|
||||
}
|
||||
|
||||
function setupChannel(path) {
|
||||
var chan = NetUtil.newChannel ({
|
||||
uri: URL + path,
|
||||
loadUsingSystemPrincipal: true
|
||||
}).QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
chan.requestMethod = "GET";
|
||||
return chan;
|
||||
}
|
||||
|
||||
function serverHandler(metadata, response) {
|
||||
if (dbg) { print("============== serverHandler: in"); }
|
||||
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.setStatusLine("1.1", 200, "OK");
|
||||
|
||||
// Set a empty header. A empty link header will not appear in header list,
|
||||
// but in the "original headers", it will be still exactly as received.
|
||||
response.setHeaderNoCheck("Link", "", true);
|
||||
response.setHeaderNoCheck("Link", "value1");
|
||||
response.setHeaderNoCheck("Link", "value2");
|
||||
response.setHeaderNoCheck("Location", "loc");
|
||||
response.bodyOutputStream.write(httpbody, httpbody.length);
|
||||
|
||||
if (dbg) { print("============== serverHandler: out"); }
|
||||
}
|
||||
|
||||
function checkResponse(request, data, context) {
|
||||
if (dbg) { print("============== checkResponse: in"); }
|
||||
|
||||
do_check_eq(channel.responseStatus, 200);
|
||||
do_check_eq(channel.responseStatusText, "OK");
|
||||
do_check_true(channel.requestSucceeded);
|
||||
|
||||
// Response header have only one link header.
|
||||
var linkHeaderFound = 0;
|
||||
var locationHeaderFound = 0;
|
||||
channel.visitResponseHeaders({
|
||||
visitHeader: function visit(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
linkHeaderFound++;
|
||||
do_check_eq(aValue, "value1, value2");
|
||||
}
|
||||
if (aName == "Location") {
|
||||
locationHeaderFound++;
|
||||
do_check_eq(aValue, "loc");
|
||||
}
|
||||
}
|
||||
});
|
||||
do_check_eq(linkHeaderFound, 1);
|
||||
do_check_eq(locationHeaderFound, 1);
|
||||
|
||||
// The "original header" still contains 3 link headers.
|
||||
var linkOrgHeaderFound = 0;
|
||||
var locationOrgHeaderFound = 0;
|
||||
channel.visitOriginalResponseHeaders({
|
||||
visitHeader: function visitOrg(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
if (linkOrgHeaderFound == 0) {
|
||||
do_check_eq(aValue, "");
|
||||
} else if (linkOrgHeaderFound == 1 ) {
|
||||
do_check_eq(aValue, "value1");
|
||||
} else {
|
||||
do_check_eq(aValue, "value2");
|
||||
}
|
||||
linkOrgHeaderFound++;
|
||||
}
|
||||
if (aName == "Location") {
|
||||
locationOrgHeaderFound++;
|
||||
do_check_eq(aValue, "loc");
|
||||
}
|
||||
}
|
||||
});
|
||||
do_check_eq(linkOrgHeaderFound, 3);
|
||||
do_check_eq(locationOrgHeaderFound, 1);
|
||||
|
||||
if (dbg) { print("============== Remove headers"); }
|
||||
// Remove header.
|
||||
channel.setResponseHeader("Link", "", false);
|
||||
channel.setResponseHeader("Location", "", false);
|
||||
|
||||
var linkHeaderFound2 = false;
|
||||
var locationHeaderFound2 = 0;
|
||||
channel.visitResponseHeaders({
|
||||
visitHeader: function visit(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
linkHeaderFound2 = true;
|
||||
}
|
||||
if (aName == "Location") {
|
||||
locationHeaderFound2 = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
do_check_false(linkHeaderFound2, "There should be no link header");
|
||||
do_check_false(locationHeaderFound2, "There should be no location headers.");
|
||||
|
||||
// The "original header" still contains the empty header.
|
||||
var linkOrgHeaderFound2 = 0;
|
||||
var locationOrgHeaderFound2 = 0;
|
||||
channel.visitOriginalResponseHeaders({
|
||||
visitHeader: function visitOrg(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
if (linkOrgHeaderFound2 == 0) {
|
||||
do_check_eq(aValue, "");
|
||||
} else if (linkOrgHeaderFound2 == 1 ) {
|
||||
do_check_eq(aValue, "value1");
|
||||
} else {
|
||||
do_check_eq(aValue, "value2");
|
||||
}
|
||||
linkOrgHeaderFound2++;
|
||||
}
|
||||
if (aName == "Location") {
|
||||
locationOrgHeaderFound2++;
|
||||
do_check_eq(aValue, "loc");
|
||||
}
|
||||
}
|
||||
});
|
||||
do_check_true(linkOrgHeaderFound2 == 3,
|
||||
"Original link header still here.");
|
||||
do_check_true(locationOrgHeaderFound2 == 1,
|
||||
"Original location header still here.");
|
||||
|
||||
if (dbg) { print("============== Test GetResponseHeader"); }
|
||||
var linkOrgHeaderFound3 = 0;
|
||||
channel.getOriginalResponseHeader("Link",{
|
||||
visitHeader: function visitOrg(aName, aValue) {
|
||||
if (linkOrgHeaderFound3 == 0) {
|
||||
do_check_eq(aValue, "");
|
||||
} else if (linkOrgHeaderFound3 == 1 ) {
|
||||
do_check_eq(aValue, "value1");
|
||||
} else {
|
||||
do_check_eq(aValue, "value2");
|
||||
}
|
||||
linkOrgHeaderFound3++;
|
||||
}
|
||||
});
|
||||
do_check_true(linkOrgHeaderFound2 == 3,
|
||||
"Original link header still here.");
|
||||
|
||||
httpserver.stop(do_test_finished);
|
||||
if (dbg) { print("============== checkResponse: out"); }
|
||||
}
|
|
@ -241,6 +241,7 @@ run-sequentially = node server exceptions dont replay well
|
|||
skip-if = os == "win"
|
||||
[test_nojsredir.js]
|
||||
[test_offline_status.js]
|
||||
[test_original_sent_received_head.js]
|
||||
[test_parse_content_type.js]
|
||||
[test_permmgr.js]
|
||||
[test_plaintext_sniff.js]
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
//
|
||||
// Run test script in content process instead of chrome (xpcshell's default)
|
||||
//
|
||||
|
||||
function run_test() {
|
||||
run_test_in_child("../unit/test_original_sent_received_head.js");
|
||||
}
|
|
@ -93,3 +93,4 @@ skip-if = true
|
|||
[test_app_offline_http.js]
|
||||
[test_getHost_wrap.js]
|
||||
[test_app_offline_notifications.js]
|
||||
[test_original_sent_received_head_wrap.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче