haga-rak / fluxzy.core

Fast and fully streamed Man-On-The-Middle engine and a CLI app to intercept, record and alter HTTP/1.1, H2, websocket traffic over plain or secure channels.
https://docs.fluxzy.io
Other
120 stars 9 forks source link

How to save response content to file #285

Closed Pureaznangel closed 3 months ago

Pureaznangel commented 3 months ago

If my program is running as a system proxy and captures all traffic going through via the Chrome browser. I just don't know how I can capture and save the response of a request, for example, just to the abc.com host to a text file.

Would you give me an example code to do this? 

Thanks,

haga-rak commented 3 months ago

Hello.

Do you want to have the response body only or the full response data (including header) ?

Pureaznangel commented 3 months ago

Hello Sir, currently I need only the response body, It would be great if I could get both of them (headers and body).

Thanks,

On Tue, Jul 9, 2024, 03:26 Haga R. @.***> wrote:

Hello.

Do you want to have the response body only or the full response data (including header) ?

— Reply to this email directly, view it on GitHub https://github.com/haga-rak/fluxzy.core/issues/285#issuecomment-2215216063, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ3563UU73DNUNWKMJKLE2LZLLYYVAVCNFSM6AAAAABKNONIJGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJVGIYTMMBWGM . You are receiving this because you authored the thread.Message ID: @.***>

haga-rak commented 3 months ago

Here is a sample that:


internal class Program
{
    static async Task Main()
    {
        var fluxzyStartupSetting = FluxzySetting.CreateLocalRandomPort();
        var outDirectory = Path.GetRandomFileName();

        fluxzyStartupSetting
            .SetOutDirectory(outDirectory)
            .SetSaveFilter(new HostFilter("abc.com", StringSelectorOperation.EndsWith));

        // Avoid deciphering traffic that is not from abc.com
        fluxzyStartupSetting.ConfigureRule()
            .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith)
            {
                Inverted = true
            }).Do(new SkipSslTunnelingAction());

        // Create a proxy instance
        await using (var proxy = new Proxy(fluxzyStartupSetting))
        {
            var endpoints = proxy.Run();

            await using var proxyRegistration = await SystemProxyRegistrationHelper.Create(endpoints.First());

            // Fluxzy is now registered as the system proxy, the proxy will revert
            // back to the original settings when proxyRegistration is disposed.

            Console.WriteLine("Press any key to halt proxy and unregistered");
            Console.ReadKey();
        }

        var archiveReader = new DirectoryArchiveReader(outDirectory);

        foreach (var exchange in archiveReader.ReadAllExchanges())
        {
            var url = exchange.FullUrl;
            var requestHeaders = exchange.GetRequestHeaders();
            var responseHeaders = exchange.GetResponseHeaders(); 

            using var stream = archiveReader.GetResponseBody(exchange.Id);

            // stream contains the response body - do want you want with it

            Console.WriteLine(url);

            foreach (var header in requestHeaders)
            {
                Console.WriteLine($"{header.Name}: {header.Value}");
            }

            if (responseHeaders != null)
            {
                foreach (var header in responseHeaders)
                {
                    Console.WriteLine($"{header.Name}: {header.Value}");
                }
            }

            if (stream != null)
            {
                Console.WriteLine("Response body length: " + await stream.DrainAsync());
            }
        }
    }
}

If you wish to have the response body in real-time, you will have to create your own Action

Pureaznangel commented 3 months ago

Yes sir, I need to process the traffic it in realtime. How could I do this? Can you give me an example? I currently using Fiddler Extension for this, but they didnt support .Net 6,7,8 so I found your Library for my solutions, I wish it could solve my problem.

Thanks,

On Tue, Jul 9, 2024, 05:26 Haga R. @.***> wrote:

Here is a sample that:

  • saves only traffic from abc.com (with SetSaveFilter)
  • (optional) deciphers only traffic from abc.com (with SkipSslTunnelingAction)

internal class Program{ static async Task Main() { var fluxzyStartupSetting = FluxzySetting.CreateLocalRandomPort(); var outDirectory = Path.GetRandomFileName();

    fluxzyStartupSetting
        .SetOutDirectory(outDirectory)
        .SetSaveFilter(new HostFilter("abc.com", StringSelectorOperation.EndsWith));

