diff --git a/DevProxy.Plugins/Inspection/CDPModel.cs b/DevProxy.Plugins/Inspection/CDPModel.cs index ce1529a0..89b3c8f3 100644 --- a/DevProxy.Plugins/Inspection/CDPModel.cs +++ b/DevProxy.Plugins/Inspection/CDPModel.cs @@ -10,7 +10,7 @@ namespace DevProxy.Plugins.Inspection.CDP; #pragma warning restore IDE0130 -public abstract class Message +public class Message { [JsonPropertyName("id")] public int? Id { get; set; } @@ -296,4 +296,38 @@ public class GetResponseBodyResultParams public string? Body { get; set; } [JsonPropertyName("base64Encoded")] public bool? Base64Encoded { get; set; } +} + +public class StreamResourceContentResult : MessageResult +{ +} + +public class StreamResourceContentResultParams +{ + [JsonPropertyName("bufferedData")] + public string? BufferedData { get; set; } +} + +public class DataReceivedMessage : Message +{ + public DataReceivedMessage() + { + Method = "Network.dataReceived"; + } +} + +public class DataReceivedParams +{ + [JsonPropertyName("data")] + public string? Data { get; set; } + [JsonPropertyName("dataLength")] + public long? DataLength { get; set; } + [JsonPropertyName("encodedDataLength")] + public long? EncodedDataLength { get; set; } + [JsonPropertyName("requestId")] + public string? RequestId { get; set; } + + [JsonPropertyName("timestamp")] + public double? Timestamp { get; set; } + } \ No newline at end of file diff --git a/DevProxy.Plugins/Inspection/DevToolsPlugin.cs b/DevProxy.Plugins/Inspection/DevToolsPlugin.cs index 6fdd5dd2..fbd50491 100644 --- a/DevProxy.Plugins/Inspection/DevToolsPlugin.cs +++ b/DevProxy.Plugins/Inspection/DevToolsPlugin.cs @@ -14,6 +14,7 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Globalization; +using System.Text; namespace DevProxy.Plugins.Inspection; @@ -192,6 +193,11 @@ public override async Task AfterResponseAsync(ProxyResponseArgs e, CancellationT await _webSocket.SendAsync(responseReceivedMessage, cancellationToken); + if (e.Session.HttpClient.Response.ContentType == "text/event-stream") + { + await SendBodyAsDataReceivedAsync(requestId, body.Body, cancellationToken); + } + var loadingFinishedMessage = new LoadingFinishedMessage { Params = new() @@ -340,35 +346,98 @@ private void SocketMessageReceived(string msg) try { - var message = JsonSerializer.Deserialize(msg, ProxyUtils.JsonSerializerOptions); - if (message?.Method == "Network.getResponseBody") + var message = JsonSerializer.Deserialize(msg, ProxyUtils.JsonSerializerOptions); + switch (message?.Method) { - var requestId = message.Params?.RequestId; - if (requestId is null || - !_responseBody.TryGetValue(requestId, out var value) || - // should never happen because the message is sent from devtools - // and Id is required on all socket messages but theoretically - // it is possible - message.Id is null) - { - return; - } - - var result = new GetResponseBodyResult - { - Id = (int)message.Id, - Result = new() + case "Network.getResponseBody": + var getResponseBodyMessage = JsonSerializer.Deserialize(msg, ProxyUtils.JsonSerializerOptions); + if (getResponseBodyMessage is null) { - Body = value.Body, - Base64Encoded = value.Base64Encoded + return; } - }; - _ = _webSocket.SendAsync(result, _cancellationToken ?? CancellationToken.None); + _ = HandleNetworkGetResponseBodyAsync(getResponseBodyMessage, _cancellationToken ?? CancellationToken.None); + break; + case "Network.streamResourceContent": + _ = HandleNetworkStreamResourceContentAsync(message, _cancellationToken ?? CancellationToken.None); + break; + default: + break; } } catch { } } + private async Task HandleNetworkStreamResourceContentAsync(Message message, CancellationToken cancellationToken) + { + if (_webSocket is null || message.Id is null) + { + return; + } + + var result = new StreamResourceContentResult + { + Id = (int)message.Id, + Result = new() + { + BufferedData = string.Empty + } + }; + + await _webSocket.SendAsync(result, cancellationToken); + } + + private async Task HandleNetworkGetResponseBodyAsync(GetResponseBodyMessage message, CancellationToken cancellationToken) + { + if (_webSocket is null || message.Params?.RequestId is null) + { + return; + } + + if (!_responseBody.TryGetValue(message.Params.RequestId, out var value) || + // should never happen because the message is sent from devtools + // and Id is required on all socket messages but theoretically + // it is possible + message.Id is null) + { + return; + } + + var result = new GetResponseBodyResult + { + Id = (int)message.Id, + Result = new() + { + Body = value.Body, + Base64Encoded = value.Base64Encoded + } + }; + + await _webSocket.SendAsync(result, cancellationToken); + } + + private async Task SendBodyAsDataReceivedAsync(string requestId, string? body, CancellationToken cancellationToken) + { + if (_webSocket is null || string.IsNullOrEmpty(body)) + { + return; + } + + var base64Encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(body)); + var dataReceivedMessage = new DataReceivedMessage + { + Params = new() + { + RequestId = requestId, + Timestamp = (double)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000, + Data = base64Encoded, + DataLength = body.Length, + EncodedDataLength = base64Encoded.Length + } + }; + + await _webSocket.SendAsync(dataReceivedMessage, cancellationToken); + } + private static int GetFreePort() { using var listener = new TcpListener(IPAddress.Loopback, 0);