Как создать простой прокси на C#?

Я загрузил Privoxy несколько недель назад, и 9X_dot-net ради забавы мне было любопытно узнать, как 9X_.cs-file можно сделать простую его версию.

Я понимаю, что 9X_dotnet мне нужно настроить браузер (клиент) для 9X_c# отправки запроса на прокси. Прокси-сервер 9X_c-sharp отправляет запрос в Интернет (допустим, это 9X_c# http-прокси). Прокси-сервер получит ответ 9X_c#-language ... но как он может отправить запрос браузеру 9X_c#-language (клиенту)?

Я искал в Интернете C# и http-прокси, но 9X_dot-net не нашел ничего, что позволило бы мне понять, как 9X_csharp это правильно работает за сценой. (Я считаю, что 9X_dotnet мне не нужен обратный прокси, но я не уверен).

У 9X_c#.net кого-нибудь из вас есть пояснения или информация, которые 9X_.net-framework позволят мне продолжить этот небольшой проект?

Обновить

Это 9X_csharp то, что я понимаю (см. рисунок ниже).

Шаг 1. Я 9X_.net-framework настраиваю клиент (браузер) для отправки 9X_c#.net всех запросов на 127.0.0.1 на порт, который 9X_c# прослушивает прокси. Таким образом, запрос 9X_c#-language не будет отправлен в Интернет напрямую, а 9X_.net будет обработан прокси-сервером.

Шаг 2. Прокси-сервер 9X_c-sharp видит новое соединение, читает заголовок 9X_.net HTTP и видит запрос, который он должен выполнить. Он 9X_.cs-file выполняет запрос.

Шаг 3. Прокси-сервер получает 9X_.net ответ на запрос. Теперь он должен отправить 9X_c-sharp ответ клиенту из Интернета, но как ???

9X_Как создать простой прокси на C#?_.cs-file

Полезная ссылка

Mentalis Proxy: Я 9X_csharp нашел этот проект, который является прокси 9X_dotnet (но мне бы хотелось большего). Я мог бы 9X_.net-framework проверить источник, но мне действительно 9X_.net нужно что-то базовое, чтобы лучше понять 9X_csharp концепцию.

ASP Proxy: Я тоже могу получить здесь некоторую 9X_.cs-file информацию.

Request reflector: это простой пример.

Вот Git Hub Repository with a Simple Http Proxy.

150
0
10
Общее количество ответов: 10

Ответ #1

Ответ на вопрос: Как создать простой прокси на C#?

Я бы не стал использовать HttpListener или 9X_visual-c# что-то в этом роде, так вы столкнетесь с 9X_c#-language множеством проблем.

Что еще важнее, поддержка 9X_dotnet будет огромной болью:

  • Прокси Keep-Alives
  • SSL не будет работать (при правильном использовании будут всплывающие окна)
  • Библиотеки .NET строго следуют RFC, что приводит к сбою некоторых запросов (даже если IE, FF и любой другой браузер в мире будут работать.)

Что вам нужно сделать, это:

  • Прослушивание TCP-порта.
  • Разобрать запрос браузера.
  • Извлечь хост, подключенный к этому хосту на уровне TCP
  • Пересылайте все вперед и назад, если вы не хотите добавлять собственные заголовки и т. д.

Я 9X_proxy написал 2 разных HTTP-прокси в .NET с разными 9X_.cs-file требованиями и могу сказать вам, что это 9X_dotnet лучший способ сделать это.

Mentalis делает 9X_visual-c# это, но их код - "спагетти делегата", хуже, чем 9X_csharp GoTo :)

95
5

  • Не могли бы вы поделиться своим опытом, п ...

Ответ #2

Ответ на вопрос: Как создать простой прокси на C#?

Недавно я написал облегченный прокси на 9X_c#.net C# .net, используя TcpListener и TcpClient.

https://github.com/titanium007/Titanium-Web-Proxy

Он правильно поддерживает 9X_visual-c# безопасный HTTP, клиентский компьютер должен 9X_csharp доверять корневому сертификату, используемому 9X_dotnet прокси. Также поддерживает ретрансляцию 9X_dotnet WebSockets. Поддерживаются все функции HTTP 9X_c#.net 1.1, кроме конвейерной обработки. В любом 9X_c#-language случае конвейерная обработка не используется 9X_c#.net большинством современных браузеров. Также 9X_.net поддерживает проверку подлинности Windows 9X_.net (обычная, дайджест).

