isomorphic-git / isomorphic-git/isomorphic-git
Proxy middleware - Koa
- Dominant language
- JavaScript
- Stars
- 8.4k
- Forks
- 491
- Avg merge
- 2h 53m
- Merged PRs (30d)
- 7
Description
### Problem to solve
There is a middleware, but only for express. I needed one for Koa.
### Intended users
Developers with a Koa (based) backend.
### Further details
I needed one quickly, so I have created a simple middleware with the help of your `middleware.js`.
Do keep in mind, this is without the CORS or Authorization, and with the path prefix `/proxy`.
Also, I made this quite quickly, so please don't be harsh :blush:.
### Proposed code
```typescript
import { ParameterizedContext } from 'koa';
import KoaRouter from 'koa-router';
import fetch from 'node-fetch';
import rawBody from 'raw-body';
const allowHeaders = [
'accept-encoding',
'accept-language',
'accept',
'access-control-allow-origin',
'authorization',
'cache-control',
'connection',
'content-length',
'content-type',
'dnt',
'pragma',
'range',
'referer',
'user-agent',
'x-authorization',
'x-http-method-override',
'x-requested-with'
];
const exposeHeaders = [
'accept-ranges',
'age',
'cache-control',
'content-length',
'content-language',
'content-type',
'date',
'etag',
'expires',
'last-modified',
'pragma',
'server',
'transfer-encoding',
'vary',
'x-github-request-id',
'x-redirected-url'
];
const allowMethods = [
'POST',
'GET',
'OPTIONS'
];
const INSECURE_ORIGINS: string[] = [];
type Context = ParameterizedContext>;
export default async function (ctx: Context, next: () => Promise) {
if (!allow(ctx)) return next();
const { url, method } = ctx.request;
const headers: Record = {};
for (const headername of allowHeaders) {
if (ctx.request.headers[headername]) {
headers[headername] = ctx.request.headers[headername];
}
}
// GitHub uses user-agent sniffing for git/* and changes its behavior which is frustrating
if (!headers['user-agent'] || !headers['user-agent'].startsWith('git/')) {
headers['user-agent'] = 'git/@isomorphic-git/cors-proxy';
}
const [_, pathdomain, remainingpath] = url.match(/\/proxy\/([^\/]*)\/(.*)/)!;
const protocol = INSECURE_ORIGINS.includes(pathdomain) ? 'http' : 'https';
const body: any = (method !== 'GET' && method !== 'HEAD' && !!rawBody)
? await rawBody(ctx.req) : undefined;
const response = await fetch(`${protocol}://${pathdomain}/${remainingpath}`, {
method, headers, body
});
ctx.status = response.status;
for (const headername of exposeHeaders) {
if (headername === 'content-length') continue;
if (response.headers.has(headername)) {
ctx.header[headername] = response.headers.get(headername);
}
}
if (response.redirected) ctx.header['x-redirected-url'] = response.url;
ctx.body = response.body;
}
function allow(context: Context) {
const { method, headers, path, query } = context.request;
if (method === 'OPTIONS' && path.endsWith('/info/refs') && (query.service === 'git-upload-pack' || query.service === 'git-receive-pack')) return true;
if (method === 'GET' && path.endsWith('/info/refs') && (query.service === 'git-upload-pack' || query.service === 'git-receive-pack')) return true;
if (method === 'OPTIONS' && headers['access-control-request-headers'].includes('content-type') && path.endsWith('git-upload-pack')) return true;
if (method === 'POST' && headers['content-type'] === 'application/x-git-upload-pack-request' && path.endsWith('git-upload-pack')) return true;
if (method === 'OPTIONS' && headers['access-control-request-headers'].includes('content-type') && path.endsWith('git-receive-pack')) return true;
if (method === 'POST' && headers['content-type'] === 'application/x-git-receive-pack-request' && path.endsWith('git-receive-pack')) return true;
return false;
}
```
Updated (2020-09-03) to have `raw-body` util for parsing the body.
Contributor guide
Assessment
This issue has not been assessed yet.