1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sw=4 sts=4 et cin: */
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 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsHttp.h"
41 : #include "pldhash.h"
42 : #include "mozilla/Mutex.h"
43 : #include "mozilla/HashFunctions.h"
44 : #include "nsCRT.h"
45 : #include "prbit.h"
46 :
47 : using namespace mozilla;
48 :
49 : #if defined(PR_LOGGING)
50 : PRLogModuleInfo *gHttpLog = nsnull;
51 : #endif
52 :
53 : // define storage for all atoms
54 : #define HTTP_ATOM(_name, _value) nsHttpAtom nsHttp::_name = { _value };
55 : #include "nsHttpAtomList.h"
56 : #undef HTTP_ATOM
57 :
58 : // find out how many atoms we have
59 : #define HTTP_ATOM(_name, _value) Unused_ ## _name,
60 : enum {
61 : #include "nsHttpAtomList.h"
62 : NUM_HTTP_ATOMS
63 : };
64 : #undef HTTP_ATOM
65 :
66 : using namespace mozilla;
67 :
68 : // we keep a linked list of atoms allocated on the heap for easy clean up when
69 : // the atom table is destroyed. The structure and value string are allocated
70 : // as one contiguous block.
71 :
72 : struct HttpHeapAtom {
73 : struct HttpHeapAtom *next;
74 : char value[1];
75 : };
76 :
77 : static struct PLDHashTable sAtomTable = {0};
78 : static struct HttpHeapAtom *sHeapAtoms = nsnull;
79 : static Mutex *sLock = nsnull;
80 :
81 : HttpHeapAtom *
82 1897 : NewHeapAtom(const char *value) {
83 1897 : int len = strlen(value);
84 :
85 : HttpHeapAtom *a =
86 1897 : reinterpret_cast<HttpHeapAtom *>(malloc(sizeof(*a) + len));
87 1897 : if (!a)
88 0 : return nsnull;
89 1897 : memcpy(a->value, value, len + 1);
90 :
91 : // add this heap atom to the list of all heap atoms
92 1897 : a->next = sHeapAtoms;
93 1897 : sHeapAtoms = a;
94 :
95 1897 : return a;
96 : }
97 :
98 : // Hash string ignore case, based on PL_HashString
99 : static PLDHashNumber
100 96042 : StringHash(PLDHashTable *table, const void *key)
101 : {
102 96042 : PLDHashNumber h = 0;
103 1009308 : for (const char *s = reinterpret_cast<const char*>(key); *s; ++s)
104 913266 : h = AddToHash(h, nsCRT::ToLower(*s));
105 96042 : return h;
106 : }
107 :
108 : static bool
109 36267 : StringCompare(PLDHashTable *table, const PLDHashEntryHdr *entry,
110 : const void *testKey)
111 : {
112 : const void *entryKey =
113 36267 : reinterpret_cast<const PLDHashEntryStub *>(entry)->key;
114 :
115 : return PL_strcasecmp(reinterpret_cast<const char *>(entryKey),
116 36267 : reinterpret_cast<const char *>(testKey)) == 0;
117 : }
118 :
119 : static const PLDHashTableOps ops = {
120 : PL_DHashAllocTable,
121 : PL_DHashFreeTable,
122 : StringHash,
123 : StringCompare,
124 : PL_DHashMoveEntryStub,
125 : PL_DHashClearEntryStub,
126 : PL_DHashFinalizeStub,
127 : nsnull
128 : };
129 :
130 : // We put the atoms in a hash table for speedy lookup.. see ResolveAtom.
131 : nsresult
132 673 : nsHttp::CreateAtomTable()
133 : {
134 673 : NS_ASSERTION(!sAtomTable.ops, "atom table already initialized");
135 :
136 673 : if (!sLock) {
137 673 : sLock = new Mutex("nsHttp.sLock");
138 : }
139 :
140 : // The capacity for this table is initialized to a value greater than the
141 : // number of known atoms (NUM_HTTP_ATOMS) because we expect to encounter a
142 : // few random headers right off the bat.
143 673 : if (!PL_DHashTableInit(&sAtomTable, &ops, nsnull, sizeof(PLDHashEntryStub),
144 673 : NUM_HTTP_ATOMS + 10)) {
145 0 : sAtomTable.ops = nsnull;
146 0 : return NS_ERROR_OUT_OF_MEMORY;
147 : }
148 :
149 : // fill the table with our known atoms
150 : const char *const atoms[] = {
151 : #define HTTP_ATOM(_name, _value) nsHttp::_name._val,
152 : #include "nsHttpAtomList.h"
153 : #undef HTTP_ATOM
154 : nsnull
155 673 : };
156 :
157 58551 : for (int i = 0; atoms[i]; ++i) {
158 : PLDHashEntryStub *stub = reinterpret_cast<PLDHashEntryStub *>
159 57878 : (PL_DHashTableOperate(&sAtomTable, atoms[i], PL_DHASH_ADD));
160 57878 : if (!stub)
161 0 : return NS_ERROR_OUT_OF_MEMORY;
162 :
163 57878 : NS_ASSERTION(!stub->key, "duplicate static atom");
164 57878 : stub->key = atoms[i];
165 : }
166 :
167 673 : return NS_OK;
168 : }
169 :
170 : void
171 671 : nsHttp::DestroyAtomTable()
172 : {
173 671 : if (sAtomTable.ops) {
174 671 : PL_DHashTableFinish(&sAtomTable);
175 671 : sAtomTable.ops = nsnull;
176 : }
177 :
178 3239 : while (sHeapAtoms) {
179 1897 : HttpHeapAtom *next = sHeapAtoms->next;
180 1897 : free(sHeapAtoms);
181 1897 : sHeapAtoms = next;
182 : }
183 :
184 671 : if (sLock) {
185 671 : delete sLock;
186 671 : sLock = nsnull;
187 : }
188 671 : }
189 :
190 : // this function may be called from multiple threads
191 : nsHttpAtom
192 38164 : nsHttp::ResolveAtom(const char *str)
193 : {
194 38164 : nsHttpAtom atom = { nsnull };
195 :
196 38164 : if (!str || !sAtomTable.ops)
197 0 : return atom;
198 :
199 76328 : MutexAutoLock lock(*sLock);
200 :
201 : PLDHashEntryStub *stub = reinterpret_cast<PLDHashEntryStub *>
202 38164 : (PL_DHashTableOperate(&sAtomTable, str, PL_DHASH_ADD));
203 38164 : if (!stub)
204 : return atom; // out of memory
205 :
206 38164 : if (stub->key) {
207 36267 : atom._val = reinterpret_cast<const char *>(stub->key);
208 : return atom;
209 : }
210 :
211 : // if the atom could not be found in the atom table, then we'll go
212 : // and allocate a new atom on the heap.
213 1897 : HttpHeapAtom *heapAtom = NewHeapAtom(str);
214 1897 : if (!heapAtom)
215 : return atom; // out of memory
216 :
217 1897 : stub->key = atom._val = heapAtom->value;
218 : return atom;
219 : }
220 :
221 : //
222 : // From section 2.2 of RFC 2616, a token is defined as:
223 : //
224 : // token = 1*<any CHAR except CTLs or separators>
225 : // CHAR = <any US-ASCII character (octets 0 - 127)>
226 : // separators = "(" | ")" | "<" | ">" | "@"
227 : // | "," | ";" | ":" | "\" | <">
228 : // | "/" | "[" | "]" | "?" | "="
229 : // | "{" | "}" | SP | HT
230 : // CTL = <any US-ASCII control character
231 : // (octets 0 - 31) and DEL (127)>
232 : // SP = <US-ASCII SP, space (32)>
233 : // HT = <US-ASCII HT, horizontal-tab (9)>
234 : //
235 : static const char kValidTokenMap[128] = {
236 : 0, 0, 0, 0, 0, 0, 0, 0, // 0
237 : 0, 0, 0, 0, 0, 0, 0, 0, // 8
238 : 0, 0, 0, 0, 0, 0, 0, 0, // 16
239 : 0, 0, 0, 0, 0, 0, 0, 0, // 24
240 :
241 : 0, 1, 0, 1, 1, 1, 1, 1, // 32
242 : 0, 0, 1, 1, 0, 1, 1, 0, // 40
243 : 1, 1, 1, 1, 1, 1, 1, 1, // 48
244 : 1, 1, 0, 0, 0, 0, 0, 0, // 56
245 :
246 : 0, 1, 1, 1, 1, 1, 1, 1, // 64
247 : 1, 1, 1, 1, 1, 1, 1, 1, // 72
248 : 1, 1, 1, 1, 1, 1, 1, 1, // 80
249 : 1, 1, 1, 0, 0, 0, 1, 1, // 88
250 :
251 : 1, 1, 1, 1, 1, 1, 1, 1, // 96
252 : 1, 1, 1, 1, 1, 1, 1, 1, // 104
253 : 1, 1, 1, 1, 1, 1, 1, 1, // 112
254 : 1, 1, 1, 0, 1, 0, 1, 0 // 120
255 : };
256 : bool
257 28783 : nsHttp::IsValidToken(const char *start, const char *end)
258 : {
259 28783 : if (start == end)
260 0 : return false;
261 :
262 291175 : for (; start != end; ++start) {
263 262394 : const unsigned char idx = *start;
264 262394 : if (idx > 127 || !kValidTokenMap[idx])
265 2 : return false;
266 : }
267 :
268 28781 : return true;
269 : }
270 :
271 : const char *
272 11971 : nsHttp::FindToken(const char *input, const char *token, const char *seps)
273 : {
274 11971 : if (!input)
275 8134 : return nsnull;
276 :
277 3837 : int inputLen = strlen(input);
278 3837 : int tokenLen = strlen(token);
279 :
280 3837 : if (inputLen < tokenLen)
281 32 : return nsnull;
282 :
283 3805 : const char *inputTop = input;
284 3805 : const char *inputEnd = input + inputLen - tokenLen;
285 25953 : for (; input <= inputEnd; ++input) {
286 22211 : if (PL_strncasecmp(input, token, tokenLen) == 0) {
287 64 : if (input > inputTop && !strchr(seps, *(input - 1)))
288 0 : continue;
289 64 : if (input < inputEnd && !strchr(seps, *(input + tokenLen)))
290 1 : continue;
291 63 : return input;
292 : }
293 : }
294 :
295 3742 : return nsnull;
296 : }
297 :
298 : bool
299 3411 : nsHttp::ParseInt64(const char *input, const char **next, PRInt64 *r)
300 : {
301 3411 : const char *start = input;
302 3411 : *r = 0;
303 15043 : while (*input >= '0' && *input <= '9') {
304 8221 : PRInt64 next = 10 * (*r) + (*input - '0');
305 8221 : if (next < *r) // overflow?
306 0 : return false;
307 8221 : *r = next;
308 8221 : ++input;
309 : }
310 3411 : if (input == start) // nothing parsed?
311 6 : return false;
312 3405 : if (next)
313 3405 : *next = input;
314 3405 : return true;
315 : }
|