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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications.
20 : * Portions created by the Initial Developer are Copyright (C) 2001
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Darin Fisher <darin@netscape.com> (original author)
25 : * Patrick McManus <mcmanus@ducksong.com>
26 : * Jason Duell <jduell.mcbugs@gmail.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "nsHttpHeaderArray.h"
43 : #include "nsHttp.h"
44 :
45 : //-----------------------------------------------------------------------------
46 : // nsHttpHeaderArray <public>
47 : //-----------------------------------------------------------------------------
48 : nsresult
49 32765 : nsHttpHeaderArray::SetHeader(nsHttpAtom header,
50 : const nsACString &value,
51 : bool merge)
52 : {
53 32765 : nsEntry *entry = nsnull;
54 : PRInt32 index;
55 :
56 32765 : index = LookupEntry(header, &entry);
57 :
58 : // If an empty value is passed in, then delete the header entry...
59 : // unless we are merging, in which case this function becomes a NOP.
60 32765 : if (value.IsEmpty()) {
61 3557 : if (!merge && entry)
62 12 : mHeaders.RemoveElementAt(index);
63 3557 : return NS_OK;
64 : }
65 :
66 29208 : if (!entry) {
67 27639 : entry = mHeaders.AppendElement(); // new nsEntry()
68 27639 : if (!entry)
69 0 : return NS_ERROR_OUT_OF_MEMORY;
70 27639 : entry->header = header;
71 27639 : entry->value = value;
72 1569 : } else if (merge && !IsSingletonHeader(header)) {
73 4 : MergeHeader(header, entry, value);
74 : } else {
75 : // Replace the existing string with the new value
76 1565 : entry->value = value;
77 : }
78 :
79 29208 : return NS_OK;
80 : }
81 :
82 : nsresult
83 18781 : nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value)
84 : {
85 18781 : nsEntry *entry = nsnull;
86 : PRInt32 index;
87 :
88 18781 : index = LookupEntry(header, &entry);
89 :
90 18781 : if (!entry) {
91 18761 : if (value.IsEmpty()) {
92 39 : if (!TrackEmptyHeader(header)) {
93 13 : LOG(("Ignoring Empty Header: %s\n", header.get()));
94 13 : return NS_OK; // ignore empty headers by default
95 : }
96 : }
97 18748 : entry = mHeaders.AppendElement(); //new nsEntry(header, value);
98 18748 : if (!entry)
99 0 : return NS_ERROR_OUT_OF_MEMORY;
100 18748 : entry->header = header;
101 18748 : entry->value = value;
102 20 : } else if (!IsSingletonHeader(header)) {
103 6 : MergeHeader(header, entry, value);
104 : } else {
105 : // Multiple instances of non-mergeable header received from network
106 : // - ignore if same value
107 14 : if (!entry->value.Equals(value)) {
108 12 : if (IsSuspectDuplicateHeader(header)) {
109 : // reply may be corrupt/hacked (ex: CLRF injection attacks)
110 11 : return NS_ERROR_CORRUPTED_CONTENT;
111 : } // else silently drop value: keep value from 1st header seen
112 1 : LOG(("Header %s silently dropped as non mergeable header\n",
113 : header.get()));
114 : }
115 : }
116 :
117 18757 : return NS_OK;
118 : }
119 :
120 : void
121 38 : nsHttpHeaderArray::ClearHeader(nsHttpAtom header)
122 : {
123 38 : mHeaders.RemoveElement(header, nsEntry::MatchHeader());
124 38 : }
125 :
126 : const char *
127 44127 : nsHttpHeaderArray::PeekHeader(nsHttpAtom header)
128 : {
129 44127 : nsEntry *entry = nsnull;
130 44127 : LookupEntry(header, &entry);
131 44127 : return entry ? entry->value.get() : nsnull;
132 : }
133 :
134 : nsresult
135 13546 : nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result)
136 : {
137 13546 : nsEntry *entry = nsnull;
138 13546 : LookupEntry(header, &entry);
139 13546 : if (!entry)
140 10799 : return NS_ERROR_NOT_AVAILABLE;
141 2747 : result = entry->value;
142 2747 : return NS_OK;
143 : }
144 :
145 : nsresult
146 1386 : nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor)
147 : {
148 1386 : NS_ENSURE_ARG_POINTER(visitor);
149 1386 : PRUint32 i, count = mHeaders.Length();
150 8830 : for (i = 0; i < count; ++i) {
151 7444 : const nsEntry &entry = mHeaders[i];
152 7444 : if (NS_FAILED(visitor->VisitHeader(nsDependentCString(entry.header),
153 : entry.value)))
154 0 : break;
155 : }
156 1386 : return NS_OK;
157 : }
158 :
159 : nsresult
160 18781 : nsHttpHeaderArray::ParseHeaderLine(const char *line,
161 : nsHttpAtom *hdr,
162 : char **val)
163 : {
164 : //
165 : // BNF from section 4.2 of RFC 2616:
166 : //
167 : // message-header = field-name ":" [ field-value ]
168 : // field-name = token
169 : // field-value = *( field-content | LWS )
170 : // field-content = <the OCTETs making up the field-value
171 : // and consisting of either *TEXT or combinations
172 : // of token, separators, and quoted-string>
173 : //
174 :
175 : // We skip over mal-formed headers in the hope that we'll still be able to
176 : // do something useful with the response.
177 :
178 18781 : char *p = (char *) strchr(line, ':');
179 18781 : if (!p) {
180 0 : LOG(("malformed header [%s]: no colon\n", line));
181 0 : return NS_OK;
182 : }
183 :
184 : // make sure we have a valid token for the field-name
185 18781 : if (!nsHttp::IsValidToken(line, p)) {
186 0 : LOG(("malformed header [%s]: field-name not a token\n", line));
187 0 : return NS_OK;
188 : }
189 :
190 18781 : *p = 0; // null terminate field-name
191 :
192 18781 : nsHttpAtom atom = nsHttp::ResolveAtom(line);
193 18781 : if (!atom) {
194 0 : LOG(("failed to resolve atom [%s]\n", line));
195 0 : return NS_OK;
196 : }
197 :
198 : // skip over whitespace
199 18781 : p = net_FindCharNotInSet(++p, HTTP_LWS);
200 :
201 : // trim trailing whitespace - bug 86608
202 18781 : char *p2 = net_RFindCharNotInSet(p, HTTP_LWS);
203 :
204 18781 : *++p2 = 0; // null terminate header value; if all chars starting at |p|
205 : // consisted of LWS, then p2 would have pointed at |p-1|, so
206 : // the prefix increment is always valid.
207 :
208 : // assign return values
209 18781 : if (hdr) *hdr = atom;
210 18781 : if (val) *val = p;
211 :
212 : // assign response header
213 18781 : return SetHeaderFromNet(atom, nsDependentCString(p, p2 - p));
214 : }
215 :
216 : void
217 2997 : nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders)
218 : {
219 2997 : PRUint32 i, count = mHeaders.Length();
220 27390 : for (i = 0; i < count; ++i) {
221 24393 : const nsEntry &entry = mHeaders[i];
222 : // prune proxy headers if requested
223 24393 : if (pruneProxyHeaders && ((entry.header == nsHttp::Proxy_Authorization) ||
224 0 : (entry.header == nsHttp::Proxy_Connection)))
225 0 : continue;
226 24393 : buf.Append(entry.header);
227 24393 : buf.AppendLiteral(": ");
228 24393 : buf.Append(entry.value);
229 24393 : buf.AppendLiteral("\r\n");
230 : }
231 2997 : }
232 :
233 : const char *
234 8080 : nsHttpHeaderArray::PeekHeaderAt(PRUint32 index, nsHttpAtom &header)
235 : {
236 8080 : const nsEntry &entry = mHeaders[index];
237 :
238 8080 : header = entry.header;
239 8080 : return entry.value.get();
240 : }
241 :
242 : void
243 10034 : nsHttpHeaderArray::Clear()
244 : {
245 10034 : mHeaders.Clear();
246 10034 : }
|