Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set sw=4 ts=8 et tw=80 : */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef nsHttpHeaderArray_h__
8 : #define nsHttpHeaderArray_h__
9 :
10 : #include "nsHttp.h"
11 : #include "nsTArray.h"
12 : #include "nsString.h"
13 :
14 : class nsIHttpHeaderVisitor;
15 :
16 : // This needs to be forward declared here so we can include only this header
17 : // without also including PHttpChannelParams.h
18 : namespace IPC {
19 : template <typename> struct ParamTraits;
20 : } // namespace IPC
21 :
22 : namespace mozilla { namespace net {
23 :
24 77 : class nsHttpHeaderArray
25 : {
26 : public:
27 : const char *PeekHeader(nsHttpAtom header) const;
28 :
29 : // For nsHttpResponseHead nsHttpHeaderArray will keep track of the original
30 : // headers as they come from the network and the parse headers used in
31 : // firefox.
32 : // If the original and the firefox header are the same, we will keep just
33 : // one copy and marked it as eVarietyResponseNetOriginalAndResponse.
34 : // If firefox header representation changes a header coming from the
35 : // network (e.g. merged it) or a eVarietyResponseNetOriginalAndResponse
36 : // header has been changed by SetHeader method, we will keep the original
37 : // header as eVarietyResponseNetOriginal and make a copy for the new header
38 : // and mark it as eVarietyResponse.
39 : enum HeaderVariety
40 : {
41 : eVarietyUnknown,
42 : // Used only for request header.
43 : eVarietyRequestOverride,
44 : eVarietyRequestDefault,
45 : // Used only for response header.
46 : eVarietyResponseNetOriginalAndResponse,
47 : eVarietyResponseNetOriginal,
48 : eVarietyResponse
49 : };
50 :
51 : // Used by internal setters: to set header from network use SetHeaderFromNet
52 : MOZ_MUST_USE nsresult SetHeader(const nsACString &headerName,
53 : const nsACString &value,
54 : bool merge, HeaderVariety variety);
55 : MOZ_MUST_USE nsresult SetHeader(nsHttpAtom header, const nsACString &value,
56 : bool merge, HeaderVariety variety);
57 : MOZ_MUST_USE nsresult SetHeader(nsHttpAtom header,
58 : const nsACString &headerName,
59 : const nsACString &value,
60 : bool merge, HeaderVariety variety);
61 :
62 : // Used by internal setters to set an empty header
63 : MOZ_MUST_USE nsresult SetEmptyHeader(const nsACString &headerName,
64 : HeaderVariety variety);
65 :
66 : // Merges supported headers. For other duplicate values, determines if error
67 : // needs to be thrown or 1st value kept.
68 : // For the response header we keep the original headers as well.
69 : MOZ_MUST_USE nsresult SetHeaderFromNet(nsHttpAtom header,
70 : const nsACString &headerNameOriginal,
71 : const nsACString &value,
72 : bool response);
73 :
74 : MOZ_MUST_USE nsresult SetResponseHeaderFromCache(nsHttpAtom header,
75 : const nsACString &headerNameOriginal,
76 : const nsACString &value,
77 : HeaderVariety variety);
78 :
79 : MOZ_MUST_USE nsresult GetHeader(nsHttpAtom header, nsACString &value) const;
80 : MOZ_MUST_USE nsresult GetOriginalHeader(nsHttpAtom aHeader,
81 : nsIHttpHeaderVisitor *aVisitor);
82 : void ClearHeader(nsHttpAtom h);
83 :
84 : // Find the location of the given header value, or null if none exists.
85 25 : const char *FindHeaderValue(nsHttpAtom header, const char *value) const
86 : {
87 25 : return nsHttp::FindToken(PeekHeader(header), value,
88 25 : HTTP_HEADER_VALUE_SEPS);
89 : }
90 :
91 : // Determine if the given header value exists.
92 25 : bool HasHeaderValue(nsHttpAtom header, const char *value) const
93 : {
94 25 : return FindHeaderValue(header, value) != nullptr;
95 : }
96 :
97 : bool HasHeader(nsHttpAtom header) const;
98 :
99 : enum VisitorFilter
100 : {
101 : eFilterAll,
102 : eFilterSkipDefault,
103 : eFilterResponse,
104 : eFilterResponseOriginal
105 : };
106 :
107 : MOZ_MUST_USE nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor,
108 : VisitorFilter filter = eFilterAll);
109 :
110 : // parse a header line, return the header atom and a pointer to the
111 : // header value (the substring of the header line -- do not free).
112 : static MOZ_MUST_USE nsresult ParseHeaderLine(const nsACString& line,
113 : nsHttpAtom *header = nullptr,
114 : nsACString *headerNameOriginal = nullptr,
115 : nsACString *value = nullptr);
116 :
117 : void Flatten(nsACString &, bool pruneProxyHeaders, bool pruneTransients);
118 : void FlattenOriginalHeader(nsACString &);
119 :
120 0 : uint32_t Count() const { return mHeaders.Length(); }
121 :
122 : const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header,
123 : nsACString &headerNameOriginal) const;
124 :
125 : void Clear();
126 :
127 : // Must be copy-constructable and assignable
128 447 : struct nsEntry
129 : {
130 : nsHttpAtom header;
131 : nsCString headerNameOriginal;
132 : nsCString value;
133 : HeaderVariety variety = eVarietyUnknown;
134 :
135 : struct MatchHeader {
136 1544 : bool Equals(const nsEntry &aEntry, const nsHttpAtom &aHeader) const {
137 1544 : return aEntry.header == aHeader;
138 : }
139 : };
140 :
141 0 : bool operator==(const nsEntry& aOther) const
142 : {
143 0 : return header == aOther.header && value == aOther.value;
144 : }
145 : };
146 :
147 0 : bool operator==(const nsHttpHeaderArray& aOther) const
148 : {
149 0 : return mHeaders == aOther.mHeaders;
150 : }
151 :
152 : private:
153 : // LookupEntry function will never return eVarietyResponseNetOriginal.
154 : // It will ignore original headers from the network.
155 : int32_t LookupEntry(nsHttpAtom header, const nsEntry **) const;
156 : int32_t LookupEntry(nsHttpAtom header, nsEntry **);
157 : MOZ_MUST_USE nsresult MergeHeader(nsHttpAtom header, nsEntry *entry,
158 : const nsACString &value,
159 : HeaderVariety variety);
160 : MOZ_MUST_USE nsresult SetHeader_internal(nsHttpAtom header,
161 : const nsACString &headeName,
162 : const nsACString &value,
163 : HeaderVariety variety);
164 :
165 : // Header cannot be merged: only one value possible
166 : bool IsSingletonHeader(nsHttpAtom header);
167 : // Header cannot be merged, and subsequent values should be ignored
168 : bool IsIgnoreMultipleHeader(nsHttpAtom header);
169 : // For some headers we want to track empty values to prevent them being
170 : // combined with non-empty ones as a CRLF attack vector
171 : bool TrackEmptyHeader(nsHttpAtom header);
172 :
173 : // Subset of singleton headers: should never see multiple, different
174 : // instances of these, else something fishy may be going on (like CLRF
175 : // injection)
176 : bool IsSuspectDuplicateHeader(nsHttpAtom header);
177 :
178 : // All members must be copy-constructable and assignable
179 : nsTArray<nsEntry> mHeaders;
180 :
181 : friend struct IPC::ParamTraits<nsHttpHeaderArray>;
182 : friend class nsHttpRequestHead;
183 : };
184 :
185 :
186 : //-----------------------------------------------------------------------------
187 : // nsHttpHeaderArray <private>: inline functions
188 : //-----------------------------------------------------------------------------
189 :
190 : inline int32_t
191 196 : nsHttpHeaderArray::LookupEntry(nsHttpAtom header, const nsEntry **entry) const
192 : {
193 196 : uint32_t index = 0;
194 534 : while (index != UINT32_MAX) {
195 196 : index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
196 196 : if (index != UINT32_MAX) {
197 27 : if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
198 27 : *entry = &mHeaders[index];
199 27 : return index;
200 : }
201 0 : index++;
202 : }
203 : }
204 :
205 169 : return index;
206 : }
207 :
208 : inline int32_t
209 105 : nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
210 : {
211 105 : uint32_t index = 0;
212 299 : while (index != UINT32_MAX) {
213 105 : index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
214 105 : if (index != UINT32_MAX) {
215 8 : if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
216 8 : *entry = &mHeaders[index];
217 8 : return index;
218 : }
219 0 : index++;
220 : }
221 : }
222 97 : return index;
223 : }
224 :
225 : inline bool
226 0 : nsHttpHeaderArray::IsSingletonHeader(nsHttpAtom header)
227 : {
228 0 : return header == nsHttp::Content_Type ||
229 0 : header == nsHttp::Content_Disposition ||
230 0 : header == nsHttp::Content_Length ||
231 0 : header == nsHttp::User_Agent ||
232 0 : header == nsHttp::Referer ||
233 0 : header == nsHttp::Host ||
234 0 : header == nsHttp::Authorization ||
235 0 : header == nsHttp::Proxy_Authorization ||
236 0 : header == nsHttp::If_Modified_Since ||
237 0 : header == nsHttp::If_Unmodified_Since ||
238 0 : header == nsHttp::From ||
239 0 : header == nsHttp::Location ||
240 0 : header == nsHttp::Max_Forwards ||
241 : // Ignore-multiple-headers are singletons in the sense that they
242 : // shouldn't be merged.
243 0 : IsIgnoreMultipleHeader(header);
244 : }
245 :
246 : // These are headers for which, in the presence of multiple values, we only
247 : // consider the first.
248 8 : inline bool nsHttpHeaderArray::IsIgnoreMultipleHeader(nsHttpAtom header)
249 : {
250 : // https://tools.ietf.org/html/rfc6797#section-8:
251 : //
252 : // If a UA receives more than one STS header field in an HTTP
253 : // response message over secure transport, then the UA MUST process
254 : // only the first such header field.
255 8 : return header == nsHttp::Strict_Transport_Security;
256 : }
257 :
258 : inline bool
259 0 : nsHttpHeaderArray::TrackEmptyHeader(nsHttpAtom header)
260 : {
261 0 : return header == nsHttp::Content_Length ||
262 0 : header == nsHttp::Location ||
263 0 : header == nsHttp::Access_Control_Allow_Origin;
264 : }
265 :
266 : inline MOZ_MUST_USE nsresult
267 0 : nsHttpHeaderArray::MergeHeader(nsHttpAtom header,
268 : nsEntry *entry,
269 : const nsACString &value,
270 : nsHttpHeaderArray::HeaderVariety variety)
271 : {
272 0 : if (value.IsEmpty())
273 0 : return NS_OK; // merge of empty header = no-op
274 :
275 0 : nsCString newValue = entry->value;
276 0 : if (!newValue.IsEmpty()) {
277 : // Append the new value to the existing value
278 0 : if (header == nsHttp::Set_Cookie ||
279 0 : header == nsHttp::WWW_Authenticate ||
280 0 : header == nsHttp::Proxy_Authenticate)
281 : {
282 : // Special case these headers and use a newline delimiter to
283 : // delimit the values from one another as commas may appear
284 : // in the values of these headers contrary to what the spec says.
285 0 : newValue.Append('\n');
286 : } else {
287 : // Delimit each value from the others using a comma (per HTTP spec)
288 0 : newValue.AppendLiteral(", ");
289 : }
290 : }
291 :
292 0 : newValue.Append(value);
293 0 : if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
294 0 : MOZ_ASSERT(variety == eVarietyResponse);
295 0 : entry->variety = eVarietyResponseNetOriginal;
296 : // Copy entry->headerNameOriginal because in SetHeader_internal we are going
297 : // to a new one and a realocation can happen.
298 0 : nsCString headerNameOriginal = entry->headerNameOriginal;
299 : nsresult rv = SetHeader_internal(header, headerNameOriginal,
300 0 : newValue, eVarietyResponse);
301 0 : if (NS_FAILED(rv)) {
302 0 : return rv;
303 : }
304 : } else {
305 0 : entry->value = newValue;
306 0 : entry->variety = variety;
307 : }
308 0 : return NS_OK;
309 : }
310 :
311 : inline bool
312 0 : nsHttpHeaderArray::IsSuspectDuplicateHeader(nsHttpAtom header)
313 : {
314 0 : bool retval = header == nsHttp::Content_Length ||
315 0 : header == nsHttp::Content_Disposition ||
316 0 : header == nsHttp::Location;
317 :
318 0 : MOZ_ASSERT(!retval || IsSingletonHeader(header),
319 : "Only non-mergeable headers should be in this list\n");
320 :
321 0 : return retval;
322 : }
323 :
324 : } // namespace net
325 : } // namespace mozilla
326 :
327 : #endif
|