Вы можете подключить 9X_proxy свое приложение, обратившись к проекту, а 9X_visual-c# затем просмотреть и изменить весь трафик. (Запрос 9X_proxy и ответ).

Что касается производительности, я 9X_c#-language протестировал его на своей машине и работает 9X_.net-framework без каких-либо заметных задержек.

37
0

Ответ #3

Ответ на вопрос: Как создать простой прокси на C#?

Вы можете создать его с помощью класса HttpListener для 9X_dotnet прослушивания входящих запросов и класса 9X_dotnet HttpWebRequest для ретрансляции запросов.

36
3

  • Если вы используете HttpListener, вы просто пишете ответ HttpListener.GetContext(). ...

Ответ #4

Ответ на вопрос: Как создать простой прокси на C#?

Прокси может работать следующим образом.

Шаг 9X_dotnet 1. Настройте клиент для использования proxyHost: proxyPort.

Прокси-сервер 9X_csharp - это TCP-сервер, который прослушивает proxyHost: proxyPort. Браузер 9X_c# открывает соединение с прокси и отправляет 9X_dotnet Http-запрос. Прокси-сервер анализирует этот 9X_dotnet запрос и пытается обнаружить заголовок «Хост». Этот 9X_.cs-file заголовок сообщит прокси, где открыть соединение.

Шаг 9X_.net 2. Прокси-сервер открывает соединение с 9X_csharp адресом, указанным в заголовке «Хост». Затем 9X_c-sharp он отправляет HTTP-запрос на этот удаленный 9X_dotnet сервер. Читает ответ.

Шаг 3. После считывания 9X_.net ответа с удаленного HTTP-сервера прокси-сервер 9X_.net-framework отправляет ответ через ранее открытое TCP-соединение 9X_c-sharp с браузером.

Схематично это будет выглядеть 9X_.net так:

Browser                            Proxy                     HTTP server
  Open TCP connection  
  Send HTTP request  ----------->                       
                                 Read HTTP header
                                 detect Host header
                                 Send request to HTTP ----------->
                                 Server
                                                      <-----------
                                 Read response and send
                   <-----------  it back to the browser
Render content

20
0

Ответ #5

Ответ на вопрос: Как создать простой прокси на C#?

Если вы просто хотите перехватить трафик, вы 9X_csharp можете использовать ядро ​​Fiddler для создания 9X_.net-2.0 прокси ...

http://fiddler.wikidot.com/fiddlercore

сначала запустите fiddler с пользовательским 9X_dotnet интерфейсом, чтобы увидеть, что он делает, это 9X_.cs-file прокси, который позволяет вам отлаживать 9X_visual-c# трафик http / https. Он написан на C# и 9X_c#-language имеет ядро, которое вы можете встроить в 9X_.net-framework свои собственные приложения.

Имейте в виду, что 9X_proxy FiddlerCore не бесплатен для коммерческих 9X_c# приложений.

14
0

Ответ #6

Ответ на вопрос: Как создать простой прокси на C#?

Согласен с доктором злом если вы используете 9X_c#-language HTTPListener, у вас будет много проблем, вам 9X_csharp придется разбирать запросы и заниматься 9X_proxy заголовками и ...

  1. Используйте прослушиватель TCP для прослушивания запросов браузера
  2. проанализировать только первую строку запроса и получить домен хоста и порт для подключения.
  3. отправить точный необработанный запрос на найденный хост в первой строке запроса браузера.
  4. получать данные с целевого сайта (у меня проблема в этом разделе)
  5. отправлять точные данные, полученные от хоста, в браузер.

вы видите, что вам не нужно 9X_csharp даже знать, что находится в запросе браузера, и 9X_.net анализировать его, только получите адрес 9X_c#-language целевого сайта из первой строки первая строка 9X_.net-2.0 обычно любит это ПОЛУЧИТЬ http://google.com HTTP1.1 или ПОДКЛЮЧИТЕ 9X_.net facebook.com:443 (это для ssl-запросов)

6
0

Ответ #7

Ответ на вопрос: Как создать простой прокси на C#?

С OWIN и WebAPI все стало очень просто. В 9X_c#.net поисках прокси-сервера C# я также наткнулся 9X_csharp на этот пост http://blog.kloud.com.au/2013/11/24/do-it-yourself-web-api-proxy/. Я пойду по этой дороге.

6
0

Ответ #8

Ответ на вопрос: Как создать простой прокси на C#?

Socks4 - очень простой в реализации протокол. Вы 9X_.net-2.0 слушаете начальное соединение, подключаетесь 9X_c-sharp к хосту / порту, который был запрошен клиентом, отправляете 9X_.cs-file код успеха клиенту, а затем перенаправляете 9X_c# исходящие и входящие потоки через сокеты.

