1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsURIChecker.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,348 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsURIChecker.h" 1.10 +#include "nsIAuthPrompt.h" 1.11 +#include "nsIHttpChannel.h" 1.12 +#include "nsNetUtil.h" 1.13 +#include "nsString.h" 1.14 +#include "nsIAsyncVerifyRedirectCallback.h" 1.15 + 1.16 +//----------------------------------------------------------------------------- 1.17 + 1.18 +static bool 1.19 +ServerIsNES3x(nsIHttpChannel *httpChannel) 1.20 +{ 1.21 + nsAutoCString server; 1.22 + httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server); 1.23 + // case sensitive string comparison is OK here. the server string 1.24 + // is a well-known value, so we should not have to worry about it 1.25 + // being case-smashed or otherwise case-mutated. 1.26 + return StringBeginsWith(server, 1.27 + NS_LITERAL_CSTRING("Netscape-Enterprise/3.")); 1.28 +} 1.29 + 1.30 +//----------------------------------------------------------------------------- 1.31 + 1.32 +NS_IMPL_ISUPPORTS(nsURIChecker, 1.33 + nsIURIChecker, 1.34 + nsIRequest, 1.35 + nsIRequestObserver, 1.36 + nsIStreamListener, 1.37 + nsIChannelEventSink, 1.38 + nsIInterfaceRequestor) 1.39 + 1.40 +nsURIChecker::nsURIChecker() 1.41 + : mStatus(NS_OK) 1.42 + , mIsPending(false) 1.43 + , mAllowHead(true) 1.44 +{ 1.45 +} 1.46 + 1.47 +void 1.48 +nsURIChecker::SetStatusAndCallBack(nsresult aStatus) 1.49 +{ 1.50 + mStatus = aStatus; 1.51 + mIsPending = false; 1.52 + 1.53 + if (mObserver) { 1.54 + mObserver->OnStartRequest(this, mObserverContext); 1.55 + mObserver->OnStopRequest(this, mObserverContext, mStatus); 1.56 + mObserver = nullptr; 1.57 + mObserverContext = nullptr; 1.58 + } 1.59 +} 1.60 + 1.61 +nsresult 1.62 +nsURIChecker::CheckStatus() 1.63 +{ 1.64 + NS_ASSERTION(mChannel, "no channel"); 1.65 + 1.66 + nsresult status; 1.67 + nsresult rv = mChannel->GetStatus(&status); 1.68 + // DNS errors and other obvious problems will return failure status 1.69 + if (NS_FAILED(rv) || NS_FAILED(status)) 1.70 + return NS_BINDING_FAILED; 1.71 + 1.72 + // If status is zero, it might still be an error if it's http: 1.73 + // http has data even when there's an error like a 404. 1.74 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); 1.75 + if (!httpChannel) 1.76 + return NS_BINDING_SUCCEEDED; 1.77 + 1.78 + uint32_t responseStatus; 1.79 + rv = httpChannel->GetResponseStatus(&responseStatus); 1.80 + if (NS_FAILED(rv)) 1.81 + return NS_BINDING_FAILED; 1.82 + 1.83 + // If it's between 200-299, it's valid: 1.84 + if (responseStatus / 100 == 2) 1.85 + return NS_BINDING_SUCCEEDED; 1.86 + 1.87 + // If we got a 404 (not found), we need some extra checking: 1.88 + // toplevel urls from Netscape Enterprise Server 3.6, like the old AOL- 1.89 + // hosted http://www.mozilla.org, generate a 404 and will have to be 1.90 + // retried without the head. 1.91 + if (responseStatus == 404) { 1.92 + if (mAllowHead && ServerIsNES3x(httpChannel)) { 1.93 + mAllowHead = false; 1.94 + 1.95 + // save the current value of mChannel in case we can't issue 1.96 + // the new request for some reason. 1.97 + nsCOMPtr<nsIChannel> lastChannel = mChannel; 1.98 + 1.99 + nsCOMPtr<nsIURI> uri; 1.100 + uint32_t loadFlags; 1.101 + 1.102 + rv = lastChannel->GetOriginalURI(getter_AddRefs(uri)); 1.103 + nsresult tmp = lastChannel->GetLoadFlags(&loadFlags); 1.104 + if (NS_FAILED(tmp)) { 1.105 + rv = tmp; 1.106 + } 1.107 + 1.108 + // XXX we are carrying over the load flags, but what about other 1.109 + // parameters that may have been set on lastChannel?? 1.110 + 1.111 + if (NS_SUCCEEDED(rv)) { 1.112 + rv = Init(uri); 1.113 + if (NS_SUCCEEDED(rv)) { 1.114 + rv = mChannel->SetLoadFlags(loadFlags); 1.115 + if (NS_SUCCEEDED(rv)) { 1.116 + rv = AsyncCheck(mObserver, mObserverContext); 1.117 + // if we succeeded in loading the new channel, then we 1.118 + // want to return without notifying our observer. 1.119 + if (NS_SUCCEEDED(rv)) 1.120 + return NS_BASE_STREAM_WOULD_BLOCK; 1.121 + } 1.122 + } 1.123 + } 1.124 + // it is important to update this so our observer will be able 1.125 + // to access our baseChannel attribute if they want. 1.126 + mChannel = lastChannel; 1.127 + } 1.128 + } 1.129 + 1.130 + // If we get here, assume the resource does not exist. 1.131 + return NS_BINDING_FAILED; 1.132 +} 1.133 + 1.134 +//----------------------------------------------------------------------------- 1.135 +// nsIURIChecker methods: 1.136 +//----------------------------------------------------------------------------- 1.137 + 1.138 +NS_IMETHODIMP 1.139 +nsURIChecker::Init(nsIURI *aURI) 1.140 +{ 1.141 + nsresult rv; 1.142 + nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv); 1.143 + if (NS_FAILED(rv)) return rv; 1.144 + 1.145 + rv = ios->NewChannelFromURI(aURI, getter_AddRefs(mChannel)); 1.146 + if (NS_FAILED(rv)) return rv; 1.147 + 1.148 + if (mAllowHead) { 1.149 + mAllowHead = false; 1.150 + // See if it's an http channel, which needs special treatment: 1.151 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); 1.152 + if (httpChannel) { 1.153 + // We can have an HTTP channel that has a non-HTTP URL if 1.154 + // we're doing FTP via an HTTP proxy, for example. See for 1.155 + // example bug 148813 1.156 + bool isReallyHTTP = false; 1.157 + aURI->SchemeIs("http", &isReallyHTTP); 1.158 + if (!isReallyHTTP) 1.159 + aURI->SchemeIs("https", &isReallyHTTP); 1.160 + if (isReallyHTTP) { 1.161 + httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD")); 1.162 + // set back to true so we'll know that this request is issuing 1.163 + // a HEAD request. this is used down in OnStartRequest to 1.164 + // handle cases where we need to repeat the request as a normal 1.165 + // GET to deal with server borkage. 1.166 + mAllowHead = true; 1.167 + } 1.168 + } 1.169 + } 1.170 + return NS_OK; 1.171 +} 1.172 + 1.173 +NS_IMETHODIMP 1.174 +nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver, 1.175 + nsISupports *aObserverContext) 1.176 +{ 1.177 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.178 + 1.179 + // Hook us up to listen to redirects and the like (this creates a reference 1.180 + // cycle!) 1.181 + mChannel->SetNotificationCallbacks(this); 1.182 + 1.183 + // and start the request: 1.184 + nsresult rv = mChannel->AsyncOpen(this, nullptr); 1.185 + if (NS_FAILED(rv)) 1.186 + mChannel = nullptr; 1.187 + else { 1.188 + // ok, wait for OnStartRequest to fire. 1.189 + mIsPending = true; 1.190 + mObserver = aObserver; 1.191 + mObserverContext = aObserverContext; 1.192 + } 1.193 + return rv; 1.194 +} 1.195 + 1.196 +NS_IMETHODIMP 1.197 +nsURIChecker::GetBaseChannel(nsIChannel **aChannel) 1.198 +{ 1.199 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.200 + NS_ADDREF(*aChannel = mChannel); 1.201 + return NS_OK; 1.202 +} 1.203 + 1.204 +//----------------------------------------------------------------------------- 1.205 +// nsIRequest methods: 1.206 +//----------------------------------------------------------------------------- 1.207 + 1.208 +NS_IMETHODIMP 1.209 +nsURIChecker::GetName(nsACString &aName) 1.210 +{ 1.211 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.212 + return mChannel->GetName(aName); 1.213 +} 1.214 + 1.215 +NS_IMETHODIMP 1.216 +nsURIChecker::IsPending(bool *aPendingRet) 1.217 +{ 1.218 + *aPendingRet = mIsPending; 1.219 + return NS_OK; 1.220 +} 1.221 + 1.222 +NS_IMETHODIMP 1.223 +nsURIChecker::GetStatus(nsresult* aStatusRet) 1.224 +{ 1.225 + *aStatusRet = mStatus; 1.226 + return NS_OK; 1.227 +} 1.228 + 1.229 +NS_IMETHODIMP 1.230 +nsURIChecker::Cancel(nsresult status) 1.231 +{ 1.232 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.233 + return mChannel->Cancel(status); 1.234 +} 1.235 + 1.236 +NS_IMETHODIMP 1.237 +nsURIChecker::Suspend() 1.238 +{ 1.239 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.240 + return mChannel->Suspend(); 1.241 +} 1.242 + 1.243 +NS_IMETHODIMP 1.244 +nsURIChecker::Resume() 1.245 +{ 1.246 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.247 + return mChannel->Resume(); 1.248 +} 1.249 + 1.250 +NS_IMETHODIMP 1.251 +nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup) 1.252 +{ 1.253 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.254 + return mChannel->GetLoadGroup(aLoadGroup); 1.255 +} 1.256 + 1.257 +NS_IMETHODIMP 1.258 +nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup) 1.259 +{ 1.260 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.261 + return mChannel->SetLoadGroup(aLoadGroup); 1.262 +} 1.263 + 1.264 +NS_IMETHODIMP 1.265 +nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags) 1.266 +{ 1.267 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.268 + return mChannel->GetLoadFlags(aLoadFlags); 1.269 +} 1.270 + 1.271 +NS_IMETHODIMP 1.272 +nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags) 1.273 +{ 1.274 + NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED); 1.275 + return mChannel->SetLoadFlags(aLoadFlags); 1.276 +} 1.277 + 1.278 +//----------------------------------------------------------------------------- 1.279 +// nsIRequestObserver methods: 1.280 +//----------------------------------------------------------------------------- 1.281 + 1.282 +NS_IMETHODIMP 1.283 +nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt) 1.284 +{ 1.285 + NS_ASSERTION(aRequest == mChannel, "unexpected request"); 1.286 + 1.287 + nsresult rv = CheckStatus(); 1.288 + if (rv != NS_BASE_STREAM_WOULD_BLOCK) 1.289 + SetStatusAndCallBack(rv); 1.290 + 1.291 + // cancel the request (we don't care to look at the data). 1.292 + return NS_BINDING_ABORTED; 1.293 +} 1.294 + 1.295 +NS_IMETHODIMP 1.296 +nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt, 1.297 + nsresult statusCode) 1.298 +{ 1.299 + // NOTE: we may have kicked off a subsequent request, so we should not do 1.300 + // any cleanup unless this request matches the one we are currently using. 1.301 + if (mChannel == request) { 1.302 + // break reference cycle between us and the channel (see comment in 1.303 + // AsyncCheckURI) 1.304 + mChannel = nullptr; 1.305 + } 1.306 + return NS_OK; 1.307 +} 1.308 + 1.309 +//----------------------------------------------------------------------------- 1.310 +// nsIStreamListener methods: 1.311 +//----------------------------------------------------------------------------- 1.312 + 1.313 +NS_IMETHODIMP 1.314 +nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt, 1.315 + nsIInputStream *aInput, uint64_t aOffset, 1.316 + uint32_t aCount) 1.317 +{ 1.318 + NS_NOTREACHED("nsURIChecker::OnDataAvailable"); 1.319 + return NS_BINDING_ABORTED; 1.320 +} 1.321 + 1.322 +//----------------------------------------------------------------------------- 1.323 +// nsIInterfaceRequestor methods: 1.324 +//----------------------------------------------------------------------------- 1.325 + 1.326 +NS_IMETHODIMP 1.327 +nsURIChecker::GetInterface(const nsIID & aIID, void **aResult) 1.328 +{ 1.329 + if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { 1.330 + nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver); 1.331 + if (req) 1.332 + return req->GetInterface(aIID, aResult); 1.333 + } 1.334 + return QueryInterface(aIID, aResult); 1.335 +} 1.336 + 1.337 +//----------------------------------------------------------------------------- 1.338 +// nsIChannelEventSink methods: 1.339 +//----------------------------------------------------------------------------- 1.340 + 1.341 +NS_IMETHODIMP 1.342 +nsURIChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel, 1.343 + nsIChannel *aNewChannel, 1.344 + uint32_t aFlags, 1.345 + nsIAsyncVerifyRedirectCallback *callback) 1.346 +{ 1.347 + // We have a new channel 1.348 + mChannel = aNewChannel; 1.349 + callback->OnRedirectVerifyCallback(NS_OK); 1.350 + return NS_OK; 1.351 +}