    // Avoid deciphering traffic that is not from abc.com
    fluxzyStartupSetting.ConfigureRule()
        .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith)
        {
            Inverted = true
        }).Do(new SkipSslTunnelingAction());

    // Create a proxy instance
    await using (var proxy = new Proxy(fluxzyStartupSetting))
    {
        var endpoints = proxy.Run();

        await using var proxyRegistration = await SystemProxyRegistrationHelper.Create(endpoints.First());

        // Fluxzy is now registered as the system proxy, the proxy will revert
        // back to the original settings when proxyRegistration is disposed.

        Console.WriteLine("Press any key to halt proxy and unregistered");
        Console.ReadKey();
    }

    var archiveReader = new DirectoryArchiveReader(outDirectory);

    foreach (var exchange in archiveReader.ReadAllExchanges())
    {
        var url = exchange.FullUrl;
        var requestHeaders = exchange.GetRequestHeaders();
        var responseHeaders = exchange.GetResponseHeaders();

        using var stream = archiveReader.GetResponseBody(exchange.Id);

        // stream contains the response body - do want you want with it

        Console.WriteLine(url);

        foreach (var header in requestHeaders)
        {
            Console.WriteLine($"{header.Name}: {header.Value}");
        }

        if (responseHeaders != null)
        {
            foreach (var header in responseHeaders)
            {
                Console.WriteLine($"{header.Name}: {header.Value}");
            }
        }

        if (stream != null)
        {
            Console.WriteLine("Response body length: " + await stream.DrainAsync());
        }
    }
}}

If you wish to have the response body in real-time, you will have to create your own Action

— Reply to this email directly, view it on GitHub https://github.com/haga-rak/fluxzy.core/issues/285#issuecomment-2215453641, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ3563VVTYN36YV3Z35MSEDZLMGX7AVCNFSM6AAAAABKNONIJGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJVGQ2TGNRUGE . You are receiving this because you authored the thread.Message ID: @.***>

haga-rak commented 3 months ago

Do you want to alter the response body or do you want to access it in readonly mode?

Pureaznangel commented 3 months ago

No Sir, I just need to read it only.

Regards,

On Tue, Jul 9, 2024, 06:00 Haga R. @.***> wrote:

Do you want to alter the response body or do you want to access it in readonly mode?

— Reply to this email directly, view it on GitHub https://github.com/haga-rak/fluxzy.core/issues/285#issuecomment-2215489832, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ3563RPJ643LCYGL7PGR4TZLMKYBAVCNFSM6AAAAABKNONIJGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJVGQ4DSOBTGI . You are receiving this because you authored the thread.Message ID: @.***>

haga-rak commented 3 months ago

We'll think for a more straightforward way to do this. However, for now, you can use the following code snippet to access response body in real time.

 internal class Program
 {
     static async Task Main()
     {
         var fluxzySetting = FluxzySetting.CreateLocalRandomPort();

         // Avoid deciphering traffic that is not from abc.com
         fluxzySetting.ConfigureRule()
             .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith)
             {
                 Inverted = true // negate the condition
             })
             .Do(new SkipSslTunnelingAction())
             .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith))
             .Do(new ReadResponseBodyAction());

         // Create a proxy instance
         await using (var proxy = new Proxy(fluxzySetting))
         {
             var endpoints = proxy.Run();

             await using var proxyRegistration = await SystemProxyRegistrationHelper.Create(endpoints.First());

             // Fluxzy is now registered as the system proxy, the proxy will revert
             // back to the original settings when proxyRegistration is disposed.

             Console.WriteLine("Press any key to halt proxy and unregistered");
             Console.ReadKey();
         }
     }
 }

 internal class ReadResponseBodyAction: Fluxzy.Rules.Action
 {
     public override FilterScope ActionScope => FilterScope.ResponseHeaderReceivedFromRemote;
     public override string DefaultDescription => nameof(ReadResponseBodyAction);

     public override ValueTask InternalAlter(
         ExchangeContext context, Exchange? exchange, Connection? connection, FilterScope scope,
         BreakPointManager breakPointManager)
     {
         context.RegisterResponseBodySubstitution(new ResponseBodySubstitution(exchange!));

         return new ValueTask();
     }
 }

 internal class ResponseBodySubstitution : IStreamSubstitution
 {
     private readonly Exchange _exchange;

     public ResponseBodySubstitution(Exchange exchange)
     {
         _exchange = exchange;
     }

     public async ValueTask<Stream> Substitute(Stream originalStream)
     {
         // Copy original response to MemoryStream
         var memoryStream = new MemoryStream();
         await originalStream.CopyToAsync(memoryStream);

         Console.WriteLine($"{_exchange.FullUrl}. Length: {memoryStream.Length}");

         memoryStream.Seek(0, SeekOrigin.Begin); 
         return memoryStream;
     }
 }
Pureaznangel commented 3 months ago

Thank you so much for this! I will give it a try in tonight when I back home then let you know the result. I hope I could help you make this library greater.

Regards,

On Tue, Jul 9, 2024, 06:39 Haga R. @.***> wrote:

We'll think for a more straightforward way to do this. However, for now, you can use the following code snippet to access response body in real time.

internal class Program { static async Task Main() { var fluxzySetting = FluxzySetting.CreateLocalRandomPort();

