Back to index

lightning-sunbird  0.9+nobinonly
nsHttp.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:set ts=4 sw=4 sts=4 et cin: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Darin Fisher <darin@netscape.com> (original author)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsHttp.h"
00041 #include "nsAutoLock.h"
00042 #include "pldhash.h"
00043 #include "nsCRT.h"
00044 
00045 #if defined(PR_LOGGING)
00046 PRLogModuleInfo *gHttpLog = nsnull;
00047 #endif
00048 
00049 // define storage for all atoms
00050 #define HTTP_ATOM(_name, _value) nsHttpAtom nsHttp::_name = { _value };
00051 #include "nsHttpAtomList.h"
00052 #undef HTTP_ATOM
00053 
00054 // find out how many atoms we have
00055 #define HTTP_ATOM(_name, _value) Unused_ ## _name,
00056 enum {
00057 #include "nsHttpAtomList.h"
00058     NUM_HTTP_ATOMS
00059 };
00060 #undef HTTP_ATOM
00061 
00062 // we keep a linked list of atoms allocated on the heap for easy clean up when
00063 // the atom table is destroyed.  The structure and value string are allocated
00064 // as one contiguous block.
00065 
00066 struct HttpHeapAtom {
00067     struct HttpHeapAtom *next;
00068     char                 value[1];
00069 };
00070 
00071 static struct PLDHashTable  sAtomTable = {0};
00072 static struct HttpHeapAtom *sHeapAtoms = nsnull;
00073 static PRLock              *sLock = nsnull;
00074 
00075 HttpHeapAtom *
00076 NewHeapAtom(const char *value) {
00077     int len = strlen(value);
00078 
00079     HttpHeapAtom *a =
00080         NS_REINTERPRET_CAST(HttpHeapAtom *, malloc(sizeof(*a) + len));
00081     if (!a)
00082         return nsnull;
00083     memcpy(a->value, value, len + 1);
00084 
00085     // add this heap atom to the list of all heap atoms
00086     a->next = sHeapAtoms;
00087     sHeapAtoms = a;
00088 
00089     return a;
00090 }
00091 
00092 // Hash string ignore case, based on PL_HashString
00093 PR_STATIC_CALLBACK(PLDHashNumber)
00094 StringHash(PLDHashTable *table, const void *key)
00095 {
00096     PLDHashNumber h = 0;
00097     for (const char *s = NS_REINTERPRET_CAST(const char*, key); *s; ++s)
00098         h = (h >> 28) ^ (h << 4) ^ nsCRT::ToLower(*s);
00099     return h;
00100 }
00101 
00102 PR_STATIC_CALLBACK(PRBool)
00103 StringCompare(PLDHashTable *table, const PLDHashEntryHdr *entry,
00104               const void *testKey)
00105 {
00106     const void *entryKey =
00107             NS_REINTERPRET_CAST(const PLDHashEntryStub *, entry)->key;
00108 
00109     return PL_strcasecmp(NS_REINTERPRET_CAST(const char *, entryKey),
00110                          NS_REINTERPRET_CAST(const char *, testKey)) == 0;
00111 }
00112 
00113 static const PLDHashTableOps ops = {
00114     PL_DHashAllocTable,
00115     PL_DHashFreeTable,
00116     PL_DHashGetKeyStub,
00117     StringHash,
00118     StringCompare,
00119     PL_DHashMoveEntryStub,
00120     PL_DHashClearEntryStub,
00121     PL_DHashFinalizeStub,
00122     nsnull
00123 };
00124 
00125 // We put the atoms in a hash table for speedy lookup.. see ResolveAtom.
00126 nsresult
00127 nsHttp::CreateAtomTable()
00128 {
00129     NS_ASSERTION(!sAtomTable.ops, "atom table already initialized");
00130 
00131     if (!sLock) {
00132         sLock = PR_NewLock();
00133         if (!sLock)
00134             return NS_ERROR_OUT_OF_MEMORY;
00135     }
00136 
00137     // The capacity for this table is initialized to a value greater than the
00138     // number of known atoms (NUM_HTTP_ATOMS) because we expect to encounter a
00139     // few random headers right off the bat.
00140     if (!PL_DHashTableInit(&sAtomTable, &ops, nsnull, sizeof(PLDHashEntryStub),
00141                            NUM_HTTP_ATOMS + 10)) {
00142         sAtomTable.ops = nsnull;
00143         return NS_ERROR_OUT_OF_MEMORY;
00144     }
00145 
00146     // fill the table with our known atoms
00147     const char *const atoms[] = {
00148 #define HTTP_ATOM(_name, _value) nsHttp::_name._val,
00149 #include "nsHttpAtomList.h"
00150 #undef HTTP_ATOM
00151         nsnull
00152     };
00153 
00154     for (int i = 0; atoms[i]; ++i) {
00155         PLDHashEntryStub *stub = NS_REINTERPRET_CAST(PLDHashEntryStub *,
00156                 PL_DHashTableOperate(&sAtomTable, atoms[i], PL_DHASH_ADD));
00157         if (!stub)
00158             return NS_ERROR_OUT_OF_MEMORY;
00159         
00160         NS_ASSERTION(!stub->key, "duplicate static atom");
00161         stub->key = atoms[i];
00162     }
00163 
00164     return NS_OK;
00165 }
00166 
00167 void
00168 nsHttp::DestroyAtomTable()
00169 {
00170     if (sAtomTable.ops) {
00171         PL_DHashTableFinish(&sAtomTable);
00172         sAtomTable.ops = nsnull;
00173     }
00174 
00175     while (sHeapAtoms) {
00176         HttpHeapAtom *next = sHeapAtoms->next;
00177         free(sHeapAtoms);
00178         sHeapAtoms = next;
00179     }
00180 
00181     if (sLock) {
00182         PR_DestroyLock(sLock);
00183         sLock = nsnull;
00184     }
00185 }
00186 
00187 // this function may be called from multiple threads
00188 nsHttpAtom
00189 nsHttp::ResolveAtom(const char *str)
00190 {
00191     nsHttpAtom atom = { nsnull };
00192 
00193     if (!str || !sAtomTable.ops)
00194         return atom;
00195 
00196     nsAutoLock lock(sLock);
00197 
00198     PLDHashEntryStub *stub = NS_REINTERPRET_CAST(PLDHashEntryStub *,
00199             PL_DHashTableOperate(&sAtomTable, str, PL_DHASH_ADD));
00200     if (!stub)
00201         return atom;  // out of memory
00202 
00203     if (stub->key) {
00204         atom._val = NS_REINTERPRET_CAST(const char *, stub->key);
00205         return atom;
00206     }
00207 
00208     // if the atom could not be found in the atom table, then we'll go
00209     // and allocate a new atom on the heap.
00210     HttpHeapAtom *heapAtom = NewHeapAtom(str);
00211     if (!heapAtom)
00212         return atom;  // out of memory
00213 
00214     stub->key = atom._val = heapAtom->value;
00215     return atom;
00216 }
00217 
00218 //
00219 // From section 2.2 of RFC 2616, a token is defined as:
00220 //
00221 //   token          = 1*<any CHAR except CTLs or separators>
00222 //   CHAR           = <any US-ASCII character (octets 0 - 127)>
00223 //   separators     = "(" | ")" | "<" | ">" | "@"
00224 //                  | "," | ";" | ":" | "\" | <">
00225 //                  | "/" | "[" | "]" | "?" | "="
00226 //                  | "{" | "}" | SP | HT
00227 //   CTL            = <any US-ASCII control character
00228 //                    (octets 0 - 31) and DEL (127)>
00229 //   SP             = <US-ASCII SP, space (32)>
00230 //   HT             = <US-ASCII HT, horizontal-tab (9)>
00231 //
00232 static const char kValidTokenMap[128] = {
00233     0, 0, 0, 0, 0, 0, 0, 0, //   0
00234     0, 0, 0, 0, 0, 0, 0, 0, //   8
00235     0, 0, 0, 0, 0, 0, 0, 0, //  16
00236     0, 0, 0, 0, 0, 0, 0, 0, //  24
00237 
00238     0, 1, 0, 1, 1, 1, 1, 1, //  32
00239     0, 0, 1, 1, 0, 1, 1, 0, //  40
00240     1, 1, 1, 1, 1, 1, 1, 1, //  48
00241     1, 1, 0, 0, 0, 0, 0, 0, //  56
00242 
00243     0, 1, 1, 1, 1, 1, 1, 1, //  64
00244     1, 1, 1, 1, 1, 1, 1, 1, //  72
00245     1, 1, 1, 1, 1, 1, 1, 1, //  80
00246     1, 1, 1, 0, 0, 0, 1, 1, //  88
00247 
00248     1, 1, 1, 1, 1, 1, 1, 1, //  96
00249     1, 1, 1, 1, 1, 1, 1, 1, // 104
00250     1, 1, 1, 1, 1, 1, 1, 1, // 112
00251     1, 1, 1, 0, 1, 0, 1, 0  // 120
00252 };
00253 PRBool
00254 nsHttp::IsValidToken(const char *start, const char *end)
00255 {
00256     if (start == end)
00257         return PR_FALSE;
00258 
00259     for (; start != end; ++start) {
00260         const unsigned char idx = *start;
00261         if (idx > 127 || !kValidTokenMap[idx])
00262             return PR_FALSE;
00263     }
00264 
00265     return PR_TRUE;
00266 }
00267 
00268 PRBool
00269 nsHttp::ParseInt64(const char *input, const char **next, PRInt64 *r)
00270 {
00271     const char *start = input;
00272     *r = 0;
00273     while (*input >= '0' && *input <= '9') {
00274         PRInt64 next = 10 * (*r) + (*input - '0');
00275         if (next < *r) // overflow?
00276             return PR_FALSE;
00277         *r = next;
00278         ++input;
00279     }
00280     if (input == start) // nothing parsed?
00281         return PR_FALSE;
00282     if (next)
00283         *next = input;
00284     return PR_TRUE;
00285 }