justcoding121 / titanium-web-proxy

A cross-platform asynchronous HTTP(S) proxy server in C#.
MIT License
1.93k stars 612 forks source link

How can i reverse proxy using Titanium-Web-Proxy? #131

Open selvamani-ramakirshnan opened 8 years ago

selvamani-ramakirshnan commented 8 years ago

I have used Titanium-Web-Proxy for reverse proxy. where can i specify server back-end Ip Address?. I have added end point and also the service started with endpoint. But it could not connect with backend IP Address. like my web api run in 5000 port and titanium end point run in 5001. client machine dont know about web api port. it send request to titanium end point and it should redirect to my api.

public void StartProxy()
        {
            proxyServer.BeforeRequest += OnRequest;
            proxyServer.BeforeResponse += OnResponse;
            proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
            proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;

            var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Parse("x.x.x.x"), 5001, true)
            {
                GenericCertificateName = "test"

            };

            proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "x.x.x.x", Port = 5000};     // backend IP
            proxyServer.AddEndPoint(transparentEndPoint);
            proxyServer.Start();

        }

please any one help me to add backend IP and provide a samples to authenticate backend IP.

Thanks, Selva

keyoti commented 8 years ago

I have the same question - I found that in RequestHandler.cs there is a HandleClient method, which has this code

var httpCmd = await clientStreamReader.ReadLineAsync();

            //Now create the request
            await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                 endPoint.EnableSsl ? endPoint.GenericCertificateName : null);

it seems to be expecting the httpCmd to be sent with the request, but I don't believe that's how reverse proxies work. I tried adding a line

var httpCmd = await clientStreamReader.ReadLineAsync();
            httpCmd = "GET http://stackoverflow.com/";

            //Now create the request
            await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                 endPoint.EnableSsl ? endPoint.GenericCertificateName : null);

and indeed it does go off and request the page from stackoverflow, but it doesn't successfully relay it to the client, unsure why right now.

keyoti commented 8 years ago

I also found in RequestHandler.cs, the HandleClient method, that if endPoint.EnableSsl is false, then clientStreamWriter is not set, which prevents the stream going back to the browser. Fixed it by adding

clientStreamWriter = new StreamWriter(clientStream);

to the else block


if (endPoint.EnableSsl)
            {
                var sslStream = new SslStream(clientStream, true);

                //implement in future once SNI supported by SSL stream, for now use the same certificate
                certificate = await certificateCacheManager.CreateCertificate(endPoint.GenericCertificateName, false);

                try
                {
                    //Successfully managed to authenticate the client using the fake certificate
                    await sslStream.AuthenticateAsServerAsync(certificate, false,
                        SslProtocols.Tls, false);

                    clientStreamReader = new CustomBinaryReader(sslStream);
                    clientStreamWriter = new StreamWriter(sslStream);
                    //HTTPS server created - we can now decrypt the client's traffic

                }
                catch (Exception)
                {
                    if (sslStream != null)
                    {
                        sslStream.Dispose();
                    }

                    Dispose(sslStream, clientStreamReader, clientStreamWriter, null);
                    return;
                }
                clientStream = sslStream;
            }
            else
            {
                clientStreamReader = new CustomBinaryReader(clientStream);

                clientStreamWriter = new StreamWriter(clientStream);
            }

Be great to hear from the authors if this is correct.

justcoding121 commented 8 years ago

@keyoti Proper setup for reverse proxying usually requires port forwarding or listening on port 80 in server. Feel free to PR the fix you mentioned above.

P.S: I fixed the bug you found

By the way, reverse proxying won't work for HTTPS due to inability to do SNI. Fiddlercore does it by peeking the SSL handshake headers. So it is possible to do in future. @ilushka85 @aricih If you are interested in doing that. :-)

justcoding121 commented 8 years ago

@iamselva When you say backend IP address, you are talking about the IP address of the machine running the proxy? I would think that if proxy is listening on 5000, by default it binds to all IP addresses (IP interfaces) of the machine. So first we want to know if the request even reached the proxy.

Second to redirect to another port, you would need to modify the request using the proxy before request event handler to local port. You can do that by updating the port in RequestUri object inside BeforeRequest event handler.

mcintyre321 commented 7 years ago

Hi, just been trying this myself but can't get it to work - is there a complete (minimal) example of a working reverse proxy? thanks

justcoding121 commented 7 years ago

Reverse proxy is not tested well. Will update once done with it.

keyoti commented 7 years ago

We have it working @mcintyre321 - what have you tried? Some key changes were in my posts above. You need to use the NewUri event to redirect the request to the correct server) eg.

proxy.NewUri += (object arg1, SessionEventArgs args) =>
            {

                args.WebSession.Request.RequestUri = newUri;
            };
honfika commented 4 years ago

Is this issue still relevant?