Обзор классов |
Пример написания для C# |
Как избежать общих ошибок. |
Скачать библиотеку классов |
Скачать проекты примеров |
SharpProxyService.SharpProxyPlugin
Класс, описывающий библиотеку(плагин), подключаемый к прокси. Плагины автоматически подключаются из папки Plugins. Ваш плагин должен быть унаследован от этого класса.
public abstract void AfterProxyRequestDisposed(SharpProxyService.ProxyRequest DisposedRequest) | Вызывается после завершения обслуживания запроса. Предназначено в первую очередь для DATA MINER типа плагинов. |
public abstract void AfterServerDisconnected(SharpProxyService.ProxyRequest CurrentRequest) | Вызывается после того, как удалённый сервер разорвал соединение. Из-за Keep-Alive может быть не вызван вообще, т.к. соединение разорвёт клиент. Предполагается использование там, где нужно иметь гарантию отсутствия разрыва соединения, как следствие сторонних причин. |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult BeforeSendDataToClient(byte[] BYTES, int BytesCount, SharpProxyService.ProxyRequest CurrentProxyRequest) | Вызывается когда информация от удалённого сервера получена, но ещё не передана клиенту. |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult BeforeSendDataToServer(byte[] BYTES, int BytesCount, SharpProxyService.ProxyRequest CurrentProxyRequest) | Вызывается перед тем как отправить данные запроса серверу. |
public abstract void LoadPluginSettings(string CWD) | Вызывается когда ядро прокси загрузило и инициализировало плагин. Предполагается загрузка каких-то настроек плагина. CWD - например C:\SharpProxy |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult OnClientConnected(System.Net.Sockets.Socket ClientSocket) | Вызывается, когда происходит соединение нового клиента. |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult OnClientDataArrived(byte[] BYTES, int BytesCount, SharpProxyService.ProxyRequest CurrentProxyRequest) | Вызывается когда поступают данные от клиента.
Тут есть два ньюанса для тех, кто собирается как-то ассоциировать данные с
ProxyRequest. 1. На текущий момент CurrentProxyRequest может быть null, т.к. мы должны сначала пролучить \r\n\r\n от клиента и только потом сформируем ProxyRequest 2. Те данные, которые мы получили могут оказаться частью следующего запроса клиента. |
public abstract void OnClientDisconnected(System.Net.Sockets.Socket ClientSocket) | Вызывается когда клиент закрывает соединение. |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult OnFtpDataArrived(byte[] BYTES, int BytesCount, SharpProxyService.ProxyRequest CurrentProxyRequest) | Вызывается когда поступают данные от FTP сервера. (Имеется в виду именно данные, т.к. они идут по другому сокету и через OnServerDataArrived их не поймать) |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult OnNewProxyRequestBuild(SharpProxyService.ProxyRequest CurrentProxyRequest) | Вызывается когда построен очередной ProxyRequest - если нужно что-то поменять в заголовках, хосте, порту, имени файла - меняйте в этот момент. |
public abstract void OnPlug(System.Collections.Generic.List<SharpProxyPlugin> TheAllLoadedPlugins) | Вызывается перед LoadPluginSettings - предварительная инициализация. TheAllLoadedPlugins - коллекция плагинов, с которыми придётся работать. Тут вы можете проверить наличие критических несовместимых плагинов или, наоборот, каких-то необходимых. Например, если мы ACL плагин, который высылает запрос авторизации, то неплохо бы проверить, что есть хотя бы один плагин AUTHORIZATOR, иначе, кто нам будет расшифровывать ответ клиента? |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult OnProxyRequestDisposing(SharpProxyService.ProxyRequest CurrentProxyRequest) | После завершения обслуживания ProxyRequest - стадия для добавления в ExtraLogData запроса какой-то своей информации. |
public abstract SharpProxyService.SharpProxyPlugin.PluginVoidResult OnServerDataArrived(byte[] BYTES, int BytesCount, SharpProxyService.ProxyRequest CurrentProxyRequest) | Вызывается когда поступают данные от сервера. |
public abstract void OnUnPlug(SharpProxyService.SharpProxyPlugin.UnplugReason TheReason) | Вызывается перед тем, как ядро перестанет передавать управление плагину. Уберите здесь весь мусор, уничтожьте формы, если есть и т.д. в общем, это ваш персональный Dispose ;) |
public abstract void Show_Setting_For_User_To_Edit(System.Windows.Forms.Form TheMDIParent_Form) | Вызывается когда пользователь хочет посмотреть/изменить настройки плагина. TheMDIParentForm должна быть назначена родителем MDI вашей формы. |
public abstract string PluginName { get; } | Просто верните имя плагина. |
public abstract System.Collections.Hashtable PluginSettings { get; } | Эту таблицу предполагается использовать для обмена настройками между плагинами. Я её ещё нигде не использовал, но на всякий случай, предусмотрел. Если не использутете - возвращайте null. |
public abstract int PluginVersion { get; } | Версия плагина. 1,2,3... N |
public abstract SharpProxyService.SharpProxyPlugin.PluginType TheType { get; } | Тип плагина. От типа напрямую зависит очерёдность плагина в вызове методов. (Индекс очередёности может быть прочитан в логе плагинов прокси) |
SharpProxyService.ProxyRequest
Класс, описывающий запрос к прокси серверу.
public long CurrentSpeedLimit { set; get; } | Ограничение скорости закачки с сервера (bps) Можно менять когда угодно. |
public string FileName { set; get; } | То что будет отправлено серверу после <GET|POST> и между HTTP/1.X, т.е. / или /index.html или /somedir/somesrcipt?params |
public string Headers { get; } | То, что мы отправим серверу после соединения. Формируется из заголовков, протокола, имени файла и т.д. |
public string Host { set; get; } | Хост сервера, с которым мы соединимся/соединились. |
public string Method { set; get; } | Метод: GET, POST, CONNECT |
public int Port { set; get; } | Порт сервера. |
public string Proto { set; get; } | Протокол: http, ftp. |
public string Protocol { set; get; } | HTTP/1.0, HTTP/1.1 .. |
public string URL { set; get; } | Формируется только из данных, полученных от клиента. Т.е. если вы измените host, filename или что-то ещё, URL останется неизменным. Просто, справочная информация. |
public long WholeLength { get; } | Длина заголовков + цифра указанная в Content-Length. Используется ядром. |
public System.Collections.Generic.List<ProxyRequest.Authorization> AUTHORIZATION | Список авторизаций. Если плагин AUTHORIZATOR получил данные о пользователе и они верны он должен добавить в этот список новый объект. |
public long BytesDownloaded | Сколько мы скачали с сервера. |
public long BytesUploaded | Сколько мы отправили на сервер. |
public System.Net.IPAddress ClientIP | IP адрес клиента. |
public System.Net.Sockets.Socket ClientSocket | Сокет клиента. Если не хватает функционала PluginVoidResult - пользуйтесь этим полем. |
public System.Collections.Generic.Queue<string> CommandsNeedToSentToFTP | Команды которые будут отправлены FTP серверу для получения файла. По умолчанию это "USER anonymous\r\n", "PASS anonymous@SharpProxy.ru\r\n", "PASV\r\n" |
public System.Collections.Hashtable ExtraLogData | Таблица для дополнительной информации по запросу. Если вам есть что добавить к стандартной информации - пишите сюда. Ключи и значения должны быть строковыми. Отсюда плагины типа DATA MINER будут брать дополнительную информацию. |
public System.Collections.Hashtable HeadersFields | Собственно, заголовки. Все ключи капитализированы. Например HeadersFields["CONTENT-LENGTH:"] |
public int HeadersLength | Длина заголовков отсылаемых серверу. |
public bool IsDisposed | Если истина - запрос считается обслуженным. (руками не менять) |
public System.Collections.Hashtable PluginData | Служит для хранения промежуточной информации для плагина(так называемых StateObjects). |
Класс, описывающий встраиваемую службу прокси сервера. Службы не обслуживают запросы к HTTP прокси, они напрямую расширяют сетевой функционал сервера. Пример службы - Port Mapping или простенький SMTP сервер.
public abstract void OnStart(string CWD) | Вызывается пристарте службы. В качестве параметра - рабочий директорий прокси. Например c:\\SharpPRoxy |
public abstract void OnStop() | Вызывается при остановке - освобождаем ресурсы здесь. |
public abstract void Show_Setting_For_User_To_Edit(System.Windows.Forms.Form TheMDIParent_Form) | Вызывается когда пользователь хочет посмотреть/изменить настройки службы. TheMDIParentForm должна быть назначена родителем MDI вашей формы. |
public abstract string ServiceName { get; } | Вернуть имя службы |
public abstract int ServiceVersion { get; } | Вернуть версию. |
namespace
SharpProxyService{
public class SamplePlugin:SharpProxyPlugin{
public
override void Show_Setting_For_User_To_Edit(System.Windows.Forms.Form TheMDIParent_Form)Он должен создать окно и назначить ему родителя MDI - TheMDIParent_Form переданную в параметрах.
public
override
PluginVoidResult OnNewProxyRequestBuild(ProxyRequest
CurrentProxyRequest) { if (CurrentProxyRequest.HeadersFields.ContainsKey("USER-AGENT:")){ CurrentProxyRequest.ExtraLogData.Add( "OLD USER-AGENT:", (string)CurrentProxyRequest.HeadersFields["USER-AGENT:"]); //На память логеруCurrentProxyRequest.HeadersFields[ "USER-AGENT:"] = MyConfig.UserAgent; // Вот здесь мы подменяем если есть} else{ CurrentProxyRequest.HeadersFields.Add( "USER-AGENT:", MyConfig.UserAgent); // А здесь добавляем если не былоCurrentProxyRequest.ExtraLogData.Add( "OLD USER-AGENT:", "WAS NOT SET"); //На память логеру} return DoNothing;}
|
Что делает наш код:
Проверяет - есть ли вообще такое поле в заголовках. Хранилищем заголовков у нас служит хэш-таблица CurrentProxyRequest.HeadersFields, причём все ключи в ней - капитализированные строки. Если нет - создаёт такой заголовок. Подменяет. И оставляет на откуп плагину логеру информацию в хэш-таблице CurrentProxyRequest.ExtraLogData о том, какой был на самом деле заголовок.
return DoNothing; - дело в том, что часть методов нашего плагина должна возвращать т.н. PluginVoidResult - результат обработки. Он указывает ядру нужно ли записать что-то в лог плагина, нужно ли что-то отправить клиенту(например, какие-то HTTP коды). Но, т.к. большинство методов ничего подобного не делают, мы в самом классе плагина создали переменную
PluginVoidResult
DoNothing = new PluginVoidResult();Которая эквивалентна "продолжить работу без каких либо сторонних действий" для ядра.
CurrentProxyRequest.HeadersFields["USER-AGENT:"] = MyConfig.UserAgent;
Что за MyConfig.UserAgent? MyConfig - это публичный статический экземпляр класса Config. Через него плагин узнаёт свои настройки и эти же настройки можно менять через GUI в RunTime. Это один из вариантов реализации настроек.
Прилагаю пару готовых простеньких плагинов и одну службу.
1. Проверяйте CurrentProxyRequest на значение null в событиях поступления данных от клиента. Т.к. вполне нормальна ситуация, когда от клиента поступают данные, но запрос ещё не сформирован.
2. Исключения плагинов сыпятся в лог плагинов - хоть какое-то средство диагностики.
3. Рекомендую обращаться к данным плагина в запросе таким образом:
if (CurrentProxyRequest.PluginData.ContainsKey(this.PluginName)){
Config.StateObject SO = (Config.StateObject)CurrentProxyRequest.PluginData[this.PluginName];...
Чем это лучше? Тем, что прокси не даст загрузить два плагина с эквивалентным PluginName и вы можете быть уверены, что ваши данные не испортят.