LCOV - code coverage report
Current view: top level - netwerk/protocol/http - nsHttpHeaderArray.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 127 241 52.7 %
Date: 2017-07-14 16:53:18 Functions: 13 18 72.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : // HttpLog.h should generally be included first
       8             : #include "HttpLog.h"
       9             : 
      10             : #include "nsHttpHeaderArray.h"
      11             : #include "nsURLHelper.h"
      12             : #include "nsIHttpHeaderVisitor.h"
      13             : #include "nsHttpHandler.h"
      14             : 
      15             : namespace mozilla {
      16             : namespace net {
      17             : 
      18             : //-----------------------------------------------------------------------------
      19             : // nsHttpHeaderArray <public>
      20             : //-----------------------------------------------------------------------------
      21             : 
      22             : nsresult
      23          25 : nsHttpHeaderArray::SetHeader(const nsACString &headerName,
      24             :                              const nsACString &value,
      25             :                              bool merge,
      26             :                              nsHttpHeaderArray::HeaderVariety variety)
      27             : {
      28          25 :     nsHttpAtom header = nsHttp::ResolveAtom(PromiseFlatCString(headerName).get());
      29          25 :     if (!header) {
      30           0 :         NS_WARNING("failed to resolve atom");
      31           0 :         return NS_ERROR_NOT_AVAILABLE;
      32             :     }
      33          25 :     return SetHeader(header, headerName, value, merge, variety);
      34             : }
      35             : 
      36             : nsresult
      37          60 : nsHttpHeaderArray::SetHeader(nsHttpAtom header,
      38             :                              const nsACString &value,
      39             :                              bool merge,
      40             :                              nsHttpHeaderArray::HeaderVariety variety)
      41             : {
      42          60 :     return SetHeader(header, EmptyCString(), value, merge, variety);
      43             : }
      44             : 
      45             : nsresult
      46          87 : nsHttpHeaderArray::SetHeader(nsHttpAtom header,
      47             :                              const nsACString &headerName,
      48             :                              const nsACString &value,
      49             :                              bool merge,
      50             :                              nsHttpHeaderArray::HeaderVariety variety)
      51             : {
      52          87 :     MOZ_ASSERT((variety == eVarietyResponse) ||
      53             :                (variety == eVarietyRequestDefault) ||
      54             :                (variety == eVarietyRequestOverride),
      55             :                "Net original headers can only be set using SetHeader_internal().");
      56             : 
      57          87 :     nsEntry *entry = nullptr;
      58             :     int32_t index;
      59             : 
      60          87 :     index = LookupEntry(header, &entry);
      61             : 
      62             :     // If an empty value is passed in, then delete the header entry...
      63             :     // unless we are merging, in which case this function becomes a NOP.
      64          87 :     if (value.IsEmpty()) {
      65          12 :         if (!merge && entry) {
      66           0 :             if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
      67           0 :                 MOZ_ASSERT(variety == eVarietyResponse);
      68           0 :                 entry->variety = eVarietyResponseNetOriginal;
      69             :             } else {
      70           0 :                 mHeaders.RemoveElementAt(index);
      71             :             }
      72             :         }
      73          12 :         return NS_OK;
      74             :     }
      75             : 
      76          75 :     MOZ_ASSERT(!entry || variety != eVarietyRequestDefault,
      77             :                "Cannot set default entry which overrides existing entry!");
      78          75 :     if (!entry) {
      79          67 :         return SetHeader_internal(header, headerName, value, variety);
      80           8 :     } else if (merge && !IsSingletonHeader(header)) {
      81           0 :         return MergeHeader(header, entry, value, variety);
      82           8 :     } else if (!IsIgnoreMultipleHeader(header)) {
      83             :         // Replace the existing string with the new value
      84           8 :         if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
      85           0 :             MOZ_ASSERT(variety == eVarietyResponse);
      86           0 :             entry->variety = eVarietyResponseNetOriginal;
      87           0 :             return SetHeader_internal(header, headerName, value, variety);
      88             :         } else {
      89           8 :             entry->value = value;
      90           8 :             entry->variety = variety;
      91             :         }
      92             :     }
      93             : 
      94           8 :     return NS_OK;
      95             : }
      96             : 
      97             : nsresult
      98         104 : nsHttpHeaderArray::SetHeader_internal(nsHttpAtom header,
      99             :                                       const nsACString &headerName,
     100             :                                       const nsACString &value,
     101             :                                       nsHttpHeaderArray::HeaderVariety variety)
     102             : {
     103         104 :     nsEntry *entry =  mHeaders.AppendElement();
     104         104 :     if (!entry) {
     105           0 :         return NS_ERROR_OUT_OF_MEMORY;
     106             :     }
     107         104 :     entry->header = header;
     108             :     // Only save original form of a header if it is different than the header
     109             :     // atom string.
     110         104 :     if (!headerName.Equals(header.get())) {
     111          64 :         entry->headerNameOriginal = headerName;
     112             :     }
     113         104 :     entry->value = value;
     114         104 :     entry->variety = variety;
     115         104 :     return NS_OK;
     116             : }
     117             : 
     118             : nsresult
     119           0 : nsHttpHeaderArray::SetEmptyHeader(const nsACString &headerName,
     120             :                                   HeaderVariety variety)
     121             : {
     122           0 :     nsHttpAtom header = nsHttp::ResolveAtom(PromiseFlatCString(headerName).get());
     123           0 :     if (!header) {
     124           0 :         NS_WARNING("failed to resolve atom");
     125           0 :         return NS_ERROR_NOT_AVAILABLE;
     126             :     }
     127             : 
     128           0 :     MOZ_ASSERT((variety == eVarietyResponse) ||
     129             :                (variety == eVarietyRequestDefault) ||
     130             :                (variety == eVarietyRequestOverride),
     131             :                "Original headers can only be set using SetHeader_internal().");
     132           0 :     nsEntry *entry = nullptr;
     133             : 
     134           0 :     LookupEntry(header, &entry);
     135             : 
     136           0 :     if (entry &&
     137           0 :         entry->variety != eVarietyResponseNetOriginalAndResponse) {
     138           0 :         entry->value.Truncate();
     139           0 :         return NS_OK;
     140           0 :     } else if (entry) {
     141           0 :         MOZ_ASSERT(variety == eVarietyResponse);
     142           0 :         entry->variety = eVarietyResponseNetOriginal;
     143             :     }
     144             : 
     145           0 :     return SetHeader_internal(header, headerName, EmptyCString(), variety);
     146             : }
     147             : 
     148             : nsresult
     149          15 : nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
     150             :                                     const nsACString &headerNameOriginal,
     151             :                                     const nsACString &value,
     152             :                                     bool response)
     153             : {
     154             :     // mHeader holds the consolidated (merged or updated) headers.
     155             :     // mHeader for response header will keep the original heades as well.
     156          15 :     nsEntry *entry = nullptr;
     157             : 
     158          15 :     LookupEntry(header, &entry);
     159             : 
     160          15 :     if (!entry) {
     161          15 :         if (value.IsEmpty()) {
     162           0 :             if (!gHttpHandler->KeepEmptyResponseHeadersAsEmtpyString() &&
     163           0 :                 !TrackEmptyHeader(header)) {
     164           0 :                 LOG(("Ignoring Empty Header: %s\n", header.get()));
     165           0 :                 if (response) {
     166             :                     // Set header as original but not as response header.
     167             :                     return SetHeader_internal(header, headerNameOriginal, value,
     168           0 :                                               eVarietyResponseNetOriginal);
     169             :                 }
     170           0 :                 return NS_OK; // ignore empty headers by default
     171             :             }
     172             :         }
     173          15 :         HeaderVariety variety = eVarietyRequestOverride;
     174          15 :         if (response) {
     175          15 :             variety = eVarietyResponseNetOriginalAndResponse;
     176             :         }
     177          15 :         return SetHeader_internal(header, headerNameOriginal, value, variety);
     178             : 
     179           0 :     } else if (!IsSingletonHeader(header)) {
     180           0 :         HeaderVariety variety = eVarietyRequestOverride;
     181           0 :         if (response) {
     182           0 :             variety = eVarietyResponse;
     183             :         }
     184           0 :         nsresult rv = MergeHeader(header, entry, value, variety);
     185           0 :         if (NS_FAILED(rv)) {
     186           0 :             return rv;
     187             :         }
     188           0 :         if (response) {
     189             :             rv = SetHeader_internal(header, headerNameOriginal, value,
     190           0 :                                     eVarietyResponseNetOriginal);
     191             :         }
     192           0 :         return rv;
     193           0 :     } else if (!IsIgnoreMultipleHeader(header)) {
     194             :         // Multiple instances of non-mergeable header received from network
     195             :         // - ignore if same value
     196           0 :         if (!entry->value.Equals(value)) {
     197           0 :             if (IsSuspectDuplicateHeader(header)) {
     198             :                 // reply may be corrupt/hacked (ex: CLRF injection attacks)
     199           0 :                 return NS_ERROR_CORRUPTED_CONTENT;
     200             :             } // else silently drop value: keep value from 1st header seen
     201           0 :             LOG(("Header %s silently dropped as non mergeable header\n",
     202             :                  header.get()));
     203             : 
     204             :         }
     205           0 :         if (response) {
     206             :             return SetHeader_internal(header, headerNameOriginal, value,
     207           0 :                                       eVarietyResponseNetOriginal);
     208             :         }
     209             :     }
     210             : 
     211           0 :     return NS_OK;
     212             : }
     213             : 
     214             : nsresult
     215          38 : nsHttpHeaderArray::SetResponseHeaderFromCache(nsHttpAtom header,
     216             :                                               const nsACString &headerNameOriginal,
     217             :                                               const nsACString &value,
     218             :                                               nsHttpHeaderArray::HeaderVariety variety)
     219             : {
     220          38 :     MOZ_ASSERT((variety == eVarietyResponse) ||
     221             :                (variety == eVarietyResponseNetOriginal),
     222             :                "Headers from cache can only be eVarietyResponse and "
     223             :                "eVarietyResponseNetOriginal");
     224             : 
     225          38 :     if (variety == eVarietyResponseNetOriginal) {
     226             :         return SetHeader_internal(header, headerNameOriginal, value,
     227          18 :                                   eVarietyResponseNetOriginal);
     228             :     } else {
     229          20 :         nsTArray<nsEntry>::index_type index = 0;
     230           0 :         do {
     231          20 :             index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
     232          20 :             if (index != mHeaders.NoIndex) {
     233          16 :                 nsEntry &entry = mHeaders[index];
     234          16 :                 if (value.Equals(entry.value)) {
     235          16 :                     MOZ_ASSERT((entry.variety == eVarietyResponseNetOriginal) ||
     236             :                                (entry.variety == eVarietyResponseNetOriginalAndResponse),
     237             :                                "This array must contain only eVarietyResponseNetOriginal"
     238             :                                " and eVarietyResponseNetOriginalAndRespons headers!");
     239          16 :                     entry.variety = eVarietyResponseNetOriginalAndResponse;
     240          16 :                     return NS_OK;
     241             :                 }
     242           0 :                 index++;
     243             :             }
     244           4 :         } while (index != mHeaders.NoIndex);
     245             :         // If we are here, we have not found an entry so add a new one.
     246             :         return SetHeader_internal(header, headerNameOriginal, value,
     247           4 :                                   eVarietyResponse);
     248             :     }
     249             : }
     250             : 
     251             : void
     252           3 : nsHttpHeaderArray::ClearHeader(nsHttpAtom header)
     253             : {
     254           3 :     nsEntry *entry = nullptr;
     255           3 :     int32_t index = LookupEntry(header, &entry);
     256           3 :     if (entry) {
     257           0 :         if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
     258           0 :             entry->variety = eVarietyResponseNetOriginal;
     259             :         } else {
     260           0 :             mHeaders.RemoveElementAt(index);
     261             :         }
     262             :     }
     263           3 : }
     264             : 
     265             : const char *
     266          56 : nsHttpHeaderArray::PeekHeader(nsHttpAtom header) const
     267             : {
     268          56 :     const nsEntry *entry = nullptr;
     269          56 :     LookupEntry(header, &entry);
     270          56 :     return entry ? entry->value.get() : nullptr;
     271             : }
     272             : 
     273             : nsresult
     274         101 : nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result) const
     275             : {
     276         101 :     const nsEntry *entry = nullptr;
     277         101 :     LookupEntry(header, &entry);
     278         101 :     if (!entry)
     279          92 :         return NS_ERROR_NOT_AVAILABLE;
     280           9 :     result = entry->value;
     281           9 :     return NS_OK;
     282             : }
     283             : 
     284             : nsresult
     285           0 : nsHttpHeaderArray::GetOriginalHeader(nsHttpAtom aHeader,
     286             :                                      nsIHttpHeaderVisitor *aVisitor)
     287             : {
     288           0 :     NS_ENSURE_ARG_POINTER(aVisitor);
     289           0 :     uint32_t index = 0;
     290           0 :     nsresult rv = NS_ERROR_NOT_AVAILABLE;
     291             :     while (true) {
     292           0 :         index = mHeaders.IndexOf(aHeader, index, nsEntry::MatchHeader());
     293           0 :         if (index != UINT32_MAX) {
     294           0 :             const nsEntry &entry = mHeaders[index];
     295             : 
     296           0 :             MOZ_ASSERT((entry.variety == eVarietyResponseNetOriginalAndResponse) ||
     297             :                        (entry.variety == eVarietyResponseNetOriginal) ||
     298             :                        (entry.variety == eVarietyResponse),
     299             :                        "This must be a response header.");
     300           0 :             index++;
     301           0 :             if (entry.variety == eVarietyResponse) {
     302           0 :                 continue;
     303             :             }
     304             : 
     305           0 :             nsAutoCString hdr;
     306           0 :             if (entry.headerNameOriginal.IsEmpty()) {
     307           0 :                 hdr = nsDependentCString(entry.header);
     308             :             } else {
     309           0 :                 hdr = entry.headerNameOriginal;
     310             :             }
     311             : 
     312           0 :             rv = NS_OK;
     313           0 :             if (NS_FAILED(aVisitor->VisitHeader(hdr,
     314             :                                                 entry.value))) {
     315           0 :                 break;
     316             :             }
     317             :         } else {
     318             :             // if there is no such a header, it will return
     319             :             // NS_ERROR_NOT_AVAILABLE or NS_OK otherwise.
     320           0 :             return rv;
     321             :         }
     322           0 :     }
     323           0 :     return NS_OK;
     324             : }
     325             : 
     326             : bool
     327          39 : nsHttpHeaderArray::HasHeader(nsHttpAtom header) const
     328             : {
     329          39 :     const nsEntry *entry = nullptr;
     330          39 :     LookupEntry(header, &entry);
     331          39 :     return entry;
     332             : }
     333             : 
     334             : nsresult
     335           0 : nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor, nsHttpHeaderArray::VisitorFilter filter)
     336             : {
     337           0 :     NS_ENSURE_ARG_POINTER(visitor);
     338             :     nsresult rv;
     339             : 
     340           0 :     uint32_t i, count = mHeaders.Length();
     341           0 :     for (i = 0; i < count; ++i) {
     342           0 :         const nsEntry &entry = mHeaders[i];
     343           0 :         if (filter == eFilterSkipDefault && entry.variety == eVarietyRequestDefault) {
     344           0 :             continue;
     345           0 :         } else if (filter == eFilterResponse && entry.variety == eVarietyResponseNetOriginal) {
     346           0 :             continue;
     347           0 :         } else if (filter == eFilterResponseOriginal && entry.variety == eVarietyResponse) {
     348           0 :             continue;
     349             :         }
     350             : 
     351           0 :         nsAutoCString hdr;
     352           0 :         if (entry.headerNameOriginal.IsEmpty()) {
     353           0 :             hdr = nsDependentCString(entry.header);
     354             :         } else {
     355           0 :             hdr = entry.headerNameOriginal;
     356             :         }
     357           0 :         rv = visitor->VisitHeader(hdr, entry.value);
     358           0 :         if NS_FAILED(rv) {
     359           0 :             return rv;
     360             :         }
     361             :     }
     362           0 :     return NS_OK;
     363             : }
     364             : 
     365             : /*static*/ nsresult
     366          53 : nsHttpHeaderArray::ParseHeaderLine(const nsACString& line,
     367             :                                    nsHttpAtom *hdr,
     368             :                                    nsACString *headerName,
     369             :                                    nsACString *val)
     370             : {
     371             :     //
     372             :     // BNF from section 4.2 of RFC 2616:
     373             :     //
     374             :     //   message-header = field-name ":" [ field-value ]
     375             :     //   field-name     = token
     376             :     //   field-value    = *( field-content | LWS )
     377             :     //   field-content  = <the OCTETs making up the field-value
     378             :     //                     and consisting of either *TEXT or combinations
     379             :     //                     of token, separators, and quoted-string>
     380             :     //
     381             : 
     382             :     // We skip over mal-formed headers in the hope that we'll still be able to
     383             :     // do something useful with the response.
     384          53 :     int32_t split = line.FindChar(':');
     385             : 
     386          53 :     if (split == kNotFound) {
     387           0 :         LOG(("malformed header [%s]: no colon\n",
     388             :             PromiseFlatCString(line).get()));
     389           0 :         return NS_ERROR_FAILURE;
     390             :     }
     391             : 
     392         106 :     const nsACString& sub = Substring(line, 0, split);
     393             :     const nsACString& sub2 = Substring(
     394         106 :         line, split + 1, line.Length() - split - 1);
     395             : 
     396             :     // make sure we have a valid token for the field-name
     397          53 :     if (!nsHttp::IsValidToken(sub)) {
     398           0 :         LOG(("malformed header [%s]: field-name not a token\n",
     399             :             PromiseFlatCString(line).get()));
     400           0 :         return NS_ERROR_FAILURE;
     401             :     }
     402             : 
     403          53 :     nsHttpAtom atom = nsHttp::ResolveAtom(sub);
     404          53 :     if (!atom) {
     405           0 :         LOG(("failed to resolve atom [%s]\n", PromiseFlatCString(line).get()));
     406           0 :         return NS_ERROR_FAILURE;
     407             :     }
     408             : 
     409             :     // skip over whitespace
     410          53 :     char *p = net_FindCharNotInSet(
     411          53 :         sub2.BeginReading(), sub2.EndReading(), HTTP_LWS);
     412             : 
     413             :     // trim trailing whitespace - bug 86608
     414          53 :     char *p2 = net_RFindCharNotInSet(p, sub2.EndReading(), HTTP_LWS);
     415             : 
     416             :     // assign return values
     417          53 :     if (hdr) *hdr = atom;
     418          53 :     if (val) val->Assign(p, p2 - p + 1);
     419          53 :     if (headerName) headerName->Assign(sub);
     420             : 
     421          53 :     return NS_OK;
     422             : }
     423             : 
     424             : void
     425           5 : nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders,
     426             :                            bool pruneTransients)
     427             : {
     428           5 :     uint32_t i, count = mHeaders.Length();
     429          39 :     for (i = 0; i < count; ++i) {
     430          34 :         const nsEntry &entry = mHeaders[i];
     431             :         // Skip original header.
     432          34 :         if (entry.variety == eVarietyResponseNetOriginal) {
     433           0 :             continue;
     434             :         }
     435             :         // prune proxy headers if requested
     436          34 :         if (pruneProxyHeaders &&
     437           0 :             ((entry.header == nsHttp::Proxy_Authorization) ||
     438           0 :              (entry.header == nsHttp::Proxy_Connection))) {
     439           0 :             continue;
     440             :         }
     441          46 :         if (pruneTransients &&
     442          22 :             (entry.value.IsEmpty() ||
     443          21 :              entry.header == nsHttp::Connection ||
     444          20 :              entry.header == nsHttp::Proxy_Connection ||
     445          20 :              entry.header == nsHttp::Keep_Alive ||
     446          20 :              entry.header == nsHttp::WWW_Authenticate ||
     447          20 :              entry.header == nsHttp::Proxy_Authenticate ||
     448          20 :              entry.header == nsHttp::Trailer ||
     449          20 :              entry.header == nsHttp::Transfer_Encoding ||
     450          20 :              entry.header == nsHttp::Upgrade ||
     451             :              // XXX this will cause problems when we start honoring
     452             :              // Cache-Control: no-cache="set-cookie", what to do?
     453          10 :              entry.header == nsHttp::Set_Cookie)) {
     454           1 :             continue;
     455             :         }
     456             : 
     457          33 :         if (entry.headerNameOriginal.IsEmpty()) {
     458          32 :             buf.Append(entry.header);
     459             :         } else {
     460           1 :             buf.Append(entry.headerNameOriginal);
     461             :         }
     462          33 :         buf.AppendLiteral(": ");
     463          33 :         buf.Append(entry.value);
     464          33 :         buf.AppendLiteral("\r\n");
     465             :     }
     466           5 : }
     467             : 
     468             : void
     469           2 : nsHttpHeaderArray::FlattenOriginalHeader(nsACString &buf)
     470             : {
     471           2 :     uint32_t i, count = mHeaders.Length();
     472          13 :     for (i = 0; i < count; ++i) {
     473          11 :         const nsEntry &entry = mHeaders[i];
     474             :         // Skip changed header.
     475          11 :         if (entry.variety == eVarietyResponse) {
     476           2 :             continue;
     477             :         }
     478             : 
     479           9 :         if (entry.headerNameOriginal.IsEmpty()) {
     480           8 :             buf.Append(entry.header);
     481             :         } else {
     482           1 :             buf.Append(entry.headerNameOriginal);
     483             :         }
     484             : 
     485           9 :         buf.AppendLiteral(": ");
     486           9 :         buf.Append(entry.value);
     487           9 :         buf.AppendLiteral("\r\n");
     488             :     }
     489           2 : }
     490             : 
     491             : const char *
     492           0 : nsHttpHeaderArray::PeekHeaderAt(uint32_t index, nsHttpAtom &header,
     493             :                                 nsACString &headerNameOriginal) const
     494             : {
     495           0 :     const nsEntry &entry = mHeaders[index];
     496             : 
     497           0 :     header = entry.header;
     498           0 :     headerNameOriginal = entry.headerNameOriginal;
     499           0 :     return entry.value.get();
     500             : }
     501             : 
     502             : void
     503           0 : nsHttpHeaderArray::Clear()
     504             : {
     505           0 :     mHeaders.Clear();
     506           0 : }
     507             : 
     508             : } // namespace net
     509             : } // namespace mozilla

Generated by: LCOV version 1.13