diff --git a/Cargo.lock b/Cargo.lock index 33dcbe168ebd..c41d9a5ecb84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3251,6 +3251,7 @@ dependencies = [ name = "neqo_glue" version = "0.1.0" dependencies = [ + "log", "neqo-common", "neqo-crypto", "neqo-http3", @@ -3258,6 +3259,7 @@ dependencies = [ "neqo-transport", "nserror", "nsstring", + "qlog", "thin-vec", "xpcom", ] diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 49289d784b17..6aa58b3c6aad 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -8480,6 +8480,11 @@ value: 1048576 mirror: always +- name: network.http.http3.qlog_enabled + type: RelaxedAtomicBool + value: false + mirror: always + # When true, a http request will be upgraded to https when HTTPS RR is # available. - name: network.dns.upgrade_with_https_rr diff --git a/netwerk/protocol/http/Http3Session.cpp b/netwerk/protocol/http/Http3Session.cpp index 696cb17ecfee..dca5fc3db2a1 100644 --- a/netwerk/protocol/http/Http3Session.cpp +++ b/netwerk/protocol/http/Http3Session.cpp @@ -143,11 +143,11 @@ nsresult Http3Session::Init(const nsACString& aOrigin, gHttpHandler->DefaultQpackTableSize(), gHttpHandler->DefaultHttp3MaxBlockedStreams(), this)); - nsresult rv = - NeqoHttp3Conn::Init(aOrigin, aAlpnToken, selfAddrStr, peerAddrStr, - gHttpHandler->DefaultQpackTableSize(), - gHttpHandler->DefaultHttp3MaxBlockedStreams(), - getter_AddRefs(mHttp3Connection)); + nsresult rv = NeqoHttp3Conn::Init( + aOrigin, aAlpnToken, selfAddrStr, peerAddrStr, + gHttpHandler->DefaultQpackTableSize(), + gHttpHandler->DefaultHttp3MaxBlockedStreams(), + gHttpHandler->Http3QlogDir(), getter_AddRefs(mHttp3Connection)); if (NS_FAILED(rv)) { return rv; } diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index ae703663bfc3..5ca87300688b 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -18,6 +18,7 @@ #include "nsStandardURL.h" #include "LoadContextInfo.h" #include "nsCategoryManagerUtils.h" +#include "nsDirectoryServiceDefs.h" #include "nsSocketProviderService.h" #include "nsISocketProvider.h" #include "nsPrintfCString.h" @@ -593,9 +594,42 @@ nsresult nsHttpHandler::Init() { if (pc) { pc->GetParentalControlsEnabled(&mParentalControlEnabled); } + + auto initQLogDir = [&]() { + nsCOMPtr qlogDir; + nsresult rv = + NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(qlogDir)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return EmptyCString(); + } + + nsAutoCString dirName("qlog_"); + dirName.AppendInt(mProcessId); + rv = qlogDir->AppendNative(dirName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return EmptyCString(); + } + + rv = qlogDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755); + if (NS_WARN_IF(NS_FAILED(rv))) { + return EmptyCString(); + } + + return qlogDir->HumanReadablePath(); + }; + mHttp3QlogDir = initQLogDir(); + return NS_OK; } +const nsCString& nsHttpHandler::Http3QlogDir() { + if (StaticPrefs::network_http_http3_qlog_enabled()) { + return mHttp3QlogDir; + } + + return EmptyCString(); +} + void nsHttpHandler::MakeNewRequestTokenBucket() { LOG(("nsHttpHandler::MakeNewRequestTokenBucket this=%p child=%d\n", this, IsNeckoChild())); diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index c3d3120e5f53..3c5d6146764f 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -471,6 +471,8 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, return mMaxHttpResponseHeaderSize; } + const nsCString& Http3QlogDir(); + float FocusedWindowTransactionRatio() const { return mFocusedWindowTransactionRatio; } @@ -753,6 +755,7 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, Atomic mHttp3MaxBlockedStreams; // uint16_t is enough here, but Atomic only // supports uint32_t or uint64_t. + nsCString mHttp3QlogDir; // The max size (in bytes) for received Http response header. uint32_t mMaxHttpResponseHeaderSize; diff --git a/netwerk/socket/neqo_glue/Cargo.toml b/netwerk/socket/neqo_glue/Cargo.toml index 5c488fd123ea..73be017f4794 100644 --- a/netwerk/socket/neqo_glue/Cargo.toml +++ b/netwerk/socket/neqo_glue/Cargo.toml @@ -16,6 +16,8 @@ nserror = { path = "../../../xpcom/rust/nserror" } nsstring = { path = "../../../xpcom/rust/nsstring" } xpcom = { path = "../../../xpcom/rust/xpcom" } thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } +log = "0.4.0" +qlog = "0.3.0" [dependencies.neqo-crypto] tag = "v0.4.11" diff --git a/netwerk/socket/neqo_glue/NeqoHttp3Conn.h b/netwerk/socket/neqo_glue/NeqoHttp3Conn.h index 06265d7b4341..189dc04edc8b 100644 --- a/netwerk/socket/neqo_glue/NeqoHttp3Conn.h +++ b/netwerk/socket/neqo_glue/NeqoHttp3Conn.h @@ -15,9 +15,10 @@ class NeqoHttp3Conn final { static nsresult Init(const nsACString& aOrigin, const nsACString& aAlpn, const nsACString& aLocalAddr, const nsACString& aRemoteAddr, uint32_t aMaxTableSize, - uint16_t aMaxBlockedStreams, NeqoHttp3Conn** aConn) { + uint16_t aMaxBlockedStreams, const nsACString& aQlogDir, + NeqoHttp3Conn** aConn) { return neqo_http3conn_new(&aOrigin, &aAlpn, &aLocalAddr, &aRemoteAddr, - aMaxTableSize, aMaxBlockedStreams, + aMaxTableSize, aMaxBlockedStreams, &aQlogDir, (const mozilla::net::NeqoHttp3Conn**)aConn); } diff --git a/netwerk/socket/neqo_glue/src/lib.rs b/netwerk/socket/neqo_glue/src/lib.rs index 72fec54fe26b..c5981d85f6b0 100644 --- a/netwerk/socket/neqo_glue/src/lib.rs +++ b/netwerk/socket/neqo_glue/src/lib.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use neqo_common::Datagram; +use neqo_common::{self as common, qlog::NeqoQlog, qwarn, Datagram, Role}; use neqo_crypto::{init, PRErrorCode}; use neqo_http3::Error as Http3Error; use neqo_http3::{Http3Client, Http3ClientEvent, Http3Parameters, Http3State}; @@ -11,9 +11,12 @@ use neqo_transport::Error as TransportError; use neqo_transport::{FixedConnectionIdManager, Output, QuicVersion}; use nserror::*; use nsstring::*; +use qlog::QlogStreamer; use std::cell::RefCell; use std::convert::TryFrom; +use std::fs::OpenOptions; use std::net::SocketAddr; +use std::path::PathBuf; use std::ptr; use std::rc::Rc; use std::slice; @@ -39,6 +42,7 @@ impl NeqoHttp3Conn { remote_addr: &nsACString, max_table_size: u64, max_blocked_streams: u16, + qlog_dir: &nsACString, ) -> Result, nsresult> { // Nss init. init(); @@ -79,7 +83,7 @@ impl NeqoHttp3Conn { _ => return Err(NS_ERROR_INVALID_ARG), }; - let conn = match Http3Client::new( + let mut conn = match Http3Client::new( origin_conv, &[alpn_conv], Rc::new(RefCell::new(FixedConnectionIdManager::new(3))), @@ -92,6 +96,39 @@ impl NeqoHttp3Conn { Err(_) => return Err(NS_ERROR_INVALID_ARG), }; + if !qlog_dir.is_empty() { + let qlog_dir_conv = str::from_utf8(qlog_dir).map_err(|_| NS_ERROR_INVALID_ARG)?; + let mut qlog_path = PathBuf::from(qlog_dir_conv); + qlog_path.push(format!("{}.qlog", origin)); + + // Emit warnings but to not return an error if qlog initialization + // fails. + match OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(&qlog_path) + { + Err(_) => qwarn!("Could not open qlog path: {}", qlog_path.display()), + Ok(f) => { + let streamer = QlogStreamer::new( + qlog::QLOG_VERSION.to_string(), + Some("Firefox Client qlog".to_string()), + Some("Firefox Client qlog".to_string()), + None, + std::time::Instant::now(), + common::qlog::new_trace(Role::Client), + Box::new(f), + ); + + match NeqoQlog::enabled(streamer, &qlog_path) { + Err(_) => qwarn!("Could not write to qlog path: {}", qlog_path.display()), + Ok(nq) => conn.set_qlog(nq), + } + } + } + } + let conn = Box::into_raw(Box::new(NeqoHttp3Conn { conn, local_addr: local, @@ -136,6 +173,7 @@ pub extern "C" fn neqo_http3conn_new( remote_addr: &nsACString, max_table_size: u64, max_blocked_streams: u16, + qlog_dir: &nsACString, result: &mut *const NeqoHttp3Conn, ) -> nsresult { *result = ptr::null_mut(); @@ -147,6 +185,7 @@ pub extern "C" fn neqo_http3conn_new( remote_addr, max_table_size, max_blocked_streams, + qlog_dir, ) { Ok(http3_conn) => { http3_conn.forget(result);