Если 9X_c-sharp вы используете HTTP, вам придется прочитать 9X_c#.net и, возможно, установить / удалить некоторые 9X_.net-2.0 заголовки HTTP, так что это немного больше 9X_c#.net работы.

Если я правильно помню, SSL будет 9X_.net работать через прокси HTTP и Socks. Для 9X_.net-2.0 прокси-сервера HTTP вы реализуете команду 9X_c#-language CONNECT, которая работает так же, как socks4, как 9X_c-sharp описано выше, затем клиент открывает SSL-соединение 9X_.net-framework через прокси-поток tcp.

5
0

Ответ #9

Ответ на вопрос: Как создать простой прокси на C#?

Вот пример асинхронной реализации C# на 9X_.net-framework основе HttpListener и HttpClient (я использую его, чтобы иметь 9X_dot-net возможность подключать Chrome на устройствах 9X_c-sharp Android к IIS Express, это единственный 9X_.net способ, который я нашел ... ).

И если вам 9X_.net-2.0 нужна поддержка HTTPS, для этого не потребуется 9X_dotnet больше кода, просто конфигурация сертификата: Httplistener with HTTPS support

// define http://localhost:5000 and http://127.0.0.1:5000/ to be proxies for http://localhost:53068
using (var server = new ProxyServer("http://localhost:53068", "http://localhost:5000/", "http://127.0.0.1:5000/"))
{
    server.Start();
    Console.WriteLine("Press ESC to stop server.");
    while (true)
    {
        var key = Console.ReadKey(true);
        if (key.Key == ConsoleKey.Escape)
            break;
    }
    server.Stop();
}

....

public class ProxyServer : IDisposable
{
    private readonly HttpListener _listener;
    private readonly int _targetPort;
    private readonly string _targetHost;
    private static readonly HttpClient _client = new HttpClient();

    public ProxyServer(string targetUrl, params string[] prefixes)
        : this(new Uri(targetUrl), prefixes)
    {
    }

    public ProxyServer(Uri targetUrl, params string[] prefixes)
    {
        if (targetUrl == null)
            throw new ArgumentNullException(nameof(targetUrl));

        if (prefixes == null)
            throw new ArgumentNullException(nameof(prefixes));

        if (prefixes.Length == 0)
            throw new ArgumentException(null, nameof(prefixes));

        RewriteTargetInText = true;
        RewriteHost = true;
        RewriteReferer = true;
        TargetUrl = targetUrl;
        _targetHost = targetUrl.Host;
        _targetPort = targetUrl.Port;
        Prefixes = prefixes;

        _listener = new HttpListener();
        foreach (var prefix in prefixes)
        {
            _listener.Prefixes.Add(prefix);
        }
    }

    public Uri TargetUrl { get; }
    public string[] Prefixes { get; }
    public bool RewriteTargetInText { get; set; }
    public bool RewriteHost { get; set; }
    public bool RewriteReferer { get; set; } // this can have performance impact...

    public void Start()
    {
        _listener.Start();
        _listener.BeginGetContext(ProcessRequest, null);
    }

    private async void ProcessRequest(IAsyncResult result)
    {
        if (!_listener.IsListening)
            return;

        var ctx = _listener.EndGetContext(result);
        _listener.BeginGetContext(ProcessRequest, null);
        await ProcessRequest(ctx).ConfigureAwait(false);
    }

