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 96450 : StringHash(PLDHashTable *table, const void *key)
101 : {
102 96450 : PLDHashNumber h = 0;
103 1013312 : for (const char *s = reinterpret_cast<const char*>(key); *s; ++s)
104 916862 : h = AddToHash(h, nsCRT::ToLower(*s));
105 96450 : return h;
106 : }
107 :
108 : static bool
109 36159 : StringCompare(PLDHashTable *table, const PLDHashEntryHdr *entry,
110 : const void *testKey)
111 : {
112 : const void *entryKey =
113 36159 : reinterpret_cast<const PLDHashEntryStub *>(entry)->key;
114 :
115 : return PL_strcasecmp(reinterpret_cast<const char *>(entryKey),
116 36159 : 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 679 : nsHttp::CreateAtomTable()
133 : {
134 679 : NS_ASSERTION(!sAtomTable.ops, "atom table already initialized");
135 :
136 679 : if (!sLock) {
137 679 : 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 679 : if (!PL_DHashTableInit(&sAtomTable, &ops, nsnull, sizeof(PLDHashEntryStub),
144 679 : 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 679 : };
156 :
157 59073 : for (int i = 0; atoms[i]; ++i) {
158 : PLDHashEntryStub *stub = reinterpret_cast<PLDHashEntryStub *>
159 58394 : (PL_DHashTableOperate(&sAtomTable, atoms[i], PL_DHASH_ADD));
160 58394 : if (!stub)
161 0 : return NS_ERROR_OUT_OF_MEMORY;
162 :
163 58394 : NS_ASSERTION(!stub->key, "duplicate static atom");
164 58394 : stub->key = atoms[i];
165 : }
166 :
167 679 : return NS_OK;
168 : }
169 :
170 : void
171 677 : nsHttp::DestroyAtomTable()
172 : {
173 677 : if (sAtomTable.ops) {
174 677 : PL_DHashTableFinish(&sAtomTable);
175 677 : sAtomTable.ops = nsnull;
176 : }
177 :
178 3251 : while (sHeapAtoms) {
179 1897 : HttpHeapAtom *next = sHeapAtoms->next;
180 1897 : free(sHeapAtoms);
181 1897 : sHeapAtoms = next;
182 : }
183 :
184 677 : if (sLock) {
185 677 : delete sLock;
186 677 : sLock = nsnull;
187 : }
188 677 : }
189 :
190 : // this function may be called from multiple threads
191 : nsHttpAtom
192 38056 : nsHttp::ResolveAtom(const char *str)
193 : {
194 38056 : nsHttpAtom atom = { nsnull };
195 :
196 38056 : if (!str || !sAtomTable.ops)
197 0 : return atom;
198 :
199 76112 : MutexAutoLock lock(*sLock);
200 :
201 : PLDHashEntryStub *stub = reinterpret_cast<PLDHashEntryStub *>
202 38056 : (PL_DHashTableOperate(&sAtomTable, str, PL_DHASH_ADD));
203 38056 : if (!stub)
204 : return atom; // out of memory
205 :
206 38056 : if (stub->key) {
207 36159 : 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 28675 : nsHttp::IsValidToken(const char *start, const char *end)
258 : {
259 28675 : if (start == end)
260 0 : return false;
261 :
262 290091 : for (; start != end; ++start) {
263 261418 : const unsigned char idx = *start;
264 261418 : if (idx > 127 || !kValidTokenMap[idx])
265 2 : return false;
266 : }
267 :
268 28673 : return true;
269 : }
270 :
271 : const char *
272 11933 : nsHttp::FindToken(const char *input, const char *token, const char *seps)
273 : {
274 11933 : if (!input)
275 8109 : return nsnull;
276 :
277 3824 : int inputLen = strlen(input);
278 3824 : int tokenLen = strlen(token);
279 :
280 3824 : if (inputLen < tokenLen)
281 32 : return nsnull;
282 :
283 3792 : const char *inputTop = input;
284 3792 : const char *inputEnd = input + inputLen - tokenLen;
285 25862 : for (; input <= inputEnd; ++input) {
286 22133 : 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 3729 : return nsnull;
296 : }
297 :
298 : bool
299 3398 : nsHttp::ParseInt64(const char *input, const char **next, PRInt64 *r)
300 : {
301 3398 : const char *start = input;
302 3398 : *r = 0;
303 15004 : while (*input >= '0' && *input <= '9') {
304 8208 : PRInt64 next = 10 * (*r) + (*input - '0');
305 8208 : if (next < *r) // overflow?
306 0 : return false;
307 8208 : *r = next;
308 8208 : ++input;
309 : }
310 3398 : if (input == start) // nothing parsed?
311 6 : return false;
312 3392 : if (next)
313 3392 : *next = input;
314 3392 : return true;
315 : }
|