Skip to content
This repository was archived by the owner on Apr 24, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
coverage
.idea
*.tgz
6 changes: 0 additions & 6 deletions .npmignore

This file was deleted.

8 changes: 3 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
language: node_js
node_js:
- 0.12
- 4
- 5

after_success: make coveralls
- "7"
- "8"
- "10"
7 changes: 0 additions & 7 deletions Makefile

This file was deleted.

14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# koa-proxy [![Build Status](https://travis-ci.org/popomore/koa-proxy.png?branch=master)](https://travis-ci.org/popomore/koa-proxy) [![Coverage Status](https://coveralls.io/repos/popomore/koa-proxy/badge.png?branch=master)](https://coveralls.io/r/popomore/koa-proxy?branch=master)
# koa-proxy [![Build Status](https://travis-ci.com/edorivai/koa-proxy.svg?branch=master)](https://travis-ci.com/edorivai/koa-proxy)

Proxy middleware for koa

Expand Down Expand Up @@ -70,7 +70,7 @@ app.use(proxy({
}));
```

Proxy won't send cookie to real server, you can set `jar = true` to send it.
You can configure proxy to remember cookies for future use by setting `jar = true`. This means cookies set by server will be stored and resent in subsequent requests. For me info see the documentation for [request](https://github.com/request/request).

```js
app.use(proxy({
Expand All @@ -87,6 +87,16 @@ app.use(proxy({
}));
```

You can also add new headers to your response or override existing ones
```js
app.use(proxy({
overrideResponseHeaders: {
"cow": "moo",
"duck": "quack"
},
}));
```

## LICENSE

Copyright (c) 2014 popomore. Licensed under the MIT license.
110 changes: 62 additions & 48 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,109 +1,117 @@
'use strict';

var Stream = require('stream');
var join = require('url').resolve;
var iconv = require('iconv-lite');
var coRequest = require('co-request');
var rp = require('request-promise-native');
var requestLib = require('request');
var pauseStream = require('pause-stream');

module.exports = function(options) {
options || (options = {});
var request = coRequest.defaults({ jar: options.jar === true });

if (!(options.host || options.map || options.url)) {
throw new Error('miss options');
}

return function* proxy(next) {
var url = resolve(this.path, options);
return async function proxy(ctx, next) {
var url = resolve(ctx.path, options);

if(typeof options.suppressRequestHeaders === 'object'){
options.suppressRequestHeaders.forEach(function(h, i){
if (typeof options.suppressRequestHeaders === 'object') {
options.suppressRequestHeaders.forEach(function(h, i) {
options.suppressRequestHeaders[i] = h.toLowerCase();
});
}

var suppressResponseHeaders = []; // We should not be overwriting the options object!
if(typeof options.suppressResponseHeaders === 'object'){
options.suppressResponseHeaders.forEach(function(h, i){
var suppressResponseHeaders = []; // We should not be overwriting the options object!
if (typeof options.suppressResponseHeaders === 'object') {
options.suppressResponseHeaders.forEach(function(h, i) {
suppressResponseHeaders.push(h.toLowerCase());
});
}

// don't match
if (!url) {
return yield* next;
return next();
}

// if match option supplied, restrict proxy to that match
if (options.match) {
if (!this.path.match(options.match)) {
return yield* next;
if (!ctx.path.match(options.match)) {
return next();
}
}
var parsedBody = getParsedBody(this);

var parsedBody = getParsedBody(ctx);

var opt = {
url: url + (this.querystring ? '?' + this.querystring : ''),
headers: this.header,
jar: options.jar === true,
url: url + (ctx.querystring ? '?' + ctx.querystring : ''),
headers: ctx.request.header,
encoding: null,
followRedirect: options.followRedirect === false ? false : true,
method: this.method,
method: ctx.method,
body: parsedBody,
simple: false,
resolveWithFullResponse: true // make request-promise respond with the complete response object
};

// set 'Host' header to options.host (without protocol prefix), strip trailing slash
if (options.host) opt.headers.host = options.host.slice(options.host.indexOf('://')+3).replace(/\/$/,'');
// set "Host" header to options.host (without protocol prefix), strip trailing slash
if (options.host)
opt.headers.host = options.host
.slice(options.host.indexOf('://') + 3)
.replace(/\/$/, '');

if (options.requestOptions) {
if (typeof options.requestOptions === 'function') {
opt = options.requestOptions(this.request, opt);
opt = options.requestOptions(ctx.request, opt);
} else {
Object.keys(options.requestOptions).forEach(function (option) { opt[option] = options.requestOptions[option]; });
Object.keys(options.requestOptions).forEach(function(option) {
opt[option] = options.requestOptions[option];
});
}
}

for(name in opt.headers){
if(options.suppressRequestHeaders && options.suppressRequestHeaders.indexOf(name.toLowerCase()) >= 0){
for (let name in opt.headers) {
if (
options.suppressRequestHeaders &&
options.suppressRequestHeaders.indexOf(name.toLowerCase()) >= 0
) {
delete opt.headers[name];
}
}

var requestThunk = request(opt);

if (parsedBody) {
var res = yield requestThunk;
if (parsedBody || ctx.method === 'GET') {
var res = await rp(opt);
} else {
// Is there a better way?
// https://github.com/leukhin/co-request/issues/11
var res = yield pipeRequest(this.req, requestThunk);
var res = await pipe(ctx.req, opt);
}

this.status = res.statusCode;
for (var name in res.headers) {
// http://stackoverflow.com/questions/35525715/http-get-parse-error-code-hpe-unexpected-content-length
if(suppressResponseHeaders.indexOf(name.toLowerCase())>=0){
if (suppressResponseHeaders.indexOf(name.toLowerCase()) >= 0) {
continue;
}
if (name === 'transfer-encoding') {
continue;
}
this.set(name, res.headers[name]);
ctx.set(name, res.headers[name]);
}

if (options.encoding === 'gbk') {
this.body = iconv.decode(res.body, 'gbk');
return;
if(options.overrideResponseHeaders) {
for (let headerKey in options.overrideResponseHeaders) {
ctx.set(headerKey, options.overrideResponseHeaders[headerKey]);
}
}

this.body = res.body;
ctx.body = ctx.body || res.body;
ctx.status = res.statusCode;

if (options.yieldNext) {
yield next;
return next();
}
};
};


function resolve(path, options) {
var url = options.url;
if (url) {
Expand All @@ -128,14 +136,14 @@ function ignoreQuery(url) {
return url ? url.split('?')[0] : null;
}

function getParsedBody(ctx){
function getParsedBody(ctx) {
var body = ctx.request.body;
if (body === undefined || body === null){
if (body === undefined || body === null) {
return undefined;
}
var contentType = ctx.request.header['content-type'];
if (!Buffer.isBuffer(body) && typeof body !== 'string'){
if (contentType && contentType.indexOf('json') !== -1){
if (!Buffer.isBuffer(body) && typeof body !== 'string') {
if (contentType && contentType.indexOf('json') !== -1) {
body = JSON.stringify(body);
} else {
body = body + '';
Expand All @@ -144,8 +152,14 @@ function getParsedBody(ctx){
return body;
}

function pipeRequest(readable, requestThunk){
return function(cb){
readable.pipe(requestThunk(cb));
}
/**
* Pipes the incoming request body through request()
*/
function pipe(incomingRequest, opt) {
return new Promise((resolve, reject) => {
incomingRequest.pipe(requestLib(opt, (error, response) => {
if (error) return reject(error);
resolve(response);
}))
});
}
Loading