nsUrlClassifierStreamUpdater.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsCRT.h"
  6. #include "nsIHttpChannel.h"
  7. #include "nsIObserverService.h"
  8. #include "nsIStringStream.h"
  9. #include "nsIUploadChannel.h"
  10. #include "nsIURI.h"
  11. #include "nsIUrlClassifierDBService.h"
  12. #include "nsNetUtil.h"
  13. #include "nsStreamUtils.h"
  14. #include "nsStringStream.h"
  15. #include "nsToolkitCompsCID.h"
  16. #include "nsUrlClassifierStreamUpdater.h"
  17. #include "mozilla/BasePrincipal.h"
  18. #include "mozilla/ErrorNames.h"
  19. #include "mozilla/Logging.h"
  20. #include "nsIInterfaceRequestor.h"
  21. #include "mozilla/LoadContext.h"
  22. #include "nsContentUtils.h"
  23. #include "nsIURLFormatter.h"
  24. using mozilla::DocShellOriginAttributes;
  25. static const char* gQuitApplicationMessage = "quit-application";
  26. // Limit the list file size to 32mb
  27. const uint32_t MAX_FILE_SIZE = (32 * 1024 * 1024);
  28. #undef LOG
  29. // MOZ_LOG=UrlClassifierStreamUpdater:5
  30. static mozilla::LazyLogModule gUrlClassifierStreamUpdaterLog("UrlClassifierStreamUpdater");
  31. #define LOG(args) TrimAndLog args
  32. // Calls nsIURLFormatter::TrimSensitiveURLs to remove sensitive
  33. // info from the logging message.
  34. static void TrimAndLog(const char* aFmt, ...)
  35. {
  36. nsString raw;
  37. va_list ap;
  38. va_start(ap, aFmt);
  39. raw.AppendPrintf(aFmt, ap);
  40. va_end(ap);
  41. nsCOMPtr<nsIURLFormatter> urlFormatter =
  42. do_GetService("@mozilla.org/toolkit/URLFormatterService;1");
  43. nsString trimmed;
  44. nsresult rv = urlFormatter->TrimSensitiveURLs(raw, trimmed);
  45. if (NS_FAILED(rv)) {
  46. trimmed = EmptyString();
  47. }
  48. MOZ_LOG(gUrlClassifierStreamUpdaterLog,
  49. mozilla::LogLevel::Debug,
  50. (NS_ConvertUTF16toUTF8(trimmed).get()));
  51. }
  52. // This class does absolutely nothing, except pass requests onto the DBService.
  53. ///////////////////////////////////////////////////////////////////////////////
  54. // nsIUrlClassiferStreamUpdater implementation
  55. // Handles creating/running the stream listener
  56. nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
  57. : mIsUpdating(false), mInitialized(false), mDownloadError(false),
  58. mBeganStream(false), mChannel(nullptr)
  59. {
  60. LOG(("nsUrlClassifierStreamUpdater init [this=%p]", this));
  61. }
  62. NS_IMPL_ISUPPORTS(nsUrlClassifierStreamUpdater,
  63. nsIUrlClassifierStreamUpdater,
  64. nsIUrlClassifierUpdateObserver,
  65. nsIRequestObserver,
  66. nsIStreamListener,
  67. nsIObserver,
  68. nsIInterfaceRequestor,
  69. nsITimerCallback)
  70. /**
  71. * Clear out the update.
  72. */
  73. void
  74. nsUrlClassifierStreamUpdater::DownloadDone()
  75. {
  76. LOG(("nsUrlClassifierStreamUpdater::DownloadDone [this=%p]", this));
  77. mIsUpdating = false;
  78. mPendingUpdates.Clear();
  79. mDownloadError = false;
  80. mSuccessCallback = nullptr;
  81. mUpdateErrorCallback = nullptr;
  82. mDownloadErrorCallback = nullptr;
  83. }
  84. ///////////////////////////////////////////////////////////////////////////////
  85. // nsIUrlClassifierStreamUpdater implementation
  86. nsresult
  87. nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
  88. const nsACString & aRequestPayload,
  89. bool aIsPostRequest,
  90. const nsACString & aStreamTable)
  91. {
  92. #ifdef DEBUG
  93. LOG(("Fetching update %s from %s",
  94. aRequestPayload.Data(), aUpdateUrl->GetSpecOrDefault().get()));
  95. #endif
  96. nsresult rv;
  97. uint32_t loadFlags = nsIChannel::INHIBIT_CACHING |
  98. nsIChannel::LOAD_BYPASS_CACHE;
  99. rv = NS_NewChannel(getter_AddRefs(mChannel),
  100. aUpdateUrl,
  101. nsContentUtils::GetSystemPrincipal(),
  102. nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
  103. nsIContentPolicy::TYPE_OTHER,
  104. nullptr, // aLoadGroup
  105. this, // aInterfaceRequestor
  106. loadFlags);
  107. NS_ENSURE_SUCCESS(rv, rv);
  108. nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
  109. loadInfo->SetOriginAttributes(mozilla::NeckoOriginAttributes(NECKO_SAFEBROWSING_APP_ID, false));
  110. mBeganStream = false;
  111. if (!aIsPostRequest) {
  112. // We use POST method to send our request in v2. In v4, the request
  113. // needs to be embedded to the URL and use GET method to send.
  114. // However, from the Chromium source code, a extended HTTP header has
  115. // to be sent along with the request to make the request succeed.
  116. // The following description is from Chromium source code:
  117. //
  118. // "The following header informs the envelope server (which sits in
  119. // front of Google's stubby server) that the received GET request should be
  120. // interpreted as a POST."
  121. //
  122. nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
  123. NS_ENSURE_SUCCESS(rv, rv);
  124. rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-HTTP-Method-Override"),
  125. NS_LITERAL_CSTRING("POST"),
  126. false);
  127. NS_ENSURE_SUCCESS(rv, rv);
  128. } else if (!aRequestPayload.IsEmpty()) {
  129. rv = AddRequestBody(aRequestPayload);
  130. NS_ENSURE_SUCCESS(rv, rv);
  131. }
  132. // Set the appropriate content type for file/data URIs, for unit testing
  133. // purposes.
  134. // This is only used for testing and should be deleted.
  135. bool match;
  136. if ((NS_SUCCEEDED(aUpdateUrl->SchemeIs("file", &match)) && match) ||
  137. (NS_SUCCEEDED(aUpdateUrl->SchemeIs("data", &match)) && match)) {
  138. mChannel->SetContentType(NS_LITERAL_CSTRING("application/vnd.google.safebrowsing-update"));
  139. } else {
  140. // We assume everything else is an HTTP request.
  141. // Disable keepalive.
  142. nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
  143. NS_ENSURE_SUCCESS(rv, rv);
  144. rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Connection"), NS_LITERAL_CSTRING("close"), false);
  145. NS_ENSURE_SUCCESS(rv, rv);
  146. }
  147. // Create a custom LoadContext for SafeBrowsing, so we can use callbacks on
  148. // the channel to query the appId which allows separation of safebrowsing
  149. // cookies in a separate jar.
  150. DocShellOriginAttributes attrs;
  151. attrs.mAppId = NECKO_SAFEBROWSING_APP_ID;
  152. nsCOMPtr<nsIInterfaceRequestor> sbContext = new mozilla::LoadContext(attrs);
  153. rv = mChannel->SetNotificationCallbacks(sbContext);
  154. NS_ENSURE_SUCCESS(rv, rv);
  155. // Make the request.
  156. rv = mChannel->AsyncOpen2(this);
  157. NS_ENSURE_SUCCESS(rv, rv);
  158. mStreamTable = aStreamTable;
  159. return NS_OK;
  160. }
  161. nsresult
  162. nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
  163. const nsACString & aRequestPayload,
  164. bool aIsPostRequest,
  165. const nsACString & aStreamTable)
  166. {
  167. LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get()));
  168. nsCString updateUrl(aUpdateUrl);
  169. if (!aIsPostRequest) {
  170. updateUrl.AppendPrintf("&$req=%s", nsCString(aRequestPayload).get());
  171. }
  172. nsCOMPtr<nsIURI> uri;
  173. nsresult rv = NS_NewURI(getter_AddRefs(uri), updateUrl);
  174. NS_ENSURE_SUCCESS(rv, rv);
  175. nsAutoCString urlSpec;
  176. uri->GetAsciiSpec(urlSpec);
  177. LOG(("(post) Fetching update from %s\n", urlSpec.get()));
  178. return FetchUpdate(uri, aRequestPayload, aIsPostRequest, aStreamTable);
  179. }
  180. NS_IMETHODIMP
  181. nsUrlClassifierStreamUpdater::DownloadUpdates(
  182. const nsACString &aRequestTables,
  183. const nsACString &aRequestPayload,
  184. bool aIsPostRequest,
  185. const nsACString &aUpdateUrl,
  186. nsIUrlClassifierCallback *aSuccessCallback,
  187. nsIUrlClassifierCallback *aUpdateErrorCallback,
  188. nsIUrlClassifierCallback *aDownloadErrorCallback,
  189. bool *_retval)
  190. {
  191. NS_ENSURE_ARG(aSuccessCallback);
  192. NS_ENSURE_ARG(aUpdateErrorCallback);
  193. NS_ENSURE_ARG(aDownloadErrorCallback);
  194. if (mIsUpdating) {
  195. LOG(("Already updating, queueing update %s from %s", aRequestPayload.Data(),
  196. aUpdateUrl.Data()));
  197. *_retval = false;
  198. PendingRequest *request = mPendingRequests.AppendElement();
  199. request->mTables = aRequestTables;
  200. request->mRequestPayload = aRequestPayload;
  201. request->mIsPostRequest = aIsPostRequest;
  202. request->mUrl = aUpdateUrl;
  203. request->mSuccessCallback = aSuccessCallback;
  204. request->mUpdateErrorCallback = aUpdateErrorCallback;
  205. request->mDownloadErrorCallback = aDownloadErrorCallback;
  206. return NS_OK;
  207. }
  208. if (aUpdateUrl.IsEmpty()) {
  209. NS_ERROR("updateUrl not set");
  210. return NS_ERROR_NOT_INITIALIZED;
  211. }
  212. nsresult rv;
  213. if (!mInitialized) {
  214. // Add an observer for shutdown so we can cancel any pending list
  215. // downloads. quit-application is the same event that the download
  216. // manager listens for and uses to cancel pending downloads.
  217. nsCOMPtr<nsIObserverService> observerService =
  218. mozilla::services::GetObserverService();
  219. if (!observerService)
  220. return NS_ERROR_FAILURE;
  221. observerService->AddObserver(this, gQuitApplicationMessage, false);
  222. mDBService = do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
  223. NS_ENSURE_SUCCESS(rv, rv);
  224. mInitialized = true;
  225. }
  226. rv = mDBService->BeginUpdate(this, aRequestTables);
  227. if (rv == NS_ERROR_NOT_AVAILABLE) {
  228. LOG(("Service busy, already updating, queuing update %s from %s",
  229. aRequestPayload.Data(), aUpdateUrl.Data()));
  230. *_retval = false;
  231. PendingRequest *request = mPendingRequests.AppendElement();
  232. request->mTables = aRequestTables;
  233. request->mRequestPayload = aRequestPayload;
  234. request->mIsPostRequest = aIsPostRequest;
  235. request->mUrl = aUpdateUrl;
  236. request->mSuccessCallback = aSuccessCallback;
  237. request->mUpdateErrorCallback = aUpdateErrorCallback;
  238. request->mDownloadErrorCallback = aDownloadErrorCallback;
  239. return NS_OK;
  240. }
  241. if (NS_FAILED(rv)) {
  242. return rv;
  243. }
  244. mSuccessCallback = aSuccessCallback;
  245. mUpdateErrorCallback = aUpdateErrorCallback;
  246. mDownloadErrorCallback = aDownloadErrorCallback;
  247. mIsUpdating = true;
  248. *_retval = true;
  249. LOG(("FetchUpdate: %s", aUpdateUrl.Data()));
  250. return FetchUpdate(aUpdateUrl, aRequestPayload, aIsPostRequest, EmptyCString());
  251. }
  252. ///////////////////////////////////////////////////////////////////////////////
  253. // nsIUrlClassifierUpdateObserver implementation
  254. NS_IMETHODIMP
  255. nsUrlClassifierStreamUpdater::UpdateUrlRequested(const nsACString &aUrl,
  256. const nsACString &aTable)
  257. {
  258. LOG(("Queuing requested update from %s\n", PromiseFlatCString(aUrl).get()));
  259. PendingUpdate *update = mPendingUpdates.AppendElement();
  260. if (!update)
  261. return NS_ERROR_OUT_OF_MEMORY;
  262. // Allow data: and file: urls for unit testing purposes, otherwise assume http
  263. if (StringBeginsWith(aUrl, NS_LITERAL_CSTRING("data:")) ||
  264. StringBeginsWith(aUrl, NS_LITERAL_CSTRING("file:"))) {
  265. update->mUrl = aUrl;
  266. } else {
  267. // For unittesting update urls to localhost should use http, not https
  268. // (otherwise the connection will fail silently, since there will be no
  269. // cert available).
  270. if (!StringBeginsWith(aUrl, NS_LITERAL_CSTRING("localhost"))) {
  271. update->mUrl = NS_LITERAL_CSTRING("https://") + aUrl;
  272. } else {
  273. update->mUrl = NS_LITERAL_CSTRING("http://") + aUrl;
  274. }
  275. }
  276. update->mTable = aTable;
  277. return NS_OK;
  278. }
  279. nsresult
  280. nsUrlClassifierStreamUpdater::FetchNext()
  281. {
  282. if (mPendingUpdates.Length() == 0) {
  283. return NS_OK;
  284. }
  285. PendingUpdate &update = mPendingUpdates[0];
  286. LOG(("Fetching update url: %s\n", update.mUrl.get()));
  287. nsresult rv = FetchUpdate(update.mUrl,
  288. EmptyCString(),
  289. true, // This method is for v2 and v2 is always a POST.
  290. update.mTable);
  291. if (NS_FAILED(rv)) {
  292. LOG(("Error fetching update url: %s\n", update.mUrl.get()));
  293. // We can commit the urls that we've applied so far. This is
  294. // probably a transient server problem, so trigger backoff.
  295. mDownloadErrorCallback->HandleEvent(EmptyCString());
  296. mDownloadError = true;
  297. mDBService->FinishUpdate();
  298. return rv;
  299. }
  300. mPendingUpdates.RemoveElementAt(0);
  301. return NS_OK;
  302. }
  303. nsresult
  304. nsUrlClassifierStreamUpdater::FetchNextRequest()
  305. {
  306. if (mPendingRequests.Length() == 0) {
  307. LOG(("No more requests, returning"));
  308. return NS_OK;
  309. }
  310. PendingRequest &request = mPendingRequests[0];
  311. LOG(("Stream updater: fetching next request: %s, %s",
  312. request.mTables.get(), request.mUrl.get()));
  313. bool dummy;
  314. DownloadUpdates(
  315. request.mTables,
  316. request.mRequestPayload,
  317. request.mIsPostRequest,
  318. request.mUrl,
  319. request.mSuccessCallback,
  320. request.mUpdateErrorCallback,
  321. request.mDownloadErrorCallback,
  322. &dummy);
  323. request.mSuccessCallback = nullptr;
  324. request.mUpdateErrorCallback = nullptr;
  325. request.mDownloadErrorCallback = nullptr;
  326. mPendingRequests.RemoveElementAt(0);
  327. return NS_OK;
  328. }
  329. NS_IMETHODIMP
  330. nsUrlClassifierStreamUpdater::StreamFinished(nsresult status,
  331. uint32_t requestedDelay)
  332. {
  333. // We are a service and may not be reset with Init between calls, so reset
  334. // mBeganStream manually.
  335. mBeganStream = false;
  336. LOG(("nsUrlClassifierStreamUpdater::StreamFinished [%x, %d]", status, requestedDelay));
  337. if (NS_FAILED(status) || mPendingUpdates.Length() == 0) {
  338. // We're done.
  339. LOG(("nsUrlClassifierStreamUpdater::Done [this=%p]", this));
  340. mDBService->FinishUpdate();
  341. return NS_OK;
  342. }
  343. // This timer is for fetching indirect updates ("forwards") from any "u:" lines
  344. // that we encountered while processing the server response. It is NOT for
  345. // scheduling the next time we pull the list from the server. That's a different
  346. // timer in listmanager.js (see bug 1110891).
  347. nsresult rv;
  348. mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  349. if (NS_SUCCEEDED(rv)) {
  350. rv = mTimer->InitWithCallback(this, requestedDelay,
  351. nsITimer::TYPE_ONE_SHOT);
  352. }
  353. if (NS_FAILED(rv)) {
  354. NS_WARNING("Unable to initialize timer, fetching next safebrowsing item immediately");
  355. return FetchNext();
  356. }
  357. return NS_OK;
  358. }
  359. NS_IMETHODIMP
  360. nsUrlClassifierStreamUpdater::UpdateSuccess(uint32_t requestedTimeout)
  361. {
  362. LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess [this=%p]", this));
  363. if (mPendingUpdates.Length() != 0) {
  364. NS_WARNING("Didn't fetch all safebrowsing update redirects");
  365. }
  366. // DownloadDone() clears mSuccessCallback, so we save it off here.
  367. nsCOMPtr<nsIUrlClassifierCallback> successCallback = mDownloadError ? nullptr : mSuccessCallback.get();
  368. DownloadDone();
  369. nsAutoCString strTimeout;
  370. strTimeout.AppendInt(requestedTimeout);
  371. if (successCallback) {
  372. LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess callback [this=%p]",
  373. this));
  374. successCallback->HandleEvent(strTimeout);
  375. } else {
  376. LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess skipping callback [this=%p]",
  377. this));
  378. }
  379. // Now fetch the next request
  380. LOG(("stream updater: calling into fetch next request"));
  381. FetchNextRequest();
  382. return NS_OK;
  383. }
  384. NS_IMETHODIMP
  385. nsUrlClassifierStreamUpdater::UpdateError(nsresult result)
  386. {
  387. LOG(("nsUrlClassifierStreamUpdater::UpdateError [this=%p]", this));
  388. // DownloadDone() clears mUpdateErrorCallback, so we save it off here.
  389. nsCOMPtr<nsIUrlClassifierCallback> errorCallback = mDownloadError ? nullptr : mUpdateErrorCallback.get();
  390. DownloadDone();
  391. nsAutoCString strResult;
  392. strResult.AppendInt(static_cast<uint32_t>(result));
  393. if (errorCallback) {
  394. errorCallback->HandleEvent(strResult);
  395. }
  396. return NS_OK;
  397. }
  398. nsresult
  399. nsUrlClassifierStreamUpdater::AddRequestBody(const nsACString &aRequestBody)
  400. {
  401. nsresult rv;
  402. nsCOMPtr<nsIStringInputStream> strStream =
  403. do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
  404. NS_ENSURE_SUCCESS(rv, rv);
  405. rv = strStream->SetData(aRequestBody.BeginReading(),
  406. aRequestBody.Length());
  407. NS_ENSURE_SUCCESS(rv, rv);
  408. nsCOMPtr<nsIUploadChannel> uploadChannel = do_QueryInterface(mChannel, &rv);
  409. NS_ENSURE_SUCCESS(rv, rv);
  410. rv = uploadChannel->SetUploadStream(strStream,
  411. NS_LITERAL_CSTRING("text/plain"),
  412. -1);
  413. NS_ENSURE_SUCCESS(rv, rv);
  414. nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
  415. NS_ENSURE_SUCCESS(rv, rv);
  416. rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
  417. NS_ENSURE_SUCCESS(rv, rv);
  418. return NS_OK;
  419. }
  420. ///////////////////////////////////////////////////////////////////////////////
  421. // nsIStreamListenerObserver implementation
  422. NS_IMETHODIMP
  423. nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request,
  424. nsISupports* context)
  425. {
  426. nsresult rv;
  427. bool downloadError = false;
  428. nsAutoCString strStatus;
  429. nsresult status = NS_OK;
  430. // Only update if we got http success header
  431. nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
  432. if (httpChannel) {
  433. rv = httpChannel->GetStatus(&status);
  434. NS_ENSURE_SUCCESS(rv, rv);
  435. if (MOZ_LOG_TEST(gUrlClassifierStreamUpdaterLog, mozilla::LogLevel::Debug)) {
  436. nsAutoCString errorName, spec;
  437. mozilla::GetErrorName(status, errorName);
  438. nsCOMPtr<nsIURI> uri;
  439. rv = httpChannel->GetURI(getter_AddRefs(uri));
  440. if (NS_SUCCEEDED(rv) && uri) {
  441. uri->GetAsciiSpec(spec);
  442. }
  443. LOG(("nsUrlClassifierStreamUpdater::OnStartRequest "
  444. "(status=%s, uri=%s, this=%p)", errorName.get(),
  445. spec.get(), this));
  446. }
  447. if (NS_FAILED(status)) {
  448. // Assume we're overloading the server and trigger backoff.
  449. downloadError = true;
  450. } else {
  451. bool succeeded = false;
  452. rv = httpChannel->GetRequestSucceeded(&succeeded);
  453. NS_ENSURE_SUCCESS(rv, rv);
  454. uint32_t requestStatus;
  455. rv = httpChannel->GetResponseStatus(&requestStatus);
  456. NS_ENSURE_SUCCESS(rv, rv);
  457. LOG(("nsUrlClassifierStreamUpdater::OnStartRequest %s (%d)", succeeded ?
  458. "succeeded" : "failed", requestStatus));
  459. if (!succeeded) {
  460. // 404 or other error, pass error status back
  461. strStatus.AppendInt(requestStatus);
  462. downloadError = true;
  463. }
  464. }
  465. }
  466. if (downloadError) {
  467. LOG(("nsUrlClassifierStreamUpdater::Download error [this=%p]", this));
  468. // It's possible for mDownloadErrorCallback to be null on shutdown.
  469. if (mDownloadErrorCallback) {
  470. mDownloadErrorCallback->HandleEvent(strStatus);
  471. }
  472. mDownloadError = true;
  473. status = NS_ERROR_ABORT;
  474. } else if (NS_SUCCEEDED(status)) {
  475. MOZ_ASSERT(mDownloadErrorCallback);
  476. mBeganStream = true;
  477. LOG(("nsUrlClassifierStreamUpdater::Beginning stream [this=%p]", this));
  478. rv = mDBService->BeginStream(mStreamTable);
  479. NS_ENSURE_SUCCESS(rv, rv);
  480. }
  481. mStreamTable.Truncate();
  482. return status;
  483. }
  484. NS_IMETHODIMP
  485. nsUrlClassifierStreamUpdater::OnDataAvailable(nsIRequest *request,
  486. nsISupports* context,
  487. nsIInputStream *aIStream,
  488. uint64_t aSourceOffset,
  489. uint32_t aLength)
  490. {
  491. if (!mDBService)
  492. return NS_ERROR_NOT_INITIALIZED;
  493. LOG(("OnDataAvailable (%d bytes)", aLength));
  494. if (aSourceOffset > MAX_FILE_SIZE) {
  495. LOG(("OnDataAvailable::Abort because exceeded the maximum file size(%lld)", aSourceOffset));
  496. return NS_ERROR_FILE_TOO_BIG;
  497. }
  498. nsresult rv;
  499. // Copy the data into a nsCString
  500. nsCString chunk;
  501. rv = NS_ConsumeStream(aIStream, aLength, chunk);
  502. NS_ENSURE_SUCCESS(rv, rv);
  503. //LOG(("Chunk (%d): %s\n\n", chunk.Length(), chunk.get()));
  504. rv = mDBService->UpdateStream(chunk);
  505. NS_ENSURE_SUCCESS(rv, rv);
  506. return NS_OK;
  507. }
  508. NS_IMETHODIMP
  509. nsUrlClassifierStreamUpdater::OnStopRequest(nsIRequest *request, nsISupports* context,
  510. nsresult aStatus)
  511. {
  512. if (!mDBService)
  513. return NS_ERROR_NOT_INITIALIZED;
  514. LOG(("OnStopRequest (status %x, beganStream %s, this=%p)", aStatus,
  515. mBeganStream ? "true" : "false", this));
  516. nsresult rv;
  517. if (NS_SUCCEEDED(aStatus)) {
  518. // Success, finish this stream and move on to the next.
  519. rv = mDBService->FinishStream();
  520. } else if (mBeganStream) {
  521. LOG(("OnStopRequest::Canceling update [this=%p]", this));
  522. // We began this stream and couldn't finish it. We have to cancel the
  523. // update, it's not in a consistent state.
  524. rv = mDBService->CancelUpdate();
  525. } else {
  526. LOG(("OnStopRequest::Finishing update [this=%p]", this));
  527. // The fetch failed, but we didn't start the stream (probably a
  528. // server or connection error). We can commit what we've applied
  529. // so far, and request again later.
  530. rv = mDBService->FinishUpdate();
  531. }
  532. mChannel = nullptr;
  533. // If the fetch failed, return the network status rather than NS_OK, the
  534. // result of finishing a possibly-empty update
  535. if (NS_SUCCEEDED(aStatus)) {
  536. return rv;
  537. }
  538. return aStatus;
  539. }
  540. ///////////////////////////////////////////////////////////////////////////////
  541. // nsIObserver implementation
  542. NS_IMETHODIMP
  543. nsUrlClassifierStreamUpdater::Observe(nsISupports *aSubject, const char *aTopic,
  544. const char16_t *aData)
  545. {
  546. if (nsCRT::strcmp(aTopic, gQuitApplicationMessage) == 0) {
  547. if (mIsUpdating && mChannel) {
  548. LOG(("Cancel download"));
  549. nsresult rv;
  550. rv = mChannel->Cancel(NS_ERROR_ABORT);
  551. NS_ENSURE_SUCCESS(rv, rv);
  552. mIsUpdating = false;
  553. mChannel = nullptr;
  554. }
  555. if (mTimer) {
  556. mTimer->Cancel();
  557. mTimer = nullptr;
  558. }
  559. }
  560. return NS_OK;
  561. }
  562. ///////////////////////////////////////////////////////////////////////////////
  563. // nsIInterfaceRequestor implementation
  564. NS_IMETHODIMP
  565. nsUrlClassifierStreamUpdater::GetInterface(const nsIID & eventSinkIID, void* *_retval)
  566. {
  567. return QueryInterface(eventSinkIID, _retval);
  568. }
  569. ///////////////////////////////////////////////////////////////////////////////
  570. // nsITimerCallback implementation
  571. NS_IMETHODIMP
  572. nsUrlClassifierStreamUpdater::Notify(nsITimer *timer)
  573. {
  574. LOG(("nsUrlClassifierStreamUpdater::Notify [%p]", this));
  575. mTimer = nullptr;
  576. // Start the update process up again.
  577. FetchNext();
  578. return NS_OK;
  579. }