nodeJS Быстрый веб-сервер на javascript движке V8

25Фев/111

Задерживающий прокси а-ля xkcd

Неделю назад в замечательном комиксе xkcd была упомянута интересная тема: прокси, задерживающий запросы к определённым сайтам на 30 секунд, как средство борьбы с прокрастинацией. Естественно, мне захотелось реализовать такую прокси но время для этого нашлось только сейчас.

Проксирование запросов

Сделать обычный прокси в Node.js проще простого:

var http = require('http');
 
http.createServer(function(request, response) {
 
  var proxy = http.createClient(80, request.headers['host'])
  var proxy_request = proxy.request(request.method, request.url, request.headers);
  proxy_request.on('response', function (proxy_response) {
    proxy_response.pipe(response);
    response.writeHead(proxy_response.statusCode, proxy_response.headers);
  });
 
  request.pipe(proxy_request);
}).listen(8080);

Здесь я применяю метод pipe, позволяющий соединять два потока. Сначала мы соединяем запрос от пользователя с запросом к удалённому сайту, потом соединяем ответ от сайта с ответом нашему пользователю. Весь прокси-сервер укладывается в несколько строк. Здесь не учтены многие вещи, котоорые должен уметь прокси-сервер — например, перенаправление HTTPS-запросов — но для моих целей хватит и такого.

Задержка для определённых сайтов

Теперь надо сделать задержку для определённых сайтов. Сначала просто прочитаем список сайтов и преобразуем его в объект, чтобы было проще проверять присутствие домена в списке:

var oc = function(a) {
  var o = {};
  for(var i=0;i<a.length;i++)
  {
    o[a[i]]='';
  }
  return o;
}
 
var delayed_sites_raw = fs.readFileSync('timekillers', 'ascii');
 
var delayed_sites = oc(delayed_sites_raw.split("\n"));

Функция oc (object converter) преобразует элементы массива в ключи объекта, чтобы можно было делать if ('domain' in delayed_sites) {.

Сами потоки можно задержать несколькими способами. Вообще у потока можно вызвать stream.pause (это остановит генерацию событий, но данные будут накапливатся в буфере) и потом продолжить обработку вызовом stream.resume. Я решил приостанавливать ответ от удалённого сервера — чтобы когда я вызову stream.resume ответ уже был получен и сохранён в буфере.

В результате получился вот такой код:

var oc = function(a) {
  var o = {};
  for(var i=0;i<a.length;i++)
  {
    o[a[i]]='';
  }
  return o;
}
 
var http = require('http'),
    url = require('url'),
    fs = require('fs');
 
var delayed_sites_raw = fs.readFileSync('timekillers', 'ascii');
 
var delayed_sites = oc(delayed_sites_raw.split("\n"));
 
console.log('Delayed sites:' + delayed_sites);
 
http.createServer(function(request, response) {
  var delay = 0;
 
  if (host in delayed_sites) {
    delay = 30000;
  }
 
  var proxy = http.createClient(80, request.headers['host'])
  var proxy_request = proxy.request(request.method, request.url, request.headers);
  proxy_request.on('response', function (proxy_response) {
    proxy_response.pause();
    setTimeout(function() {
        proxy_response.resume();
    }, delay);
    proxy_response.pipe(response);
    response.writeHead(proxy_response.statusCode, proxy_response.headers);
  });
 
  request.pipe(proxy_request);
}).listen(8080);

Для тестов я использовал Firefox. Список доменов читается из файла timekillers, в котором они просто записаны по одному домену на строку:

www.reddit.com
twitter.com
news.ycombinator.com
habrahabr.ru
www.tumblr.com

Если через такой прокси открыть например Яндекс, он откроется сразу же. Но если попытаться зайти на Reddit, ответ от прокси придёт только через 30 секунд (это хорошо видно в консоли FireBug).

Ссылки по теме

Источник: Механический мир

Комментарии (1) Пинги (0)
  1. Спасибо! Да, действительно с http прокси никаких проблем.
    А как дела обстоят с https прокси, чтоб не просто https over http, а полноценно, с подменой сертификатов?


Оставить комментарий


Нет обратных ссылок на эту запись.