From 019c6a51fd8ede0aa401cfad44e78ee29300bdd0 Mon Sep 17 00:00:00 2001 From: Henry Chang Date: Thu, 4 Aug 2016 18:10:06 +0800 Subject: [PATCH] Bug 1274112 - Part 1: Make update request v4. r=francois MozReview-Commit-ID: NgV4QYbDll --HG-- extra : rebase_source : 0c6c000e81e73617c6616dfa39fa868e35a43f9c --- modules/libpref/init/all.js | 2 +- .../test/unit/test_cookiejars_safebrowsing.js | 2 +- .../downloads/test/unit/test_app_rep.js | 1 + .../test/unit/test_app_rep_maclinux.js | 1 + .../test/unit/test_app_rep_windows.js | 1 + .../url-classifier/content/listmanager.js | 58 ++++++++--- .../nsIUrlClassifierStreamUpdater.idl | 7 +- .../nsUrlClassifierStreamUpdater.cpp | 63 ++++++++---- .../nsUrlClassifierStreamUpdater.h | 9 +- .../url-classifier/nsUrlClassifierUtils.cpp | 3 + .../tests/unit/head_urlclassifier.js | 2 +- .../tests/unit/test_digest256.js | 1 + .../tests/unit/test_listmanager.js | 95 ++++++++++++------- 13 files changed, 173 insertions(+), 72 deletions(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index fb02d3b4da51..5330de76ccd1 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5133,7 +5133,7 @@ pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.goo // Prefs for v4. pref("browser.safebrowsing.provider.google4.pver", "4"); pref("browser.safebrowsing.provider.google4.lists", "goog-phish-proto,googpub-phish-proto,goog-malware-proto,goog-unwanted-proto"); -pref("browser.safebrowsing.provider.google4.updateURL", "https://safebrowsing.googleapis.com/v4/threatListUpdates:fetch?$req=%REQUEST_BASE64%&$ct=application/x-protobuf&key=%GOOGLE_API_KEY%"); +pref("browser.safebrowsing.provider.google4.updateURL", "https://safebrowsing.googleapis.com/v4/threatListUpdates:fetch?$ct=application/x-protobuf&key=%GOOGLE_API_KEY%"); pref("browser.safebrowsing.provider.google4.gethashURL", "https://safebrowsing.googleapis.com/v4/fullHashes:find?$req=%REQUEST_BASE64%&$ct=application/x-protobuf&key=%GOOGLE_API_KEY%"); pref("browser.safebrowsing.provider.google4.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); diff --git a/netwerk/test/unit/test_cookiejars_safebrowsing.js b/netwerk/test/unit/test_cookiejars_safebrowsing.js index dc56d9beb6dd..c9c89cce8a9c 100644 --- a/netwerk/test/unit/test_cookiejars_safebrowsing.js +++ b/netwerk/test/unit/test_cookiejars_safebrowsing.js @@ -115,7 +115,7 @@ add_test(function test_safebrowsing_update() { } streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple", "", - URL + safebrowsingUpdatePath, onSuccess, onUpdateError, onDownloadError); + true, URL + safebrowsingUpdatePath, onSuccess, onUpdateError, onDownloadError); }); add_test(function test_non_safebrowsing_cookie() { diff --git a/toolkit/components/downloads/test/unit/test_app_rep.js b/toolkit/components/downloads/test/unit/test_app_rep.js index b9468b8ff5ea..5c1a286296b9 100644 --- a/toolkit/components/downloads/test/unit/test_app_rep.js +++ b/toolkit/components/downloads/test/unit/test_app_rep.js @@ -220,6 +220,7 @@ add_test(function test_local_list() { streamUpdater.downloadUpdates( "goog-downloadwhite-digest256,goog-badbinurl-shavar", "goog-downloadwhite-digest256,goog-badbinurl-shavar;\n", + true, // isPostRequest. "http://localhost:4444/downloads", updateSuccess, handleError, handleError); }); diff --git a/toolkit/components/downloads/test/unit/test_app_rep_maclinux.js b/toolkit/components/downloads/test/unit/test_app_rep_maclinux.js index d4ac9e192d83..97228fa02342 100644 --- a/toolkit/components/downloads/test/unit/test_app_rep_maclinux.js +++ b/toolkit/components/downloads/test/unit/test_app_rep_maclinux.js @@ -197,6 +197,7 @@ function waitForUpdates() { streamUpdater.downloadUpdates( "goog-downloadwhite-digest256", "goog-downloadwhite-digest256;\n", + true, "http://localhost:4444/downloads", updateSuccess, handleError, handleError); return deferred.promise; diff --git a/toolkit/components/downloads/test/unit/test_app_rep_windows.js b/toolkit/components/downloads/test/unit/test_app_rep_windows.js index 3b71e710664d..d1b971ed0263 100644 --- a/toolkit/components/downloads/test/unit/test_app_rep_windows.js +++ b/toolkit/components/downloads/test/unit/test_app_rep_windows.js @@ -297,6 +297,7 @@ function waitForUpdates() { streamUpdater.downloadUpdates( "goog-downloadwhite-digest256", "goog-downloadwhite-digest256;\n", + true, "http://localhost:4444/downloads", updateSuccess, handleError, handleError); return deferred.promise; diff --git a/toolkit/components/url-classifier/content/listmanager.js b/toolkit/components/url-classifier/content/listmanager.js index c6dc4d6d1a30..69673220b46c 100644 --- a/toolkit/components/url-classifier/content/listmanager.js +++ b/toolkit/components/url-classifier/content/listmanager.js @@ -12,7 +12,7 @@ Cu.import("resource://gre/modules/Services.jsm"); // // There is a single listmanager for the whole application. // -// TODO more comprehensive update tests, for example add unittest check +// TODO more comprehensive update tests, for example add unittest check // that the listmanagers tables are properly written on updates // Lower and upper limits on the server-provided polling frequency @@ -352,8 +352,13 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) { // tableNames: map of tables that need updating, // request: list of tables and existing chunk ranges from tableData // } - var streamerMap = { tableList: null, tableNames: {}, request: "" }; + var streamerMap = { tableList: null, + tableNames: {}, + requestPayload: "", + isPostRequest: true }; + let useProtobuf = false; + let onceThru = false; for (var tableName in this.tablesData) { // Skip tables not matching this update url if (this.tablesData[tableName].updateUrl != updateUrl) { @@ -364,11 +369,13 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) { // We use the table name 'goog-*-proto' and an additional provider "google4" // to describe the v4 settings. let isCurTableProto = tableName.endsWith('-proto'); - if (useProtobuf && !isCurTableProto) { - log('ERROR: Tables for the same updateURL should all be "proto" or none. ' + - 'Check "browser.safebrowsing.provider.google4.lists"'); + if (!onceThru) { + useProtobuf = isCurTableProto; + onceThru = true; + } else if (useProtobuf !== isCurTableProto) { + log('ERROR: Cannot mix "proto" tables with other types ' + + 'within the same provider.'); } - useProtobuf = isCurTableProto; if (this.needsUpdate_[this.tablesData[tableName].updateUrl][tableName]) { streamerMap.tableNames[tableName] = true; @@ -381,8 +388,24 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) { } if (useProtobuf) { - // TODO: Bug 1275507 - XPCOM API to build v4 update request. - streamerMap.request = ""; + let tableArray = streamerMap.tableList.split(','); + + // The state is a byte stream which server told us from the + // last table update. The state would be used to do the partial + // update and the empty string means the table has + // never been downloaded. See Bug 1287058 for supporting + // partial update. + let stateArray = []; + tableArray.forEach(() => stateArray.push('')); + + let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"] + .getService(Ci.nsIUrlClassifierUtils); + let requestPayload = urlUtils.makeUpdateRequestV4(tableArray, + stateArray, + tableArray.length); + // Use a base64-encoded request. + streamerMap.requestPayload = btoa(requestPayload); + streamerMap.isPostRequest = false; } else { // Build the request. For each table already in the database, include the // chunk data from the database @@ -391,23 +414,26 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) { var fields = lines[i].split(";"); var name = fields[0]; if (streamerMap.tableNames[name]) { - streamerMap.request += lines[i] + "\n"; + streamerMap.requestPayload += lines[i] + "\n"; delete streamerMap.tableNames[name]; } } // For each requested table that didn't have chunk data in the database, // request it fresh for (let tableName in streamerMap.tableNames) { - streamerMap.request += tableName + ";\n"; + streamerMap.requestPayload += tableName + ";\n"; } + + streamerMap.isPostRequest = true; } log("update request: " + JSON.stringify(streamerMap, undefined, 2) + "\n"); // Don't send an empty request. - if (streamerMap.request.length > 0) { + if (streamerMap.requestPayload.length > 0) { this.makeUpdateRequestForEntry_(updateUrl, streamerMap.tableList, - streamerMap.request); + streamerMap.requestPayload, + streamerMap.isPostRequest); } else { // We were disabled between kicking off getTables and now. log("Not sending empty request"); @@ -416,8 +442,9 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) { PROT_ListManager.prototype.makeUpdateRequestForEntry_ = function(updateUrl, tableList, - request) { - log("makeUpdateRequestForEntry_: request " + request + + requestPayload, + isPostRequest) { + log("makeUpdateRequestForEntry_: requestPayload " + requestPayload + " update: " + updateUrl + " tablelist: " + tableList + "\n"); var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"] .getService(Ci.nsIUrlClassifierStreamUpdater); @@ -426,7 +453,8 @@ PROT_ListManager.prototype.makeUpdateRequestForEntry_ = function(updateUrl, if (!streamer.downloadUpdates( tableList, - request, + requestPayload, + isPostRequest, updateUrl, BindToObject(this.updateSuccess_, this, tableList, updateUrl), BindToObject(this.updateError_, this, tableList, updateUrl), diff --git a/toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl b/toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl index ff80b856f0b1..50844d0e03cf 100644 --- a/toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl +++ b/toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl @@ -20,7 +20,9 @@ interface nsIUrlClassifierStreamUpdater : nsISupports * as well as in testing. * @param aRequestTables Comma-separated list of tables included in this * update. - * @param aRequestBody The body for the request. + * @param aRequestPayload The payload for the request. + * @param aIsPostRequest Whether the request should be sent by POST method. + * Should be 'true' for v2 usage. * @param aUpdateUrl The plaintext url from which to request updates. * @param aSuccessCallback Called after a successful update. * @param aUpdateErrorCallback Called for problems applying the update @@ -28,7 +30,8 @@ interface nsIUrlClassifierStreamUpdater : nsISupports * connection refused error. */ boolean downloadUpdates(in ACString aRequestTables, - in ACString aRequestBody, + in ACString aRequestPayload, + in boolean aIsPostRequest, in ACString aUpdateUrl, in nsIUrlClassifierCallback aSuccessCallback, in nsIUrlClassifierCallback aUpdateErrorCallback, diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp index 3c6fb1acd7e3..177440cfea41 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp @@ -103,7 +103,8 @@ nsUrlClassifierStreamUpdater::DownloadDone() nsresult nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl, - const nsACString & aRequestBody, + const nsACString & aRequestPayload, + bool aIsPostRequest, const nsACString & aStreamTable) { @@ -111,7 +112,7 @@ nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl, { nsCString spec; aUpdateUrl->GetSpec(spec); - LOG(("Fetching update %s from %s", aRequestBody.Data(), spec.get())); + LOG(("Fetching update %s from %s", aRequestPayload.Data(), spec.get())); } #endif @@ -134,9 +135,26 @@ nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl, mBeganStream = false; - // If aRequestBody is empty, construct it for the test. - if (!aRequestBody.IsEmpty()) { - rv = AddRequestBody(aRequestBody); + if (!aIsPostRequest) { + // We use POST method to send our request in v2. In v4, the request + // needs to be embedded to the URL and use GET method to send. + // However, from the Chromium source code, a extended HTTP header has + // to be sent along with the request to make the request succeed. + // The following description is from Chromium source code: + // + // "The following header informs the envelope server (which sits in + // front of Google's stubby server) that the received GET request should be + // interpreted as a POST." + // + nsCOMPtr httpChannel = do_QueryInterface(mChannel, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-HTTP-Method-Override"), + NS_LITERAL_CSTRING("POST"), + false); + NS_ENSURE_SUCCESS(rv, rv); + } else if (!aRequestPayload.IsEmpty()) { + rv = AddRequestBody(aRequestPayload); NS_ENSURE_SUCCESS(rv, rv); } @@ -176,13 +194,19 @@ nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl, nsresult nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl, - const nsACString & aRequestBody, + const nsACString & aRequestPayload, + bool aIsPostRequest, const nsACString & aStreamTable) { LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get())); + nsCString updateUrl(aUpdateUrl); + if (!aIsPostRequest) { + updateUrl.AppendPrintf("&$req=%s", nsCString(aRequestPayload).get()); + } + nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aUpdateUrl); + nsresult rv = NS_NewURI(getter_AddRefs(uri), updateUrl); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString urlSpec; @@ -190,13 +214,14 @@ nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl, LOG(("(post) Fetching update from %s\n", urlSpec.get())); - return FetchUpdate(uri, aRequestBody, aStreamTable); + return FetchUpdate(uri, aRequestPayload, aIsPostRequest, aStreamTable); } NS_IMETHODIMP nsUrlClassifierStreamUpdater::DownloadUpdates( const nsACString &aRequestTables, - const nsACString &aRequestBody, + const nsACString &aRequestPayload, + bool aIsPostRequest, const nsACString &aUpdateUrl, nsIUrlClassifierCallback *aSuccessCallback, nsIUrlClassifierCallback *aUpdateErrorCallback, @@ -208,12 +233,13 @@ nsUrlClassifierStreamUpdater::DownloadUpdates( NS_ENSURE_ARG(aDownloadErrorCallback); if (mIsUpdating) { - LOG(("Already updating, queueing update %s from %s", aRequestBody.Data(), + LOG(("Already updating, queueing update %s from %s", aRequestPayload.Data(), aUpdateUrl.Data())); *_retval = false; PendingRequest *request = mPendingRequests.AppendElement(); request->mTables = aRequestTables; - request->mRequest = aRequestBody; + request->mRequestPayload = aRequestPayload; + request->mIsPostRequest = aIsPostRequest; request->mUrl = aUpdateUrl; request->mSuccessCallback = aSuccessCallback; request->mUpdateErrorCallback = aUpdateErrorCallback; @@ -248,11 +274,12 @@ nsUrlClassifierStreamUpdater::DownloadUpdates( rv = mDBService->BeginUpdate(this, aRequestTables); if (rv == NS_ERROR_NOT_AVAILABLE) { LOG(("Service busy, already updating, queuing update %s from %s", - aRequestBody.Data(), aUpdateUrl.Data())); + aRequestPayload.Data(), aUpdateUrl.Data())); *_retval = false; PendingRequest *request = mPendingRequests.AppendElement(); request->mTables = aRequestTables; - request->mRequest = aRequestBody; + request->mRequestPayload = aRequestPayload; + request->mIsPostRequest = aIsPostRequest; request->mUrl = aUpdateUrl; request->mSuccessCallback = aSuccessCallback; request->mUpdateErrorCallback = aUpdateErrorCallback; @@ -272,9 +299,8 @@ nsUrlClassifierStreamUpdater::DownloadUpdates( *_retval = true; LOG(("FetchUpdate: %s", aUpdateUrl.Data())); - //LOG(("requestBody: %s", aRequestBody.Data())); - return FetchUpdate(aUpdateUrl, aRequestBody, EmptyCString()); + return FetchUpdate(aUpdateUrl, aRequestPayload, aIsPostRequest, EmptyCString()); } /////////////////////////////////////////////////////////////////////////////// @@ -318,7 +344,9 @@ nsUrlClassifierStreamUpdater::FetchNext() PendingUpdate &update = mPendingUpdates[0]; LOG(("Fetching update url: %s\n", update.mUrl.get())); - nsresult rv = FetchUpdate(update.mUrl, EmptyCString(), + nsresult rv = FetchUpdate(update.mUrl, + EmptyCString(), + true, // This method is for v2 and v2 is always a POST. update.mTable); if (NS_FAILED(rv)) { LOG(("Error fetching update url: %s\n", update.mUrl.get())); @@ -349,7 +377,8 @@ nsUrlClassifierStreamUpdater::FetchNextRequest() bool dummy; DownloadUpdates( request.mTables, - request.mRequest, + request.mRequestPayload, + request.mIsPostRequest, request.mUrl, request.mSuccessCallback, request.mUpdateErrorCallback, diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h index 75c3962db5ef..b24df61d2d4d 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h +++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h @@ -54,11 +54,13 @@ private: // Fetches an update for a single table. nsresult FetchUpdate(nsIURI *aURI, - const nsACString &aRequestBody, + const nsACString &aRequest, + bool aIsPostRequest, const nsACString &aTable); // Dumb wrapper so we don't have to create URIs. nsresult FetchUpdate(const nsACString &aURI, - const nsACString &aRequestBody, + const nsACString &aRequest, + bool aIsPostRequest, const nsACString &aTable); // Fetches the next table, from mPendingUpdates. @@ -78,7 +80,8 @@ private: struct PendingRequest { nsCString mTables; - nsCString mRequest; + nsCString mRequestPayload; + bool mIsPostRequest; nsCString mUrl; nsCOMPtr mSuccessCallback; nsCOMPtr mUpdateErrorCallback; diff --git a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp index 856134ace176..3bd28a9e1a82 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp @@ -211,6 +211,9 @@ static const struct { { "googpub-phish-proto", SOCIAL_ENGINEERING_PUBLIC}, // 2 { "goog-unwanted-proto", UNWANTED_SOFTWARE}, // 3 { "goog-phish-proto", SOCIAL_ENGINEERING}, // 5 + + // For testing purpose. + { "test-phish-proto", SOCIAL_ENGINEERING_PUBLIC}, // 2 }; NS_IMETHODIMP diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js index 2f6901836222..69b3867a3c06 100644 --- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js +++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js @@ -200,7 +200,7 @@ function doStreamUpdate(updateText, success, failure, downloadFailure) { downloadFailure = failure; } - streamUpdater.downloadUpdates(allTables, "", + streamUpdater.downloadUpdates(allTables, "", true, dataUpdate, success, failure, downloadFailure); } diff --git a/toolkit/components/url-classifier/tests/unit/test_digest256.js b/toolkit/components/url-classifier/tests/unit/test_digest256.js index 123bca2e1789..6ae652915939 100644 --- a/toolkit/components/url-classifier/tests/unit/test_digest256.js +++ b/toolkit/components/url-classifier/tests/unit/test_digest256.js @@ -118,6 +118,7 @@ add_test(function test_update() { streamUpdater.downloadUpdates( "goog-downloadwhite-digest256", "goog-downloadwhite-digest256;\n", + true, "http://localhost:4444/downloads", updateSuccess, handleError, handleError); }); diff --git a/toolkit/components/url-classifier/tests/unit/test_listmanager.js b/toolkit/components/url-classifier/tests/unit/test_listmanager.js index 93478b67eda4..f8434def637e 100644 --- a/toolkit/components/url-classifier/tests/unit/test_listmanager.js +++ b/toolkit/components/url-classifier/tests/unit/test_listmanager.js @@ -31,31 +31,36 @@ const TEST_TABLE_DATA_LIST = [ } ]; -// This table has a different update URL. -const TEST_TABLE_DATA_ANOTHER = { - tableName: "test-listmanageranother-digest256", - providerName: "google", - updateUrl: "http://localhost:5555/safebrowsing/update", - gethashUrl: "http://localhost:5555/safebrowsing/gethash-another", +// This table has a different update URL (for v4). +const TEST_TABLE_DATA_V4 = { + tableName: "test-phish-proto", + providerName: "google4", + updateUrl: "http://localhost:5555/safebrowsing/update?", + gethashUrl: "http://localhost:5555/safebrowsing/gethash-v4", }; const PREF_NEXTUPDATETIME = "browser.safebrowsing.provider.google.nextupdatetime"; +const PREF_NEXTUPDATETIME_V4 = "browser.safebrowsing.provider.google4.nextupdatetime"; let gListManager = Cc["@mozilla.org/url-classifier/listmanager;1"] .getService(Ci.nsIUrlListManager); +let gUrlUtils = Cc["@mozilla.org/url-classifier/utils;1"] + .getService(Ci.nsIUrlClassifierUtils); + // Global test server for serving safebrowsing updates. let gHttpServ = null; let gUpdateResponse = ""; let gExpectedUpdateRequest = ""; +let gExpectedQueryV4 = ""; -// Handles request for TEST_TABLE_DATA_ANOTHER. -let gHttpServAnother = null; +// Handles request for TEST_TABLE_DATA_V4. +let gHttpServV4 = null; // These two variables are used to synchronize the last two racing updates // (in terms of "update URL") in test_update_all_tables(). let gUpdatedCntForTableData = 0; // For TEST_TABLE_DATA_LIST. -let gIsAnotherUpdated = false; // For TEST_TABLE_DATA_ANOTHER. +let gIsV4Updated = false; // For TEST_TABLE_DATA_V4. prefBranch.setBoolPref("browser.safebrowsing.debug", true); @@ -66,10 +71,11 @@ TEST_TABLE_DATA_LIST.forEach(function(t) { t.updateUrl, t.gethashUrl); }); -gListManager.registerTable(TEST_TABLE_DATA_ANOTHER.tableName, - TEST_TABLE_DATA_ANOTHER.providerName, - TEST_TABLE_DATA_ANOTHER.updateUrl, - TEST_TABLE_DATA_ANOTHER.gethashUrl); + +gListManager.registerTable(TEST_TABLE_DATA_V4.tableName, + TEST_TABLE_DATA_V4.providerName, + TEST_TABLE_DATA_V4.updateUrl, + TEST_TABLE_DATA_V4.gethashUrl); const SERVER_INVOLVED_TEST_CASE_LIST = [ // - Do table0 update. @@ -110,17 +116,26 @@ const SERVER_INVOLVED_TEST_CASE_LIST = [ function test_update_all_tables() { disableAllUpdates(); - // Enable all tables including TEST_TABLE_DATA_ANOTHER! + // Enable all tables including TEST_TABLE_DATA_V4! TEST_TABLE_DATA_LIST.forEach(function(t) { gListManager.enableUpdate(t.tableName); }); - gListManager.enableUpdate(TEST_TABLE_DATA_ANOTHER.tableName); + gListManager.enableUpdate(TEST_TABLE_DATA_V4.tableName); + // Expected results for v2. gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";a:5:s:2-12\n" + TEST_TABLE_DATA_LIST[1].tableName + ";\n" + TEST_TABLE_DATA_LIST[2].tableName + ";\n"; gUpdateResponse = "n:1000\n"; + // We test the request against the query string since v4 request + // would be appened to the query string. The request is generated + // by protobuf API (binary) then encoded to base64 format. + let requestV4 = gUrlUtils.makeUpdateRequestV4([TEST_TABLE_DATA_V4.tableName], + [""], + 1); + gExpectedQueryV4 = "&$req=" + btoa(requestV4); + forceTableUpdate(); }, @@ -133,8 +148,8 @@ add_test(function test_getGethashUrl() { TEST_TABLE_DATA_LIST.forEach(function (t) { equal(gListManager.getGethashUrl(t.tableName), t.gethashUrl); }); - equal(gListManager.getGethashUrl(TEST_TABLE_DATA_ANOTHER.tableName), - TEST_TABLE_DATA_ANOTHER.gethashUrl); + equal(gListManager.getGethashUrl(TEST_TABLE_DATA_V4.tableName), + TEST_TABLE_DATA_V4.gethashUrl); run_next_test(); }); @@ -165,36 +180,43 @@ function run_test() { return; } - if (gIsAnotherUpdated) { + if (gIsV4Updated) { run_next_test(); // All tests are done. Just finish. return; } - do_print("Waiting for TEST_TABLE_DATA_ANOTHER to be tested ..."); + do_print("Waiting for TEST_TABLE_DATA_V4 to be tested ..."); }); gHttpServ.start(4444); - // Setup another testing server for the different update URL. - gHttpServAnother = new HttpServer(); - gHttpServAnother.registerDirectory("/", do_get_cwd()); + // Setup v4 testing server for the different update URL. + gHttpServV4 = new HttpServer(); + gHttpServV4.registerDirectory("/", do_get_cwd()); - gHttpServAnother.registerPathHandler("/safebrowsing/update", function(request, response) { - let body = NetUtil.readInputStreamToString(request.bodyInputStream, - request.bodyInputStream.available()); + gHttpServV4.registerPathHandler("/safebrowsing/update", function(request, response) { + // V4 update request body should be empty. + equal(request.bodyInputStream.available(), 0); - // Verify if the request is as expected. - equal(body, TEST_TABLE_DATA_ANOTHER.tableName + ";\n"); + // Not on the spec. Found in Chromium source code... + equal(request.getHeader("X-HTTP-Method-Override"), "POST"); - // Respond with no chunk control. + // V4 update request uses GET. + equal(request.method, "GET"); + + // V4 append the base64 encoded request to the query string. + equal(request.queryString, gExpectedQueryV4); + + // Respond a V2 compatible content for now. In the future we can + // send a meaningful response to test Bug 1284178 to see if the + // update is successfully stored to database. response.setHeader("Content-Type", "application/vnd.google.safebrowsing-update", false); response.setStatusLine(request.httpVersion, 200, "OK"); - let content = "n:1000\n"; response.bodyOutputStream.write(content, content.length); - gIsAnotherUpdated = true; + gIsV4Updated = true; if (gUpdatedCntForTableData === SERVER_INVOLVED_TEST_CASE_LIST.length) { // All tests are done! @@ -205,7 +227,7 @@ function run_test() { do_print("Wait for all sever-involved tests to be done ..."); }); - gHttpServAnother.start(5555); + gHttpServV4.start(5555); run_next_test(); } @@ -214,12 +236,13 @@ function run_test() { // call disableAllUpdates() first to clean up the updateCheckers in listmanager. function forceTableUpdate() { prefBranch.setCharPref(PREF_NEXTUPDATETIME, "1"); + prefBranch.setCharPref(PREF_NEXTUPDATETIME_V4, "1"); gListManager.maybeToggleUpdateChecking(); } function disableAllUpdates() { TEST_TABLE_DATA_LIST.forEach(t => gListManager.disableUpdate(t.tableName)); - gListManager.disableUpdate(TEST_TABLE_DATA_ANOTHER.tableName); + gListManager.disableUpdate(TEST_TABLE_DATA_V4.tableName); } // Since there's no public interface on listmanager to know the update success, @@ -243,3 +266,11 @@ function readFileToString(aFilename) { let buf = NetUtil.readInputStreamToString(stream, stream.available()); return buf; } + +function buildUpdateRequestV4InBase64() { + + let request = urlUtils.makeUpdateRequestV4([TEST_TABLE_DATA_V4.tableName], + [""], + 1); + return btoa(request); +}