如何使用Titanium-Web-Proxy获得类似AfterSessionComplete的回调

问题描述

我曾经使用另一个带有C#的代理服务器库来捕获HTTP请求,并且它只有一个AfterSessionComplete事件,该事件在整个请求/响应操作都完成后才触发,您可以获取主机,路径和查询,请求和响应主体全部来自事件参数。但是,该库在某个时间之前停止了维护,因此我正在考虑切换到Titanium-Web-Proxy,并且似乎有一个AfterResponse事件。这是我以前测试的内容

        private static async Task Server_AfterResponse(object sender,SessionEventArgs e)
        {
            try
            {
                Uri url = e.HttpClient.Request.RequestUri;
                string method = e.HttpClient.Request.Method.toupper();
                string req = e.HttpClient.Request.HasBody ? e.HttpClient.Request.BodyString : null;
                string resp = e.HttpClient.Response.HasBody ? e.HttpClient.Response.BodyString : null;
                ThreadPool.QueueUserWorkItem(new WaitCallback((s) =>
                {
                    // Goes back to my old AfterSessionComplete method.
                    HttpProxy.AfterSessionComplete?.Invoke(url,method,req,resp);
                }));
            }
            catch(Exception ex)
            {
                Debug.Print($"{ex}");
            }
        }

        public static void Start(int port = 37123)
        {
            HttpProxy.server = new ProxyServer(false,false,false);
            HttpProxy.server.AddEndPoint(new ExplicitProxyEndPoint(IPAddress.Any,port,false));
            HttpProxy.server.AfterResponse += Server_AfterResponse;
            HttpProxy.server.Start();
        }

它不起作用。 HTTP流量将随机阻塞:Chrome DevTools中的未决状态表示某些请求不会触发AfterResponse事件,最终超时。即使对服务器发出了请求,我也没有得到请求和响应主体,试图获取它们将触发诸如以下的异常:

System.Exception: You cannot get the request body after request is made to server.

System.Exception: Response body is not read yet. Use SessionEventArgs.GetResponseBody() or SessionEventArgs.GetResponseBodyAsstring() method to read the response body.

我也尝试了await GetRequestBodyAsstring()await GetResponseBodyAsstring(),但还是没有。在那个阶段我很困惑,我该怎么办?我是否需要抛弃AfterResponse并使用提供的其他事件?

解决方法

在Titanium中,AfterResponse事件标记了响应发送到浏览器的时刻(并且不再可用)。因此,要读取响应正文,您需要附加到BeforeResponse事件。此外,如您所见,您无法在*响应事件中访问请求正文。克服此限制的一种方法是使用UserData属性,并在BeforeRequest事件中保存以后可能需要的来自请求正文的所有数据。下面的示例代码显示了如何执行此操作:

void Main()
{
    var proxyServer = new ProxyServer(userTrustRootCertificate: false);

    proxyServer.BeforeRequest += OnBeforeRequest;
    proxyServer.BeforeResponse += OnBeforeResponse;

    var httpProxy = new ExplicitProxyEndPoint(IPAddress.Loopback,8080,decryptSsl: true);

    proxyServer.AddEndPoint(httpProxy);
    proxyServer.Start();

    Console.ReadLine();

    proxyServer.Stop();
}

// Define other methods and classes here

public async Task OnBeforeRequest(object sender,SessionEventArgs ev)
{
    var request = ev.HttpClient.Request;

    ev.UserData = request.HasBody ? await ev.GetRequestBodyAsString() : "";
}

public async Task OnBeforeResponse(object sender,SessionEventArgs ev)
{
    var request = ev.HttpClient.Request;
    var response = ev.HttpClient.Response;

    var requestBody = (string)ev.UserData;
    var responseBody = response.HasBody ? await ev.GetResponseBodyAsString() : "";

    // TODO: Your old AfterSessionComplete method.
}