/src/mozilla-central/netwerk/protocol/http/nsHttpHeaderArray.h
Line | Count | Source (jump to first uncovered line) |
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 | | 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 | | const char *FindHeaderValue(nsHttpAtom header, const char *value) const |
86 | 0 | { |
87 | 0 | return nsHttp::FindToken(PeekHeader(header), value, |
88 | 0 | HTTP_HEADER_VALUE_SEPS); |
89 | 0 | } |
90 | | |
91 | | // Determine if the given header value exists. |
92 | | bool HasHeaderValue(nsHttpAtom header, const char *value) const |
93 | 0 | { |
94 | 0 | return FindHeaderValue(header, value) != nullptr; |
95 | 0 | } |
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 | | struct nsEntry |
129 | | { |
130 | | nsHttpAtom header; |
131 | | nsCString headerNameOriginal; |
132 | | nsCString value; |
133 | | HeaderVariety variety = eVarietyUnknown; |
134 | | |
135 | | struct MatchHeader { |
136 | 0 | bool Equals(const nsEntry &aEntry, const nsHttpAtom &aHeader) const { |
137 | 0 | return aEntry.header == aHeader; |
138 | 0 | } |
139 | | }; |
140 | | |
141 | | bool operator==(const nsEntry& aOther) const |
142 | 0 | { |
143 | 0 | return header == aOther.header && value == aOther.value; |
144 | 0 | } |
145 | | }; |
146 | | |
147 | | bool operator==(const nsHttpHeaderArray& aOther) const |
148 | 0 | { |
149 | 0 | return mHeaders == aOther.mHeaders; |
150 | 0 | } |
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 | | |
170 | | // Subset of singleton headers: should never see multiple, different |
171 | | // instances of these, else something fishy may be going on (like CLRF |
172 | | // injection) |
173 | | bool IsSuspectDuplicateHeader(nsHttpAtom header); |
174 | | |
175 | | // All members must be copy-constructable and assignable |
176 | | nsTArray<nsEntry> mHeaders; |
177 | | |
178 | | friend struct IPC::ParamTraits<nsHttpHeaderArray>; |
179 | | friend class nsHttpRequestHead; |
180 | | }; |
181 | | |
182 | | |
183 | | //----------------------------------------------------------------------------- |
184 | | // nsHttpHeaderArray <private>: inline functions |
185 | | //----------------------------------------------------------------------------- |
186 | | |
187 | | inline int32_t |
188 | | nsHttpHeaderArray::LookupEntry(nsHttpAtom header, const nsEntry **entry) const |
189 | 0 | { |
190 | 0 | uint32_t index = 0; |
191 | 0 | while (index != UINT32_MAX) { |
192 | 0 | index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader()); |
193 | 0 | if (index != UINT32_MAX) { |
194 | 0 | if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) { |
195 | 0 | *entry = &mHeaders[index]; |
196 | 0 | return index; |
197 | 0 | } |
198 | 0 | index++; |
199 | 0 | } |
200 | 0 | } |
201 | 0 |
|
202 | 0 | return index; |
203 | 0 | } |
204 | | |
205 | | inline int32_t |
206 | | nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry) |
207 | 0 | { |
208 | 0 | uint32_t index = 0; |
209 | 0 | while (index != UINT32_MAX) { |
210 | 0 | index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader()); |
211 | 0 | if (index != UINT32_MAX) { |
212 | 0 | if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) { |
213 | 0 | *entry = &mHeaders[index]; |
214 | 0 | return index; |
215 | 0 | } |
216 | 0 | index++; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | return index; |
220 | 0 | } |
221 | | |
222 | | inline bool |
223 | | nsHttpHeaderArray::IsSingletonHeader(nsHttpAtom header) |
224 | 0 | { |
225 | 0 | return header == nsHttp::Content_Type || |
226 | 0 | header == nsHttp::Content_Disposition || |
227 | 0 | header == nsHttp::Content_Length || |
228 | 0 | header == nsHttp::User_Agent || |
229 | 0 | header == nsHttp::Referer || |
230 | 0 | header == nsHttp::Host || |
231 | 0 | header == nsHttp::Authorization || |
232 | 0 | header == nsHttp::Proxy_Authorization || |
233 | 0 | header == nsHttp::If_Modified_Since || |
234 | 0 | header == nsHttp::If_Unmodified_Since || |
235 | 0 | header == nsHttp::From || |
236 | 0 | header == nsHttp::Location || |
237 | 0 | header == nsHttp::Max_Forwards || |
238 | 0 | // Ignore-multiple-headers are singletons in the sense that they |
239 | 0 | // shouldn't be merged. |
240 | 0 | IsIgnoreMultipleHeader(header); |
241 | 0 | } |
242 | | |
243 | | // These are headers for which, in the presence of multiple values, we only |
244 | | // consider the first. |
245 | | inline bool nsHttpHeaderArray::IsIgnoreMultipleHeader(nsHttpAtom header) |
246 | 0 | { |
247 | 0 | // https://tools.ietf.org/html/rfc6797#section-8: |
248 | 0 | // |
249 | 0 | // If a UA receives more than one STS header field in an HTTP |
250 | 0 | // response message over secure transport, then the UA MUST process |
251 | 0 | // only the first such header field. |
252 | 0 | return header == nsHttp::Strict_Transport_Security; |
253 | 0 | } |
254 | | |
255 | | inline MOZ_MUST_USE nsresult |
256 | | nsHttpHeaderArray::MergeHeader(nsHttpAtom header, |
257 | | nsEntry *entry, |
258 | | const nsACString &value, |
259 | | nsHttpHeaderArray::HeaderVariety variety) |
260 | 0 | { |
261 | 0 | if (value.IsEmpty()) |
262 | 0 | return NS_OK; // merge of empty header = no-op |
263 | 0 | |
264 | 0 | nsCString newValue = entry->value; |
265 | 0 | if (!newValue.IsEmpty()) { |
266 | 0 | // Append the new value to the existing value |
267 | 0 | if (header == nsHttp::Set_Cookie || |
268 | 0 | header == nsHttp::WWW_Authenticate || |
269 | 0 | header == nsHttp::Proxy_Authenticate) |
270 | 0 | { |
271 | 0 | // Special case these headers and use a newline delimiter to |
272 | 0 | // delimit the values from one another as commas may appear |
273 | 0 | // in the values of these headers contrary to what the spec says. |
274 | 0 | newValue.Append('\n'); |
275 | 0 | } else { |
276 | 0 | // Delimit each value from the others using a comma (per HTTP spec) |
277 | 0 | newValue.AppendLiteral(", "); |
278 | 0 | } |
279 | 0 | } |
280 | 0 |
|
281 | 0 | newValue.Append(value); |
282 | 0 | if (entry->variety == eVarietyResponseNetOriginalAndResponse) { |
283 | 0 | MOZ_ASSERT(variety == eVarietyResponse); |
284 | 0 | entry->variety = eVarietyResponseNetOriginal; |
285 | 0 | // Copy entry->headerNameOriginal because in SetHeader_internal we are going |
286 | 0 | // to a new one and a realocation can happen. |
287 | 0 | nsCString headerNameOriginal = entry->headerNameOriginal; |
288 | 0 | nsresult rv = SetHeader_internal(header, headerNameOriginal, |
289 | 0 | newValue, eVarietyResponse); |
290 | 0 | if (NS_FAILED(rv)) { |
291 | 0 | return rv; |
292 | 0 | } |
293 | 0 | } else { |
294 | 0 | entry->value = newValue; |
295 | 0 | entry->variety = variety; |
296 | 0 | } |
297 | 0 | return NS_OK; |
298 | 0 | } |
299 | | |
300 | | inline bool |
301 | | nsHttpHeaderArray::IsSuspectDuplicateHeader(nsHttpAtom header) |
302 | 0 | { |
303 | 0 | bool retval = header == nsHttp::Content_Length || |
304 | 0 | header == nsHttp::Content_Disposition || |
305 | 0 | header == nsHttp::Location; |
306 | 0 |
|
307 | 0 | MOZ_ASSERT(!retval || IsSingletonHeader(header), |
308 | 0 | "Only non-mergeable headers should be in this list\n"); |
309 | 0 |
|
310 | 0 | return retval; |
311 | 0 | } |
312 | | |
313 | | } // namespace net |
314 | | } // namespace mozilla |
315 | | |
316 | | #endif |