Разрабатывайте собственные плагины

Вы просматриваете документацию Apigee Edge .
Перейдите в документацию Apigee
X.info

Edge Microgateway v. 2.5.x

Аудитория

Эта тема предназначена для разработчиков, желающих расширить функциональность Edge Microgateway путем написания собственных плагинов. Для создания нового плагина необходим опыт работы с JavaScript и Node.js.

Что такое пользовательский плагин Edge Microgateway?

Плагин — это модуль Node.js, который добавляет функциональность в Edge Microgateway. Модули-плагины следуют единому шаблону и хранятся в месте, известном Edge Microgateway, что позволяет обнаруживать и запускать их автоматически. При установке Edge Microgateway предоставляется несколько предопределенных плагинов. К ним относятся плагины для аутентификации, предотвращения всплесков активности, квотирования и аналитики. Эти существующие плагины описаны в разделе «Использование плагинов» .

Вы можете добавлять новые функции и возможности к микрошлюзу, создавая собственные плагины . По умолчанию Edge Microgateway представляет собой, по сути, защищенный прокси-сервер, который передает запросы и ответы без изменений целевым сервисам и обратно. С помощью пользовательских плагинов вы можете программно взаимодействовать с запросами и ответами, проходящими через микрошлюз.

Куда поместить код пользовательского плагина

В состав установочного пакета Edge Microgateway входит папка для пользовательских плагинов:

[prefix]/lib/node_modules/edgemicro/node_modules/microgateway-plugins

где [prefix] — это каталог префикса npm , как описано в разделе «Где установлен Edge Microgateway» в руководстве по установке Edge Microgateway .

Вы можете изменить этот каталог плагинов по умолчанию. См. раздел «Где найти плагины» .

Проверка предопределенных плагинов

Прежде чем разрабатывать собственный плагин, стоит убедиться, что ни один из предопределенных плагинов не соответствует вашим требованиям. Эти плагины находятся в следующих папках:

[prefix]/lib/node_modules/edgemicro/node_modules/microgateway-plugins

где [prefix] — это каталог префикса npm . См. также раздел «Где установлен Edge Microgateway» в руководстве по установке Edge Microgateway .

Более подробную информацию см. также в разделе «Предустановленные плагины, поставляемые с Edge Microgateway» .

Напишите простой плагин

В этом разделе мы рассмотрим шаги, необходимые для создания простого плагина. Этот плагин заменяет данные ответа (какими бы они ни были) строкой "Hello, World!" и выводит их в терминал.

  1. Если Edge Microgateway запущен, остановите его немедленно:
    edgemicro stop
  2. cd в каталог пользовательского плагина:

    cd [prefix]/lib/node_modules/edgemicro/plugins

    где [prefix] — это каталог префикса npm , как описано в разделе «Где установлен Edge Microgateway» в руководстве по установке Edge Microgateway .

  3. Создайте новый проект плагина с именем response-override и cd в него в каталог:
    mkdir response-override && cd response-override
  4. Создайте новый проект Node.js:
    npm init
    Нажмите клавишу Enter несколько раз, чтобы принять значения по умолчанию.
  5. Используйте текстовый редактор, чтобы создать новый файл с именем index.js .
  6. Скопируйте следующий код в файл index.js и сохраните его.
    'use strict';
    var debug = require('debug')
    
    module.exports.init = function(config, logger, stats) {
    
      return {
       
        ondata_response: function(req, res, data, next) {
          debug('***** plugin ondata_response');
          next(null, null);
        },
        
        onend_response: function(req, res, data, next) {
          debug('***** plugin onend_response');
          next(null, "Hello, World!\n\n");
        }
      };
    }
  7. Теперь вы создали плагин, и вам нужно добавить его в конфигурацию Edge Microgateway. Откройте файл $HOME/.edgemicro/[org]-[env]-config.yaml , где org и env — это названия вашей организации и среды Edge.
  8. Добавьте плагин response-override к элементу plugins:sequence , как показано ниже.
          ...
          
          plugins:
            dir: ../plugins
            sequence:
              - oauth
              - response-override
              
          ...
        
  9. Перезапустите Edge Microgateway.
  10. Вызовите API через Edge Microgateway. (Этот вызов API предполагает, что вы настроили ту же конфигурацию, что и в руководстве, с защитой с помощью ключа API, как описано в разделе «Настройка и конфигурирование Edge Microgateway» ):
    curl -H 'x-api-key: uAM4gBSb6YoMvTHfx5lXJizYIpr5Jd' http://localhost:8000/hello/echo
    Hello, World!

