Coverage Report

Created: 2018-09-25 14:53

/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