Back to index

lightning-sunbird  0.9+nobinonly
nsHttpHeaderArray.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 ci et: */
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 "nsHttpHeaderArray.h"
00041 #include "nsHttp.h"
00042 
00043 //-----------------------------------------------------------------------------
00044 // nsHttpHeaderArray <public>
00045 //-----------------------------------------------------------------------------
00046 
00047 nsresult
00048 nsHttpHeaderArray::SetHeader(nsHttpAtom header,
00049                              const nsACString &value,
00050                              PRBool merge)
00051 {
00052     nsEntry *entry = nsnull;
00053     PRInt32 index;
00054 
00055     index = LookupEntry(header, &entry);
00056 
00057     // If an empty value is passed in, then delete the header entry...
00058     // unless we are merging, in which case this function becomes a NOP.
00059     if (value.IsEmpty()) {
00060         if (!merge && entry) {
00061             mHeaders.RemoveElementAt(index);
00062             delete entry;
00063         }
00064         return NS_OK;
00065     }
00066 
00067     // Create a new entry, or...
00068     if (!entry) {
00069         entry = new nsEntry(header, value);
00070         if (!entry)
00071             return NS_ERROR_OUT_OF_MEMORY;
00072         if (!mHeaders.AppendElement(entry)) {
00073             NS_WARNING("AppendElement failed");
00074             delete entry;
00075         }
00076     }
00077     // Append the new value to the existing value iff...
00078     else if (merge && CanAppendToHeader(header)) {
00079         if (header == nsHttp::Set_Cookie ||
00080             header == nsHttp::WWW_Authenticate ||
00081             header == nsHttp::Proxy_Authenticate)
00082             // Special case these headers and use a newline delimiter to
00083             // delimit the values from one another as commas may appear
00084             // in the values of these headers contrary to what the spec says.
00085             entry->value.Append('\n');
00086         else
00087             // Delimit each value from the others using a comma (per HTTP spec)
00088             entry->value.AppendLiteral(", ");
00089         entry->value.Append(value);
00090     }
00091     // Replace the existing string with the new value
00092     else
00093         entry->value = value;
00094     return NS_OK;
00095 }
00096 
00097 void
00098 nsHttpHeaderArray::ClearHeader(nsHttpAtom header)
00099 {
00100     nsEntry *entry = nsnull;
00101     PRInt32 index;
00102 
00103     index = LookupEntry(header, &entry);
00104     if (entry) {
00105         mHeaders.RemoveElementAt(index);
00106         delete entry;
00107     }
00108 }
00109 
00110 const char *
00111 nsHttpHeaderArray::PeekHeader(nsHttpAtom header)
00112 {
00113     nsEntry *entry = nsnull;
00114     LookupEntry(header, &entry);
00115     return entry ? entry->value.get() : nsnull;
00116 }
00117 
00118 nsresult
00119 nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result)
00120 {
00121     nsEntry *entry = nsnull;
00122     LookupEntry(header, &entry);
00123     if (!entry)
00124         return NS_ERROR_NOT_AVAILABLE;
00125     result = entry->value;
00126     return NS_OK;
00127 }
00128 
00129 nsresult
00130 nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor)
00131 {
00132     NS_ENSURE_ARG_POINTER(visitor);
00133     PRInt32 i, count = mHeaders.Count();
00134     for (i=0; i<count; ++i) {
00135         nsEntry *entry = (nsEntry *) mHeaders[i];
00136         if (NS_FAILED(visitor->VisitHeader(nsDependentCString(entry->header), entry->value)))
00137             break;
00138     }
00139     return NS_OK;
00140 }
00141 
00142 void
00143 nsHttpHeaderArray::ParseHeaderLine(char *line, nsHttpAtom *hdr, char **val)
00144 {
00145     //
00146     // BNF from section 4.2 of RFC 2616:
00147     //
00148     //   message-header = field-name ":" [ field-value ]
00149     //   field-name     = token
00150     //   field-value    = *( field-content | LWS )
00151     //   field-content  = <the OCTETs making up the field-value
00152     //                     and consisting of either *TEXT or combinations
00153     //                     of token, separators, and quoted-string>
00154     //
00155     
00156     // We skip over mal-formed headers in the hope that we'll still be able to
00157     // do something useful with the response.
00158 
00159     char *p = (char *) strchr(line, ':');
00160     if (!p) {
00161         LOG(("malformed header [%s]: no colon\n", line));
00162         return;
00163     }
00164 
00165     // make sure we have a valid token for the field-name
00166     if (!nsHttp::IsValidToken(line, p)) {
00167         LOG(("malformed header [%s]: field-name not a token\n", line));
00168         return;
00169     }
00170     
00171     *p = 0; // null terminate field-name
00172 
00173     nsHttpAtom atom = nsHttp::ResolveAtom(line);
00174     if (!atom) {
00175         LOG(("failed to resolve atom [%s]\n", line));
00176         return;
00177     }
00178 
00179     // skip over whitespace
00180     p = net_FindCharNotInSet(++p, HTTP_LWS);
00181 
00182     // trim trailing whitespace - bug 86608
00183     char *p2 = net_RFindCharNotInSet(p, HTTP_LWS);
00184 
00185     *++p2 = 0; // null terminate header value; if all chars starting at |p|
00186                // consisted of LWS, then p2 would have pointed at |p-1|, so
00187                // the prefix increment is always valid.
00188 
00189     // assign return values
00190     if (hdr) *hdr = atom;
00191     if (val) *val = p;
00192 
00193     // assign response header
00194     SetHeader(atom, nsDependentCString(p, p2 - p), PR_TRUE);
00195 }
00196 
00197 void
00198 nsHttpHeaderArray::Flatten(nsACString &buf, PRBool pruneProxyHeaders)
00199 {
00200     PRInt32 i, count = mHeaders.Count();
00201     for (i=0; i<count; ++i) {
00202         nsEntry *entry = (nsEntry *) mHeaders[i];
00203         // prune proxy headers if requested
00204         if (pruneProxyHeaders && ((entry->header == nsHttp::Proxy_Authorization) || 
00205                                   (entry->header == nsHttp::Proxy_Connection)))
00206             continue;
00207         buf.Append(entry->header);
00208         buf.AppendLiteral(": ");
00209         buf.Append(entry->value);
00210         buf.AppendLiteral("\r\n");
00211     }
00212 }
00213 
00214 const char *
00215 nsHttpHeaderArray::PeekHeaderAt(PRUint32 index, nsHttpAtom &header)
00216 {
00217     nsEntry *entry = (nsEntry *) mHeaders[index]; 
00218     if (!entry)
00219         return nsnull;
00220 
00221     header = entry->header;
00222     return entry->value.get();
00223 }
00224 
00225 void
00226 nsHttpHeaderArray::Clear()
00227 {
00228     PRInt32 i, count = mHeaders.Count();
00229     for (i=0; i<count; ++i)
00230         delete (nsEntry *) mHeaders[i];
00231     mHeaders.Clear();
00232 }
00233 
00234 //-----------------------------------------------------------------------------
00235 // nsHttpHeaderArray <private>
00236 //-----------------------------------------------------------------------------
00237 
00238 PRInt32
00239 nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
00240 {
00241     PRInt32 i, count = mHeaders.Count();
00242     for (i=0; i<count; ++i) {
00243         *entry = (nsEntry *) mHeaders[i];
00244         if ((*entry)->header == header)
00245             return i;
00246     }
00247     *entry = nsnull;
00248     return -1;
00249 }
00250 
00251 PRBool
00252 nsHttpHeaderArray::CanAppendToHeader(nsHttpAtom header)
00253 {
00254     return header != nsHttp::Content_Type        &&
00255            header != nsHttp::Content_Length      &&
00256            header != nsHttp::User_Agent          &&
00257            header != nsHttp::Referer             &&
00258            header != nsHttp::Host                &&
00259            header != nsHttp::Authorization       &&
00260            header != nsHttp::Proxy_Authorization &&
00261            header != nsHttp::If_Modified_Since   &&
00262            header != nsHttp::If_Unmodified_Since &&
00263            header != nsHttp::From                &&
00264            header != nsHttp::Location            &&
00265            header != nsHttp::Max_Forwards;
00266 }