Анатомия плагина

Приведенный ниже пример плагина Edge Microgateway иллюстрирует подход, которому следует следовать при разработке собственных плагинов. Исходный код рассматриваемого в этом разделе примера плагина находится в файле plugins/header-uppercase/index.js.

  • Плагины представляют собой стандартные модули NPM с файлами package.json и index.js в корневой папке.
  • Плагин должен экспортировать функцию init() .
  • Функция init() принимает три аргумента: config , logger и stats . Эти аргументы описаны в разделе «Аргументы функции init() плагина».
  • Метод init() возвращает объект с именованными обработчиками функций, которые вызываются при возникновении определенных событий в течение времени выполнения запроса.

функции обработчика событий

Плагин должен реализовывать некоторые или все из этих функций обработчика событий. Реализация этих функций остается на ваше усмотрение. Любая из этих функций является необязательной, и типичный плагин будет реализовывать как минимум подмножество этих функций.

Обработчики событий потока запросов

Эти функции вызываются по запросам в Edge Microgateway.

  • onrequest
  • ondata_request
  • onend_request
  • onclose_request
  • onerror_request

функция onrequest

Эта функция вызывается в начале запроса клиента. Она срабатывает, когда Edge Microgateway получает первый байт запроса. Функция предоставляет доступ к заголовкам запроса, URL-адресу, параметрам запроса и методу HTTP. Если вы вызовете next с истинным первым аргументом (например, экземпляром Error), то обработка запроса останавливается, и целевой запрос не инициируется.

Пример:

onrequest: function(req, res, next) {
      debug('plugin onrequest');
      req.headers['x-foo-request-start'] = Date.now();
      next();
    }

функция ondata_request

Вызывается при получении фрагмента данных от клиента. Передает данные запроса следующему плагину в последовательности плагинов. Возвращаемое значение от последнего плагина в последовательности отправляется целевому объекту. Типичный пример использования, показанный ниже, — преобразование данных запроса перед отправкой их целевому объекту.

Пример:

ondata_request: function(req, res, data, next) {
      debug('plugin ondata_request ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    }

функция onend_request

Вызывается после получения всех данных запроса от клиента.

Пример:

onend_request: function(req, res, data, next) {
      debug('plugin onend_request');
      next(null, data);
    }

функция onclose_request

Указывает на закрытие клиентского соединения. Эту функцию можно использовать в случаях, когда клиентское соединение нестабильно. Она вызывается при закрытии сокетного соединения с клиентом.

Пример:

onclose_request: function(req, res, next) {
      debug('plugin onclose_request');
      next();
    }

функция onerror_request

Вызывается в случае ошибки при получении запроса от клиента.

Пример:

onerror_request: function(req, res, err, next) {
      debug('plugin onerror_request ' + err);
      next();
    }

Обработчики событий потока ответа

Эти функции вызываются при возникновении событий ответа в Edge Microgateway.

  • onresponse
  • ondata_response
  • onend_response
  • onclose_response
  • onerror_response

функция onresponse

Эта функция вызывается в начале целевого ответа. Она срабатывает, когда Edge Microgateway получает первый байт ответа. Эта функция предоставляет доступ к заголовкам ответа и коду состояния.

Пример:

onresponse: function(req, res, next) {      
    debug('plugin onresponse');     
    res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start'])    
    next();    
}


функция ondata_response

Вызывается при получении фрагмента данных от целевого устройства.

Пример:

ondata_response: function(req, res, data, next) {
      debug('plugin ondata_response ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    }


функция onend_response

Вызывается после получения всех данных ответа от целевого устройства.

Пример:

onend_response: function(req, res, data, next) {
      debug('plugin onend_response');
      next(null, data);
    }

функция onclose_response

Указывает на закрытие целевого соединения. Эту функцию можно использовать в случаях, когда целевое соединение нестабильно. Она вызывается при закрытии сокетного соединения с целевым устройством.

Пример:

onclose_response: function(req, res, next) {
      debug('plugin onclose_response');
      next();
    }


функция onerror_response

Вызывается в случае ошибки при получении целевого ответа.

Пример:

onerror_response: function(req, res, err, next) {
      debug('plugin onerror_response ' + err);
      next();
    }

Что вам нужно знать о функциях обработчика событий плагина

Функции обработчика событий плагина вызываются в ответ на определенные события, происходящие во время обработки Edge Microgateway заданного API-запроса.

  • Каждый из обработчиков функции init() ( ondata_request , ondata_response и т. д.) должен вызывать функцию обратного вызова next() после завершения обработки. Если вы не вызовете next() , обработка остановится, и запрос зависнет.
  • Первый аргумент функции next() может содержать ошибку, что приведет к прекращению обработки запроса.
  • Обработчики ondata_ и onend_ должны вызывать функцию next() со вторым аргументом, содержащим данные, которые должны быть переданы целевому объекту или клиенту. Этот аргумент может быть равен null, если плагин буферизует данные и в данный момент у него недостаточно данных для преобразования.
  • Обратите внимание, что для обработки всех запросов и ответов используется один единственный экземпляр плагина. Если плагин хочет сохранять состояние каждого запроса между вызовами, он может сохранить это состояние в свойстве, добавленном к предоставленному объекту запроса ( req ), время жизни которого равно продолжительности вызова API.
  • Будьте внимательны и перехватывайте все ошибки, вызывая метод next() с сообщением об ошибке. Если не вызвать next(), вызов API завершится зависанием.
  • Будьте осторожны, чтобы не допустить утечек памяти, так как это может повлиять на общую производительность Edge Microgateway и привести к сбою в случае нехватки памяти.
  • Следует придерживаться модели Node.js и не выполнять ресурсоемкие задачи в основном потоке, поскольку это может негативно повлиять на производительность Edge Microgateway.

О функции init() плагина

В этом разделе описываются аргументы, передаваемые функции init() : config , logger и stats .

конфигурация

Объект конфигурации, полученный после объединения файла конфигурации Edge Microgateway с информацией, загруженной из Apigee Edge, такой как продукты и квоты. В этом объекте можно найти конфигурацию, специфичную для плагина: config.<plugin-name> .

Чтобы добавить параметр конфигурации с именем param и значением foo к плагину response-override , добавьте следующий код в файл default.yaml :

response-override:
    param: foo

Затем вы можете получить доступ к параметру в коде вашего плагина следующим образом:

// Called when response data is received
    ondata_response: function(req, res, data, next) {
      debug('***** plugin ondata_response');
      debug('***** plugin ondata_response: config.param: ' + config.param);
      next(null, data);
    },

В этом случае в отладочном выводе плагина вы увидите надпись foo:

Sun, 13 Dec 2015 21:25:08 GMT plugin:response-override ***** plugin ondata_response: config.param: foo

лесоруб

Системный логгер. Используемый в настоящее время логгер экспортирует следующие функции, где объектом может быть строка, HTTP-запрос, HTTP-ответ или экземпляр Error.

  • info(object, message)
  • warn(object, message)
  • error(object, message)

статистика

Объект, содержащий количество запросов, ответов, ошибок и другую сводную статистику, относящуюся к запросам и ответам, проходящим через экземпляр микрошлюза.

  • treqErrors — количество запросов к целевому объекту с ошибками.
  • treqErrors — количество ответов целевых объектов с ошибками.
  • statusCodes — объект, содержащий количество кодов ответов:
{
  1: number of target responses with 1xx response codes
  2: number of target responses with 2xx response codes
  3: number of target responses with 3xx response codes
  4: number of target responses with 4xx response codes
  5: number of target responses with 5xx response codes
  }
  
  • Запросы - Общее количество запросов.
  • ответы - Общее количество ответов.
  • соединения — количество активных целевых соединений.

О функции next()

Для продолжения обработки следующего метода в серии все методы плагина должны вызывать next() (иначе процесс плагина зависнет). В жизненном цикле запроса первым вызывается метод onrequest(). Следующим вызывается метод ondata_request() ; однако ondata_request вызывается только в том случае, если запрос содержит данные, как, например, в случае POST-запроса. Следующим вызывается метод onend_request() , который вызывается после завершения обработки запроса. Функции onerror_* вызываются только в случае ошибки и позволяют обрабатывать ошибки с помощью пользовательского кода, если это необходимо.

Допустим, в запросе отправляются данные, и вызывается ondata_request() . Обратите внимание, что функция вызывает next() с двумя параметрами:

next(null, data);

По общепринятой практике, первый параметр используется для передачи информации об ошибке, которую затем можно обработать в последующей функции цепочки. Установив его значение равным null (ложное значение), мы говорим, что ошибок нет, и обработка запроса должна продолжаться в обычном режиме. Если этот аргумент имеет истинное значение (например, объект Error), то обработка запроса останавливается, и запрос отправляется целевому получателю.

Второй параметр передает данные запроса следующей функции в цепочке. Если дополнительная обработка не требуется, данные запроса передаются целевому объекту API без изменений. Однако у вас есть возможность изменить данные запроса внутри этого метода и передать измененный запрос целевому объекту. Например, если данные запроса представляют собой XML, а целевой объект ожидает JSON, вы можете добавить в метод ondata_request() код, который (а) изменяет Content-Type заголовка запроса на application/json и преобразует данные запроса в JSON любым удобным для вас способом (например, вы можете использовать конвертер xml2json для Node.js, полученный из NPM).

Давайте посмотрим, как это может выглядеть:

ondata_request: function(req, res, data, next) {
  debug('****** plugin ondata_request');
  var translated_data = parser.toJson(data);
  next(null, translated_data);
},

В этом случае данные запроса (предполагается, что они в формате XML) преобразуются в JSON, и преобразованные данные передаются через функцию next() следующей функции в цепочке запросов, прежде чем быть переданными на целевой бэкэнд.

Обратите внимание, что вы можете добавить еще одно отладочное сообщение для вывода преобразованных данных в целях отладки. Например:

ondata_request: function(req, res, data, next) {
  debug('****** plugin ondata_request');
  var translated_data = parser.toJson(data);
  debug('****** plugin ondata_response: translated_json: ' + translated_json);
  next(null, translated_data);
},

О порядке выполнения обработчиков плагинов

Если вы разрабатываете плагины для Edge Microgateway, вам необходимо понимать порядок выполнения обработчиков событий плагина.

Важно помнить, что при указании последовательности плагинов в конфигурационном файле Edge Microgateway обработчики запросов выполняются в порядке возрастания , а обработчики ответов — в порядке убывания .

Следующий пример призван помочь вам понять эту последовательность выполнения.

1. Создайте три простых плагина.

Рассмотрим следующий плагин. Всё, что он делает, это выводит информацию в консоль при вызове обработчиков событий:

plugins/plugin-1/index.js

module.exports.init = function(config, logger, stats) {

  return {

    onrequest: function(req, res, next) {
      console.log('plugin-1: onrequest');
      next();
    },

    onend_request: function(req, res, data, next) {
      console.log('plugin-1: onend_request');
      next(null, data);
    },

    ondata_response: function(req, res, data, next) {
      console.log('plugin-1: ondata_response ' + data.length);
      next(null, data);
    },

    onend_response: function(req, res, data, next) {
      console.log('plugin-1: onend_response');
      next(null, data);
    }
  };
}

Теперь рассмотрим создание еще двух плагинов, plugin-2 и plugin-3 , с тем же кодом (за исключением того, что операторы console.log() следует заменить на plugin-2 и plugin-3 соответственно).

2. Проверьте код плагина.

Экспортируемые функции плагина в <microgateway-root-dir>/plugins/plugin-1/index.js представляют собой обработчики событий, которые выполняются в определенные моменты во время обработки запроса и ответа. Например, onrequest выполняется при получении первого байта заголовка запроса, а onend_response — после получения последнего байта данных ответа.

Обратите внимание на обработчик ondata_response — он вызывается всякий раз, когда получен фрагмент данных ответа. Важно понимать, что данные ответа не обязательно поступают все сразу. Скорее, данные могут поступать фрагментами произвольной длины.

3. Добавьте плагины в последовательность плагинов.

Продолжая этот пример, добавим плагины в последовательность выполнения плагинов в конфигурационном файле Edge Microgateway ( ~./edgemicro/config.yaml ) следующим образом. Последовательность важна. Она определяет порядок выполнения обработчиков плагинов.

  plugins:
    dir: ../plugins
    sequence:
      - plugin-1
      - plugin-2
      - plugin-3
  

4. Изучите отладочный вывод.

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

  • Последовательность вызова плагинов в конфигурационном файле Edge Microgateway ( ~./edgemicro/config.yaml ) определяет порядок вызова обработчиков событий.
  • Обработчики запросов вызываются в порядке возрастания (в порядке их появления в последовательности плагинов — 1, 2, 3).
  • Обработчики ответов вызываются в порядке убывания — 3, 2, 1.
  • Обработчик ondata_response вызывается один раз для каждого поступающего фрагмента данных. В этом примере (вывод показан ниже) получены два фрагмента.

Вот пример отладочного вывода, получаемого при использовании этих трех плагинов и отправке запроса через Edge Microgateway. Обратите внимание на порядок вызова обработчиков:

  plugin-1: onrequest
  plugin-2: onrequest
  plugin-3: onrequest

  plugin-1: onend_request
  plugin-2: onend_request
  plugin-3: onend_request

  plugin-3: ondata_response 931
  plugin-2: ondata_response 931
  plugin-1: ondata_response 931

  plugin-3: ondata_response 1808
  plugin-3: onend_response

  plugin-2: ondata_response 1808
  plugin-2: onend_response

  plugin-1: ondata_response 1808
  plugin-1: onend_response

Краткое содержание

Понимание порядка вызова обработчиков плагинов очень важно при попытке реализовать пользовательскую функциональность плагинов, например, накопление и преобразование данных запроса или ответа.

Просто помните, что обработчики запросов выполняются в том порядке, в котором плагины указаны в конфигурационном файле Edge Microgateway, а обработчики ответов — в обратном порядке.

Об использовании глобальных переменных в плагинах

Каждый запрос к Edge Microgateway отправляется одному и тому же экземпляру плагина; следовательно, состояние второго запроса от другого клиента перезапишет состояние первого. Единственное безопасное место для сохранения состояния плагина — это хранение состояния в свойстве объекта запроса или ответа (срок жизни которого ограничен сроком жизни запроса).

Переписывание целевых URL-адресов в плагинах

Добавлено в: v2.3.3

Вы можете динамически переопределить целевой URL по умолчанию в плагине, изменив следующие переменные в коде плагина: req.targetHostname и req.targetPath .

Добавлено в: v2.4.x

Вы также можете переопределить порт целевой конечной точки и выбрать между HTTP и HTTPS. Измените следующие переменные в коде вашего плагина: req.targetPort и req.targetSecure . Чтобы выбрать HTTPS, установите req.targetSecure в значение true ; для HTTP — в значение false . Если вы установите req.targetSecure в значение true, см. эту ветку обсуждения для получения дополнительной информации.

В Edge Microgateway добавлен пример плагина под названием eurekaclient . Этот плагин демонстрирует использование переменных req.targetPort и req.targetSecure и показывает, как Edge Microgateway может выполнять динамический поиск конечных точек, используя Eureka в качестве каталога конечных точек сервиса.


Примеры плагинов

Эти плагины поставляются вместе с вашей установкой Edge Microgateway. Вы можете найти их в установке Edge Microgateway здесь:

[prefix]/lib/node_modules/edgemicro/plugins

где [prefix] — это каталог префикса npm , как описано в разделе «Где установлен Edge Microgateway» в руководстве по установке Edge Microgateway .

accume-request

Этот плагин накапливает фрагменты данных от клиента в свойство массива, прикрепленное к объекту запроса. После получения всех данных запроса массив объединяется в буфер, который затем передается следующему плагину в последовательности. Этот плагин должен быть первым в последовательности, чтобы последующие плагины получали накопленные данные запроса.

module.exports.init = function(config, logger, stats) {

  function accumulate(req, data) {

    if (!req._chunks) req._chunks = [];
    req._chunks.push(data);

  }

  return {

    ondata_request: function(req, res, data, next) {

      if (data && data.length > 0) accumulate(req, data);

      next(null, null);

    },


    onend_request: function(req, res, data, next) {

      if (data && data.length > 0) accumulate(req, data);

      var content = null;

      if (req._chunks && req._chunks.length) {

        content = Buffer.concat(req._chunks);

      }

      delete req._chunks;

      next(null, content);

    }

  };

}

накопление-ответ

Этот плагин накапливает фрагменты данных от целевого объекта в свойство массива, прикрепленное к объекту ответа. После получения всех данных ответа массив объединяется в буфер, который затем передается следующему плагину в последовательности. Поскольку этот плагин работает с ответами, которые обрабатываются в обратном порядке, его следует размещать последним плагином в последовательности.

module.exports.init = function(config, logger, stats) {

  function accumulate(res, data) {
    if (!res._chunks) res._chunks = [];
    res._chunks.push(data);
  }

  return {

    ondata_response: function(req, res, data, next) {
      if (data && data.length > 0) accumulate(res, data);
      next(null, null);
    },

    onend_response: function(req, res, data, next) {
      if (data && data.length > 0) accumulate(res, data);
      var content = Buffer.concat(res._chunks);
      delete res._chunks;
      next(null, content);
    }

  };

}

плагин header-uppercase

В дистрибутивах Edge Microgateway есть пример плагина под названием <microgateway-root-dir>/plugins/header-uppercase . Пример содержит комментарии, описывающие каждый из обработчиков функций. Этот пример выполняет простое преобразование данных целевого ответа и добавляет пользовательские заголовки к запросу клиента и целевому ответу.

Вот исходный код файла <microgateway-root-dir>/plugins/header-uppercase/index.js :

'use strict';

var debug = require('debug')('plugin:header-uppercase');

// required
module.exports.init = function(config, logger, stats) {

  var counter = 0;

  return {

    // indicates start of client request
    // request headers, url, query params, method should be available at this time
    // request processing stops (and a target request is not initiated) if
    // next is called with a truthy first argument (an instance of Error, for example)
    onrequest: function(req, res, next) {
      debug('plugin onrequest');
      req.headers['x-foo-request-id'] = counter++;
      req.headers['x-foo-request-start'] = Date.now();
      next();
    },

    // indicates start of target response
    // response headers and status code should be available at this time
    onresponse: function(req, res, next) {
      debug('plugin onresponse');
      res.setHeader('x-foo-response-id', req.headers['x-foo-request-id']);
      res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start']);
      next();
    },

    // chunk of request body data received from client
    // should return (potentially) transformed data for next plugin in chain
    // the returned value from the last plugin in the chain is written to the target
    ondata_request: function(req, res, data, next) {
      debug('plugin ondata_request ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    },

    // chunk of response body data received from target
    // should return (potentially) transformed data for next plugin in chain
    // the returned value from the last plugin in the chain is written to the client
    ondata_response: function(req, res, data, next) {
      debug('plugin ondata_response ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    },

    // indicates end of client request
    onend_request: function(req, res, data, next) {
      debug('plugin onend_request');
      next(null, data);
    },

    // indicates end of target response
    onend_response: function(req, res, data, next) {
      debug('plugin onend_response');
      next(null, data);
    },

    // error receiving client request
    onerror_request: function(req, res, err, next) {
      debug('plugin onerror_request ' + err);
      next();
    },

    // error receiving target response
    onerror_response: function(req, res, err, next) {
      debug('plugin onerror_response ' + err);
      next();
    },

    // indicates client connection closed
    onclose_request: function(req, res, next) {
      debug('plugin onclose_request');
      next();
    },

    // indicates target connection closed
    onclose_response: function(req, res, next) {
      debug('plugin onclose_response');
      next();
    }

  };

}

transform-uppercase

Это универсальный плагин для преобразования данных, который можно модифицировать для выполнения любых необходимых преобразований. В этом примере данные ответа и запроса просто преобразуются в верхний регистр.

 */
module.exports.init = function(config, logger, stats) {

  // perform content transformation here
  // the result of the transformation must be another Buffer
  function transform(data) {
    return new Buffer(data.toString().toUpperCase());
  }

  return {

    ondata_response: function(req, res, data, next) {
      // transform each chunk as it is received
      next(null, data ? transform(data) : null);
    },

    onend_response: function(req, res, data, next) {
      // transform accumulated data, if any
      next(null, data ? transform(data) : null);
    },

    ondata_request: function(req, res, data, next) {
      // transform each chunk as it is received
      next(null, data ? transform(data) : null);
    },

    onend_request: function(req, res, data, next) {
      // transform accumulated data, if any
      next(null, data ? transform(data) : null);
    }

  };

}