     // Avoid deciphering traffic that is not from abc.com
     fluxzySetting.ConfigureRule()
         .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith)
         {
             Inverted = true // negate the condition
         })
         .Do(new SkipSslTunnelingAction())
         .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith))
         .Do(new ReadResponseBodyAction());

     // Create a proxy instance
     await using (var proxy = new Proxy(fluxzySetting))
     {
         var endpoints = proxy.Run();

         await using var proxyRegistration = await SystemProxyRegistrationHelper.Create(endpoints.First());

         // Fluxzy is now registered as the system proxy, the proxy will revert
         // back to the original settings when proxyRegistration is disposed.

         Console.WriteLine("Press any key to halt proxy and unregistered");
         Console.ReadKey();
     }
 }

}

internal class ReadResponseBodyAction: Fluxzy.Rules.Action { public override FilterScope ActionScope => FilterScope.ResponseHeaderReceivedFromRemote; public override string DefaultDescription => nameof(ReadResponseBodyAction);

 public override ValueTask InternalAlter(
     ExchangeContext context, Exchange? exchange, Connection? connection, FilterScope scope,
     BreakPointManager breakPointManager)
 {
     context.RegisterResponseBodySubstitution(new ResponseBodySubstitution(exchange!));

     return new ValueTask();
 }

}

internal class ResponseBodySubstitution : IStreamSubstitution { private readonly Exchange _exchange;

 public ResponseBodySubstitution(Exchange exchange)
 {
     _exchange = exchange;
 }

 public async ValueTask<Stream> Substitute(Stream originalStream)
 {
     // Copy original response to MemoryStream
     var memoryStream = new MemoryStream();
     await originalStream.CopyToAsync(memoryStream);

     Console.WriteLine($"{_exchange.FullUrl}. Length: {memoryStream.Length}");

     memoryStream.Seek(0, SeekOrigin.Begin);
     return memoryStream;
 }

}

— Reply to this email directly, view it on GitHub https://github.com/haga-rak/fluxzy.core/issues/285#issuecomment-2215526727, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ3563VM3ECKDLLX247JCBLZLMPKPAVCNFSM6AAAAABKNONIJGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJVGUZDMNZSG4 . You are receiving this because you authored the thread.Message ID: @.***>

Pureaznangel commented 3 months ago

We'll think for a more straightforward way to do this. However, for now, you can use the following code snippet to access response body in real time.

 internal class Program
 {
     static async Task Main()
     {
         var fluxzySetting = FluxzySetting.CreateLocalRandomPort();

         // Avoid deciphering traffic that is not from abc.com
         fluxzySetting.ConfigureRule()
             .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith)
             {
                 Inverted = true // negate the condition
             })
             .Do(new SkipSslTunnelingAction())
             .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith))
             .Do(new ReadResponseBodyAction());

         // Create a proxy instance
         await using (var proxy = new Proxy(fluxzySetting))
         {
             var endpoints = proxy.Run();

             await using var proxyRegistration = await SystemProxyRegistrationHelper.Create(endpoints.First());

             // Fluxzy is now registered as the system proxy, the proxy will revert
             // back to the original settings when proxyRegistration is disposed.

             Console.WriteLine("Press any key to halt proxy and unregistered");
             Console.ReadKey();
         }
     }
 }

 internal class ReadResponseBodyAction: Fluxzy.Rules.Action
 {
     public override FilterScope ActionScope => FilterScope.ResponseHeaderReceivedFromRemote;
     public override string DefaultDescription => nameof(ReadResponseBodyAction);

     public override ValueTask InternalAlter(
         ExchangeContext context, Exchange? exchange, Connection? connection, FilterScope scope,
         BreakPointManager breakPointManager)
     {
         context.RegisterResponseBodySubstitution(new ResponseBodySubstitution(exchange!));

         return new ValueTask();
     }
 }

 internal class ResponseBodySubstitution : IStreamSubstitution
 {
     private readonly Exchange _exchange;

     public ResponseBodySubstitution(Exchange exchange)
     {
         _exchange = exchange;
     }

     public async ValueTask<Stream> Substitute(Stream originalStream)
     {
         // Copy original response to MemoryStream
         var memoryStream = new MemoryStream();
         await originalStream.CopyToAsync(memoryStream);

         Console.WriteLine($"{_exchange.FullUrl}. Length: {memoryStream.Length}");

         memoryStream.Seek(0, SeekOrigin.Begin); 
         return memoryStream;
     }
 }

It worked better than expected. While waiting for you to complete these ideas, I will temporarily use this code to handle the problem inside the core of my software. Hopefully, the next version will be better and have more available methods to use for many necessary cases. For now, I can delete the fiddlercore and use your library instead.

Thank you for your helping!

