Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsURIChecker.cpp
blob9a65cb0a3ea42f00b361ea6d0a9f2f1810680921
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
13 * License.
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.
22 * Contributor(s):
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"
45 #include "nsString.h"
47 //-----------------------------------------------------------------------------
49 static PRBool
50 ServerIsNES3x(nsIHttpChannel *httpChannel)
52 nsCAutoString server;
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,
64 nsIURIChecker,
65 nsIRequest,
66 nsIRequestObserver,
67 nsIStreamListener,
68 nsIChannelEventSink,
69 nsIInterfaceRequestor)
71 nsURIChecker::nsURIChecker()
72 : mStatus(NS_OK)
73 , mIsPending(PR_FALSE)
74 , mAllowHead(PR_TRUE)
78 void
79 nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
81 mStatus = aStatus;
82 mIsPending = PR_FALSE;
84 if (mObserver) {
85 mObserver->OnStartRequest(this, mObserverContext);
86 mObserver->OnStopRequest(this, mObserverContext, mStatus);
87 mObserver = nsnull;
88 mObserverContext = nsnull;
92 nsresult
93 nsURIChecker::CheckStatus()
95 NS_ASSERTION(mChannel, "no channel");
97 nsresult status;
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);
106 if (!httpChannel)
107 return NS_BINDING_SUCCEEDED;
109 PRUint32 responseStatus;
110 rv = httpChannel->GetResponseStatus(&responseStatus);
111 if (NS_FAILED(rv))
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;
131 PRUint32 loadFlags;
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)) {
140 rv = Init(uri);
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 //-----------------------------------------------------------------------------
166 NS_IMETHODIMP
167 nsURIChecker::Init(nsIURI *aURI)
169 nsresult rv;
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;
176 if (mAllowHead) {
177 mAllowHead = PR_FALSE;
178 // See if it's an http channel, which needs special treatment:
179 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
180 if (httpChannel) {
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);
186 if (!isReallyHTTP)
187 aURI->SchemeIs("https", &isReallyHTTP);
188 if (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;
198 return NS_OK;
201 NS_IMETHODIMP
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
208 // cycle!)
209 mChannel->SetNotificationCallbacks(this);
211 // and start the request:
212 nsresult rv = mChannel->AsyncOpen(this, nsnull);
213 if (NS_FAILED(rv))
214 mChannel = nsnull;
215 else {
216 // ok, wait for OnStartRequest to fire.
217 mIsPending = PR_TRUE;
218 mObserver = aObserver;
219 mObserverContext = aObserverContext;
221 return rv;
224 NS_IMETHODIMP
225 nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
227 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
228 NS_ADDREF(*aChannel = mChannel);
229 return NS_OK;
232 //-----------------------------------------------------------------------------
233 // nsIRequest methods:
234 //-----------------------------------------------------------------------------
236 NS_IMETHODIMP
237 nsURIChecker::GetName(nsACString &aName)
239 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
240 return mChannel->GetName(aName);
243 NS_IMETHODIMP
244 nsURIChecker::IsPending(PRBool *aPendingRet)
246 *aPendingRet = mIsPending;
247 return NS_OK;
250 NS_IMETHODIMP
251 nsURIChecker::GetStatus(nsresult* aStatusRet)
253 *aStatusRet = mStatus;
254 return NS_OK;
257 NS_IMETHODIMP
258 nsURIChecker::Cancel(nsresult status)
260 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
261 return mChannel->Cancel(status);
264 NS_IMETHODIMP
265 nsURIChecker::Suspend()
267 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
268 return mChannel->Suspend();
271 NS_IMETHODIMP
272 nsURIChecker::Resume()
274 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
275 return mChannel->Resume();
278 NS_IMETHODIMP
279 nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
281 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
282 return mChannel->GetLoadGroup(aLoadGroup);
285 NS_IMETHODIMP
286 nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
288 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
289 return mChannel->SetLoadGroup(aLoadGroup);
292 NS_IMETHODIMP
293 nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
295 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
296 return mChannel->GetLoadFlags(aLoadFlags);
299 NS_IMETHODIMP
300 nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
302 NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
303 return mChannel->SetLoadFlags(aLoadFlags);
306 //-----------------------------------------------------------------------------
307 // nsIRequestObserver methods:
308 //-----------------------------------------------------------------------------
310 NS_IMETHODIMP
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;
323 NS_IMETHODIMP
324 nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
325 nsresult statusCode)
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
331 // AsyncCheckURI)
332 mChannel = nsnull;
334 return NS_OK;
337 //-----------------------------------------------------------------------------
338 // nsIStreamListener methods:
339 //-----------------------------------------------------------------------------
341 NS_IMETHODIMP
342 nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
343 nsIInputStream *aInput, PRUint32 aOffset,
344 PRUint32 aCount)
346 NS_NOTREACHED("nsURIChecker::OnDataAvailable");
347 return NS_BINDING_ABORTED;
350 //-----------------------------------------------------------------------------
351 // nsIInterfaceRequestor methods:
352 //-----------------------------------------------------------------------------
354 NS_IMETHODIMP
355 nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
357 if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
358 nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
359 if (req)
360 return req->GetInterface(aIID, aResult);
362 return QueryInterface(aIID, aResult);
365 //-----------------------------------------------------------------------------
366 // nsIChannelEventSink methods:
367 //-----------------------------------------------------------------------------
369 NS_IMETHODIMP
370 nsURIChecker::OnChannelRedirect(nsIChannel *aOldChannel,
371 nsIChannel *aNewChannel,
372 PRUint32 aFlags)
374 // We have a new channel
375 mChannel = aNewChannel;
376 return NS_OK;