michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:set ts=4 sw=4 sts=4 ci et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: michael@0: #include "nsHttpHeaderArray.h" michael@0: #include "nsURLHelper.h" michael@0: #include "nsIHttpHeaderVisitor.h" michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHeaderArray michael@0: //----------------------------------------------------------------------------- michael@0: nsresult michael@0: nsHttpHeaderArray::SetHeader(nsHttpAtom header, michael@0: const nsACString &value, michael@0: bool merge) michael@0: { michael@0: nsEntry *entry = nullptr; michael@0: int32_t index; michael@0: michael@0: index = LookupEntry(header, &entry); michael@0: michael@0: // If an empty value is passed in, then delete the header entry... michael@0: // unless we are merging, in which case this function becomes a NOP. michael@0: if (value.IsEmpty()) { michael@0: if (!merge && entry) michael@0: mHeaders.RemoveElementAt(index); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!entry) { michael@0: entry = mHeaders.AppendElement(); // new nsEntry() michael@0: if (!entry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: entry->header = header; michael@0: entry->value = value; michael@0: } else if (merge && !IsSingletonHeader(header)) { michael@0: MergeHeader(header, entry, value); michael@0: } else { michael@0: // Replace the existing string with the new value michael@0: entry->value = value; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value) michael@0: { michael@0: nsEntry *entry = nullptr; michael@0: michael@0: LookupEntry(header, &entry); michael@0: michael@0: if (!entry) { michael@0: if (value.IsEmpty()) { michael@0: if (!TrackEmptyHeader(header)) { michael@0: LOG(("Ignoring Empty Header: %s\n", header.get())); michael@0: return NS_OK; // ignore empty headers by default michael@0: } michael@0: } michael@0: entry = mHeaders.AppendElement(); //new nsEntry(header, value); michael@0: if (!entry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: entry->header = header; michael@0: entry->value = value; michael@0: } else if (!IsSingletonHeader(header)) { michael@0: MergeHeader(header, entry, value); michael@0: } else { michael@0: // Multiple instances of non-mergeable header received from network michael@0: // - ignore if same value michael@0: if (!entry->value.Equals(value)) { michael@0: if (IsSuspectDuplicateHeader(header)) { michael@0: // reply may be corrupt/hacked (ex: CLRF injection attacks) michael@0: return NS_ERROR_CORRUPTED_CONTENT; michael@0: } // else silently drop value: keep value from 1st header seen michael@0: LOG(("Header %s silently dropped as non mergeable header\n", michael@0: header.get())); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsHttpHeaderArray::ClearHeader(nsHttpAtom header) michael@0: { michael@0: mHeaders.RemoveElement(header, nsEntry::MatchHeader()); michael@0: } michael@0: michael@0: const char * michael@0: nsHttpHeaderArray::PeekHeader(nsHttpAtom header) const michael@0: { michael@0: const nsEntry *entry = nullptr; michael@0: LookupEntry(header, &entry); michael@0: return entry ? entry->value.get() : nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result) const michael@0: { michael@0: const nsEntry *entry = nullptr; michael@0: LookupEntry(header, &entry); michael@0: if (!entry) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: result = entry->value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(visitor); michael@0: uint32_t i, count = mHeaders.Length(); michael@0: for (i = 0; i < count; ++i) { michael@0: const nsEntry &entry = mHeaders[i]; michael@0: if (NS_FAILED(visitor->VisitHeader(nsDependentCString(entry.header), michael@0: entry.value))) michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHeaderArray::ParseHeaderLine(const char *line, michael@0: nsHttpAtom *hdr, michael@0: char **val) michael@0: { michael@0: // michael@0: // BNF from section 4.2 of RFC 2616: michael@0: // michael@0: // message-header = field-name ":" [ field-value ] michael@0: // field-name = token michael@0: // field-value = *( field-content | LWS ) michael@0: // field-content = michael@0: // michael@0: michael@0: // We skip over mal-formed headers in the hope that we'll still be able to michael@0: // do something useful with the response. michael@0: michael@0: char *p = (char *) strchr(line, ':'); michael@0: if (!p) { michael@0: LOG(("malformed header [%s]: no colon\n", line)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // make sure we have a valid token for the field-name michael@0: if (!nsHttp::IsValidToken(line, p)) { michael@0: LOG(("malformed header [%s]: field-name not a token\n", line)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: *p = 0; // null terminate field-name michael@0: michael@0: nsHttpAtom atom = nsHttp::ResolveAtom(line); michael@0: if (!atom) { michael@0: LOG(("failed to resolve atom [%s]\n", line)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // skip over whitespace michael@0: p = net_FindCharNotInSet(++p, HTTP_LWS); michael@0: michael@0: // trim trailing whitespace - bug 86608 michael@0: char *p2 = net_RFindCharNotInSet(p, HTTP_LWS); michael@0: michael@0: *++p2 = 0; // null terminate header value; if all chars starting at |p| michael@0: // consisted of LWS, then p2 would have pointed at |p-1|, so michael@0: // the prefix increment is always valid. michael@0: michael@0: // assign return values michael@0: if (hdr) *hdr = atom; michael@0: if (val) *val = p; michael@0: michael@0: // assign response header michael@0: return SetHeaderFromNet(atom, nsDependentCString(p, p2 - p)); michael@0: } michael@0: michael@0: void michael@0: nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders) michael@0: { michael@0: uint32_t i, count = mHeaders.Length(); michael@0: for (i = 0; i < count; ++i) { michael@0: const nsEntry &entry = mHeaders[i]; michael@0: // prune proxy headers if requested michael@0: if (pruneProxyHeaders && ((entry.header == nsHttp::Proxy_Authorization) || michael@0: (entry.header == nsHttp::Proxy_Connection))) michael@0: continue; michael@0: buf.Append(entry.header); michael@0: buf.AppendLiteral(": "); michael@0: buf.Append(entry.value); michael@0: buf.AppendLiteral("\r\n"); michael@0: } michael@0: } michael@0: michael@0: const char * michael@0: nsHttpHeaderArray::PeekHeaderAt(uint32_t index, nsHttpAtom &header) const michael@0: { michael@0: const nsEntry &entry = mHeaders[index]; michael@0: michael@0: header = entry.header; michael@0: return entry.value.get(); michael@0: } michael@0: michael@0: void michael@0: nsHttpHeaderArray::Clear() michael@0: { michael@0: mHeaders.Clear(); michael@0: } michael@0: michael@0: } // namespace mozilla::net michael@0: } // namespace mozilla