netwerk/base/src/nsURIChecker.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsURIChecker.h"
michael@0 7 #include "nsIAuthPrompt.h"
michael@0 8 #include "nsIHttpChannel.h"
michael@0 9 #include "nsNetUtil.h"
michael@0 10 #include "nsString.h"
michael@0 11 #include "nsIAsyncVerifyRedirectCallback.h"
michael@0 12
michael@0 13 //-----------------------------------------------------------------------------
michael@0 14
michael@0 15 static bool
michael@0 16 ServerIsNES3x(nsIHttpChannel *httpChannel)
michael@0 17 {
michael@0 18 nsAutoCString server;
michael@0 19 httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
michael@0 20 // case sensitive string comparison is OK here. the server string
michael@0 21 // is a well-known value, so we should not have to worry about it
michael@0 22 // being case-smashed or otherwise case-mutated.
michael@0 23 return StringBeginsWith(server,
michael@0 24 NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
michael@0 25 }
michael@0 26
michael@0 27 //-----------------------------------------------------------------------------
michael@0 28
michael@0 29 NS_IMPL_ISUPPORTS(nsURIChecker,
michael@0 30 nsIURIChecker,
michael@0 31 nsIRequest,
michael@0 32 nsIRequestObserver,
michael@0 33 nsIStreamListener,
michael@0 34 nsIChannelEventSink,
michael@0 35 nsIInterfaceRequestor)
michael@0 36
michael@0 37 nsURIChecker::nsURIChecker()
michael@0 38 : mStatus(NS_OK)
michael@0 39 , mIsPending(false)
michael@0 40 , mAllowHead(true)
michael@0 41 {
michael@0 42 }
michael@0 43
michael@0 44 void
michael@0 45 nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
michael@0 46 {
michael@0 47 mStatus = aStatus;
michael@0 48 mIsPending = false;
michael@0 49
michael@0 50 if (mObserver) {
michael@0 51 mObserver->OnStartRequest(this, mObserverContext);
michael@0 52 mObserver->OnStopRequest(this, mObserverContext, mStatus);
michael@0 53 mObserver = nullptr;
michael@0 54 mObserverContext = nullptr;
michael@0 55 }
michael@0 56 }
michael@0 57
michael@0 58 nsresult
michael@0 59 nsURIChecker::CheckStatus()
michael@0 60 {
michael@0 61 NS_ASSERTION(mChannel, "no channel");
michael@0 62
michael@0 63 nsresult status;
michael@0 64 nsresult rv = mChannel->GetStatus(&status);
michael@0 65 // DNS errors and other obvious problems will return failure status
michael@0 66 if (NS_FAILED(rv) || NS_FAILED(status))
michael@0 67 return NS_BINDING_FAILED;
michael@0 68
michael@0 69 // If status is zero, it might still be an error if it's http:
michael@0 70 // http has data even when there's an error like a 404.
michael@0 71 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
michael@0 72 if (!httpChannel)
michael@0 73 return NS_BINDING_SUCCEEDED;
michael@0 74
michael@0 75 uint32_t responseStatus;
michael@0 76 rv = httpChannel->GetResponseStatus(&responseStatus);
michael@0 77 if (NS_FAILED(rv))
michael@0 78 return NS_BINDING_FAILED;
michael@0 79
michael@0 80 // If it's between 200-299, it's valid:
michael@0 81 if (responseStatus / 100 == 2)
michael@0 82 return NS_BINDING_SUCCEEDED;
michael@0 83
michael@0 84 // If we got a 404 (not found), we need some extra checking:
michael@0 85 // toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
michael@0 86 // hosted http://www.mozilla.org, generate a 404 and will have to be
michael@0 87 // retried without the head.
michael@0 88 if (responseStatus == 404) {
michael@0 89 if (mAllowHead && ServerIsNES3x(httpChannel)) {
michael@0 90 mAllowHead = false;
michael@0 91
michael@0 92 // save the current value of mChannel in case we can't issue
michael@0 93 // the new request for some reason.
michael@0 94 nsCOMPtr<nsIChannel> lastChannel = mChannel;
michael@0 95
michael@0 96 nsCOMPtr<nsIURI> uri;
michael@0 97 uint32_t loadFlags;
michael@0 98
michael@0 99 rv = lastChannel->GetOriginalURI(getter_AddRefs(uri));
michael@0 100 nsresult tmp = lastChannel->GetLoadFlags(&loadFlags);
michael@0 101 if (NS_FAILED(tmp)) {
michael@0 102 rv = tmp;
michael@0 103 }
michael@0 104
michael@0 105 // XXX we are carrying over the load flags, but what about other
michael@0 106 // parameters that may have been set on lastChannel??
michael@0 107
michael@0 108 if (NS_SUCCEEDED(rv)) {
michael@0 109 rv = Init(uri);
michael@0 110 if (NS_SUCCEEDED(rv)) {
michael@0 111 rv = mChannel->SetLoadFlags(loadFlags);
michael@0 112 if (NS_SUCCEEDED(rv)) {
michael@0 113 rv = AsyncCheck(mObserver, mObserverContext);
michael@0 114 // if we succeeded in loading the new channel, then we
michael@0 115 // want to return without notifying our observer.
michael@0 116 if (NS_SUCCEEDED(rv))
michael@0 117 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 118 }
michael@0 119 }
michael@0 120 }
michael@0 121 // it is important to update this so our observer will be able
michael@0 122 // to access our baseChannel attribute if they want.
michael@0 123 mChannel = lastChannel;
michael@0 124 }
michael@0 125 }
michael@0 126
michael@0 127 // If we get here, assume the resource does not exist.
michael@0 128 return NS_BINDING_FAILED;
michael@0 129 }
michael@0 130
michael@0 131 //-----------------------------------------------------------------------------
michael@0 132 // nsIURIChecker methods:
michael@0 133 //-----------------------------------------------------------------------------
michael@0 134
michael@0 135 NS_IMETHODIMP
michael@0 136 nsURIChecker::Init(nsIURI *aURI)
michael@0 137 {
michael@0 138 nsresult rv;
michael@0 139 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
michael@0 140 if (NS_FAILED(rv)) return rv;
michael@0 141
michael@0 142 rv = ios->NewChannelFromURI(aURI, getter_AddRefs(mChannel));
michael@0 143 if (NS_FAILED(rv)) return rv;
michael@0 144
michael@0 145 if (mAllowHead) {
michael@0 146 mAllowHead = false;
michael@0 147 // See if it's an http channel, which needs special treatment:
michael@0 148 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
michael@0 149 if (httpChannel) {
michael@0 150 // We can have an HTTP channel that has a non-HTTP URL if
michael@0 151 // we're doing FTP via an HTTP proxy, for example. See for
michael@0 152 // example bug 148813
michael@0 153 bool isReallyHTTP = false;
michael@0 154 aURI->SchemeIs("http", &isReallyHTTP);
michael@0 155 if (!isReallyHTTP)
michael@0 156 aURI->SchemeIs("https", &isReallyHTTP);
michael@0 157 if (isReallyHTTP) {
michael@0 158 httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
michael@0 159 // set back to true so we'll know that this request is issuing
michael@0 160 // a HEAD request. this is used down in OnStartRequest to
michael@0 161 // handle cases where we need to repeat the request as a normal
michael@0 162 // GET to deal with server borkage.
michael@0 163 mAllowHead = true;
michael@0 164 }
michael@0 165 }
michael@0 166 }
michael@0 167 return NS_OK;
michael@0 168 }
michael@0 169
michael@0 170 NS_IMETHODIMP
michael@0 171 nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver,
michael@0 172 nsISupports *aObserverContext)
michael@0 173 {
michael@0 174 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 175
michael@0 176 // Hook us up to listen to redirects and the like (this creates a reference
michael@0 177 // cycle!)
michael@0 178 mChannel->SetNotificationCallbacks(this);
michael@0 179
michael@0 180 // and start the request:
michael@0 181 nsresult rv = mChannel->AsyncOpen(this, nullptr);
michael@0 182 if (NS_FAILED(rv))
michael@0 183 mChannel = nullptr;
michael@0 184 else {
michael@0 185 // ok, wait for OnStartRequest to fire.
michael@0 186 mIsPending = true;
michael@0 187 mObserver = aObserver;
michael@0 188 mObserverContext = aObserverContext;
michael@0 189 }
michael@0 190 return rv;
michael@0 191 }
michael@0 192
michael@0 193 NS_IMETHODIMP
michael@0 194 nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
michael@0 195 {
michael@0 196 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 197 NS_ADDREF(*aChannel = mChannel);
michael@0 198 return NS_OK;
michael@0 199 }
michael@0 200
michael@0 201 //-----------------------------------------------------------------------------
michael@0 202 // nsIRequest methods:
michael@0 203 //-----------------------------------------------------------------------------
michael@0 204
michael@0 205 NS_IMETHODIMP
michael@0 206 nsURIChecker::GetName(nsACString &aName)
michael@0 207 {
michael@0 208 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 209 return mChannel->GetName(aName);
michael@0 210 }
michael@0 211
michael@0 212 NS_IMETHODIMP
michael@0 213 nsURIChecker::IsPending(bool *aPendingRet)
michael@0 214 {
michael@0 215 *aPendingRet = mIsPending;
michael@0 216 return NS_OK;
michael@0 217 }
michael@0 218
michael@0 219 NS_IMETHODIMP
michael@0 220 nsURIChecker::GetStatus(nsresult* aStatusRet)
michael@0 221 {
michael@0 222 *aStatusRet = mStatus;
michael@0 223 return NS_OK;
michael@0 224 }
michael@0 225
michael@0 226 NS_IMETHODIMP
michael@0 227 nsURIChecker::Cancel(nsresult status)
michael@0 228 {
michael@0 229 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 230 return mChannel->Cancel(status);
michael@0 231 }
michael@0 232
michael@0 233 NS_IMETHODIMP
michael@0 234 nsURIChecker::Suspend()
michael@0 235 {
michael@0 236 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 237 return mChannel->Suspend();
michael@0 238 }
michael@0 239
michael@0 240 NS_IMETHODIMP
michael@0 241 nsURIChecker::Resume()
michael@0 242 {
michael@0 243 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 244 return mChannel->Resume();
michael@0 245 }
michael@0 246
michael@0 247 NS_IMETHODIMP
michael@0 248 nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
michael@0 249 {
michael@0 250 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 251 return mChannel->GetLoadGroup(aLoadGroup);
michael@0 252 }
michael@0 253
michael@0 254 NS_IMETHODIMP
michael@0 255 nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
michael@0 256 {
michael@0 257 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 258 return mChannel->SetLoadGroup(aLoadGroup);
michael@0 259 }
michael@0 260
michael@0 261 NS_IMETHODIMP
michael@0 262 nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
michael@0 263 {
michael@0 264 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 265 return mChannel->GetLoadFlags(aLoadFlags);
michael@0 266 }
michael@0 267
michael@0 268 NS_IMETHODIMP
michael@0 269 nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
michael@0 270 {
michael@0 271 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
michael@0 272 return mChannel->SetLoadFlags(aLoadFlags);
michael@0 273 }
michael@0 274
michael@0 275 //-----------------------------------------------------------------------------
michael@0 276 // nsIRequestObserver methods:
michael@0 277 //-----------------------------------------------------------------------------
michael@0 278
michael@0 279 NS_IMETHODIMP
michael@0 280 nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt)
michael@0 281 {
michael@0 282 NS_ASSERTION(aRequest == mChannel, "unexpected request");
michael@0 283
michael@0 284 nsresult rv = CheckStatus();
michael@0 285 if (rv != NS_BASE_STREAM_WOULD_BLOCK)
michael@0 286 SetStatusAndCallBack(rv);
michael@0 287
michael@0 288 // cancel the request (we don't care to look at the data).
michael@0 289 return NS_BINDING_ABORTED;
michael@0 290 }
michael@0 291
michael@0 292 NS_IMETHODIMP
michael@0 293 nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
michael@0 294 nsresult statusCode)
michael@0 295 {
michael@0 296 // NOTE: we may have kicked off a subsequent request, so we should not do
michael@0 297 // any cleanup unless this request matches the one we are currently using.
michael@0 298 if (mChannel == request) {
michael@0 299 // break reference cycle between us and the channel (see comment in
michael@0 300 // AsyncCheckURI)
michael@0 301 mChannel = nullptr;
michael@0 302 }
michael@0 303 return NS_OK;
michael@0 304 }
michael@0 305
michael@0 306 //-----------------------------------------------------------------------------
michael@0 307 // nsIStreamListener methods:
michael@0 308 //-----------------------------------------------------------------------------
michael@0 309
michael@0 310 NS_IMETHODIMP
michael@0 311 nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
michael@0 312 nsIInputStream *aInput, uint64_t aOffset,
michael@0 313 uint32_t aCount)
michael@0 314 {
michael@0 315 NS_NOTREACHED("nsURIChecker::OnDataAvailable");
michael@0 316 return NS_BINDING_ABORTED;
michael@0 317 }
michael@0 318
michael@0 319 //-----------------------------------------------------------------------------
michael@0 320 // nsIInterfaceRequestor methods:
michael@0 321 //-----------------------------------------------------------------------------
michael@0 322
michael@0 323 NS_IMETHODIMP
michael@0 324 nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
michael@0 325 {
michael@0 326 if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
michael@0 327 nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
michael@0 328 if (req)
michael@0 329 return req->GetInterface(aIID, aResult);
michael@0 330 }
michael@0 331 return QueryInterface(aIID, aResult);
michael@0 332 }
michael@0 333
michael@0 334 //-----------------------------------------------------------------------------
michael@0 335 // nsIChannelEventSink methods:
michael@0 336 //-----------------------------------------------------------------------------
michael@0 337
michael@0 338 NS_IMETHODIMP
michael@0 339 nsURIChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
michael@0 340 nsIChannel *aNewChannel,
michael@0 341 uint32_t aFlags,
michael@0 342 nsIAsyncVerifyRedirectCallback *callback)
michael@0 343 {
michael@0 344 // We have a new channel
michael@0 345 mChannel = aNewChannel;
michael@0 346 callback->OnRedirectVerifyCallback(NS_OK);
michael@0 347 return NS_OK;
michael@0 348 }

mercurial