Skip to content
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
10 changes: 10 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"parserOptions": {
"sourceType": "module"
},
"env": {
"browser": true,
"node": true,
"es2022": true
}
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"workbench.colorTheme": "GitHub Light"
}
81 changes: 24 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,45 @@
# CORS proxy

Simple AWS Lambda based proxy server for making CORS requests from browser to any HTTP server.

### Run locally

#### 1. Install
```
npm install
```
#### 2. Run
```
node_modules/.bin/serverless offline
```
or
```
npm run offline
```
#### 3. Use
Open your browser and go to `http://localhost:3000/?url=<origin page>`. For example:
```
http://localhost:3000/?url=https://github.com/
```
Simple AWS Lambda URL based proxy for making CORS requests from browser to any HTTP server.

### Run on AWS

#### 0. Requirements

You need AWS account first.
Also you need your IAM user has access to deploy AWS lambdas.
```
node_modules/.bin/serverless config credentials --provider aws --key <aws_key> --secret <aws_secret>

As was done [here](./serverless.yml#L5) - provide your local AWS profile
with properly scoped permissions

And set the [region](./serverless.yml#L10)

```yml
provider:
profile: <your_local_aws_profile>
name: aws
runtime: nodejs16.x
timeout: 10 # max = 30
stage: dev
region: <your-region>
```

#### 1. Install

```
npm install
```

#### 2. Deploy
```
node_modules/.bin/serverless deploy --region <aws_region>
```
or

```
npm run deploy
```
You will see such console response:
```
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (16.08 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............................
Serverless: Stack update finished...
Service Information
service: cors-proxy
stage: dev
region: eu-central-1
stack: cors-proxy-dev
api keys:
None
endpoints:
GET - https://qwertyuiop.execute-api.eu-central-1.amazonaws.com/dev/
functions:
lambda: cors-proxy-dev-lambda
```

#### 3. Use

Open your browser and use `endpoint` field from previous console response as base address. For example:

```
https://xxxxxxxxx.lambda-url.us-east-1.on.aws/https://github.com/
```
https://qwertyuiop.execute-api.eu-central-1.amazonaws.com/dev/?url=https://github.com/
```
170 changes: 111 additions & 59 deletions handler.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,120 @@
'use strict';
'use strict'

const fetch = require('node-fetch');
import fetch from 'node-fetch'

/**
* Use this command to launch the handler from console:
*
* node_modules/.bin/serverless invoke local -f lambda -d '{"httpMethod":"GET","queryStringParameters":{"url":"http://github.com"}}'
*
* or from browser
*
* http://localhost:3000/?url=https://github.com
*/
module.exports.corsProxy = async (event) => {
return new Promise(async (resolve, reject) => {
let params = event.queryStringParameters;
let { Host, host, Origin, origin, ...headers } = event.headers;
export const corsProxy = async (event) => {
return new Promise(async (resolve, reject) => {
const {
body,
headers: h,
// "rawPath": "/https://api.census.gov/data/2021/acs/acs1/cprofile"
rawPath,
// "rawQueryString": "get=NAME,CP02_2021_001E,CP02_2017_013E&for=state:*"
rawQueryString,
requestContext: {
http: { method }
}
} = event

console.log(event);
console.log(`Got request with params:`, params);
//console.log(JSON.stringify(event, null, 2))
let { Host, host, Origin, origin, ...headers } = h

if (!params.url) {
const errorResponse = {
statusCode: 400,
body: "Unable get url from 'url' query parameter",
};
reject(Error(errorResponse));
return;
}
const path = rawPath.substr(1)
const query = rawQueryString ? '?' + rawQueryString : ''
const url = path + query

const requestParams = Object.entries(params)
.reduce((acc, param) => {
if (param[0] !== 'url') acc.push(param.join('='));
return acc;
}, [])
.join('&');
if (!url) {
const errorResponse = {
statusCode: 400,
body: "Unable get url from 'url' query parameter"
}
reject(Error(errorResponse))
return
}

const url = `${params.url}${requestParams}`;
const hasBody = /(POST|PUT)/i.test(event.httpMethod);
try {
const res = await fetch(url, {
method: event.httpMethod,
timeout: 20000,
body: hasBody ? event.body : null,
headers,
});
console.log(`Got response from ${url} ---> {statusCode: ${res.status}}`);
const hasBody = /(POST|PUT)/i.test(method)
try {
const res = await fetch(url, {
method,
timeout: 20000,
body: hasBody ? body : null,
headers
})
console.log(
`Got response from ${url} ---> {statusCode: ${res.status}}`
)

let proxyResponse = {
statusCode: res.status,
headers: {
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS
'content-type': res.headers['content-type'],
},
};
let proxyResponse = {
statusCode: res.status,
headers: {
// @deprecated: function URL CORS support is enabled in serverless.yml
// Required for CORS support to work
//'Access-Control-Allow-Origin': '*',
// Required for cookies, authorization headers with HTTPS
//'Access-Control-Allow-Credentials': true,
'content-type': res.headers['content-type']
}
}

const body = await res.text();
proxyResponse.body = body;
resolve(proxyResponse);
} catch (err) {
console.error(`Caught error: `, err);
const text = await res.text()
proxyResponse.body = text
resolve(proxyResponse)
} catch (err) {
console.error(`Caught error: `, err)

reject(err);
return;
}
});
};
reject(JSON.stringify(err, null, 2))
return
}
})
}

//example function URL event:
//{
// "version": "2.0",
// "routeKey": "$default",
// "rawPath": "/https://api.census.gov/data/2021/acs/acs1/cprofile",
// "rawQueryString": "get=NAME,CP02_2021_001E,CP02_2017_013E&for=state:*",
// "headers": {
// "sec-fetch-mode": "cors",
// "x-amzn-tls-version": "TLSv1.2",
// "sec-fetch-site": "none",
// "accept-language": "en-US,en;q=0.9,pt-PT;q=0.8,pt;q=0.7,fr;q=0.6,la;q=0.5",
// "x-forwarded-proto": "https",
// "postman-token": "76e9e064-df79-762d-233e-5f76e27f584f",
// "x-forwarded-port": "443",
// "x-forwarded-for": "108.45.130.51",
// "accept": "*/*",
// "x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
// "sec-ch-ua": "\"Google Chrome\";v=\"105\", \"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"105\"",
// "sec-ch-ua-mobile": "?0",
// "x-amzn-trace-id": "Root=1-63251f33-7b98f5a105cccae234c99190",
// "sec-ch-ua-platform": "\"Windows\"",
// "host": "xxxxxxxxx.lambda-url.us-east-1.on.aws",
// "cache-control": "no-cache",
// "accept-encoding": "gzip, deflate, br",
// "sec-fetch-dest": "empty",
// "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
// },
// "queryStringParameters": {
// "get": "NAME,CP02_2021_001E,CP02_2017_013E",
// "for": "state:*"
// },
// "requestContext": {
// "accountId": "anonymous",
// "domainName": "xxxxxxxxx.lambda-url.us-east-1.on.aws",
// "domainPrefix": "xxxxxxxxx",
// "http": {
// "method": "GET",
// "path": "/https://api.census.gov/data/2021/acs/acs1/cprofile",
// "protocol": "HTTP/1.1",
// "sourceIp": "108.45.130.51",
// "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
// },
// "requestId": "86a54954-b4c2-4de7-87ff-506c5d7b4a72",
// "routeKey": "$default",
// "stage": "$default",
// "time": "17/Sep/2022:01:13:23 +0000",
// "timeEpoch": 1663377203304
// },
// "isBase64Encoded": false
//}.
Loading