NW+Edge.js+Fiddler или сказ о стыкуемости нестыкуемого

от автора

Привет всем.
Не так давно прочел статью Что нам стоит сайт распарсить. Основы webdriver API и вспомнил, что давно собирался довести хотя бы до относительно рабочего состояния одну забавную задумку. Руки все-таки дошли, а значит пора поведать, что же получилось.
Есть такая замечательная программа — Fiddler, позволяющая перехватывать и модифицировать http/https запросы. Есть замечательная штука под названием NW.js, она же node-webkit, позволяющая тыр… парсить разнообразные сайты в том числе. Вы красивы, я прекрасен — почему бы нам не подружиться?
Собственно, затея вот в чем: можно было бы, конечно, отдельно поднять Fiddler, написать в нем логику и гонять через него трафик с node-webkit-но это не так интересно. А значит, будем совмещать все под одной крышей, благо у Fiddler есть библиотека на C# — FiddlerCore.
Под ноду есть отличный модуль — Edge.js. Это такая хитрая штука, которая позволяет исполнять код C# (и не только). Есть под ноду? Замечательно, можно завести и под nw.js, благо даже мануал есть — да вот же он!


Итак, пропустим пару часов секаса по сборке этой замечательной библиотеки(сколько раз говорить себе, читай мануалы внимательно! Нужна 13 студия, не 12 и не 15!) и приступим к написанию кода. Останавливаться на подключении и загрузке модулей тоже не буду, предположим, что те, кого заинтересует эта статья, мануалы читать умеют(да понял я, понял, что мануалы нужно читать внимательно!).

//#r "System.Windows.Forms.dll" //#r "fiddler/FiddlerCore.dll"  using Fiddler; using System; using System.Windows.Forms; using System.Collections.Generic; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks;  public class Startup {     Func<object, Task<object>> _console;     Func<object, Task<object>> _html;      public async void _print(object text){         if(_console!=null)             await _console(text);     }          public async void _getHtml(object html)     {         if (_html != null)             await _html(html);     }      public async Task<object> Invoke(dynamic data)     {         _console = (Func<object, Task<object>>)data.console;         _html = (Func<object, Task<object>>)data._html;          _print("Started");         FiddlerApplication.Shutdown();         new FiddlerLogic         {             _beforeRequest = (oS) => {                 var proxy = oS.oRequest.headers["POverride"];                 if (proxy != null)                 {                     oS["X-OverrideGateway"] = proxy;                 }             },             _beforeResponse = (oS) =>             {                 var response = oS.GetResponseBodyAsString();                 _html(response);             }         }._start(5000);         return Task.FromResult("Done");     } }   class FiddlerLogic {     public Action<Session> _beforeRequest;     public Action<Session> _beforeResponse;      public void _start(int port=5555)     {         FiddlerApplication.BeforeRequest += (oS) => _beforeRequest(oS);         FiddlerApplication.BeforeResponse += (oS) => _beforeResponse(oS);          FiddlerApplication.Startup(port, false, true);     } } 

Итак, что же здесь происходит?
Эти вот 2 строки
//#r «System.Windows.Forms.dll»
//#r «fiddler/FiddlerCore.dll»

являются маркером для edge.js, нужны для подключения библиотек.
Класс FiddlerLogic — всего лишь небольшой враппер над Fiddler’ом, в котором был код для подключения сертификатов, но потом при помощи какой-то уличной магии(скорее всего, нашел в загашнике старую версию, которая этот самый код не требовала) оно заработало и так. Теперь этот класс, по факту, ничего особого не делает, но куда ж нынче без legacy-кода? Собственно, в конструкторе объекта мы указываем 2 callback’а, которые будут вызываться перед/после отправки запроса, порт(при необходимости) и все.
Ах да, в FiddlerApplication.Startup 2 и 3 аргументы отвечают за использование системного прокси/перехват https-запросов соответственно. Так как хотелось бы перехватывать https-запросы и нафиг не нужно использование системного прокси, значения — false и true.

Теперь про Startup. Это такой забавный класс, нужный для работы с edge.js(об этом модуле позже). Собственно, Invoke — это точка входа, console/_html — функции из js. FiddlerApplication.Shutdown() отвечает за завершение всех предыдущих инстансов фиддлера. В _beforeRequest происходит подмена прокси при наличии заголовка POverride в запросе. В _beforeResponse ничего шибко полезного не происходит, оставил для примера. Функции _print и _getHtml — просто обертки с проверкой на наличие переданных из js функций.

Теперь рассмотрим js-часть.

var edge = require('edge'); var gui = require('nw.gui');  var async = require('async'); var request = require('request');     var start=Date.now(); var arr=[]; var count=50; var prev=Date.now(); var proxyList = ['160.92.56.41:80'];  _init();   for(var i=0; i<count; i++){ 	arr.push('http://myip.ru/index_small.php') }  var i=0; var _node=function(url, c){ 	console.log(url); 	var options = { 	  url: url, 	  headers: { 	    'POverride': proxyList[0] 	  } 	}; 	request(options, function(err, response, html){ 		if(err) 			console.log(err); 		var j=_html(html); 		console.log('Container:', j.find('.network-info tbody>:nth-child(2) td').text()); 		c(); 	}); };  async.map(arr, _node, 	function(){ 		var ms=Date.now()-start; 		console.info('Node parse got %f seconds. Mid time: %f. Mid page per second: %f', ms/1000, ms/count, count*1000/ms); 	} );          /***DEFINITIONS***/ function _html(html){    return $('<div></div>').html(html); }; function _init(){	 	try{ 		request=request.defaults({'proxy':'http://localhost:5000'}); 		gui.App.setProxyConfig("http://localhost:5000"); 		func = edge.func("fiddler/Main.cs"); 		func({ 			console: function(data, callback){ 				console.log(data); 			}, 			_html: function(html, callback){ 				//var container=_html(html); 				//console.log('Container:', container, container.find('.network-info tbody>:nth-child(2) td')); 			} 		}); 	} 	catch(ex){ 		console.log(ex); 	} }; 

В первую очередь стоит обратить внимание на 2 функции снизу — _html и _init. Первая занимается непотребством в виде построения DOM из строки, 2 — базовые настройки и подключение C# кода.
Edge.func подгружает содержимое Main.cs(см. код выше), и передает в качестве аргументов ссылки на нужные функции. Собственно, аргумент функции func и есть data из public async Task

ссылка на оригинал статьи http://habrahabr.ru/post/273311/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *