1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
23 * Akkana Peck <akkana@netscape.com> (original author)
24 * Darin Fisher <darin@meer.net>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsURIChecker.h"
41 #include "nsIServiceManager.h"
42 #include "nsIAuthPrompt.h"
43 #include "nsIHttpChannel.h"
44 #include "nsNetUtil.h"
47 //-----------------------------------------------------------------------------
50 ServerIsNES3x(nsIHttpChannel
*httpChannel
)
53 httpChannel
->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server
);
54 // case sensitive string comparison is OK here. the server string
55 // is a well-known value, so we should not have to worry about it
56 // being case-smashed or otherwise case-mutated.
57 return StringBeginsWith(server
,
58 NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
61 //-----------------------------------------------------------------------------
63 NS_IMPL_ISUPPORTS6(nsURIChecker
,
69 nsIInterfaceRequestor
)
71 nsURIChecker::nsURIChecker()
73 , mIsPending(PR_FALSE
)
79 nsURIChecker::SetStatusAndCallBack(nsresult aStatus
)
82 mIsPending
= PR_FALSE
;
85 mObserver
->OnStartRequest(this, mObserverContext
);
86 mObserver
->OnStopRequest(this, mObserverContext
, mStatus
);
88 mObserverContext
= nsnull
;
93 nsURIChecker::CheckStatus()
95 NS_ASSERTION(mChannel
, "no channel");
98 nsresult rv
= mChannel
->GetStatus(&status
);
99 // DNS errors and other obvious problems will return failure status
100 if (NS_FAILED(rv
) || NS_FAILED(status
))
101 return NS_BINDING_FAILED
;
103 // If status is zero, it might still be an error if it's http:
104 // http has data even when there's an error like a 404.
105 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
107 return NS_BINDING_SUCCEEDED
;
109 PRUint32 responseStatus
;
110 rv
= httpChannel
->GetResponseStatus(&responseStatus
);
112 return NS_BINDING_FAILED
;
114 // If it's between 200-299, it's valid:
115 if (responseStatus
/ 100 == 2)
116 return NS_BINDING_SUCCEEDED
;
118 // If we got a 404 (not found), we need some extra checking:
119 // toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
120 // hosted http://www.mozilla.org, generate a 404 and will have to be
121 // retried without the head.
122 if (responseStatus
== 404) {
123 if (mAllowHead
&& ServerIsNES3x(httpChannel
)) {
124 mAllowHead
= PR_FALSE
;
126 // save the current value of mChannel in case we can't issue
127 // the new request for some reason.
128 nsCOMPtr
<nsIChannel
> lastChannel
= mChannel
;
130 nsCOMPtr
<nsIURI
> uri
;
133 rv
= lastChannel
->GetOriginalURI(getter_AddRefs(uri
));
134 rv
|= lastChannel
->GetLoadFlags(&loadFlags
);
136 // XXX we are carrying over the load flags, but what about other
137 // parameters that may have been set on lastChannel??
139 if (NS_SUCCEEDED(rv
)) {
141 if (NS_SUCCEEDED(rv
)) {
142 rv
= mChannel
->SetLoadFlags(loadFlags
);
143 if (NS_SUCCEEDED(rv
)) {
144 rv
= AsyncCheck(mObserver
, mObserverContext
);
145 // if we succeeded in loading the new channel, then we
146 // want to return without notifying our observer.
147 if (NS_SUCCEEDED(rv
))
148 return NS_BASE_STREAM_WOULD_BLOCK
;
152 // it is important to update this so our observer will be able
153 // to access our baseChannel attribute if they want.
154 mChannel
= lastChannel
;
158 // If we get here, assume the resource does not exist.
159 return NS_BINDING_FAILED
;
162 //-----------------------------------------------------------------------------
163 // nsIURIChecker methods:
164 //-----------------------------------------------------------------------------
167 nsURIChecker::Init(nsIURI
*aURI
)
170 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
171 if (NS_FAILED(rv
)) return rv
;
173 rv
= ios
->NewChannelFromURI(aURI
, getter_AddRefs(mChannel
));
174 if (NS_FAILED(rv
)) return rv
;
177 mAllowHead
= PR_FALSE
;
178 // See if it's an http channel, which needs special treatment:
179 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
181 // We can have an HTTP channel that has a non-HTTP URL if
182 // we're doing FTP via an HTTP proxy, for example. See for
183 // example bug 148813
184 PRBool isReallyHTTP
= PR_FALSE
;
185 aURI
->SchemeIs("http", &isReallyHTTP
);
187 aURI
->SchemeIs("https", &isReallyHTTP
);
189 httpChannel
->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
190 // set back to true so we'll know that this request is issuing
191 // a HEAD request. this is used down in OnStartRequest to
192 // handle cases where we need to repeat the request as a normal
193 // GET to deal with server borkage.
194 mAllowHead
= PR_TRUE
;
202 nsURIChecker::AsyncCheck(nsIRequestObserver
*aObserver
,
203 nsISupports
*aObserverContext
)
205 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
207 // Hook us up to listen to redirects and the like (this creates a reference
209 mChannel
->SetNotificationCallbacks(this);
211 // and start the request:
212 nsresult rv
= mChannel
->AsyncOpen(this, nsnull
);
216 // ok, wait for OnStartRequest to fire.
217 mIsPending
= PR_TRUE
;
218 mObserver
= aObserver
;
219 mObserverContext
= aObserverContext
;
225 nsURIChecker::GetBaseChannel(nsIChannel
**aChannel
)
227 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
228 NS_ADDREF(*aChannel
= mChannel
);
232 //-----------------------------------------------------------------------------
233 // nsIRequest methods:
234 //-----------------------------------------------------------------------------
237 nsURIChecker::GetName(nsACString
&aName
)
239 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
240 return mChannel
->GetName(aName
);
244 nsURIChecker::IsPending(PRBool
*aPendingRet
)
246 *aPendingRet
= mIsPending
;
251 nsURIChecker::GetStatus(nsresult
* aStatusRet
)
253 *aStatusRet
= mStatus
;
258 nsURIChecker::Cancel(nsresult status
)
260 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
261 return mChannel
->Cancel(status
);
265 nsURIChecker::Suspend()
267 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
268 return mChannel
->Suspend();
272 nsURIChecker::Resume()
274 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
275 return mChannel
->Resume();
279 nsURIChecker::GetLoadGroup(nsILoadGroup
**aLoadGroup
)
281 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
282 return mChannel
->GetLoadGroup(aLoadGroup
);
286 nsURIChecker::SetLoadGroup(nsILoadGroup
*aLoadGroup
)
288 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
289 return mChannel
->SetLoadGroup(aLoadGroup
);
293 nsURIChecker::GetLoadFlags(nsLoadFlags
*aLoadFlags
)
295 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
296 return mChannel
->GetLoadFlags(aLoadFlags
);
300 nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags
)
302 NS_ENSURE_TRUE(mChannel
, NS_ERROR_NOT_INITIALIZED
);
303 return mChannel
->SetLoadFlags(aLoadFlags
);
306 //-----------------------------------------------------------------------------
307 // nsIRequestObserver methods:
308 //-----------------------------------------------------------------------------
311 nsURIChecker::OnStartRequest(nsIRequest
*aRequest
, nsISupports
*aCtxt
)
313 NS_ASSERTION(aRequest
== mChannel
, "unexpected request");
315 nsresult rv
= CheckStatus();
316 if (rv
!= NS_BASE_STREAM_WOULD_BLOCK
)
317 SetStatusAndCallBack(rv
);
319 // cancel the request (we don't care to look at the data).
320 return NS_BINDING_ABORTED
;
324 nsURIChecker::OnStopRequest(nsIRequest
*request
, nsISupports
*ctxt
,
327 // NOTE: we may have kicked off a subsequent request, so we should not do
328 // any cleanup unless this request matches the one we are currently using.
329 if (mChannel
== request
) {
330 // break reference cycle between us and the channel (see comment in
337 //-----------------------------------------------------------------------------
338 // nsIStreamListener methods:
339 //-----------------------------------------------------------------------------
342 nsURIChecker::OnDataAvailable(nsIRequest
*aRequest
, nsISupports
*aCtxt
,
343 nsIInputStream
*aInput
, PRUint32 aOffset
,
346 NS_NOTREACHED("nsURIChecker::OnDataAvailable");
347 return NS_BINDING_ABORTED
;
350 //-----------------------------------------------------------------------------
351 // nsIInterfaceRequestor methods:
352 //-----------------------------------------------------------------------------
355 nsURIChecker::GetInterface(const nsIID
& aIID
, void **aResult
)
357 if (mObserver
&& aIID
.Equals(NS_GET_IID(nsIAuthPrompt
))) {
358 nsCOMPtr
<nsIInterfaceRequestor
> req
= do_QueryInterface(mObserver
);
360 return req
->GetInterface(aIID
, aResult
);
362 return QueryInterface(aIID
, aResult
);
365 //-----------------------------------------------------------------------------
366 // nsIChannelEventSink methods:
367 //-----------------------------------------------------------------------------
370 nsURIChecker::OnChannelRedirect(nsIChannel
*aOldChannel
,
371 nsIChannel
*aNewChannel
,
374 // We have a new channel
375 mChannel
= aNewChannel
;