LCOV - code coverage report
Current view: directory - content/base/src - nsCrossSiteListenerProxy.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 490 16 3.3 %
Date: 2012-04-21 Functions: 44 3 6.8 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Mozilla Foundation.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Jonas Sicking <jonas@sicking.cc> (Original Author)
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsCrossSiteListenerProxy.h"
      39                 : #include "nsIChannel.h"
      40                 : #include "nsIHttpChannel.h"
      41                 : #include "nsDOMError.h"
      42                 : #include "nsContentUtils.h"
      43                 : #include "nsIScriptSecurityManager.h"
      44                 : #include "nsNetUtil.h"
      45                 : #include "nsIParser.h"
      46                 : #include "nsParserCIID.h"
      47                 : #include "nsMimeTypes.h"
      48                 : #include "nsIStreamConverterService.h"
      49                 : #include "nsStringStream.h"
      50                 : #include "nsGkAtoms.h"
      51                 : #include "nsWhitespaceTokenizer.h"
      52                 : #include "nsIChannelEventSink.h"
      53                 : #include "nsIAsyncVerifyRedirectCallback.h"
      54                 : #include "nsCharSeparatedTokenizer.h"
      55                 : #include "nsAsyncRedirectVerifyHelper.h"
      56                 : #include "prclist.h"
      57                 : #include "prtime.h"
      58                 : #include "nsClassHashtable.h"
      59                 : #include "nsHashKeys.h"
      60                 : #include "nsStreamUtils.h"
      61                 : #include "mozilla/Preferences.h"
      62                 : 
      63                 : using namespace mozilla;
      64                 : 
      65                 : #define PREFLIGHT_CACHE_SIZE 100
      66                 : 
      67                 : static bool gDisableCORS = false;
      68                 : static bool gDisableCORSPrivateData = false;
      69                 : 
      70                 : //////////////////////////////////////////////////////////////////////////
      71                 : // Preflight cache
      72                 : 
      73                 : class nsPreflightCache
      74                 : {
      75                 : public:
      76                 :   struct TokenTime
      77               0 :   {
      78                 :     nsCString token;
      79                 :     PRTime expirationTime;
      80                 :   };
      81                 : 
      82                 :   struct CacheEntry : public PRCList
      83                 :   {
      84               0 :     CacheEntry(nsCString& aKey)
      85               0 :       : mKey(aKey)
      86                 :     {
      87               0 :       MOZ_COUNT_CTOR(nsPreflightCache::CacheEntry);
      88               0 :     }
      89                 :     
      90               0 :     ~CacheEntry()
      91               0 :     {
      92               0 :       MOZ_COUNT_DTOR(nsPreflightCache::CacheEntry);
      93               0 :     }
      94                 : 
      95                 :     void PurgeExpired(PRTime now);
      96                 :     bool CheckRequest(const nsCString& aMethod,
      97                 :                         const nsTArray<nsCString>& aCustomHeaders);
      98                 : 
      99                 :     nsCString mKey;
     100                 :     nsTArray<TokenTime> mMethods;
     101                 :     nsTArray<TokenTime> mHeaders;
     102                 :   };
     103                 : 
     104               0 :   nsPreflightCache()
     105               0 :   {
     106               0 :     MOZ_COUNT_CTOR(nsPreflightCache);
     107               0 :     PR_INIT_CLIST(&mList);
     108               0 :   }
     109                 : 
     110               0 :   ~nsPreflightCache()
     111               0 :   {
     112               0 :     Clear();
     113               0 :     MOZ_COUNT_DTOR(nsPreflightCache);
     114               0 :   }
     115                 : 
     116               0 :   bool Initialize()
     117                 :   {
     118               0 :     return mTable.Init();
     119                 :   }
     120                 : 
     121                 :   CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
     122                 :                        bool aWithCredentials, bool aCreate);
     123                 :   void RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal);
     124                 : 
     125                 :   void Clear();
     126                 : 
     127                 : private:
     128                 :   static PLDHashOperator
     129                 :     RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
     130                 :                          void* aUserData);
     131                 : 
     132                 :   static bool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
     133                 :                             bool aWithCredentials, nsACString& _retval);
     134                 : 
     135                 :   nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
     136                 :   PRCList mList;
     137                 : };
     138                 : 
     139                 : // Will be initialized in EnsurePreflightCache.
     140                 : static nsPreflightCache* sPreflightCache = nsnull;
     141                 : 
     142               0 : static bool EnsurePreflightCache()
     143                 : {
     144               0 :   if (sPreflightCache)
     145               0 :     return true;
     146                 : 
     147               0 :   nsAutoPtr<nsPreflightCache> newCache(new nsPreflightCache());
     148                 : 
     149               0 :   if (newCache->Initialize()) {
     150               0 :     sPreflightCache = newCache.forget();
     151               0 :     return true;
     152                 :   }
     153                 : 
     154               0 :   return false;
     155                 : }
     156                 : 
     157                 : void
     158               0 : nsPreflightCache::CacheEntry::PurgeExpired(PRTime now)
     159                 : {
     160                 :   PRUint32 i;
     161               0 :   for (i = 0; i < mMethods.Length(); ++i) {
     162               0 :     if (now >= mMethods[i].expirationTime) {
     163               0 :       mMethods.RemoveElementAt(i--);
     164                 :     }
     165                 :   }
     166               0 :   for (i = 0; i < mHeaders.Length(); ++i) {
     167               0 :     if (now >= mHeaders[i].expirationTime) {
     168               0 :       mHeaders.RemoveElementAt(i--);
     169                 :     }
     170                 :   }
     171               0 : }
     172                 : 
     173                 : bool
     174               0 : nsPreflightCache::CacheEntry::CheckRequest(const nsCString& aMethod,
     175                 :                                            const nsTArray<nsCString>& aHeaders)
     176                 : {
     177               0 :   PurgeExpired(PR_Now());
     178                 : 
     179               0 :   if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
     180                 :     PRUint32 i;
     181               0 :     for (i = 0; i < mMethods.Length(); ++i) {
     182               0 :       if (aMethod.Equals(mMethods[i].token))
     183               0 :         break;
     184                 :     }
     185               0 :     if (i == mMethods.Length()) {
     186               0 :       return false;
     187                 :     }
     188                 :   }
     189                 : 
     190               0 :   for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
     191                 :     PRUint32 j;
     192               0 :     for (j = 0; j < mHeaders.Length(); ++j) {
     193               0 :       if (aHeaders[i].Equals(mHeaders[j].token,
     194               0 :                              nsCaseInsensitiveCStringComparator())) {
     195               0 :         break;
     196                 :       }
     197                 :     }
     198               0 :     if (j == mHeaders.Length()) {
     199               0 :       return false;
     200                 :     }
     201                 :   }
     202                 : 
     203               0 :   return true;
     204                 : }
     205                 : 
     206                 : nsPreflightCache::CacheEntry*
     207               0 : nsPreflightCache::GetEntry(nsIURI* aURI,
     208                 :                            nsIPrincipal* aPrincipal,
     209                 :                            bool aWithCredentials,
     210                 :                            bool aCreate)
     211                 : {
     212               0 :   nsCString key;
     213               0 :   if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
     214               0 :     NS_WARNING("Invalid cache key!");
     215               0 :     return nsnull;
     216                 :   }
     217                 : 
     218                 :   CacheEntry* entry;
     219                 : 
     220               0 :   if (mTable.Get(key, &entry)) {
     221                 :     // Entry already existed so just return it. Also update the LRU list.
     222                 : 
     223                 :     // Move to the head of the list.
     224               0 :     PR_REMOVE_LINK(entry);
     225               0 :     PR_INSERT_LINK(entry, &mList);
     226                 : 
     227               0 :     return entry;
     228                 :   }
     229                 : 
     230               0 :   if (!aCreate) {
     231               0 :     return nsnull;
     232                 :   }
     233                 : 
     234                 :   // This is a new entry, allocate and insert into the table now so that any
     235                 :   // failures don't cause items to be removed from a full cache.
     236               0 :   entry = new CacheEntry(key);
     237               0 :   if (!entry) {
     238               0 :     NS_WARNING("Failed to allocate new cache entry!");
     239               0 :     return nsnull;
     240                 :   }
     241                 : 
     242               0 :   NS_ASSERTION(mTable.Count() <= PREFLIGHT_CACHE_SIZE,
     243                 :                "Something is borked, too many entries in the cache!");
     244                 : 
     245                 :   // Now enforce the max count.
     246               0 :   if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
     247                 :     // Try to kick out all the expired entries.
     248               0 :     PRTime now = PR_Now();
     249               0 :     mTable.Enumerate(RemoveExpiredEntries, &now);
     250                 : 
     251                 :     // If that didn't remove anything then kick out the least recently used
     252                 :     // entry.
     253               0 :     if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
     254               0 :       CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
     255               0 :       PR_REMOVE_LINK(lruEntry);
     256                 : 
     257                 :       // This will delete 'lruEntry'.
     258               0 :       mTable.Remove(lruEntry->mKey);
     259                 : 
     260               0 :       NS_ASSERTION(mTable.Count() == PREFLIGHT_CACHE_SIZE - 1,
     261                 :                    "Somehow tried to remove an entry that was never added!");
     262                 :     }
     263                 :   }
     264                 :   
     265               0 :   if (!mTable.Put(key, entry)) {
     266                 :     // Failed, clean up the new entry.
     267               0 :     delete entry;
     268                 : 
     269               0 :     NS_WARNING("Failed to add entry to the CORS preflight cache!");
     270               0 :     return nsnull;
     271                 :   }
     272                 : 
     273               0 :   PR_INSERT_LINK(entry, &mList);
     274                 : 
     275               0 :   return entry;
     276                 : }
     277                 : 
     278                 : void
     279               0 : nsPreflightCache::RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal)
     280                 : {
     281                 :   CacheEntry* entry;
     282               0 :   nsCString key;
     283               0 :   if (GetCacheKey(aURI, aPrincipal, true, key) &&
     284               0 :       mTable.Get(key, &entry)) {
     285               0 :     PR_REMOVE_LINK(entry);
     286               0 :     mTable.Remove(key);
     287                 :   }
     288                 : 
     289               0 :   if (GetCacheKey(aURI, aPrincipal, false, key) &&
     290               0 :       mTable.Get(key, &entry)) {
     291               0 :     PR_REMOVE_LINK(entry);
     292               0 :     mTable.Remove(key);
     293                 :   }
     294               0 : }
     295                 : 
     296                 : void
     297               0 : nsPreflightCache::Clear()
     298                 : {
     299               0 :   PR_INIT_CLIST(&mList);
     300               0 :   mTable.Clear();
     301               0 : }
     302                 : 
     303                 : /* static */ PLDHashOperator
     304               0 : nsPreflightCache::RemoveExpiredEntries(const nsACString& aKey,
     305                 :                                            nsAutoPtr<CacheEntry>& aValue,
     306                 :                                            void* aUserData)
     307                 : {
     308               0 :   PRTime* now = static_cast<PRTime*>(aUserData);
     309                 :   
     310               0 :   aValue->PurgeExpired(*now);
     311                 :   
     312               0 :   if (aValue->mHeaders.IsEmpty() &&
     313               0 :       aValue->mMethods.IsEmpty()) {
     314                 :     // Expired, remove from the list as well as the hash table.
     315               0 :     PR_REMOVE_LINK(aValue);
     316               0 :     return PL_DHASH_REMOVE;
     317                 :   }
     318                 :   
     319               0 :   return PL_DHASH_NEXT;
     320                 : }
     321                 : 
     322                 : /* static */ bool
     323               0 : nsPreflightCache::GetCacheKey(nsIURI* aURI,
     324                 :                               nsIPrincipal* aPrincipal,
     325                 :                               bool aWithCredentials,
     326                 :                               nsACString& _retval)
     327                 : {
     328               0 :   NS_ASSERTION(aURI, "Null uri!");
     329               0 :   NS_ASSERTION(aPrincipal, "Null principal!");
     330                 :   
     331               0 :   NS_NAMED_LITERAL_CSTRING(space, " ");
     332                 : 
     333               0 :   nsCOMPtr<nsIURI> uri;
     334               0 :   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
     335               0 :   NS_ENSURE_SUCCESS(rv, false);
     336                 :   
     337               0 :   nsCAutoString scheme, host, port;
     338               0 :   if (uri) {
     339               0 :     uri->GetScheme(scheme);
     340               0 :     uri->GetHost(host);
     341               0 :     port.AppendInt(NS_GetRealPort(uri));
     342                 :   }
     343                 : 
     344               0 :   nsCAutoString cred;
     345               0 :   if (aWithCredentials) {
     346               0 :     _retval.AssignLiteral("cred");
     347                 :   }
     348                 :   else {
     349               0 :     _retval.AssignLiteral("nocred");
     350                 :   }
     351                 : 
     352               0 :   nsCAutoString spec;
     353               0 :   rv = aURI->GetSpec(spec);
     354               0 :   NS_ENSURE_SUCCESS(rv, false);
     355                 : 
     356               0 :   _retval.Assign(cred + space + scheme + space + host + space + port + space +
     357               0 :                  spec);
     358                 : 
     359               0 :   return true;
     360                 : }
     361                 : 
     362                 : //////////////////////////////////////////////////////////////////////////
     363                 : // nsCORSListenerProxy
     364                 : 
     365               0 : NS_IMPL_ISUPPORTS5(nsCORSListenerProxy, nsIStreamListener,
     366                 :                    nsIRequestObserver, nsIChannelEventSink,
     367                 :                    nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback)
     368                 : 
     369                 : /* static */
     370                 : void
     371            1365 : nsCORSListenerProxy::Startup()
     372                 : {
     373                 :   Preferences::AddBoolVarCache(&gDisableCORS,
     374            1365 :                                "content.cors.disable");
     375                 :   Preferences::AddBoolVarCache(&gDisableCORSPrivateData,
     376            1365 :                                "content.cors.no_private_data");
     377            1365 : }
     378                 : 
     379                 : /* static */
     380                 : void
     381            1364 : nsCORSListenerProxy::Shutdown()
     382                 : {
     383            1364 :   delete sPreflightCache;
     384            1364 :   sPreflightCache = nsnull;
     385            1364 : }
     386                 : 
     387               0 : nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
     388                 :                                          nsIPrincipal* aRequestingPrincipal,
     389                 :                                          nsIChannel* aChannel,
     390                 :                                          bool aWithCredentials,
     391                 :                                          nsresult* aResult)
     392                 :   : mOuterListener(aOuter),
     393                 :     mRequestingPrincipal(aRequestingPrincipal),
     394               0 :     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     395                 :     mRequestApproved(false),
     396                 :     mHasBeenCrossSite(false),
     397               0 :     mIsPreflight(false)
     398                 : {
     399               0 :   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
     400               0 :   aChannel->SetNotificationCallbacks(this);
     401                 : 
     402               0 :   *aResult = UpdateChannel(aChannel);
     403               0 :   if (NS_FAILED(*aResult)) {
     404               0 :     mOuterListener = nsnull;
     405               0 :     mRequestingPrincipal = nsnull;
     406               0 :     mOuterNotificationCallbacks = nsnull;
     407                 :   }
     408               0 : }
     409                 : 
     410               0 : nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
     411                 :                                          nsIPrincipal* aRequestingPrincipal,
     412                 :                                          nsIChannel* aChannel,
     413                 :                                          bool aWithCredentials,
     414                 :                                          bool aAllowDataURI,
     415                 :                                          nsresult* aResult)
     416                 :   : mOuterListener(aOuter),
     417                 :     mRequestingPrincipal(aRequestingPrincipal),
     418               0 :     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     419                 :     mRequestApproved(false),
     420                 :     mHasBeenCrossSite(false),
     421               0 :     mIsPreflight(false)
     422                 : {
     423               0 :   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
     424               0 :   aChannel->SetNotificationCallbacks(this);
     425                 : 
     426               0 :   *aResult = UpdateChannel(aChannel, aAllowDataURI);
     427               0 :   if (NS_FAILED(*aResult)) {
     428               0 :     mOuterListener = nsnull;
     429               0 :     mRequestingPrincipal = nsnull;
     430               0 :     mOuterNotificationCallbacks = nsnull;
     431                 :   }
     432               0 : }
     433                 : 
     434               0 : nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
     435                 :                                          nsIPrincipal* aRequestingPrincipal,
     436                 :                                          nsIChannel* aChannel,
     437                 :                                          bool aWithCredentials,
     438                 :                                          const nsCString& aPreflightMethod,
     439                 :                                          const nsTArray<nsCString>& aPreflightHeaders,
     440                 :                                          nsresult* aResult)
     441                 :   : mOuterListener(aOuter),
     442                 :     mRequestingPrincipal(aRequestingPrincipal),
     443               0 :     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     444                 :     mRequestApproved(false),
     445                 :     mHasBeenCrossSite(false),
     446                 :     mIsPreflight(true),
     447                 :     mPreflightMethod(aPreflightMethod),
     448               0 :     mPreflightHeaders(aPreflightHeaders)
     449                 : {
     450               0 :   for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
     451               0 :     ToLowerCase(mPreflightHeaders[i]);
     452                 :   }
     453               0 :   mPreflightHeaders.Sort();
     454                 : 
     455               0 :   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
     456               0 :   aChannel->SetNotificationCallbacks(this);
     457                 : 
     458               0 :   *aResult = UpdateChannel(aChannel);
     459               0 :   if (NS_FAILED(*aResult)) {
     460               0 :     mOuterListener = nsnull;
     461               0 :     mRequestingPrincipal = nsnull;
     462               0 :     mOuterNotificationCallbacks = nsnull;
     463                 :   }
     464               0 : }
     465                 : 
     466                 : NS_IMETHODIMP
     467               0 : nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
     468                 :                                     nsISupports* aContext)
     469                 : {
     470               0 :   mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
     471               0 :   if (!mRequestApproved) {
     472               0 :     if (sPreflightCache) {
     473               0 :       nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     474               0 :       if (channel) {
     475               0 :         nsCOMPtr<nsIURI> uri;
     476               0 :         NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
     477               0 :         if (uri) {
     478               0 :           sPreflightCache->RemoveEntries(uri, mRequestingPrincipal);
     479                 :         }
     480                 :       }
     481                 :     }
     482                 : 
     483               0 :     aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
     484               0 :     mOuterListener->OnStartRequest(aRequest, aContext);
     485                 : 
     486               0 :     return NS_ERROR_DOM_BAD_URI;
     487                 :   }
     488                 : 
     489               0 :   return mOuterListener->OnStartRequest(aRequest, aContext);
     490                 : }
     491                 : 
     492                 : bool
     493              80 : IsValidHTTPToken(const nsCSubstring& aToken)
     494                 : {
     495              80 :   if (aToken.IsEmpty()) {
     496               0 :     return false;
     497                 :   }
     498                 : 
     499                 :   nsCSubstring::const_char_iterator iter, end;
     500                 : 
     501              80 :   aToken.BeginReading(iter);
     502              80 :   aToken.EndReading(end);
     503                 : 
     504            1214 :   while (iter != end) {
     505            1054 :     if (*iter <= 32 ||
     506                 :         *iter >= 127 ||
     507                 :         *iter == '(' ||
     508                 :         *iter == ')' ||
     509                 :         *iter == '<' ||
     510                 :         *iter == '>' ||
     511                 :         *iter == '@' ||
     512                 :         *iter == ',' ||
     513                 :         *iter == ';' ||
     514                 :         *iter == ':' ||
     515                 :         *iter == '\\' ||
     516                 :         *iter == '\"' ||
     517                 :         *iter == '/' ||
     518                 :         *iter == '[' ||
     519                 :         *iter == ']' ||
     520                 :         *iter == '?' ||
     521                 :         *iter == '=' ||
     522                 :         *iter == '{' ||
     523                 :         *iter == '}') {
     524               0 :       return false;
     525                 :     }
     526            1054 :     ++iter;
     527                 :   }
     528                 : 
     529              80 :   return true;
     530                 : }
     531                 : 
     532                 : nsresult
     533               0 : nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
     534                 : {
     535                 :   // Check if this was actually a cross domain request
     536               0 :   if (!mHasBeenCrossSite) {
     537               0 :     return NS_OK;
     538                 :   }
     539                 : 
     540               0 :   if (gDisableCORS) {
     541               0 :     return NS_ERROR_DOM_BAD_URI;
     542                 :   }
     543                 : 
     544                 :   // Check if the request failed
     545                 :   nsresult status;
     546               0 :   nsresult rv = aRequest->GetStatus(&status);
     547               0 :   NS_ENSURE_SUCCESS(rv, rv);
     548               0 :   NS_ENSURE_SUCCESS(status, status);
     549                 : 
     550                 :   // Test that things worked on a HTTP level
     551               0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
     552               0 :   NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI);
     553                 : 
     554                 :   // Check the Access-Control-Allow-Origin header
     555               0 :   nsCAutoString allowedOriginHeader;
     556               0 :   rv = http->GetResponseHeader(
     557               0 :     NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
     558               0 :   NS_ENSURE_SUCCESS(rv, rv);
     559                 : 
     560               0 :   if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) {
     561               0 :     nsCAutoString origin;
     562               0 :     rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin);
     563               0 :     NS_ENSURE_SUCCESS(rv, rv);
     564                 : 
     565               0 :     if (!allowedOriginHeader.Equals(origin)) {
     566               0 :       return NS_ERROR_DOM_BAD_URI;
     567                 :     }
     568                 :   }
     569                 : 
     570                 :   // Check Access-Control-Allow-Credentials header
     571               0 :   if (mWithCredentials) {
     572               0 :     nsCAutoString allowCredentialsHeader;
     573               0 :     rv = http->GetResponseHeader(
     574               0 :       NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
     575               0 :     NS_ENSURE_SUCCESS(rv, rv);
     576                 : 
     577               0 :     if (!allowCredentialsHeader.EqualsLiteral("true")) {
     578               0 :       return NS_ERROR_DOM_BAD_URI;
     579                 :     }
     580                 :   }
     581                 : 
     582               0 :   if (mIsPreflight) {
     583                 :     bool succeeded;
     584               0 :     rv = http->GetRequestSucceeded(&succeeded);
     585               0 :     NS_ENSURE_SUCCESS(rv, rv);
     586               0 :     if (!succeeded) {
     587               0 :       return NS_ERROR_DOM_BAD_URI;
     588                 :     }
     589                 : 
     590               0 :     nsCAutoString headerVal;
     591                 :     // The "Access-Control-Allow-Methods" header contains a comma separated
     592                 :     // list of method names.
     593               0 :     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
     594               0 :                             headerVal);
     595               0 :     bool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
     596               0 :                          mPreflightMethod.EqualsLiteral("HEAD") ||
     597               0 :                          mPreflightMethod.EqualsLiteral("POST");
     598               0 :     nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
     599               0 :     while(methodTokens.hasMoreTokens()) {
     600               0 :       const nsDependentCSubstring& method = methodTokens.nextToken();
     601               0 :       if (method.IsEmpty()) {
     602               0 :         continue;
     603                 :       }
     604               0 :       if (!IsValidHTTPToken(method)) {
     605               0 :         return NS_ERROR_DOM_BAD_URI;
     606                 :       }
     607               0 :       foundMethod |= mPreflightMethod.Equals(method);
     608                 :     }
     609               0 :     NS_ENSURE_TRUE(foundMethod, NS_ERROR_DOM_BAD_URI);
     610                 : 
     611                 :     // The "Access-Control-Allow-Headers" header contains a comma separated
     612                 :     // list of header names.
     613               0 :     headerVal.Truncate();
     614               0 :     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
     615               0 :                             headerVal);
     616               0 :     nsTArray<nsCString> headers;
     617               0 :     nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
     618               0 :     while(headerTokens.hasMoreTokens()) {
     619               0 :       const nsDependentCSubstring& header = headerTokens.nextToken();
     620               0 :       if (header.IsEmpty()) {
     621               0 :         continue;
     622                 :       }
     623               0 :       if (!IsValidHTTPToken(header)) {
     624               0 :         return NS_ERROR_DOM_BAD_URI;
     625                 :       }
     626               0 :       headers.AppendElement(header);
     627                 :     }
     628               0 :     for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
     629               0 :       if (!headers.Contains(mPreflightHeaders[i],
     630               0 :                             nsCaseInsensitiveCStringArrayComparator())) {
     631               0 :         return NS_ERROR_DOM_BAD_URI;
     632                 :       }
     633                 :     }
     634                 :   }
     635                 : 
     636               0 :   return NS_OK;
     637                 : }
     638                 : 
     639                 : NS_IMETHODIMP
     640               0 : nsCORSListenerProxy::OnStopRequest(nsIRequest* aRequest,
     641                 :                                    nsISupports* aContext,
     642                 :                                    nsresult aStatusCode)
     643                 : {
     644               0 :   return mOuterListener->OnStopRequest(aRequest, aContext, aStatusCode);
     645                 : }
     646                 : 
     647                 : NS_IMETHODIMP
     648               0 : nsCORSListenerProxy::OnDataAvailable(nsIRequest* aRequest,
     649                 :                                      nsISupports* aContext, 
     650                 :                                      nsIInputStream* aInputStream,
     651                 :                                      PRUint32 aOffset,
     652                 :                                      PRUint32 aCount)
     653                 : {
     654               0 :   if (!mRequestApproved) {
     655               0 :     return NS_ERROR_DOM_BAD_URI;
     656                 :   }
     657               0 :   return mOuterListener->OnDataAvailable(aRequest, aContext, aInputStream,
     658               0 :                                          aOffset, aCount);
     659                 : }
     660                 : 
     661                 : NS_IMETHODIMP
     662               0 : nsCORSListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
     663                 : {
     664               0 :   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
     665               0 :     *aResult = static_cast<nsIChannelEventSink*>(this);
     666               0 :     NS_ADDREF_THIS();
     667                 : 
     668               0 :     return NS_OK;
     669                 :   }
     670                 : 
     671                 :   return mOuterNotificationCallbacks ?
     672               0 :     mOuterNotificationCallbacks->GetInterface(aIID, aResult) :
     673               0 :     NS_ERROR_NO_INTERFACE;
     674                 : }
     675                 : 
     676                 : NS_IMETHODIMP
     677               0 : nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
     678                 :                                             nsIChannel *aNewChannel,
     679                 :                                             PRUint32 aFlags,
     680                 :                                             nsIAsyncVerifyRedirectCallback *cb)
     681                 : {
     682                 :   nsresult rv;
     683               0 :   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
     684               0 :     rv = CheckRequestApproved(aOldChannel);
     685               0 :     if (NS_FAILED(rv)) {
     686               0 :       if (sPreflightCache) {
     687               0 :         nsCOMPtr<nsIURI> oldURI;
     688               0 :         NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
     689               0 :         if (oldURI) {
     690               0 :           sPreflightCache->RemoveEntries(oldURI, mRequestingPrincipal);
     691                 :         }
     692                 :       }
     693               0 :       aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     694               0 :       return NS_ERROR_DOM_BAD_URI;
     695                 :     }
     696                 :   }
     697                 : 
     698                 :   // Prepare to receive callback
     699               0 :   mRedirectCallback = cb;
     700               0 :   mOldRedirectChannel = aOldChannel;
     701               0 :   mNewRedirectChannel = aNewChannel;
     702                 : 
     703                 :   nsCOMPtr<nsIChannelEventSink> outer =
     704               0 :     do_GetInterface(mOuterNotificationCallbacks);
     705               0 :   if (outer) {
     706               0 :     rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this);
     707               0 :     if (NS_FAILED(rv)) {
     708               0 :         aOldChannel->Cancel(rv); // is this necessary...?
     709               0 :         mRedirectCallback = nsnull;
     710               0 :         mOldRedirectChannel = nsnull;
     711               0 :         mNewRedirectChannel = nsnull;
     712                 :     }
     713               0 :     return rv;  
     714                 :   }
     715                 : 
     716               0 :   (void) OnRedirectVerifyCallback(NS_OK);
     717               0 :   return NS_OK;
     718                 : }
     719                 : 
     720                 : NS_IMETHODIMP
     721               0 : nsCORSListenerProxy::OnRedirectVerifyCallback(nsresult result)
     722                 : {
     723               0 :   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
     724               0 :   NS_ASSERTION(mOldRedirectChannel, "mOldRedirectChannel not set in callback");
     725               0 :   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
     726                 : 
     727               0 :   if (NS_SUCCEEDED(result)) {
     728               0 :       nsresult rv = UpdateChannel(mNewRedirectChannel);
     729               0 :       if (NS_FAILED(rv)) {
     730                 :           NS_WARNING("nsCORSListenerProxy::OnRedirectVerifyCallback: "
     731               0 :                      "UpdateChannel() returned failure");
     732                 :       }
     733               0 :       result = rv;
     734                 :   }
     735                 : 
     736               0 :   if (NS_FAILED(result)) {
     737               0 :     mOldRedirectChannel->Cancel(result);
     738                 :   }
     739                 : 
     740               0 :   mOldRedirectChannel = nsnull;
     741               0 :   mNewRedirectChannel = nsnull;
     742               0 :   mRedirectCallback->OnRedirectVerifyCallback(result);
     743               0 :   mRedirectCallback   = nsnull;
     744               0 :   return NS_OK;
     745                 : }
     746                 : 
     747                 : nsresult
     748               0 : nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI)
     749                 : {
     750               0 :   nsCOMPtr<nsIURI> uri, originalURI;
     751               0 :   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
     752               0 :   NS_ENSURE_SUCCESS(rv, rv);
     753               0 :   rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
     754               0 :   NS_ENSURE_SUCCESS(rv, rv);
     755                 : 
     756                 :   // exempt data URIs from the same origin check.
     757               0 :   if (aAllowDataURI && originalURI == uri) {
     758               0 :     bool dataScheme = false;
     759               0 :     rv = uri->SchemeIs("data", &dataScheme);
     760               0 :     NS_ENSURE_SUCCESS(rv, rv);
     761               0 :     if (dataScheme) {
     762               0 :       return NS_OK;
     763                 :     }
     764                 :   }
     765                 : 
     766                 :   // Check that the uri is ok to load
     767               0 :   rv = nsContentUtils::GetSecurityManager()->
     768                 :     CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
     769               0 :                               nsIScriptSecurityManager::STANDARD);
     770               0 :   NS_ENSURE_SUCCESS(rv, rv);
     771                 : 
     772               0 :   if (originalURI != uri) {
     773               0 :     rv = nsContentUtils::GetSecurityManager()->
     774                 :       CheckLoadURIWithPrincipal(mRequestingPrincipal, originalURI,
     775               0 :                                 nsIScriptSecurityManager::STANDARD);
     776               0 :     NS_ENSURE_SUCCESS(rv, rv);
     777                 :   }
     778                 : 
     779               0 :   if (!mHasBeenCrossSite &&
     780               0 :       NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, false)) &&
     781               0 :       (originalURI == uri ||
     782               0 :        NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(originalURI,
     783                 :                                                        false)))) {
     784               0 :     return NS_OK;
     785                 :   }
     786                 : 
     787                 :   // It's a cross site load
     788               0 :   mHasBeenCrossSite = true;
     789                 : 
     790               0 :   nsCString userpass;
     791               0 :   uri->GetUserPass(userpass);
     792               0 :   NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI);
     793                 : 
     794                 :   // Add the Origin header
     795               0 :   nsCAutoString origin;
     796               0 :   rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin);
     797               0 :   NS_ENSURE_SUCCESS(rv, rv);
     798                 : 
     799               0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
     800               0 :   NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
     801                 : 
     802               0 :   rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, false);
     803               0 :   NS_ENSURE_SUCCESS(rv, rv);
     804                 : 
     805                 :   // Add preflight headers if this is a preflight request
     806               0 :   if (mIsPreflight) {
     807               0 :     rv = http->
     808               0 :       SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
     809               0 :                        mPreflightMethod, false);
     810               0 :     NS_ENSURE_SUCCESS(rv, rv);
     811                 : 
     812               0 :     if (!mPreflightHeaders.IsEmpty()) {
     813               0 :       nsCAutoString headers;
     814               0 :       for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
     815               0 :         if (i != 0) {
     816               0 :           headers += ',';
     817                 :         }
     818               0 :         headers += mPreflightHeaders[i];
     819                 :       }
     820               0 :       rv = http->
     821               0 :         SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
     822               0 :                          headers, false);
     823               0 :       NS_ENSURE_SUCCESS(rv, rv);
     824                 :     }
     825                 :   }
     826                 : 
     827                 :   // Make cookie-less if needed
     828               0 :   if (mIsPreflight || !mWithCredentials) {
     829                 :     nsLoadFlags flags;
     830               0 :     rv = http->GetLoadFlags(&flags);
     831               0 :     NS_ENSURE_SUCCESS(rv, rv);
     832                 : 
     833               0 :     flags |= nsIRequest::LOAD_ANONYMOUS;
     834               0 :     rv = http->SetLoadFlags(flags);
     835               0 :     NS_ENSURE_SUCCESS(rv, rv);
     836                 :   }
     837                 : 
     838               0 :   return NS_OK;
     839                 : }
     840                 : 
     841                 : //////////////////////////////////////////////////////////////////////////
     842                 : // Preflight proxy
     843                 : 
     844                 : // Class used as streamlistener and notification callback when
     845                 : // doing the initial OPTIONS request for a CORS check
     846                 : class nsCORSPreflightListener : public nsIStreamListener,
     847                 :                                 public nsIInterfaceRequestor,
     848                 :                                 public nsIChannelEventSink
     849               0 : {
     850                 : public:
     851               0 :   nsCORSPreflightListener(nsIChannel* aOuterChannel,
     852                 :                           nsIStreamListener* aOuterListener,
     853                 :                           nsISupports* aOuterContext,
     854                 :                           nsIPrincipal* aReferrerPrincipal,
     855                 :                           const nsACString& aRequestMethod,
     856                 :                           bool aWithCredentials)
     857                 :    : mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
     858                 :      mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
     859               0 :      mRequestMethod(aRequestMethod), mWithCredentials(aWithCredentials)
     860               0 :   { }
     861                 : 
     862                 :   NS_DECL_ISUPPORTS
     863                 :   NS_DECL_NSISTREAMLISTENER
     864                 :   NS_DECL_NSIREQUESTOBSERVER
     865                 :   NS_DECL_NSIINTERFACEREQUESTOR
     866                 :   NS_DECL_NSICHANNELEVENTSINK
     867                 : 
     868                 : private:
     869                 :   void AddResultToCache(nsIRequest* aRequest);
     870                 : 
     871                 :   nsCOMPtr<nsIChannel> mOuterChannel;
     872                 :   nsCOMPtr<nsIStreamListener> mOuterListener;
     873                 :   nsCOMPtr<nsISupports> mOuterContext;
     874                 :   nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
     875                 :   nsCString mRequestMethod;
     876                 :   bool mWithCredentials;
     877                 : };
     878                 : 
     879               0 : NS_IMPL_ISUPPORTS4(nsCORSPreflightListener, nsIStreamListener,
     880                 :                    nsIRequestObserver, nsIInterfaceRequestor,
     881                 :                    nsIChannelEventSink)
     882                 : 
     883                 : void
     884               0 : nsCORSPreflightListener::AddResultToCache(nsIRequest *aRequest)
     885                 : {
     886               0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
     887               0 :   NS_ASSERTION(http, "Request was not http");
     888                 : 
     889                 :   // The "Access-Control-Max-Age" header should return an age in seconds.
     890               0 :   nsCAutoString headerVal;
     891               0 :   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
     892               0 :                           headerVal);
     893               0 :   if (headerVal.IsEmpty()) {
     894                 :     return;
     895                 :   }
     896                 : 
     897                 :   // Sanitize the string. We only allow 'delta-seconds' as specified by
     898                 :   // http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
     899                 :   // trailing non-whitespace characters).
     900               0 :   PRUint32 age = 0;
     901                 :   nsCSubstring::const_char_iterator iter, end;
     902               0 :   headerVal.BeginReading(iter);
     903               0 :   headerVal.EndReading(end);
     904               0 :   while (iter != end) {
     905               0 :     if (*iter < '0' || *iter > '9') {
     906                 :       return;
     907                 :     }
     908               0 :     age = age * 10 + (*iter - '0');
     909                 :     // Cap at 24 hours. This also avoids overflow
     910               0 :     age = NS_MIN(age, 86400U);
     911               0 :     ++iter;
     912                 :   }
     913                 : 
     914               0 :   if (!age || !EnsurePreflightCache()) {
     915                 :     return;
     916                 :   }
     917                 : 
     918                 : 
     919                 :   // String seems fine, go ahead and cache.
     920                 :   // Note that we have already checked that these headers follow the correct
     921                 :   // syntax.
     922                 : 
     923               0 :   nsCOMPtr<nsIURI> uri;
     924               0 :   NS_GetFinalChannelURI(http, getter_AddRefs(uri));
     925                 : 
     926                 :   // PR_Now gives microseconds
     927               0 :   PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
     928                 : 
     929                 :   nsPreflightCache::CacheEntry* entry =
     930                 :     sPreflightCache->GetEntry(uri, mReferrerPrincipal, mWithCredentials,
     931               0 :                               true);
     932               0 :   if (!entry) {
     933                 :     return;
     934                 :   }
     935                 : 
     936                 :   // The "Access-Control-Allow-Methods" header contains a comma separated
     937                 :   // list of method names.
     938               0 :   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
     939               0 :                           headerVal);
     940                 : 
     941               0 :   nsCCharSeparatedTokenizer methods(headerVal, ',');
     942               0 :   while(methods.hasMoreTokens()) {
     943               0 :     const nsDependentCSubstring& method = methods.nextToken();
     944               0 :     if (method.IsEmpty()) {
     945               0 :       continue;
     946                 :     }
     947                 :     PRUint32 i;
     948               0 :     for (i = 0; i < entry->mMethods.Length(); ++i) {
     949               0 :       if (entry->mMethods[i].token.Equals(method)) {
     950               0 :         entry->mMethods[i].expirationTime = expirationTime;
     951               0 :         break;
     952                 :       }
     953                 :     }
     954               0 :     if (i == entry->mMethods.Length()) {
     955                 :       nsPreflightCache::TokenTime* newMethod =
     956               0 :         entry->mMethods.AppendElement();
     957               0 :       if (!newMethod) {
     958                 :         return;
     959                 :       }
     960                 : 
     961               0 :       newMethod->token = method;
     962               0 :       newMethod->expirationTime = expirationTime;
     963                 :     }
     964                 :   }
     965                 : 
     966                 :   // The "Access-Control-Allow-Headers" header contains a comma separated
     967                 :   // list of method names.
     968               0 :   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
     969               0 :                           headerVal);
     970                 : 
     971               0 :   nsCCharSeparatedTokenizer headers(headerVal, ',');
     972               0 :   while(headers.hasMoreTokens()) {
     973               0 :     const nsDependentCSubstring& header = headers.nextToken();
     974               0 :     if (header.IsEmpty()) {
     975               0 :       continue;
     976                 :     }
     977                 :     PRUint32 i;
     978               0 :     for (i = 0; i < entry->mHeaders.Length(); ++i) {
     979               0 :       if (entry->mHeaders[i].token.Equals(header)) {
     980               0 :         entry->mHeaders[i].expirationTime = expirationTime;
     981               0 :         break;
     982                 :       }
     983                 :     }
     984               0 :     if (i == entry->mHeaders.Length()) {
     985                 :       nsPreflightCache::TokenTime* newHeader =
     986               0 :         entry->mHeaders.AppendElement();
     987               0 :       if (!newHeader) {
     988                 :         return;
     989                 :       }
     990                 : 
     991               0 :       newHeader->token = header;
     992               0 :       newHeader->expirationTime = expirationTime;
     993                 :     }
     994                 :   }
     995                 : }
     996                 : 
     997                 : NS_IMETHODIMP
     998               0 : nsCORSPreflightListener::OnStartRequest(nsIRequest *aRequest,
     999                 :                                         nsISupports *aContext)
    1000                 : {
    1001                 :   nsresult status;
    1002               0 :   nsresult rv = aRequest->GetStatus(&status);
    1003                 : 
    1004               0 :   if (NS_SUCCEEDED(rv)) {
    1005               0 :     rv = status;
    1006                 :   }
    1007                 : 
    1008               0 :   if (NS_SUCCEEDED(rv)) {
    1009                 :     // Everything worked, try to cache and then fire off the actual request.
    1010               0 :     AddResultToCache(aRequest);
    1011                 : 
    1012               0 :     rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
    1013                 :   }
    1014                 : 
    1015               0 :   if (NS_FAILED(rv)) {
    1016               0 :     mOuterChannel->Cancel(rv);
    1017               0 :     mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
    1018               0 :     mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
    1019                 :     
    1020               0 :     return rv;
    1021                 :   }
    1022                 : 
    1023               0 :   return NS_OK;
    1024                 : }
    1025                 : 
    1026                 : NS_IMETHODIMP
    1027               0 : nsCORSPreflightListener::OnStopRequest(nsIRequest *aRequest,
    1028                 :                                        nsISupports *aContext,
    1029                 :                                        nsresult aStatus)
    1030                 : {
    1031               0 :   return NS_OK;
    1032                 : }
    1033                 : 
    1034                 : /** nsIStreamListener methods **/
    1035                 : 
    1036                 : NS_IMETHODIMP
    1037               0 : nsCORSPreflightListener::OnDataAvailable(nsIRequest *aRequest,
    1038                 :                                          nsISupports *ctxt,
    1039                 :                                          nsIInputStream *inStr,
    1040                 :                                          PRUint32 sourceOffset,
    1041                 :                                          PRUint32 count)
    1042                 : {
    1043                 :   PRUint32 totalRead;
    1044               0 :   return inStr->ReadSegments(NS_DiscardSegment, nsnull, count, &totalRead);
    1045                 : }
    1046                 : 
    1047                 : NS_IMETHODIMP
    1048               0 : nsCORSPreflightListener::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
    1049                 :                                                 nsIChannel *aNewChannel,
    1050                 :                                                 PRUint32 aFlags,
    1051                 :                                                 nsIAsyncVerifyRedirectCallback *callback)
    1052                 : {
    1053                 :   // Only internal redirects allowed for now.
    1054               0 :   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags))
    1055               0 :     return NS_ERROR_DOM_BAD_URI;
    1056                 : 
    1057               0 :   callback->OnRedirectVerifyCallback(NS_OK);
    1058               0 :   return NS_OK;
    1059                 : }
    1060                 : 
    1061                 : NS_IMETHODIMP
    1062               0 : nsCORSPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
    1063                 : {
    1064               0 :   return QueryInterface(aIID, aResult);
    1065                 : }
    1066                 : 
    1067                 : 
    1068                 : nsresult
    1069               0 : NS_StartCORSPreflight(nsIChannel* aRequestChannel,
    1070                 :                       nsIStreamListener* aListener,
    1071                 :                       nsIPrincipal* aPrincipal,
    1072                 :                       bool aWithCredentials,
    1073                 :                       nsTArray<nsCString>& aUnsafeHeaders,
    1074                 :                       nsIChannel** aPreflightChannel)
    1075                 : {
    1076               0 :   *aPreflightChannel = nsnull;
    1077                 : 
    1078               0 :   nsCAutoString method;
    1079               0 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
    1080               0 :   NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
    1081               0 :   httpChannel->GetRequestMethod(method);
    1082                 : 
    1083               0 :   nsCOMPtr<nsIURI> uri;
    1084               0 :   nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(uri));
    1085               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1086                 : 
    1087                 :   nsPreflightCache::CacheEntry* entry =
    1088                 :     sPreflightCache ?
    1089               0 :     sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, false) :
    1090               0 :     nsnull;
    1091                 : 
    1092               0 :   if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
    1093                 :     // We have a cached preflight result, just start the original channel
    1094               0 :     return aRequestChannel->AsyncOpen(aListener, nsnull);
    1095                 :   }
    1096                 : 
    1097                 :   // Either it wasn't cached or the cached result has expired. Build a
    1098                 :   // channel for the OPTIONS request.
    1099                 : 
    1100               0 :   nsCOMPtr<nsILoadGroup> loadGroup;
    1101               0 :   rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    1102               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1103                 : 
    1104                 :   nsLoadFlags loadFlags;
    1105               0 :   rv = aRequestChannel->GetLoadFlags(&loadFlags);
    1106               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1107                 : 
    1108               0 :   nsCOMPtr<nsIChannel> preflightChannel;
    1109               0 :   rv = NS_NewChannel(getter_AddRefs(preflightChannel), uri, nsnull,
    1110               0 :                      loadGroup, nsnull, loadFlags);
    1111               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1112                 : 
    1113               0 :   nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
    1114               0 :   NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
    1115                 : 
    1116               0 :   rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
    1117               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1118                 :   
    1119                 :   // Set up listener which will start the original channel
    1120                 :   nsCOMPtr<nsIStreamListener> preflightListener =
    1121                 :     new nsCORSPreflightListener(aRequestChannel, aListener, nsnull, aPrincipal,
    1122               0 :                                 method, aWithCredentials);
    1123               0 :   NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
    1124                 : 
    1125                 :   preflightListener =
    1126                 :     new nsCORSListenerProxy(preflightListener, aPrincipal,
    1127                 :                             preflightChannel, aWithCredentials,
    1128               0 :                             method, aUnsafeHeaders, &rv);
    1129               0 :   NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
    1130               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1131                 : 
    1132                 :   // Start preflight
    1133               0 :   rv = preflightChannel->AsyncOpen(preflightListener, nsnull);
    1134               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1135                 :   
    1136                 :   // Return newly created preflight channel
    1137               0 :   preflightChannel.forget(aPreflightChannel);
    1138                 : 
    1139               0 :   return NS_OK;
    1140                 : }
    1141                 : 

Generated by: LCOV version 1.7