Back to index

lightning-sunbird  0.9+nobinonly
nsUrlClassifierStreamUpdater.cpp
Go to the documentation of this file.
00001 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Url Classifier code
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Google Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2006
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Tony Chang <tony@ponderer.org> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsIURI.h"
00040 #include "nsIUrlClassifierDBService.h"
00041 #include "nsNetUtil.h"
00042 #include "nsStreamUtils.h"
00043 #include "nsToolkitCompsCID.h"
00044 #include "nsUrlClassifierStreamUpdater.h"
00045 #include "prlog.h"
00046 
00047 // NSPR_LOG_MODULES=UrlClassifierStreamUpdater:5
00048 #if defined(PR_LOGGING)
00049 static const PRLogModuleInfo *gUrlClassifierStreamUpdaterLog = nsnull;
00050 #define LOG(args) PR_LOG(gUrlClassifierStreamUpdaterLog, PR_LOG_DEBUG, args)
00051 #else
00052 #define LOG(args)
00053 #endif
00054 
00056 // Stream listener for incremental download of url tables.
00057 
00058 class nsUrlClassifierStreamUpdater;
00059 
00060 class TableUpdateListener : public nsIStreamListener
00061 {
00062 public:
00063   TableUpdateListener(nsIUrlClassifierCallback *aTableCallback,
00064                       nsIUrlClassifierCallback *aErrorCallback);
00065   nsCOMPtr<nsIUrlClassifierDBService> mDBService;
00066 
00067   NS_DECL_ISUPPORTS
00068   NS_DECL_NSIREQUESTOBSERVER
00069   NS_DECL_NSISTREAMLISTENER
00070 
00071 private:
00072   ~TableUpdateListener() {};
00073 
00074   // Callback when table updates complete.
00075   nsCOMPtr<nsIUrlClassifierCallback> mTableCallback;
00076   nsCOMPtr<nsIUrlClassifierCallback> mErrorCallback;
00077 };
00078 
00079 TableUpdateListener::TableUpdateListener(
00080                                 nsIUrlClassifierCallback *aTableCallback,
00081                                 nsIUrlClassifierCallback *aErrorCallback)
00082 {
00083   mTableCallback = aTableCallback;
00084   mErrorCallback = aErrorCallback;
00085 }
00086 
00087 NS_IMPL_ISUPPORTS2(TableUpdateListener, nsIStreamListener, nsIRequestObserver)
00088 
00089 NS_IMETHODIMP
00090 TableUpdateListener::OnStartRequest(nsIRequest *request, nsISupports* context)
00091 {
00092   nsresult rv;
00093   if (!mDBService) {
00094     mDBService = do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
00095     NS_ENSURE_SUCCESS(rv, rv);
00096   }
00097 
00098   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
00099   NS_ENSURE_STATE(httpChannel);
00100 
00101   nsresult status;
00102   rv = httpChannel->GetStatus(&status);
00103   NS_ENSURE_SUCCESS(rv, rv);
00104   if (NS_ERROR_CONNECTION_REFUSED == status ||
00105       NS_ERROR_NET_TIMEOUT == status) {
00106     // Assume that we're overloading the server and trigger backoff.
00107     mErrorCallback->HandleEvent(nsCString());
00108     return NS_ERROR_ABORT;
00109   }
00110 
00111   return NS_OK;
00112 }
00113 
00114 NS_IMETHODIMP
00115 TableUpdateListener::OnDataAvailable(nsIRequest *request,
00116                                      nsISupports* context,
00117                                      nsIInputStream *aIStream,
00118                                      PRUint32 aSourceOffset,
00119                                      PRUint32 aLength)
00120 {
00121   if (!mDBService)
00122     return NS_ERROR_NOT_INITIALIZED;
00123 
00124   LOG(("OnDataAvailable (%d bytes)", aLength));
00125 
00126   // Only update if we got http success header
00127   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
00128   NS_ENSURE_STATE(httpChannel);
00129 
00130   nsresult rv;
00131   PRBool succeeded = PR_FALSE;
00132   rv = httpChannel->GetRequestSucceeded(&succeeded);
00133   NS_ENSURE_SUCCESS(rv, rv);
00134 
00135   if (!succeeded) {
00136     // 404 or other error, pass error status back
00137     LOG(("HTTP request returned failure code."));
00138 
00139     PRUint32 status;
00140     rv = httpChannel->GetResponseStatus(&status);
00141     NS_ENSURE_SUCCESS(rv, rv);
00142 
00143     nsCAutoString strStatus;
00144     strStatus.AppendInt(status);
00145     mErrorCallback->HandleEvent(strStatus);
00146     return NS_ERROR_ABORT;
00147   }
00148 
00149   // Copy the data into a nsCString
00150   nsCString chunk;
00151   rv = NS_ConsumeStream(aIStream, aLength, chunk);
00152   NS_ENSURE_SUCCESS(rv, rv);
00153 
00154   //LOG(("Chunk (%d): %s\n\n", chunk.Length(), chunk.get()));
00155 
00156   rv = mDBService->Update(chunk);
00157   NS_ENSURE_SUCCESS(rv, rv);
00158 
00159   return NS_OK;
00160 }
00161 
00162 NS_IMETHODIMP
00163 TableUpdateListener::OnStopRequest(nsIRequest *request, nsISupports* context,
00164                                    nsresult aStatus)
00165 {
00166   if (!mDBService)
00167     return NS_ERROR_NOT_INITIALIZED;
00168 
00169   LOG(("OnStopRequest status: %d", aStatus));
00170 
00171   // If we got the whole stream, call Finish to commit the changes.
00172   // Otherwise, call Cancel to rollback the changes.
00173   nsresult rv;
00174   if (NS_SUCCEEDED(aStatus))
00175     rv = mDBService->Finish(mTableCallback);
00176   else
00177     rv = mDBService->CancelStream();
00178   NS_ENSURE_SUCCESS(rv, rv);
00179 
00180   nsUrlClassifierStreamUpdater* updater =
00181         NS_STATIC_CAST(nsUrlClassifierStreamUpdater*, context);
00182   NS_ASSERTION(updater != nsnull, "failed to cast context");
00183   updater->mIsUpdating = PR_FALSE;
00184 
00185   return NS_OK;
00186 }
00187 
00189 // nsIUrlClassiferStreamUpdater implementation
00190 // Handles creating/running the stream listener
00191 
00192 nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
00193   : mIsUpdating(PR_FALSE), mUpdateUrl(nsnull)
00194 {
00195 #if defined(PR_LOGGING)
00196   if (!gUrlClassifierStreamUpdaterLog)
00197     gUrlClassifierStreamUpdaterLog = PR_NewLogModule("UrlClassifierStreamUpdater");
00198 #endif
00199 }
00200 
00201 NS_IMPL_ISUPPORTS1(nsUrlClassifierStreamUpdater, nsIUrlClassifierStreamUpdater)
00202 
00203 NS_IMETHODIMP
00204 nsUrlClassifierStreamUpdater::GetUpdateUrl(nsACString & aUpdateUrl)
00205 {
00206   if (mUpdateUrl) {
00207     mUpdateUrl->GetSpec(aUpdateUrl);
00208   } else {
00209     aUpdateUrl.Truncate();
00210   }
00211   return NS_OK;
00212 }
00213 
00214 NS_IMETHODIMP
00215 nsUrlClassifierStreamUpdater::SetUpdateUrl(const nsACString & aUpdateUrl)
00216 {
00217   nsresult rv = NS_NewURI(getter_AddRefs(mUpdateUrl), aUpdateUrl);
00218   NS_ENSURE_SUCCESS(rv, rv);
00219 
00220   return NS_OK;
00221 }
00222 
00223 NS_IMETHODIMP
00224 nsUrlClassifierStreamUpdater::DownloadUpdates(
00225                                 nsIUrlClassifierCallback *aTableCallback,
00226                                 nsIUrlClassifierCallback *aErrorCallback,
00227                                 PRBool *_retval)
00228 {
00229   if (mIsUpdating) {
00230     LOG(("already updating, skipping update"));
00231     *_retval = PR_FALSE;
00232     return NS_OK;
00233   }
00234 
00235   if (!mUpdateUrl) {
00236     NS_ERROR("updateUrl not set");
00237     return NS_ERROR_NOT_INITIALIZED;
00238   }
00239 
00240   // Ok, try to create the download channel.
00241   nsresult rv;
00242   nsCOMPtr<nsIChannel> channel;
00243   rv = NS_NewChannel(getter_AddRefs(channel), mUpdateUrl);
00244   NS_ENSURE_SUCCESS(rv, rv);
00245 
00246   // Bind to a different callback each time we invoke this method.
00247   mListener = new TableUpdateListener(aTableCallback, aErrorCallback);
00248 
00249   // Make the request
00250   rv = channel->AsyncOpen(mListener.get(), this);
00251   NS_ENSURE_SUCCESS(rv, rv);
00252 
00253   mIsUpdating = PR_TRUE;
00254   *_retval = PR_TRUE;
00255 
00256   return NS_OK;
00257 }