Bug 1675205 - Report telemetry when load an empty file via a JAR archive r=zbraniecki

This will report when a telemetry even whenever we load an empty file via a
JAR channel. It will not catch situations where onStopRequest is called with
an error code, or when the listener of the channel does not consume the stream.

Differential Revision: https://phabricator.services.mozilla.com/D96412
This commit is contained in:
Valentin Gosu 2020-11-14 22:06:35 +00:00
Родитель bd132e112d
Коммит 5f2e68c256
6 изменённых файлов: 165 добавлений и 0 удалений

Просмотреть файл

@ -19,6 +19,8 @@
#include "mozilla/BasePrincipal.h" #include "mozilla/BasePrincipal.h"
#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TelemetryComms.h"
#include "private/pprio.h" #include "private/pprio.h"
#include "nsInputStreamPump.h" #include "nsInputStreamPump.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
@ -1023,6 +1025,38 @@ nsJARChannel::OnStartRequest(nsIRequest* req) {
return rv; return rv;
} }
static void RecordEmptyFileEvent(const nsCString& aFileName) {
// Send Telemetry
// The event can only hold 80 characters.
// We only save the file name and path inside the jar.
auto findFilenameStart = [](const nsCString& aFileName) -> uint32_t {
int32_t pos = aFileName.Find("!/");
if (pos == kNotFound) {
MOZ_ASSERT(false, "This should not happen");
return 0;
}
int32_t from = aFileName.RFindChar('/', pos);
if (from == kNotFound) {
MOZ_ASSERT(false, "This should not happen");
return 0;
}
// Skip over the slash
from++;
return from;
};
// If for some reason we are unable to extract the filename we report the
// entire string, or 80 characters of it, to make sure we don't miss any
// events.
uint32_t from = findFilenameStart(aFileName);
Telemetry::EventID eventType =
Telemetry::EventID::NetworkJarChannel_Nodata_Onstop;
Telemetry::RecordEvent(eventType, mozilla::Some(Substring(aFileName, from)),
Nothing{});
}
NS_IMETHODIMP NS_IMETHODIMP
nsJARChannel::OnStopRequest(nsIRequest* req, nsresult status) { nsJARChannel::OnStopRequest(nsIRequest* req, nsresult status) {
LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n", this, LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n", this,
@ -1031,6 +1065,10 @@ nsJARChannel::OnStopRequest(nsIRequest* req, nsresult status) {
if (NS_SUCCEEDED(mStatus)) mStatus = status; if (NS_SUCCEEDED(mStatus)) mStatus = status;
if (mListener) { if (mListener) {
if (NS_SUCCEEDED(status) && !mOnDataCalled) {
RecordEmptyFileEvent(mSpec);
}
mListener->OnStopRequest(this, status); mListener->OnStopRequest(this, status);
mListener = nullptr; mListener = nullptr;
} }
@ -1061,6 +1099,7 @@ nsJARChannel::OnDataAvailable(nsIRequest* req, nsIInputStream* stream,
nsresult rv; nsresult rv;
mOnDataCalled = true;
rv = mListener->OnDataAvailable(this, stream, offset, count); rv = mListener->OnDataAvailable(this, stream, offset, count);
// simply report progress here instead of hooking ourselves up as a // simply report progress here instead of hooking ourselves up as a

Просмотреть файл

@ -69,6 +69,7 @@ class nsJARChannel final : public nsIJARChannel,
bool mOpened; bool mOpened;
bool mCanceled; bool mCanceled;
bool mOnDataCalled = false;
RefPtr<nsJARProtocolHandler> mJarHandler; RefPtr<nsJARProtocolHandler> mJarHandler;
nsCOMPtr<nsIJARURI> mJarURI; nsCOMPtr<nsIJARURI> mJarURI;

Двоичные данные
modules/libjar/test/unit/data/test_empty_file.zip Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
const { TelemetryTestUtils } = ChromeUtils.import(
"resource://testing-common/TelemetryTestUtils.jsm"
);
const { TelemetryController } = ChromeUtils.import(
"resource://gre/modules/TelemetryController.jsm"
);
const nsIBinaryInputStream = Components.Constructor(
"@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream"
);
const fileBase = "test_empty_file.zip";
const file = do_get_file("data/" + fileBase);
const jarBase = "jar:" + Services.io.newFileURI(file).spec + "!";
const tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
function Listener(callback) {
this._callback = callback;
}
Listener.prototype = {
gotStartRequest: false,
available: -1,
gotStopRequest: false,
QueryInterface: ChromeUtils.generateQI(["nsIRequestObserver"]),
onDataAvailable(request, stream, offset, count) {
try {
this.available = stream.available();
Assert.equal(this.available, count);
// Need to consume stream to avoid assertion
new nsIBinaryInputStream(stream).readBytes(count);
} catch (ex) {
do_throw(ex);
}
},
onStartRequest(request) {
this.gotStartRequest = true;
},
onStopRequest(request, status) {
this.gotStopRequest = true;
Assert.equal(status, 0);
if (this._callback) {
this._callback.call(null, this);
}
},
};
const TELEMETRY_EVENTS_FILTERS = {
category: "network.jar.channel",
method: "noData",
};
add_task(async function test_empty_jar_file() {
var copy = tmpDir.clone();
copy.append("zzdxphd909dbc6r2bxtqss2m000017");
copy.append("zzdxphd909dbc6r2bxtqss2m000017");
copy.append(fileBase);
file.copyTo(copy.parent, copy.leafName);
var uri = "jar:" + Services.io.newFileURI(copy).spec + "!/test.txt";
var chan = NetUtil.newChannel({ uri, loadUsingSystemPrincipal: true });
Services.telemetry.setEventRecordingEnabled("network.jar.channel", true);
Services.telemetry.clearEvents();
await new Promise(resolve => {
chan.asyncOpen(
new Listener(function(l) {
Assert.ok(chan.contentLength == 0);
resolve();
})
);
});
try {
copy.remove(false);
} catch (e) {}
TelemetryTestUtils.assertEvents(
[
{
category: "network.jar.channel",
method: "noData",
object: "onStop",
value: `${fileBase}!/test.txt`,
},
],
TELEMETRY_EVENTS_FILTERS
);
});

Просмотреть файл

@ -17,6 +17,7 @@ support-files =
data/test_crx_dummy.crx data/test_crx_dummy.crx
data/test_umlaute.zip data/test_umlaute.zip
data/uncompressed.zip data/uncompressed.zip
data/test_empty_file.zip
[test_jarchannel.js] [test_jarchannel.js]
skip-if = os == "mac" skip-if = os == "mac"
@ -44,3 +45,4 @@ skip-if = os == "mac"
[test_bug1550815.js] [test_bug1550815.js]
# recovering from SIGBUS is temporarily disabled by bug 1583735 # recovering from SIGBUS is temporarily disabled by bug 1583735
skip-if = true skip-if = true
[test_empty_jar_telemetry.js]

Просмотреть файл

@ -2085,6 +2085,27 @@ intl.ui.browserLanguage:
record_in_processes: ["main"] record_in_processes: ["main"]
bug_numbers: [1486507, 1553311, 1607501, 1672571] bug_numbers: [1486507, 1553311, 1607501, 1672571]
network.jar.channel:
noData:
objects: ["onStop"]
bug_numbers:
- 1675205
description: >
This event gets recorded whenever onStopRequest gets called with a
success code, but no onDataAvailable calls have been made. This indicates
an empty file in the JAR archive.
notification_emails:
- vgosu@mozilla.com
- zbraniecki@mozilla.com
release_channel_collection: opt-out
products:
- firefox
expiry_version: "93"
record_in_processes:
- main
extra_keys:
spec: The path of the file being loaded.
security: security:
evalUsage: evalUsage:
objects: ["systemContext", "parentProcess"] objects: ["systemContext", "parentProcess"]