Pureaznangel commented 2 months ago

We'll think for a more straightforward way to do this. However, for now, you can use the following code snippet to access response body in real time.

 internal class Program
 {
     static async Task Main()
     {
         var fluxzySetting = FluxzySetting.CreateLocalRandomPort();

         // Avoid deciphering traffic that is not from abc.com
         fluxzySetting.ConfigureRule()
             .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith)
             {
                 Inverted = true // negate the condition
             })
             .Do(new SkipSslTunnelingAction())
             .When(new HostFilter("abc.com", StringSelectorOperation.EndsWith))
             .Do(new ReadResponseBodyAction());

         // Create a proxy instance
         await using (var proxy = new Proxy(fluxzySetting))
         {
             var endpoints = proxy.Run();

             await using var proxyRegistration = await SystemProxyRegistrationHelper.Create(endpoints.First());

             // Fluxzy is now registered as the system proxy, the proxy will revert
             // back to the original settings when proxyRegistration is disposed.

             Console.WriteLine("Press any key to halt proxy and unregistered");
             Console.ReadKey();
         }
     }
 }

 internal class ReadResponseBodyAction: Fluxzy.Rules.Action
 {
     public override FilterScope ActionScope => FilterScope.ResponseHeaderReceivedFromRemote;
     public override string DefaultDescription => nameof(ReadResponseBodyAction);

     public override ValueTask InternalAlter(
         ExchangeContext context, Exchange? exchange, Connection? connection, FilterScope scope,
         BreakPointManager breakPointManager)
     {
         context.RegisterResponseBodySubstitution(new ResponseBodySubstitution(exchange!));

         return new ValueTask();
     }
 }

 internal class ResponseBodySubstitution : IStreamSubstitution
 {
     private readonly Exchange _exchange;

     public ResponseBodySubstitution(Exchange exchange)
     {
         _exchange = exchange;
     }

     public async ValueTask<Stream> Substitute(Stream originalStream)
     {
         // Copy original response to MemoryStream
         var memoryStream = new MemoryStream();
         await originalStream.CopyToAsync(memoryStream);

         Console.WriteLine($"{_exchange.FullUrl}. Length: {memoryStream.Length}");

         memoryStream.Seek(0, SeekOrigin.Begin); 
         return memoryStream;
     }
 }

How could I get the response header and body sir, Please help.

haga-rak commented 2 months ago

You can access the response headers with exchange..GetResponseHeaders(). This can be null if the remote close the connection before sending the response.

For example, if you want to access it in the ResponseBodySubstitution

internal class ResponseBodySubstitution : IStreamSubstitution
{
    private readonly Exchange _exchange;

    public ResponseBodySubstitution(Exchange exchange)
    {
        _exchange = exchange;
    }

    public async ValueTask<Stream> Substitute(Stream originalStream)
    {
        // Copy original response to MemoryStream
        var memoryStream = new MemoryStream();
        await originalStream.CopyToAsync(memoryStream);

        // Response headers 

        var headers = _exchange.GetResponseHeaders()!;

        foreach (var header in headers)
        {
            Console.WriteLine($"{header.Name}: {header.Value}");
        }

        Console.WriteLine($"{_exchange.FullUrl}. Length: {memoryStream.Length}");

        memoryStream.Seek(0, SeekOrigin.Begin);
        return memoryStream;
    }
}

In this example also, the response body is in the MemoryStream. If you want a string out of it:

var responseByteArray = memoryStream.ToArray();
var responseString = Encoding.UTF8.GetString(responseByteArray);
Pureaznangel commented 2 months ago

You can access the response headers with exchange..GetResponseHeaders(). This can be null if the remote close the connection before sending the response.

For example, if you want to access it in the ResponseBodySubstitution

internal class ResponseBodySubstitution : IStreamSubstitution
{
    private readonly Exchange _exchange;

    public ResponseBodySubstitution(Exchange exchange)
    {
        _exchange = exchange;
    }

    public async ValueTask<Stream> Substitute(Stream originalStream)
    {
        // Copy original response to MemoryStream
        var memoryStream = new MemoryStream();
        await originalStream.CopyToAsync(memoryStream);

        // Response headers 

        var headers = _exchange.GetResponseHeaders()!;

        foreach (var header in headers)
        {
            Console.WriteLine($"{header.Name}: {header.Value}");
        }

        Console.WriteLine($"{_exchange.FullUrl}. Length: {memoryStream.Length}");

        memoryStream.Seek(0, SeekOrigin.Begin);
        return memoryStream;
    }
}

In this example also, the response body is in the MemoryStream. If you want a string out of it:

var responseByteArray = memoryStream.ToArray();
var responseString = Encoding.UTF8.GetString(responseByteArray);

Success! Thank you very much. It's actually simpler than I thought.