Coverage Report

Created: 2018-09-25 14:53

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