|
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 |
|
14 namespace mozilla { |
|
15 namespace net { |
|
16 |
|
17 //----------------------------------------------------------------------------- |
|
18 // nsHttpHeaderArray <public> |
|
19 //----------------------------------------------------------------------------- |
|
20 nsresult |
|
21 nsHttpHeaderArray::SetHeader(nsHttpAtom header, |
|
22 const nsACString &value, |
|
23 bool merge) |
|
24 { |
|
25 nsEntry *entry = nullptr; |
|
26 int32_t index; |
|
27 |
|
28 index = LookupEntry(header, &entry); |
|
29 |
|
30 // If an empty value is passed in, then delete the header entry... |
|
31 // unless we are merging, in which case this function becomes a NOP. |
|
32 if (value.IsEmpty()) { |
|
33 if (!merge && entry) |
|
34 mHeaders.RemoveElementAt(index); |
|
35 return NS_OK; |
|
36 } |
|
37 |
|
38 if (!entry) { |
|
39 entry = mHeaders.AppendElement(); // new nsEntry() |
|
40 if (!entry) |
|
41 return NS_ERROR_OUT_OF_MEMORY; |
|
42 entry->header = header; |
|
43 entry->value = value; |
|
44 } else if (merge && !IsSingletonHeader(header)) { |
|
45 MergeHeader(header, entry, value); |
|
46 } else { |
|
47 // Replace the existing string with the new value |
|
48 entry->value = value; |
|
49 } |
|
50 |
|
51 return NS_OK; |
|
52 } |
|
53 |
|
54 nsresult |
|
55 nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value) |
|
56 { |
|
57 nsEntry *entry = nullptr; |
|
58 |
|
59 LookupEntry(header, &entry); |
|
60 |
|
61 if (!entry) { |
|
62 if (value.IsEmpty()) { |
|
63 if (!TrackEmptyHeader(header)) { |
|
64 LOG(("Ignoring Empty Header: %s\n", header.get())); |
|
65 return NS_OK; // ignore empty headers by default |
|
66 } |
|
67 } |
|
68 entry = mHeaders.AppendElement(); //new nsEntry(header, value); |
|
69 if (!entry) |
|
70 return NS_ERROR_OUT_OF_MEMORY; |
|
71 entry->header = header; |
|
72 entry->value = value; |
|
73 } else if (!IsSingletonHeader(header)) { |
|
74 MergeHeader(header, entry, value); |
|
75 } else { |
|
76 // Multiple instances of non-mergeable header received from network |
|
77 // - ignore if same value |
|
78 if (!entry->value.Equals(value)) { |
|
79 if (IsSuspectDuplicateHeader(header)) { |
|
80 // reply may be corrupt/hacked (ex: CLRF injection attacks) |
|
81 return NS_ERROR_CORRUPTED_CONTENT; |
|
82 } // else silently drop value: keep value from 1st header seen |
|
83 LOG(("Header %s silently dropped as non mergeable header\n", |
|
84 header.get())); |
|
85 } |
|
86 } |
|
87 |
|
88 return NS_OK; |
|
89 } |
|
90 |
|
91 void |
|
92 nsHttpHeaderArray::ClearHeader(nsHttpAtom header) |
|
93 { |
|
94 mHeaders.RemoveElement(header, nsEntry::MatchHeader()); |
|
95 } |
|
96 |
|
97 const char * |
|
98 nsHttpHeaderArray::PeekHeader(nsHttpAtom header) const |
|
99 { |
|
100 const nsEntry *entry = nullptr; |
|
101 LookupEntry(header, &entry); |
|
102 return entry ? entry->value.get() : nullptr; |
|
103 } |
|
104 |
|
105 nsresult |
|
106 nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result) const |
|
107 { |
|
108 const nsEntry *entry = nullptr; |
|
109 LookupEntry(header, &entry); |
|
110 if (!entry) |
|
111 return NS_ERROR_NOT_AVAILABLE; |
|
112 result = entry->value; |
|
113 return NS_OK; |
|
114 } |
|
115 |
|
116 nsresult |
|
117 nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor) |
|
118 { |
|
119 NS_ENSURE_ARG_POINTER(visitor); |
|
120 uint32_t i, count = mHeaders.Length(); |
|
121 for (i = 0; i < count; ++i) { |
|
122 const nsEntry &entry = mHeaders[i]; |
|
123 if (NS_FAILED(visitor->VisitHeader(nsDependentCString(entry.header), |
|
124 entry.value))) |
|
125 break; |
|
126 } |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 nsresult |
|
131 nsHttpHeaderArray::ParseHeaderLine(const char *line, |
|
132 nsHttpAtom *hdr, |
|
133 char **val) |
|
134 { |
|
135 // |
|
136 // BNF from section 4.2 of RFC 2616: |
|
137 // |
|
138 // message-header = field-name ":" [ field-value ] |
|
139 // field-name = token |
|
140 // field-value = *( field-content | LWS ) |
|
141 // field-content = <the OCTETs making up the field-value |
|
142 // and consisting of either *TEXT or combinations |
|
143 // of token, separators, and quoted-string> |
|
144 // |
|
145 |
|
146 // We skip over mal-formed headers in the hope that we'll still be able to |
|
147 // do something useful with the response. |
|
148 |
|
149 char *p = (char *) strchr(line, ':'); |
|
150 if (!p) { |
|
151 LOG(("malformed header [%s]: no colon\n", line)); |
|
152 return NS_OK; |
|
153 } |
|
154 |
|
155 // make sure we have a valid token for the field-name |
|
156 if (!nsHttp::IsValidToken(line, p)) { |
|
157 LOG(("malformed header [%s]: field-name not a token\n", line)); |
|
158 return NS_OK; |
|
159 } |
|
160 |
|
161 *p = 0; // null terminate field-name |
|
162 |
|
163 nsHttpAtom atom = nsHttp::ResolveAtom(line); |
|
164 if (!atom) { |
|
165 LOG(("failed to resolve atom [%s]\n", line)); |
|
166 return NS_OK; |
|
167 } |
|
168 |
|
169 // skip over whitespace |
|
170 p = net_FindCharNotInSet(++p, HTTP_LWS); |
|
171 |
|
172 // trim trailing whitespace - bug 86608 |
|
173 char *p2 = net_RFindCharNotInSet(p, HTTP_LWS); |
|
174 |
|
175 *++p2 = 0; // null terminate header value; if all chars starting at |p| |
|
176 // consisted of LWS, then p2 would have pointed at |p-1|, so |
|
177 // the prefix increment is always valid. |
|
178 |
|
179 // assign return values |
|
180 if (hdr) *hdr = atom; |
|
181 if (val) *val = p; |
|
182 |
|
183 // assign response header |
|
184 return SetHeaderFromNet(atom, nsDependentCString(p, p2 - p)); |
|
185 } |
|
186 |
|
187 void |
|
188 nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders) |
|
189 { |
|
190 uint32_t i, count = mHeaders.Length(); |
|
191 for (i = 0; i < count; ++i) { |
|
192 const nsEntry &entry = mHeaders[i]; |
|
193 // prune proxy headers if requested |
|
194 if (pruneProxyHeaders && ((entry.header == nsHttp::Proxy_Authorization) || |
|
195 (entry.header == nsHttp::Proxy_Connection))) |
|
196 continue; |
|
197 buf.Append(entry.header); |
|
198 buf.AppendLiteral(": "); |
|
199 buf.Append(entry.value); |
|
200 buf.AppendLiteral("\r\n"); |
|
201 } |
|
202 } |
|
203 |
|
204 const char * |
|
205 nsHttpHeaderArray::PeekHeaderAt(uint32_t index, nsHttpAtom &header) const |
|
206 { |
|
207 const nsEntry &entry = mHeaders[index]; |
|
208 |
|
209 header = entry.header; |
|
210 return entry.value.get(); |
|
211 } |
|
212 |
|
213 void |
|
214 nsHttpHeaderArray::Clear() |
|
215 { |
|
216 mHeaders.Clear(); |
|
217 } |
|
218 |
|
219 } // namespace mozilla::net |
|
220 } // namespace mozilla |