Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sw=4 sts=4 ci et: */
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 : // HttpLog.h should generally be included first
8 : #include "HttpLog.h"
9 :
10 : #include "nsHttpHeaderArray.h"
11 : #include "nsURLHelper.h"
12 : #include "nsIHttpHeaderVisitor.h"
13 : #include "nsHttpHandler.h"
14 :
15 : namespace mozilla {
16 : namespace net {
17 :
18 : //-----------------------------------------------------------------------------
19 : // nsHttpHeaderArray <public>
20 : //-----------------------------------------------------------------------------
21 :
22 : nsresult
23 25 : nsHttpHeaderArray::SetHeader(const nsACString &headerName,
24 : const nsACString &value,
25 : bool merge,
26 : nsHttpHeaderArray::HeaderVariety variety)
27 : {
28 25 : nsHttpAtom header = nsHttp::ResolveAtom(PromiseFlatCString(headerName).get());
29 25 : if (!header) {
30 0 : NS_WARNING("failed to resolve atom");
31 0 : return NS_ERROR_NOT_AVAILABLE;
32 : }
33 25 : return SetHeader(header, headerName, value, merge, variety);
34 : }
35 :
36 : nsresult
37 60 : nsHttpHeaderArray::SetHeader(nsHttpAtom header,
38 : const nsACString &value,
39 : bool merge,
40 : nsHttpHeaderArray::HeaderVariety variety)
41 : {
42 60 : return SetHeader(header, EmptyCString(), value, merge, variety);
43 : }
44 :
45 : nsresult
46 87 : nsHttpHeaderArray::SetHeader(nsHttpAtom header,
47 : const nsACString &headerName,
48 : const nsACString &value,
49 : bool merge,
50 : nsHttpHeaderArray::HeaderVariety variety)
51 : {
52 87 : MOZ_ASSERT((variety == eVarietyResponse) ||
53 : (variety == eVarietyRequestDefault) ||
54 : (variety == eVarietyRequestOverride),
55 : "Net original headers can only be set using SetHeader_internal().");
56 :
57 87 : nsEntry *entry = nullptr;
58 : int32_t index;
59 :
60 87 : index = LookupEntry(header, &entry);
61 :
62 : // If an empty value is passed in, then delete the header entry...
63 : // unless we are merging, in which case this function becomes a NOP.
64 87 : if (value.IsEmpty()) {
65 12 : if (!merge && entry) {
66 0 : if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
67 0 : MOZ_ASSERT(variety == eVarietyResponse);
68 0 : entry->variety = eVarietyResponseNetOriginal;
69 : } else {
70 0 : mHeaders.RemoveElementAt(index);
71 : }
72 : }
73 12 : return NS_OK;
74 : }
75 :
76 75 : MOZ_ASSERT(!entry || variety != eVarietyRequestDefault,
77 : "Cannot set default entry which overrides existing entry!");
78 75 : if (!entry) {
79 67 : return SetHeader_internal(header, headerName, value, variety);
80 8 : } else if (merge && !IsSingletonHeader(header)) {
81 0 : return MergeHeader(header, entry, value, variety);
82 8 : } else if (!IsIgnoreMultipleHeader(header)) {
83 : // Replace the existing string with the new value
84 8 : if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
85 0 : MOZ_ASSERT(variety == eVarietyResponse);
86 0 : entry->variety = eVarietyResponseNetOriginal;
87 0 : return SetHeader_internal(header, headerName, value, variety);
88 : } else {
89 8 : entry->value = value;
90 8 : entry->variety = variety;
91 : }
92 : }
93 :
94 8 : return NS_OK;
95 : }
96 :
97 : nsresult
98 104 : nsHttpHeaderArray::SetHeader_internal(nsHttpAtom header,
99 : const nsACString &headerName,
100 : const nsACString &value,
101 : nsHttpHeaderArray::HeaderVariety variety)
102 : {
103 104 : nsEntry *entry = mHeaders.AppendElement();
104 104 : if (!entry) {
105 0 : return NS_ERROR_OUT_OF_MEMORY;
106 : }
107 104 : entry->header = header;
108 : // Only save original form of a header if it is different than the header
109 : // atom string.
110 104 : if (!headerName.Equals(header.get())) {
111 64 : entry->headerNameOriginal = headerName;
112 : }
113 104 : entry->value = value;
114 104 : entry->variety = variety;
115 104 : return NS_OK;
116 : }
117 :
118 : nsresult
119 0 : nsHttpHeaderArray::SetEmptyHeader(const nsACString &headerName,
120 : HeaderVariety variety)
121 : {
122 0 : nsHttpAtom header = nsHttp::ResolveAtom(PromiseFlatCString(headerName).get());
123 0 : if (!header) {
124 0 : NS_WARNING("failed to resolve atom");
125 0 : return NS_ERROR_NOT_AVAILABLE;
126 : }
127 :
128 0 : MOZ_ASSERT((variety == eVarietyResponse) ||
129 : (variety == eVarietyRequestDefault) ||
130 : (variety == eVarietyRequestOverride),
131 : "Original headers can only be set using SetHeader_internal().");
132 0 : nsEntry *entry = nullptr;
133 :
134 0 : LookupEntry(header, &entry);
135 :
136 0 : if (entry &&
137 0 : entry->variety != eVarietyResponseNetOriginalAndResponse) {
138 0 : entry->value.Truncate();
139 0 : return NS_OK;
140 0 : } else if (entry) {
141 0 : MOZ_ASSERT(variety == eVarietyResponse);
142 0 : entry->variety = eVarietyResponseNetOriginal;
143 : }
144 :
145 0 : return SetHeader_internal(header, headerName, EmptyCString(), variety);
146 : }
147 :
148 : nsresult
149 15 : nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
150 : const nsACString &headerNameOriginal,
151 : const nsACString &value,
152 : bool response)
153 : {
154 : // mHeader holds the consolidated (merged or updated) headers.
155 : // mHeader for response header will keep the original heades as well.
156 15 : nsEntry *entry = nullptr;
157 :
158 15 : LookupEntry(header, &entry);
159 :
160 15 : if (!entry) {
161 15 : if (value.IsEmpty()) {
162 0 : if (!gHttpHandler->KeepEmptyResponseHeadersAsEmtpyString() &&
163 0 : !TrackEmptyHeader(header)) {
164 0 : LOG(("Ignoring Empty Header: %s\n", header.get()));
165 0 : if (response) {
166 : // Set header as original but not as response header.
167 : return SetHeader_internal(header, headerNameOriginal, value,
168 0 : eVarietyResponseNetOriginal);
169 : }
170 0 : return NS_OK; // ignore empty headers by default
171 : }
172 : }
173 15 : HeaderVariety variety = eVarietyRequestOverride;
174 15 : if (response) {
175 15 : variety = eVarietyResponseNetOriginalAndResponse;
176 : }
177 15 : return SetHeader_internal(header, headerNameOriginal, value, variety);
178 :
179 0 : } else if (!IsSingletonHeader(header)) {
180 0 : HeaderVariety variety = eVarietyRequestOverride;
181 0 : if (response) {
182 0 : variety = eVarietyResponse;
183 : }
184 0 : nsresult rv = MergeHeader(header, entry, value, variety);
185 0 : if (NS_FAILED(rv)) {
186 0 : return rv;
187 : }
188 0 : if (response) {
189 : rv = SetHeader_internal(header, headerNameOriginal, value,
190 0 : eVarietyResponseNetOriginal);
191 : }
192 0 : return rv;
193 0 : } else if (!IsIgnoreMultipleHeader(header)) {
194 : // Multiple instances of non-mergeable header received from network
195 : // - ignore if same value
196 0 : if (!entry->value.Equals(value)) {
197 0 : if (IsSuspectDuplicateHeader(header)) {
198 : // reply may be corrupt/hacked (ex: CLRF injection attacks)
199 0 : return NS_ERROR_CORRUPTED_CONTENT;
200 : } // else silently drop value: keep value from 1st header seen
201 0 : LOG(("Header %s silently dropped as non mergeable header\n",
202 : header.get()));
203 :
204 : }
205 0 : if (response) {
206 : return SetHeader_internal(header, headerNameOriginal, value,
207 0 : eVarietyResponseNetOriginal);
208 : }
209 : }
210 :
211 0 : return NS_OK;
212 : }
213 :
214 : nsresult
215 38 : nsHttpHeaderArray::SetResponseHeaderFromCache(nsHttpAtom header,
216 : const nsACString &headerNameOriginal,
217 : const nsACString &value,
218 : nsHttpHeaderArray::HeaderVariety variety)
219 : {
220 38 : MOZ_ASSERT((variety == eVarietyResponse) ||
221 : (variety == eVarietyResponseNetOriginal),
222 : "Headers from cache can only be eVarietyResponse and "
223 : "eVarietyResponseNetOriginal");
224 :
225 38 : if (variety == eVarietyResponseNetOriginal) {
226 : return SetHeader_internal(header, headerNameOriginal, value,
227 18 : eVarietyResponseNetOriginal);
228 : } else {
229 20 : nsTArray<nsEntry>::index_type index = 0;
230 0 : do {
231 20 : index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
232 20 : if (index != mHeaders.NoIndex) {
233 16 : nsEntry &entry = mHeaders[index];
234 16 : if (value.Equals(entry.value)) {
235 16 : MOZ_ASSERT((entry.variety == eVarietyResponseNetOriginal) ||
236 : (entry.variety == eVarietyResponseNetOriginalAndResponse),
237 : "This array must contain only eVarietyResponseNetOriginal"
238 : " and eVarietyResponseNetOriginalAndRespons headers!");
239 16 : entry.variety = eVarietyResponseNetOriginalAndResponse;
240 16 : return NS_OK;
241 : }
242 0 : index++;
243 : }
244 4 : } while (index != mHeaders.NoIndex);
245 : // If we are here, we have not found an entry so add a new one.
246 : return SetHeader_internal(header, headerNameOriginal, value,
247 4 : eVarietyResponse);
248 : }
249 : }
250 :
251 : void
252 3 : nsHttpHeaderArray::ClearHeader(nsHttpAtom header)
253 : {
254 3 : nsEntry *entry = nullptr;
255 3 : int32_t index = LookupEntry(header, &entry);
256 3 : if (entry) {
257 0 : if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
258 0 : entry->variety = eVarietyResponseNetOriginal;
259 : } else {
260 0 : mHeaders.RemoveElementAt(index);
261 : }
262 : }
263 3 : }
264 :
265 : const char *
266 56 : nsHttpHeaderArray::PeekHeader(nsHttpAtom header) const
267 : {
268 56 : const nsEntry *entry = nullptr;
269 56 : LookupEntry(header, &entry);
270 56 : return entry ? entry->value.get() : nullptr;
271 : }
272 :
273 : nsresult
274 101 : nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result) const
275 : {
276 101 : const nsEntry *entry = nullptr;
277 101 : LookupEntry(header, &entry);
278 101 : if (!entry)
279 92 : return NS_ERROR_NOT_AVAILABLE;
280 9 : result = entry->value;
281 9 : return NS_OK;
282 : }
283 :
284 : nsresult
285 0 : nsHttpHeaderArray::GetOriginalHeader(nsHttpAtom aHeader,
286 : nsIHttpHeaderVisitor *aVisitor)
287 : {
288 0 : NS_ENSURE_ARG_POINTER(aVisitor);
289 0 : uint32_t index = 0;
290 0 : nsresult rv = NS_ERROR_NOT_AVAILABLE;
291 : while (true) {
292 0 : index = mHeaders.IndexOf(aHeader, index, nsEntry::MatchHeader());
293 0 : if (index != UINT32_MAX) {
294 0 : const nsEntry &entry = mHeaders[index];
295 :
296 0 : MOZ_ASSERT((entry.variety == eVarietyResponseNetOriginalAndResponse) ||
297 : (entry.variety == eVarietyResponseNetOriginal) ||
298 : (entry.variety == eVarietyResponse),
299 : "This must be a response header.");
300 0 : index++;
301 0 : if (entry.variety == eVarietyResponse) {
302 0 : continue;
303 : }
304 :
305 0 : nsAutoCString hdr;
306 0 : if (entry.headerNameOriginal.IsEmpty()) {
307 0 : hdr = nsDependentCString(entry.header);
308 : } else {
309 0 : hdr = entry.headerNameOriginal;
310 : }
311 :
312 0 : rv = NS_OK;
313 0 : if (NS_FAILED(aVisitor->VisitHeader(hdr,
314 : entry.value))) {
315 0 : break;
316 : }
317 : } else {
318 : // if there is no such a header, it will return
319 : // NS_ERROR_NOT_AVAILABLE or NS_OK otherwise.
320 0 : return rv;
321 : }
322 0 : }
323 0 : return NS_OK;
324 : }
325 :
326 : bool
327 39 : nsHttpHeaderArray::HasHeader(nsHttpAtom header) const
328 : {
329 39 : const nsEntry *entry = nullptr;
330 39 : LookupEntry(header, &entry);
331 39 : return entry;
332 : }
333 :
334 : nsresult
335 0 : nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor, nsHttpHeaderArray::VisitorFilter filter)
336 : {
337 0 : NS_ENSURE_ARG_POINTER(visitor);
338 : nsresult rv;
339 :
340 0 : uint32_t i, count = mHeaders.Length();
341 0 : for (i = 0; i < count; ++i) {
342 0 : const nsEntry &entry = mHeaders[i];
343 0 : if (filter == eFilterSkipDefault && entry.variety == eVarietyRequestDefault) {
344 0 : continue;
345 0 : } else if (filter == eFilterResponse && entry.variety == eVarietyResponseNetOriginal) {
346 0 : continue;
347 0 : } else if (filter == eFilterResponseOriginal && entry.variety == eVarietyResponse) {
348 0 : continue;
349 : }
350 :
351 0 : nsAutoCString hdr;
352 0 : if (entry.headerNameOriginal.IsEmpty()) {
353 0 : hdr = nsDependentCString(entry.header);
354 : } else {
355 0 : hdr = entry.headerNameOriginal;
356 : }
357 0 : rv = visitor->VisitHeader(hdr, entry.value);
358 0 : if NS_FAILED(rv) {
359 0 : return rv;
360 : }
361 : }
362 0 : return NS_OK;
363 : }
364 :
365 : /*static*/ nsresult
366 53 : nsHttpHeaderArray::ParseHeaderLine(const nsACString& line,
367 : nsHttpAtom *hdr,
368 : nsACString *headerName,
369 : nsACString *val)
370 : {
371 : //
372 : // BNF from section 4.2 of RFC 2616:
373 : //
374 : // message-header = field-name ":" [ field-value ]
375 : // field-name = token
376 : // field-value = *( field-content | LWS )
377 : // field-content = <the OCTETs making up the field-value
378 : // and consisting of either *TEXT or combinations
379 : // of token, separators, and quoted-string>
380 : //
381 :
382 : // We skip over mal-formed headers in the hope that we'll still be able to
383 : // do something useful with the response.
384 53 : int32_t split = line.FindChar(':');
385 :
386 53 : if (split == kNotFound) {
387 0 : LOG(("malformed header [%s]: no colon\n",
388 : PromiseFlatCString(line).get()));
389 0 : return NS_ERROR_FAILURE;
390 : }
391 :
392 106 : const nsACString& sub = Substring(line, 0, split);
393 : const nsACString& sub2 = Substring(
394 106 : line, split + 1, line.Length() - split - 1);
395 :
396 : // make sure we have a valid token for the field-name
397 53 : if (!nsHttp::IsValidToken(sub)) {
398 0 : LOG(("malformed header [%s]: field-name not a token\n",
399 : PromiseFlatCString(line).get()));
400 0 : return NS_ERROR_FAILURE;
401 : }
402 :
403 53 : nsHttpAtom atom = nsHttp::ResolveAtom(sub);
404 53 : if (!atom) {
405 0 : LOG(("failed to resolve atom [%s]\n", PromiseFlatCString(line).get()));
406 0 : return NS_ERROR_FAILURE;
407 : }
408 :
409 : // skip over whitespace
410 53 : char *p = net_FindCharNotInSet(
411 53 : sub2.BeginReading(), sub2.EndReading(), HTTP_LWS);
412 :
413 : // trim trailing whitespace - bug 86608
414 53 : char *p2 = net_RFindCharNotInSet(p, sub2.EndReading(), HTTP_LWS);
415 :
416 : // assign return values
417 53 : if (hdr) *hdr = atom;
418 53 : if (val) val->Assign(p, p2 - p + 1);
419 53 : if (headerName) headerName->Assign(sub);
420 :
421 53 : return NS_OK;
422 : }
423 :
424 : void
425 5 : nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders,
426 : bool pruneTransients)
427 : {
428 5 : uint32_t i, count = mHeaders.Length();
429 39 : for (i = 0; i < count; ++i) {
430 34 : const nsEntry &entry = mHeaders[i];
431 : // Skip original header.
432 34 : if (entry.variety == eVarietyResponseNetOriginal) {
433 0 : continue;
434 : }
435 : // prune proxy headers if requested
436 34 : if (pruneProxyHeaders &&
437 0 : ((entry.header == nsHttp::Proxy_Authorization) ||
438 0 : (entry.header == nsHttp::Proxy_Connection))) {
439 0 : continue;
440 : }
441 46 : if (pruneTransients &&
442 22 : (entry.value.IsEmpty() ||
443 21 : entry.header == nsHttp::Connection ||
444 20 : entry.header == nsHttp::Proxy_Connection ||
445 20 : entry.header == nsHttp::Keep_Alive ||
446 20 : entry.header == nsHttp::WWW_Authenticate ||
447 20 : entry.header == nsHttp::Proxy_Authenticate ||
448 20 : entry.header == nsHttp::Trailer ||
449 20 : entry.header == nsHttp::Transfer_Encoding ||
450 20 : entry.header == nsHttp::Upgrade ||
451 : // XXX this will cause problems when we start honoring
452 : // Cache-Control: no-cache="set-cookie", what to do?
453 10 : entry.header == nsHttp::Set_Cookie)) {
454 1 : continue;
455 : }
456 :
457 33 : if (entry.headerNameOriginal.IsEmpty()) {
458 32 : buf.Append(entry.header);
459 : } else {
460 1 : buf.Append(entry.headerNameOriginal);
461 : }
462 33 : buf.AppendLiteral(": ");
463 33 : buf.Append(entry.value);
464 33 : buf.AppendLiteral("\r\n");
465 : }
466 5 : }
467 :
468 : void
469 2 : nsHttpHeaderArray::FlattenOriginalHeader(nsACString &buf)
470 : {
471 2 : uint32_t i, count = mHeaders.Length();
472 13 : for (i = 0; i < count; ++i) {
473 11 : const nsEntry &entry = mHeaders[i];
474 : // Skip changed header.
475 11 : if (entry.variety == eVarietyResponse) {
476 2 : continue;
477 : }
478 :
479 9 : if (entry.headerNameOriginal.IsEmpty()) {
480 8 : buf.Append(entry.header);
481 : } else {
482 1 : buf.Append(entry.headerNameOriginal);
483 : }
484 :
485 9 : buf.AppendLiteral(": ");
486 9 : buf.Append(entry.value);
487 9 : buf.AppendLiteral("\r\n");
488 : }
489 2 : }
490 :
491 : const char *
492 0 : nsHttpHeaderArray::PeekHeaderAt(uint32_t index, nsHttpAtom &header,
493 : nsACString &headerNameOriginal) const
494 : {
495 0 : const nsEntry &entry = mHeaders[index];
496 :
497 0 : header = entry.header;
498 0 : headerNameOriginal = entry.headerNameOriginal;
499 0 : return entry.value.get();
500 : }
501 :
502 : void
503 0 : nsHttpHeaderArray::Clear()
504 : {
505 0 : mHeaders.Clear();
506 0 : }
507 :
508 : } // namespace net
509 : } // namespace mozilla
|