Fix network event update Message (#37543)

- Add `ResourceArrayType` with `Available` and `Updated` variants
- Rename `resources-available` and `resource-available` to
`resources-array` ,`resource-array`
- Add `ResourceArrayType` as an argument to decide the type of resources
- Add `Option<ResponseContentMsg>`,`Option<ResponseStartMsg>`,
`Option<ResponseCookiesMsg>`,
`Option<ResponseHeadersMsg>`,`Option<RequestCookiesMsg>`,
`Option<RequestHeadersMsg>`, `total_time`, `security_state` to
`NetworkEventActor` struct , and serialize the data in each to
`resource_updates` , flattening the nested arrays into a single JSON
- Refactor the following methods `request_headers`,`response_start` ,
`response_content`,`response_cookies`,`response_headers`,
`request_cookies`,`total_time` to associated functions passing
`HttpRequest` and `HttpResponse` as parameters .

Testing: Ran servo with devtools flag to see the logs corresponding to
the changes
Fixes: https://github.com/servo/servo/issues/37479

This PR Builds on https://github.com/servo/servo/pull/37517 and was
opened due to merge conflicts and branch issues

---------

Signed-off-by: Uthman Yahaya Baba <uthmanyahayababa@gmail.com>
This commit is contained in:
Usman Yahaya Baba 2025-06-19 17:00:37 +01:00 committed by GitHub
parent 8edae71286
commit 7d2c9ec19c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 216 additions and 138 deletions

View file

@ -30,7 +30,7 @@ use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::object::ObjectActor;
use crate::actors::worker::WorkerActor;
use crate::protocol::JsonPacketStream;
use crate::resource::ResourceAvailable;
use crate::resource::{ResourceArrayType, ResourceAvailable};
use crate::{StreamId, UniqueId};
trait EncodableConsoleMessage {
@ -261,11 +261,10 @@ impl ConsoleActor {
.push(CachedConsoleMessage::PageError(page_error.clone()));
if id == self.current_unique_id(registry) {
if let Root::BrowsingContext(bc) = &self.root {
registry
.find::<BrowsingContextActor>(bc)
.resource_available(
registry.find::<BrowsingContextActor>(bc).resource_array(
PageErrorWrapper { page_error },
"error-message".into(),
ResourceArrayType::Available,
stream,
)
};
@ -287,9 +286,12 @@ impl ConsoleActor {
.push(CachedConsoleMessage::ConsoleLog(log_message.clone()));
if id == self.current_unique_id(registry) {
if let Root::BrowsingContext(bc) = &self.root {
registry
.find::<BrowsingContextActor>(bc)
.resource_available(log_message, "console-message".into(), stream)
registry.find::<BrowsingContextActor>(bc).resource_array(
log_message,
"console-message".into(),
ResourceArrayType::Available,
stream,
)
};
}
}

View file

@ -21,7 +21,8 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::network_handler::Cause;
use crate::protocol::JsonPacketStream;
struct HttpRequest {
#[derive(Clone)]
pub struct HttpRequest {
url: String,
method: Method,
headers: HeaderMap,
@ -32,7 +33,8 @@ struct HttpRequest {
send_time: Duration,
}
struct HttpResponse {
#[derive(Clone)]
pub struct HttpResponse {
headers: Option<HeaderMap>,
status: HttpStatus,
body: Option<Vec<u8>>,
@ -40,9 +42,27 @@ struct HttpResponse {
pub struct NetworkEventActor {
pub name: String,
request: HttpRequest,
response: HttpResponse,
is_xhr: bool,
pub request: HttpRequest,
pub response: HttpResponse,
pub is_xhr: bool,
pub response_content: Option<ResponseContentMsg>,
pub response_start: Option<ResponseStartMsg>,
pub response_cookies: Option<ResponseCookiesMsg>,
pub response_headers: Option<ResponseHeadersMsg>,
pub request_cookies: Option<RequestCookiesMsg>,
pub request_headers: Option<RequestHeadersMsg>,
pub total_time: Duration,
pub security_state: String,
pub event_timing: Option<Timings>,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkEventResource {
pub resource_id: u64,
pub resource_updates: Map<String, Value>,
pub browsing_context_id: u64,
pub inner_window_id: u64,
}
#[derive(Clone, Serialize)]
@ -157,7 +177,7 @@ struct GetResponseCookiesReply {
}
#[derive(Serialize)]
struct Timings {
pub struct Timings {
blocked: u32,
dns: u32,
connect: u64,
@ -336,9 +356,7 @@ impl Actor for NetworkEventActor {
impl NetworkEventActor {
pub fn new(name: String) -> NetworkEventActor {
NetworkEventActor {
name,
request: HttpRequest {
let request = HttpRequest {
url: String::new(),
method: Method::GET,
headers: HeaderMap::new(),
@ -350,13 +368,27 @@ impl NetworkEventActor {
.as_secs() as i64,
send_time: Duration::ZERO,
connect_time: Duration::ZERO,
},
response: HttpResponse {
};
let response = HttpResponse {
headers: None,
status: HttpStatus::default(),
body: None,
},
};
NetworkEventActor {
name,
request: request.clone(),
response: response.clone(),
is_xhr: false,
response_content: None,
response_start: None,
response_cookies: None,
response_headers: None,
request_cookies: None,
request_headers: None,
total_time: Self::total_time(&request),
security_state: "insecure".to_owned(), // Default security state
event_timing: None,
}
}
@ -417,12 +449,13 @@ impl NetworkEventActor {
}
}
pub fn response_start(&self) -> ResponseStartMsg {
#[allow(dead_code)]
pub fn response_start(response: &HttpResponse) -> ResponseStartMsg {
// TODO: Send the correct values for all these fields.
let h_size_option = self.response.headers.as_ref().map(|headers| headers.len());
let h_size = h_size_option.unwrap_or(0);
let status = &self.response.status;
// TODO: Send the correct values for remoteAddress and remotePort and http_version.
let h_size = response.headers.as_ref().map(|h| h.len()).unwrap_or(0);
let status = &response.status;
// TODO: Send the correct values for remoteAddress and remotePort and http_version
ResponseStartMsg {
http_version: "HTTP/1.1".to_owned(),
remote_address: "63.245.217.43".to_owned(),
@ -434,26 +467,29 @@ impl NetworkEventActor {
}
}
pub fn response_content(&self) -> ResponseContentMsg {
let mut m_string = "".to_owned();
if let Some(ref headers) = self.response.headers {
m_string = match headers.typed_get::<ContentType>() {
#[allow(dead_code)]
pub fn response_content(response: &HttpResponse) -> ResponseContentMsg {
let mime_type = if let Some(ref headers) = response.headers {
match headers.typed_get::<ContentType>() {
Some(ct) => ct.to_string(),
_ => "".to_owned(),
};
None => "".to_string(),
}
} else {
"".to_string()
};
// TODO: Set correct values when response's body is sent to the devtools in http_loader.
ResponseContentMsg {
mime_type: m_string,
mime_type,
content_size: 0,
transferred_size: 0,
discard_response_body: true,
}
}
pub fn response_cookies(&self) -> ResponseCookiesMsg {
#[allow(dead_code)]
pub fn response_cookies(response: &HttpResponse) -> ResponseCookiesMsg {
let mut cookies_size = 0;
if let Some(ref headers) = self.response.headers {
if let Some(ref headers) = response.headers {
cookies_size = match headers.typed_get::<Cookie>() {
Some(ref cookie) => cookie.len(),
_ => 0,
@ -464,10 +500,11 @@ impl NetworkEventActor {
}
}
pub fn response_headers(&self) -> ResponseHeadersMsg {
#[allow(dead_code)]
pub fn response_headers(response: &HttpResponse) -> ResponseHeadersMsg {
let mut headers_size = 0;
let mut headers_byte_count = 0;
if let Some(ref headers) = self.response.headers {
if let Some(ref headers) = response.headers {
headers_size = headers.len();
for (name, value) in headers.iter() {
headers_byte_count += name.as_str().len() + value.len();
@ -479,18 +516,20 @@ impl NetworkEventActor {
}
}
pub fn request_headers(&self) -> RequestHeadersMsg {
let size = self.request.headers.iter().fold(0, |acc, (name, value)| {
#[allow(dead_code)]
pub fn request_headers(request: &HttpRequest) -> RequestHeadersMsg {
let size = request.headers.iter().fold(0, |acc, (name, value)| {
acc + name.as_str().len() + value.len()
});
RequestHeadersMsg {
headers: self.request.headers.len(),
headers: request.headers.len(),
headers_size: size,
}
}
pub fn request_cookies(&self) -> RequestCookiesMsg {
let cookies_size = match self.request.headers.typed_get::<Cookie>() {
#[allow(dead_code)]
pub fn request_cookies(request: &HttpRequest) -> RequestCookiesMsg {
let cookies_size = match request.headers.typed_get::<Cookie>() {
Some(ref cookie) => cookie.len(),
_ => 0,
};
@ -499,7 +538,78 @@ impl NetworkEventActor {
}
}
pub fn total_time(&self) -> Duration {
self.request.connect_time + self.request.send_time
pub fn total_time(request: &HttpRequest) -> Duration {
request.connect_time + request.send_time
}
fn insert_serialized_map<T: Serialize>(map: &mut Map<String, Value>, obj: &Option<T>) {
if let Some(value) = obj {
if let Ok(Value::Object(serialized)) = serde_json::to_value(value) {
for (key, val) in serialized {
map.insert(key, val);
}
}
}
}
pub fn resource_updates(&self) -> NetworkEventResource {
let mut resource_updates = Map::new();
resource_updates.insert(
"requestCookiesAvailable".to_owned(),
Value::Bool(self.request_cookies.is_some()),
);
resource_updates.insert(
"requestHeadersAvailable".to_owned(),
Value::Bool(self.request_headers.is_some()),
);
resource_updates.insert(
"responseHeadersAvailable".to_owned(),
Value::Bool(self.response_headers.is_some()),
);
resource_updates.insert(
"responseCookiesAvailable".to_owned(),
Value::Bool(self.response_cookies.is_some()),
);
resource_updates.insert(
"responseStartAvailable".to_owned(),
Value::Bool(self.response_start.is_some()),
);
resource_updates.insert(
"responseContentAvailable".to_owned(),
Value::Bool(self.response_content.is_some()),
);
resource_updates.insert(
"totalTime".to_string(),
Value::from(self.total_time.as_secs_f64()),
);
resource_updates.insert(
"securityState".to_string(),
Value::String(self.security_state.clone()),
);
resource_updates.insert(
"eventTimingsAvailable".to_owned(),
Value::Bool(self.event_timing.is_some()),
);
Self::insert_serialized_map(&mut resource_updates, &self.response_content);
Self::insert_serialized_map(&mut resource_updates, &self.response_headers);
Self::insert_serialized_map(&mut resource_updates, &self.response_cookies);
Self::insert_serialized_map(&mut resource_updates, &self.request_headers);
Self::insert_serialized_map(&mut resource_updates, &self.request_cookies);
Self::insert_serialized_map(&mut resource_updates, &self.response_start);
Self::insert_serialized_map(&mut resource_updates, &self.event_timing);
// TODO: Set the correct values for these fields
NetworkEventResource {
resource_id: 0,
resource_updates,
browsing_context_id: 0,
inner_window_id: 0,
}
}
}

View file

@ -32,7 +32,7 @@ use crate::actors::watcher::thread_configuration::{
ThreadConfigurationActor, ThreadConfigurationActorMsg,
};
use crate::protocol::JsonPacketStream;
use crate::resource::ResourceAvailable;
use crate::resource::{ResourceArrayType, ResourceAvailable};
use crate::{EmptyReplyMsg, StreamId, WorkerActor};
pub mod network_parent;
@ -292,14 +292,20 @@ impl Actor for WatcherActor {
title: Some(target.title.borrow().clone()),
url: Some(target.url.borrow().clone()),
};
target.resource_available(event, "document-event".into(), stream);
target.resource_array(
event,
"document-event".into(),
ResourceArrayType::Available,
stream,
);
}
},
"source" => {
let thread_actor = registry.find::<ThreadActor>(&target.thread);
target.resources_available(
target.resources_array(
thread_actor.source_manager.source_forms(registry),
"source".into(),
ResourceArrayType::Available,
stream,
);
@ -307,9 +313,10 @@ impl Actor for WatcherActor {
let worker = registry.find::<WorkerActor>(worker_name);
let thread = registry.find::<ThreadActor>(&worker.thread);
worker.resources_available(
worker.resources_array(
thread.source_manager.source_forms(registry),
"source".into(),
ResourceArrayType::Available,
stream,
);
}

View file

@ -29,7 +29,7 @@ use devtools_traits::{
use embedder_traits::{AllowOrDeny, EmbedderMsg, EmbedderProxy};
use ipc_channel::ipc::{self, IpcSender};
use log::trace;
use resource::ResourceAvailable;
use resource::{ResourceArrayType, ResourceAvailable};
use serde::Serialize;
use servo_rand::RngCore;
@ -540,7 +540,12 @@ impl DevtoolsInstance {
let worker_actor = actors.find::<WorkerActor>(worker_actor_name);
for stream in self.connections.values_mut() {
worker_actor.resource_available(&source_form, "source".into(), stream);
worker_actor.resource_array(
&source_form,
"source".into(),
ResourceArrayType::Available,
stream,
);
}
} else {
let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
@ -563,7 +568,12 @@ impl DevtoolsInstance {
let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
for stream in self.connections.values_mut() {
browsing_context.resource_available(&source_form, "source".into(), stream);
browsing_context.resource_array(
&source_form,
"source".into(),
ResourceArrayType::Available,
stream,
);
}
}
}

View file

@ -11,30 +11,8 @@ use serde::Serialize;
use crate::actor::ActorRegistry;
use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::network_event::NetworkEventActor;
use crate::resource::ResourceAvailable;
use crate::resource::{ResourceArrayType, ResourceAvailable};
#[derive(Clone, Serialize)]
struct ResourcesUpdatedArray {
updates: Vec<UpdateEntry>,
}
#[derive(Clone, Serialize)]
struct UpdateEntry {
#[serde(rename = "updateType")]
update_type: String,
data: serde_json::Value,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct EventTimingsUpdateMsg {
total_time: u64,
}
#[derive(Serialize)]
struct SecurityInfoUpdateMsg {
state: String,
}
#[derive(Clone, Serialize)]
pub struct Cause {
#[serde(rename = "type")]
@ -63,9 +41,10 @@ pub(crate) fn handle_network_event(
let browsing_context_actor =
actors.find::<BrowsingContextActor>(&browsing_context_actor_name);
for stream in &mut connections {
browsing_context_actor.resource_available(
browsing_context_actor.resource_array(
event_actor.clone(),
"network-event".to_string(),
ResourceArrayType::Available,
stream,
);
}
@ -76,56 +55,16 @@ pub(crate) fn handle_network_event(
let actor = actors.find_mut::<NetworkEventActor>(&netevent_actor_name);
// Store the response information in the actor
actor.add_response(httpresponse);
ResourcesUpdatedArray {
updates: vec![
UpdateEntry {
update_type: "requestHeaders".to_owned(),
data: serde_json::to_value(actor.request_headers()).unwrap(),
},
UpdateEntry {
update_type: "requestCookies".to_owned(),
data: serde_json::to_value(actor.request_cookies()).unwrap(),
},
UpdateEntry {
update_type: "responseStart".to_owned(),
data: serde_json::to_value(actor.response_start()).unwrap(),
},
UpdateEntry {
update_type: "eventTimings".to_owned(),
data: serde_json::to_value(EventTimingsUpdateMsg {
total_time: actor.total_time().as_millis() as u64,
})
.unwrap(),
},
UpdateEntry {
update_type: "securityInfo".to_owned(),
data: serde_json::to_value(SecurityInfoUpdateMsg {
state: "insecure".to_owned(),
})
.unwrap(),
},
UpdateEntry {
update_type: "responseContent".to_owned(),
data: serde_json::to_value(actor.response_content()).unwrap(),
},
UpdateEntry {
update_type: "responseCookies".to_owned(),
data: serde_json::to_value(actor.response_cookies()).unwrap(),
},
UpdateEntry {
update_type: "responseHeaders".to_owned(),
data: serde_json::to_value(actor.response_headers()).unwrap(),
},
],
}
actor.resource_updates()
};
let browsing_context_actor =
actors.find::<BrowsingContextActor>(&browsing_context_actor_name);
for stream in &mut connections {
browsing_context_actor.resource_available(
browsing_context_actor.resource_array(
resource.clone(),
"resources-updated".to_string(),
"network-event".to_string(),
ResourceArrayType::Updated,
stream,
);
}

View file

@ -8,6 +8,11 @@ use serde::Serialize;
use crate::protocol::JsonPacketStream;
pub enum ResourceArrayType {
Available,
Updated,
}
#[derive(Serialize)]
pub(crate) struct ResourceAvailableReply<T: Serialize> {
pub from: String,
@ -19,24 +24,29 @@ pub(crate) struct ResourceAvailableReply<T: Serialize> {
pub(crate) trait ResourceAvailable {
fn actor_name(&self) -> String;
fn resource_available<T: Serialize>(
fn resource_array<T: Serialize>(
&self,
resource: T,
resource_type: String,
array_type: ResourceArrayType,
stream: &mut TcpStream,
) {
self.resources_available(vec![resource], resource_type, stream);
self.resources_array(vec![resource], resource_type, array_type, stream);
}
fn resources_available<T: Serialize>(
fn resources_array<T: Serialize>(
&self,
resources: Vec<T>,
resource_type: String,
array_type: ResourceArrayType,
stream: &mut TcpStream,
) {
let msg = ResourceAvailableReply::<T> {
from: self.actor_name(),
type_: "resources-available-array".into(),
type_: match array_type {
ResourceArrayType::Available => "resources-available-array".to_string(),
ResourceArrayType::Updated => "resources-updated-array".to_string(),
},
array: vec![(resource_type, resources)],
};