    protected virtual async Task ProcessRequest(HttpListenerContext context)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));

        var url = TargetUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped);
        using (var msg = new HttpRequestMessage(new HttpMethod(context.Request.HttpMethod), url + context.Request.RawUrl))
        {
            msg.Version = context.Request.ProtocolVersion;

            if (context.Request.HasEntityBody)
            {
                msg.Content = new StreamContent(context.Request.InputStream); // disposed with msg
            }

            string host = null;
            foreach (string headerName in context.Request.Headers)
            {
                var headerValue = context.Request.Headers[headerName];
                if (headerName == "Content-Length" && headerValue == "0") // useless plus don't send if we have no entity body
                    continue;

                bool contentHeader = false;
                switch (headerName)
                {
                    // some headers go to content...
                    case "Allow":
                    case "Content-Disposition":
                    case "Content-Encoding":
                    case "Content-Language":
                    case "Content-Length":
                    case "Content-Location":
                    case "Content-MD5":
                    case "Content-Range":
                    case "Content-Type":
                    case "Expires":
                    case "Last-Modified":
                        contentHeader = true;
                        break;

                    case "Referer":
                        if (RewriteReferer && Uri.TryCreate(headerValue, UriKind.Absolute, out var referer)) // if relative, don't handle
                        {
                            var builder = new UriBuilder(referer);
                            builder.Host = TargetUrl.Host;
                            builder.Port = TargetUrl.Port;
                            headerValue = builder.ToString();
                        }
                        break;

                    case "Host":
                        host = headerValue;
                        if (RewriteHost)
                        {
                            headerValue = TargetUrl.Host + ":" + TargetUrl.Port;
                        }
                        break;
                }

                if (contentHeader)
                {
                    msg.Content.Headers.Add(headerName, headerValue);
                }
                else
                {
                    msg.Headers.Add(headerName, headerValue);
                }
            }

            using (var response = await _client.SendAsync(msg).ConfigureAwait(false))
            {
                using (var os = context.Response.OutputStream)
                {
                    context.Response.ProtocolVersion = response.Version;
                    context.Response.StatusCode = (int)response.StatusCode;
                    context.Response.StatusDescription = response.ReasonPhrase;

                    foreach (var header in response.Headers)
                    {
                        context.Response.Headers.Add(header.Key, string.Join(", ", header.Value));
                    }

                    foreach (var header in response.Content.Headers)
                    {
                        if (header.Key == "Content-Length") // this will be set automatically at dispose time
                            continue;

                        context.Response.Headers.Add(header.Key, string.Join(", ", header.Value));
                    }

                    var ct = context.Response.ContentType;
                    if (RewriteTargetInText && host != null && ct != null &&
                        (ct.IndexOf("text/html", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        ct.IndexOf("application/json", StringComparison.OrdinalIgnoreCase) >= 0))
                    {
                        using (var ms = new MemoryStream())
                        {
                            using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                            {
                                await stream.CopyToAsync(ms).ConfigureAwait(false);
                                var enc = context.Response.ContentEncoding ?? Encoding.UTF8;
                                var html = enc.GetString(ms.ToArray());
                                if (TryReplace(html, "//" + _targetHost + ":" + _targetPort + "/", "//" + host + "/", out var replaced))
                                {
                                    var bytes = enc.GetBytes(replaced);
                                    using (var ms2 = new MemoryStream(bytes))
                                    {
                                        ms2.Position = 0;
                                        await ms2.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false);
                                    }
                                }
                                else
                                {
                                    ms.Position = 0;
                                    await ms.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false);
                                }
                            }
                        }
                    }
                    else
                    {
                        using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                        {
                            await stream.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false);
                        }
                    }
                }
            }
        }
    }

    public void Stop() => _listener.Stop();
    public override string ToString() => string.Join(", ", Prefixes) + " => " + TargetUrl;
    public void Dispose() => ((IDisposable)_listener)?.Dispose();

    // out-of-the-box replace doesn't tell if something *was* replaced or not
    private static bool TryReplace(string input, string oldValue, string newValue, out string result)
    {
        if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(oldValue))
        {
            result = input;
            return false;
        }

        var oldLen = oldValue.Length;
        var sb = new StringBuilder(input.Length);
        bool changed = false;
        var offset = 0;
        for (int i = 0; i < input.Length; i++)
        {
            var c = input[i];

            if (offset > 0)
            {
                if (c == oldValue[offset])
                {
                    offset++;
                    if (oldLen == offset)
                    {
                        changed = true;
                        sb.Append(newValue);
                        offset = 0;
                    }
                    continue;
                }

                for (int j = 0; j < offset; j++)
                {
                    sb.Append(input[i - offset + j]);
                }

                sb.Append(c);
                offset = 0;
            }
            else
            {
                if (c == oldValue[0])
                {
                    if (oldLen == 1)
                    {
                        changed = true;
                        sb.Append(newValue);
                    }
                    else
                    {
                        offset = 1;
                    }
                    continue;
                }

                sb.Append(c);
            }
        }

        if (changed)
        {
            result = sb.ToString();
            return true;
        }

        result = input;
        return false;
    }
}

4
1

  • Очень простой! Хороший человек!<p><span cla ...

Ответ #10

Ответ на вопрос: Как создать простой прокси на C#?

Браузер подключен к прокси, поэтому данные, которые 9X_dot-net прокси получает от веб-сервера, просто отправляются 9X_c#.net через то же соединение, которое браузер 9X_.cs-file инициировал для прокси.

2
0