mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-24 03:39:11 +00:00
Compare commits
39 Commits
@now/rust@
...
@now/bash@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1049985af | ||
|
|
214388ccf3 | ||
|
|
b1d6b7bfc0 | ||
|
|
ece3564dfd | ||
|
|
a88af1f077 | ||
|
|
d92f7b26c0 | ||
|
|
52198af750 | ||
|
|
d58bff2453 | ||
|
|
8c0a144ae4 | ||
|
|
106e4d5f36 | ||
|
|
66c28bd695 | ||
|
|
55e75296ff | ||
|
|
36cbb36737 | ||
|
|
978ca328ef | ||
|
|
7b383e0f7c | ||
|
|
faa5ab36aa | ||
|
|
c0a21969dd | ||
|
|
73d0a1723f | ||
|
|
7c515544ae | ||
|
|
b53c9a6299 | ||
|
|
35ff11e6e4 | ||
|
|
64ee4905cd | ||
|
|
e50dd7e50a | ||
|
|
6101ba9d95 | ||
|
|
8dc0c92c58 | ||
|
|
44c9f3765a | ||
|
|
92c05ca338 | ||
|
|
069b557906 | ||
|
|
692a0df909 | ||
|
|
aeafeb5441 | ||
|
|
a09d5fb355 | ||
|
|
d8017aa9aa | ||
|
|
702f56b9b5 | ||
|
|
183b117152 | ||
|
|
75b3fb4981 | ||
|
|
49e63de5fe | ||
|
|
4742cd32f2 | ||
|
|
377b73105d | ||
|
|
a5577efb3d |
12
.circleci/build.sh
Executable file
12
.circleci/build.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
circleci_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
needs_build="$(grep -rn '"build"' packages/*/package.json | cut -d: -f1)"
|
||||
|
||||
for pkg in $needs_build; do
|
||||
dir="$(dirname "$pkg")"
|
||||
cd "$circleci_dir/../$dir"
|
||||
echo "Building \`$dir\`"
|
||||
yarn build
|
||||
done
|
||||
@@ -23,6 +23,9 @@ jobs:
|
||||
- run:
|
||||
name: Linting
|
||||
command: yarn lint
|
||||
- run:
|
||||
name: Building
|
||||
command: ./.circleci/build.sh
|
||||
- run:
|
||||
name: Tests
|
||||
command: yarn test
|
||||
@@ -30,11 +33,8 @@ jobs:
|
||||
name: Potentially save npm token
|
||||
command: "([[ ! -z $NPM_TOKEN ]] && echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" >> ~/.npmrc) || echo \"Did not write npm token\""
|
||||
- run:
|
||||
name: Potentially publish canary release
|
||||
command: "if ls ~/.npmrc >/dev/null 2>&1 && [[ $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --npm-tag canary --yes; else echo \"Did not publish\"; fi"
|
||||
- run:
|
||||
name: Potentially publish stable release
|
||||
command: "if ls ~/.npmrc >/dev/null 2>&1 && [[ ! $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --yes; else echo \"Did not publish\"; fi"
|
||||
name: Potentially publish releases to npm
|
||||
command: ./.circleci/publish.sh
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-deploy:
|
||||
|
||||
24
.circleci/publish.sh
Executable file
24
.circleci/publish.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ ! -e ~/.npmrc ]; then
|
||||
echo "~/.npmrc file does not exist, skipping publish"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
npm_tag=""
|
||||
tag="$(git describe --tags --exact-match 2> /dev/null || :)"
|
||||
|
||||
if [ -z "$tag" ]; then
|
||||
echo "Not a tagged commit, skipping publish"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$tag" =~ -canary ]]; then
|
||||
echo "Publishing canary release"
|
||||
npm_tag="--npm-tag canary"
|
||||
else
|
||||
echo "Publishing stable release"
|
||||
fi
|
||||
|
||||
yarn run lerna publish from-git $npm_tag --yes
|
||||
3
.prettierrc.json
Normal file
3
.prettierrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"pre-commit": "lint-staged",
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"prettier --write --single-quote",
|
||||
"prettier --write",
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/bash",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3-canary.0",
|
||||
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
|
||||
"main": "index.js",
|
||||
"author": "Nathan Rajlich <nate@zeit.co>",
|
||||
|
||||
@@ -76,7 +76,7 @@ _lambda_runtime_next() {
|
||||
_lambda_runtime_body() {
|
||||
if [ "$(jq --raw-output '.body | type' < "$1")" = "string" ]; then
|
||||
if [ "$(jq --raw-output '.encoding' < "$1")" = "base64" ]; then
|
||||
jq --raw-output '.body' < "$1" | base64 -d
|
||||
jq --raw-output '.body' < "$1" | base64 --decode
|
||||
else
|
||||
# assume plain-text body
|
||||
jq --raw-output '.body' < "$1"
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
const prod = process.env.AWS_EXECUTION_ENV || process.env.X_GOOGLE_CODE_LOCATION;
|
||||
const TMP_PATH = prod ? '/tmp' : path.join(__dirname, 'tmp');
|
||||
const { join } = require('path');
|
||||
const { tmpdir } = require('os');
|
||||
const { mkdirp } = require('fs-extra');
|
||||
|
||||
module.exports = async function getWritableDirectory() {
|
||||
const name = Math.floor(Math.random() * 0x7fffffff).toString(16);
|
||||
const directory = path.join(TMP_PATH, name);
|
||||
await fs.mkdirp(directory);
|
||||
const directory = join(tmpdir(), name);
|
||||
await mkdirp(directory);
|
||||
return directory;
|
||||
};
|
||||
|
||||
@@ -13,9 +13,18 @@ function spawnAsync(command, args, cwd) {
|
||||
});
|
||||
}
|
||||
|
||||
async function chmodPlusX(fsPath) {
|
||||
const s = await fs.stat(fsPath);
|
||||
const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise
|
||||
if (s.mode === newMode) return;
|
||||
const base8 = newMode.toString(8).slice(-3);
|
||||
await fs.chmod(fsPath, base8);
|
||||
}
|
||||
|
||||
async function runShellScript(fsPath) {
|
||||
assert(path.isAbsolute(fsPath));
|
||||
const destPath = path.dirname(fsPath);
|
||||
await chmodPlusX(fsPath);
|
||||
await spawnAsync(`./${path.basename(fsPath)}`, [], destPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/build-utils",
|
||||
"version": "0.4.35-canary.2",
|
||||
"version": "0.4.37-canary.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -9,6 +9,7 @@ const {
|
||||
runPackageJsonScript,
|
||||
} = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const fs = require('fs-extra');
|
||||
const semver = require('semver');
|
||||
const nextLegacyVersions = require('./legacy-versions');
|
||||
const {
|
||||
@@ -39,7 +40,7 @@ async function readPackageJson(entryPath) {
|
||||
try {
|
||||
return JSON.parse(await readFile(packagePath, 'utf8'));
|
||||
} catch (err) {
|
||||
console.log('no package.json found in entry');
|
||||
console.log('package.json not found in entry');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -68,6 +69,36 @@ async function writeNpmRc(workPath, token) {
|
||||
);
|
||||
}
|
||||
|
||||
function getNextVersion(packageJson) {
|
||||
let nextVersion;
|
||||
if (packageJson.dependencies && packageJson.dependencies.next) {
|
||||
nextVersion = packageJson.dependencies.next;
|
||||
} else if (packageJson.devDependencies && packageJson.devDependencies.next) {
|
||||
nextVersion = packageJson.devDependencies.next;
|
||||
}
|
||||
return nextVersion;
|
||||
}
|
||||
|
||||
function isLegacyNext(nextVersion) {
|
||||
// If version is using the dist-tag instead of a version range
|
||||
if (nextVersion === 'canary' || nextVersion === 'latest') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the version is an exact match with the legacy versions
|
||||
if (nextLegacyVersions.indexOf(nextVersion) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const maxSatisfying = semver.maxSatisfying(nextLegacyVersions, nextVersion);
|
||||
// When the version can't be matched with legacy versions, so it must be a newer version
|
||||
if (maxSatisfying === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
exports.config = {
|
||||
maxLambdaSize: '5mb',
|
||||
};
|
||||
@@ -86,38 +117,14 @@ exports.build = async ({ files, workPath, entrypoint }) => {
|
||||
|
||||
const pkg = await readPackageJson(entryPath);
|
||||
|
||||
let nextVersion;
|
||||
if (pkg.dependencies && pkg.dependencies.next) {
|
||||
nextVersion = pkg.dependencies.next;
|
||||
} else if (pkg.devDependencies && pkg.devDependencies.next) {
|
||||
nextVersion = pkg.devDependencies.next;
|
||||
}
|
||||
|
||||
const nextVersion = getNextVersion(pkg);
|
||||
if (!nextVersion) {
|
||||
throw new Error(
|
||||
'No Next.js version could be detected in "package.json". Make sure `"next"` is installed in "dependencies" or "devDependencies"',
|
||||
);
|
||||
}
|
||||
|
||||
const isLegacy = (() => {
|
||||
// If version is using the dist-tag instead of a version range
|
||||
if (nextVersion === 'canary' || nextVersion === 'latest') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the version is an exact match with the legacy versions
|
||||
if (nextLegacyVersions.indexOf(nextVersion) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const maxSatisfying = semver.maxSatisfying(nextLegacyVersions, nextVersion);
|
||||
// When the version can't be matched with legacy versions, so it must be a newer version
|
||||
if (maxSatisfying === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})();
|
||||
const isLegacy = isLegacyNext(nextVersion);
|
||||
|
||||
console.log(`MODE: ${isLegacy ? 'legacy' : 'serverless'}`);
|
||||
|
||||
@@ -278,6 +285,18 @@ exports.build = async ({ files, workPath, entrypoint }) => {
|
||||
);
|
||||
}
|
||||
|
||||
// An optional assets folder that is placed alongside every page entrypoint
|
||||
const assets = await glob(
|
||||
'assets/**',
|
||||
path.join(entryPath, '.next', 'serverless'),
|
||||
);
|
||||
|
||||
const assetKeys = Object.keys(assets);
|
||||
if (assetKeys.length > 0) {
|
||||
console.log('detected assets to be bundled with lambda:');
|
||||
assetKeys.forEach(assetFile => console.log(`\t${assetFile}`));
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
pageKeys.map(async (page) => {
|
||||
// These default pages don't have to be handled as they'd always 404
|
||||
@@ -291,6 +310,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
|
||||
lambdas[path.join(entryDirectory, pathname)] = await createLambda({
|
||||
files: {
|
||||
...launcherFiles,
|
||||
...assets,
|
||||
'page.js': pages[page],
|
||||
},
|
||||
handler: 'now__launcher.launcher',
|
||||
@@ -308,7 +328,9 @@ exports.build = async ({ files, workPath, entrypoint }) => {
|
||||
const staticFiles = Object.keys(nextStaticFiles).reduce(
|
||||
(mappedFiles, file) => ({
|
||||
...mappedFiles,
|
||||
[path.join(entryDirectory, `_next/static/${file}`)]: nextStaticFiles[file],
|
||||
[path.join(entryDirectory, `_next/static/${file}`)]: nextStaticFiles[
|
||||
file
|
||||
],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
@@ -326,3 +348,39 @@ exports.build = async ({ files, workPath, entrypoint }) => {
|
||||
|
||||
return { ...lambdas, ...staticFiles, ...staticDirectoryFiles };
|
||||
};
|
||||
|
||||
exports.prepareCache = async ({ cachePath, workPath, entrypoint }) => {
|
||||
console.log('preparing cache ...');
|
||||
|
||||
const entryDirectory = path.dirname(entrypoint);
|
||||
const entryPath = path.join(workPath, entryDirectory);
|
||||
const cacheEntryPath = path.join(cachePath, entryDirectory);
|
||||
|
||||
const pkg = await readPackageJson(entryPath);
|
||||
const nextVersion = getNextVersion(pkg);
|
||||
const isLegacy = isLegacyNext(nextVersion);
|
||||
|
||||
if (isLegacy) {
|
||||
// skip caching legacy mode (swapping deps between all and production can get bug-prone)
|
||||
return {};
|
||||
}
|
||||
|
||||
console.log('clearing old cache ...');
|
||||
fs.removeSync(cacheEntryPath);
|
||||
fs.mkdirpSync(cacheEntryPath);
|
||||
|
||||
console.log('copying build files for cache ...');
|
||||
fs.renameSync(entryPath, cacheEntryPath);
|
||||
|
||||
console.log('producing cache file manifest ...');
|
||||
|
||||
const cacheEntrypoint = path.relative(cachePath, cacheEntryPath);
|
||||
return {
|
||||
...(await glob(
|
||||
path.join(cacheEntrypoint, 'node_modules/{**,!.*,.yarn*}'),
|
||||
cachePath,
|
||||
)),
|
||||
...(await glob(path.join(cacheEntrypoint, 'package-lock.json'), cachePath)),
|
||||
...(await glob(path.join(cacheEntrypoint, 'yarn.lock'), cachePath)),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,10 +4,8 @@ const { Server } = require('http');
|
||||
const { Bridge } = require('./now__bridge.js');
|
||||
const page = require('./page.js');
|
||||
|
||||
const bridge = new Bridge();
|
||||
bridge.port = 3000;
|
||||
|
||||
const server = new Server(page.render);
|
||||
server.listen(bridge.port);
|
||||
const bridge = new Bridge(server);
|
||||
bridge.listen();
|
||||
|
||||
exports.launcher = bridge.launcher;
|
||||
|
||||
@@ -3,9 +3,6 @@ const next = require('next-server');
|
||||
const url = require('url');
|
||||
const { Bridge } = require('./now__bridge.js');
|
||||
|
||||
const bridge = new Bridge();
|
||||
bridge.port = 3000;
|
||||
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
const app = next({});
|
||||
@@ -14,6 +11,8 @@ const server = new Server((req, res) => {
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
app.render(req, res, 'PATHNAME_PLACEHOLDER', parsedUrl.query, parsedUrl);
|
||||
});
|
||||
server.listen(bridge.port);
|
||||
|
||||
const bridge = new Bridge(server);
|
||||
bridge.listen();
|
||||
|
||||
exports.launcher = bridge.launcher;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/next",
|
||||
"version": "0.0.85-canary.4",
|
||||
"version": "0.0.85-canary.8",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -8,8 +8,9 @@
|
||||
"directory": "packages/now-next"
|
||||
},
|
||||
"dependencies": {
|
||||
"@now/node-bridge": "0.1.4",
|
||||
"@now/node-bridge": "1.0.0-canary.2",
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"fs.promised": "^3.0.0",
|
||||
"semver": "^5.6.0"
|
||||
}
|
||||
|
||||
24
packages/now-node-bridge/.eslintrc
Normal file
24
packages/now-node-bridge/.eslintrc
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": ["prettier", "airbnb-base"],
|
||||
"rules": {
|
||||
"no-console": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-dynamic-require": 0,
|
||||
"global-require": 0
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["test/**"],
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": 0
|
||||
},
|
||||
"globals": {
|
||||
"describe": true,
|
||||
"it": true,
|
||||
"test": true,
|
||||
"expect": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
1
packages/now-node-bridge/.gitignore
vendored
Normal file
1
packages/now-node-bridge/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/bridge.*
|
||||
@@ -1,110 +0,0 @@
|
||||
const http = require('http');
|
||||
|
||||
function normalizeEvent(event) {
|
||||
let isApiGateway = true;
|
||||
|
||||
if (event.Action === 'Invoke') {
|
||||
isApiGateway = false;
|
||||
const invokeEvent = JSON.parse(event.body);
|
||||
|
||||
const {
|
||||
method, path, headers, encoding,
|
||||
} = invokeEvent;
|
||||
|
||||
let { body } = invokeEvent;
|
||||
|
||||
if (body) {
|
||||
if (encoding === 'base64') {
|
||||
body = Buffer.from(body, encoding);
|
||||
} else if (encoding === undefined) {
|
||||
body = Buffer.from(body);
|
||||
} else {
|
||||
throw new Error(`Unsupported encoding: ${encoding}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isApiGateway, method, path, headers, body,
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
httpMethod: method, path, headers, body,
|
||||
} = event;
|
||||
|
||||
return {
|
||||
isApiGateway, method, path, headers, body,
|
||||
};
|
||||
}
|
||||
|
||||
class Bridge {
|
||||
constructor() {
|
||||
this.launcher = this.launcher.bind(this);
|
||||
}
|
||||
|
||||
launcher(event) {
|
||||
// eslint-disable-next-line consistent-return
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.userError) {
|
||||
console.error('Error while initializing entrypoint:', this.userError);
|
||||
return resolve({ statusCode: 500, body: '' });
|
||||
}
|
||||
|
||||
if (!this.port) {
|
||||
return resolve({ statusCode: 504, body: '' });
|
||||
}
|
||||
|
||||
const {
|
||||
isApiGateway, method, path, headers, body,
|
||||
} = normalizeEvent(event);
|
||||
|
||||
const opts = {
|
||||
hostname: '127.0.0.1',
|
||||
port: this.port,
|
||||
path,
|
||||
method,
|
||||
headers,
|
||||
};
|
||||
|
||||
const req = http.request(opts, (res) => {
|
||||
const response = res;
|
||||
const respBodyChunks = [];
|
||||
response.on('data', chunk => respBodyChunks.push(Buffer.from(chunk)));
|
||||
response.on('error', reject);
|
||||
response.on('end', () => {
|
||||
const bodyBuffer = Buffer.concat(respBodyChunks);
|
||||
delete response.headers.connection;
|
||||
|
||||
if (isApiGateway) {
|
||||
delete response.headers['content-length'];
|
||||
} else
|
||||
if (response.headers['content-length']) {
|
||||
response.headers['content-length'] = bodyBuffer.length;
|
||||
}
|
||||
|
||||
resolve({
|
||||
statusCode: response.statusCode,
|
||||
headers: response.headers,
|
||||
body: bodyBuffer.toString('base64'),
|
||||
encoding: 'base64',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
setTimeout(() => {
|
||||
// this lets express print the true error of why the connection was closed.
|
||||
// it is probably 'Cannot set headers after they are sent to the client'
|
||||
reject(error);
|
||||
}, 2);
|
||||
});
|
||||
|
||||
if (body) req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Bridge,
|
||||
};
|
||||
@@ -1,10 +1,26 @@
|
||||
{
|
||||
"name": "@now/node-bridge",
|
||||
"version": "0.1.11-canary.0",
|
||||
"version": "1.0.0-canary.2",
|
||||
"license": "MIT",
|
||||
"main": "./index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zeit/now-builders.git",
|
||||
"directory": "packages/now-node-bridge"
|
||||
},
|
||||
"files": [
|
||||
"bridge.*",
|
||||
"index.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "npm run build && jest",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
"@types/node": "11.9.4",
|
||||
"jest": "24.1.0",
|
||||
"typescript": "3.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
183
packages/now-node-bridge/src/bridge.ts
Normal file
183
packages/now-node-bridge/src/bridge.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import { AddressInfo } from 'net';
|
||||
import { APIGatewayProxyEvent } from 'aws-lambda';
|
||||
import {
|
||||
Server,
|
||||
IncomingHttpHeaders,
|
||||
OutgoingHttpHeaders,
|
||||
request
|
||||
} from 'http';
|
||||
|
||||
interface NowProxyEvent {
|
||||
Action: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface NowProxyRequest {
|
||||
isApiGateway?: boolean;
|
||||
method: string;
|
||||
path: string;
|
||||
headers: IncomingHttpHeaders;
|
||||
body: Buffer;
|
||||
}
|
||||
|
||||
export interface NowProxyResponse {
|
||||
statusCode: number;
|
||||
headers: OutgoingHttpHeaders;
|
||||
body: string;
|
||||
encoding: string;
|
||||
}
|
||||
|
||||
function normalizeNowProxyEvent(event: NowProxyEvent): NowProxyRequest {
|
||||
let bodyBuffer: Buffer | null;
|
||||
const { method, path, headers, encoding, body } = JSON.parse(event.body);
|
||||
|
||||
if (body) {
|
||||
if (encoding === 'base64') {
|
||||
bodyBuffer = Buffer.from(body, encoding);
|
||||
} else if (encoding === undefined) {
|
||||
bodyBuffer = Buffer.from(body);
|
||||
} else {
|
||||
throw new Error(`Unsupported encoding: ${encoding}`);
|
||||
}
|
||||
} else {
|
||||
bodyBuffer = Buffer.alloc(0);
|
||||
}
|
||||
|
||||
return { isApiGateway: false, method, path, headers, body: bodyBuffer };
|
||||
}
|
||||
|
||||
function normalizeAPIGatewayProxyEvent(
|
||||
event: APIGatewayProxyEvent
|
||||
): NowProxyRequest {
|
||||
let bodyBuffer: Buffer | null;
|
||||
const { httpMethod: method, path, headers, body } = event;
|
||||
|
||||
if (body) {
|
||||
if (event.isBase64Encoded) {
|
||||
bodyBuffer = Buffer.from(body, 'base64');
|
||||
} else {
|
||||
bodyBuffer = Buffer.from(body);
|
||||
}
|
||||
} else {
|
||||
bodyBuffer = Buffer.alloc(0);
|
||||
}
|
||||
|
||||
return { isApiGateway: true, method, path, headers, body: bodyBuffer };
|
||||
}
|
||||
|
||||
function normalizeEvent(
|
||||
event: NowProxyEvent | APIGatewayProxyEvent
|
||||
): NowProxyRequest {
|
||||
if ('Action' in event) {
|
||||
if (event.Action === 'Invoke') {
|
||||
return normalizeNowProxyEvent(event);
|
||||
} else {
|
||||
throw new Error(`Unexpected event.Action: ${event.Action}`);
|
||||
}
|
||||
} else {
|
||||
return normalizeAPIGatewayProxyEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
export class Bridge {
|
||||
private server: Server | null;
|
||||
private listening: Promise<AddressInfo>;
|
||||
private resolveListening: (info: AddressInfo) => void;
|
||||
|
||||
constructor(server?: Server) {
|
||||
this.server = null;
|
||||
if (server) {
|
||||
this.setServer(server);
|
||||
}
|
||||
this.launcher = this.launcher.bind(this);
|
||||
|
||||
// This is just to appease TypeScript strict mode, since it doesn't
|
||||
// understand that the Promise constructor is synchronous
|
||||
this.resolveListening = (info: AddressInfo) => {};
|
||||
|
||||
this.listening = new Promise(resolve => {
|
||||
this.resolveListening = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
setServer(server: Server) {
|
||||
this.server = server;
|
||||
server.once('listening', () => {
|
||||
const addr = server.address();
|
||||
if (typeof addr === 'string') {
|
||||
throw new Error(`Unexpected string for \`server.address()\`: ${addr}`);
|
||||
} else if (!addr) {
|
||||
throw new Error('`server.address()` returned `null`');
|
||||
} else {
|
||||
this.resolveListening(addr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
listen() {
|
||||
if (!this.server) {
|
||||
throw new Error('Server has not been set!');
|
||||
}
|
||||
|
||||
return this.server.listen({
|
||||
host: '127.0.0.1',
|
||||
port: 0
|
||||
});
|
||||
}
|
||||
|
||||
async launcher(
|
||||
event: NowProxyEvent | APIGatewayProxyEvent
|
||||
): Promise<NowProxyResponse> {
|
||||
const { port } = await this.listening;
|
||||
|
||||
const { isApiGateway, method, path, headers, body } = normalizeEvent(
|
||||
event
|
||||
);
|
||||
|
||||
const opts = {
|
||||
hostname: '127.0.0.1',
|
||||
port,
|
||||
path,
|
||||
method,
|
||||
headers
|
||||
};
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = request(opts, res => {
|
||||
const response = res;
|
||||
const respBodyChunks: Buffer[] = [];
|
||||
response.on('data', chunk => respBodyChunks.push(Buffer.from(chunk)));
|
||||
response.on('error', reject);
|
||||
response.on('end', () => {
|
||||
const bodyBuffer = Buffer.concat(respBodyChunks);
|
||||
delete response.headers.connection;
|
||||
|
||||
if (isApiGateway) {
|
||||
delete response.headers['content-length'];
|
||||
} else if (response.headers['content-length']) {
|
||||
response.headers['content-length'] = String(bodyBuffer.length);
|
||||
}
|
||||
|
||||
resolve({
|
||||
statusCode: response.statusCode || 200,
|
||||
headers: response.headers,
|
||||
body: bodyBuffer.toString('base64'),
|
||||
encoding: 'base64'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', error => {
|
||||
setTimeout(() => {
|
||||
// this lets express print the true error of why the connection was closed.
|
||||
// it is probably 'Cannot set headers after they are sent to the client'
|
||||
reject(error);
|
||||
}, 2);
|
||||
});
|
||||
|
||||
if (body) req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
71
packages/now-node-bridge/test/bridge.test.js
Normal file
71
packages/now-node-bridge/test/bridge.test.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const assert = require('assert');
|
||||
const { Server } = require('http');
|
||||
const { Bridge } = require('../bridge');
|
||||
|
||||
test('port binding', async () => {
|
||||
const server = new Server();
|
||||
const bridge = new Bridge(server);
|
||||
bridge.listen();
|
||||
|
||||
// Test port binding
|
||||
const info = await bridge.listening;
|
||||
assert.equal(info.address, '127.0.0.1');
|
||||
assert.equal(typeof info.port, 'number');
|
||||
|
||||
server.close();
|
||||
});
|
||||
|
||||
test('`APIGatewayProxyEvent` normalizing', async () => {
|
||||
const server = new Server((req, res) => res.end(
|
||||
JSON.stringify({
|
||||
method: req.method,
|
||||
path: req.url,
|
||||
headers: req.headers,
|
||||
}),
|
||||
));
|
||||
const bridge = new Bridge(server);
|
||||
bridge.listen();
|
||||
const result = await bridge.launcher({
|
||||
httpMethod: 'GET',
|
||||
headers: { foo: 'bar' },
|
||||
path: '/apigateway',
|
||||
body: null,
|
||||
});
|
||||
assert.equal(result.encoding, 'base64');
|
||||
assert.equal(result.statusCode, 200);
|
||||
const body = JSON.parse(Buffer.from(result.body, 'base64').toString());
|
||||
assert.equal(body.method, 'GET');
|
||||
assert.equal(body.path, '/apigateway');
|
||||
assert.equal(body.headers.foo, 'bar');
|
||||
|
||||
server.close();
|
||||
});
|
||||
|
||||
test('`NowProxyEvent` normalizing', async () => {
|
||||
const server = new Server((req, res) => res.end(
|
||||
JSON.stringify({
|
||||
method: req.method,
|
||||
path: req.url,
|
||||
headers: req.headers,
|
||||
}),
|
||||
));
|
||||
const bridge = new Bridge(server);
|
||||
bridge.listen();
|
||||
const result = await bridge.launcher({
|
||||
Action: 'Invoke',
|
||||
body: JSON.stringify({
|
||||
method: 'POST',
|
||||
headers: { foo: 'baz' },
|
||||
path: '/nowproxy',
|
||||
body: 'body=1',
|
||||
}),
|
||||
});
|
||||
assert.equal(result.encoding, 'base64');
|
||||
assert.equal(result.statusCode, 200);
|
||||
const body = JSON.parse(Buffer.from(result.body, 'base64').toString());
|
||||
assert.equal(body.method, 'POST');
|
||||
assert.equal(body.path, '/nowproxy');
|
||||
assert.equal(body.headers.foo, 'baz');
|
||||
|
||||
server.close();
|
||||
});
|
||||
16
packages/now-node-bridge/tsconfig.json
Normal file
16
packages/now-node-bridge/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"outDir": ".",
|
||||
"strict": true,
|
||||
"sourceMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -48,7 +48,7 @@ async function downloadInstallAndBundle(
|
||||
data: JSON.stringify({
|
||||
license: 'UNLICENSED',
|
||||
dependencies: {
|
||||
'@zeit/ncc': '0.15.1',
|
||||
'@zeit/ncc': '0.15.2',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
@@ -64,7 +64,7 @@ async function downloadInstallAndBundle(
|
||||
async function compile(workNccPath, downloadedFiles, entrypoint) {
|
||||
const input = downloadedFiles[entrypoint].fsPath;
|
||||
const ncc = require(path.join(workNccPath, 'node_modules/@zeit/ncc'));
|
||||
const { code, assets } = await ncc(input);
|
||||
const { code, assets } = await ncc(input, { sourceMap: true });
|
||||
|
||||
const preparedFiles = {};
|
||||
const blob = new FileBlob({ data: code });
|
||||
|
||||
@@ -4,22 +4,16 @@ const { Bridge } = require('./bridge.js');
|
||||
const bridge = new Bridge();
|
||||
|
||||
const saveListen = Server.prototype.listen;
|
||||
Server.prototype.listen = function listen(...args) {
|
||||
this.on('listening', function listening() {
|
||||
bridge.port = this.address().port;
|
||||
});
|
||||
saveListen.apply(this, args);
|
||||
Server.prototype.listen = function listen() {
|
||||
bridge.setServer(this);
|
||||
Server.prototype.listen = saveListen;
|
||||
return bridge.listen();
|
||||
};
|
||||
|
||||
try {
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'production';
|
||||
}
|
||||
|
||||
// PLACEHOLDER
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
bridge.userError = error;
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'production';
|
||||
}
|
||||
|
||||
// PLACEHOLDER
|
||||
|
||||
exports.launcher = bridge.launcher;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/node-server",
|
||||
"version": "0.4.27-canary.4",
|
||||
"version": "0.5.0-canary.3",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -8,7 +8,7 @@
|
||||
"directory": "packages/now-node-server"
|
||||
},
|
||||
"dependencies": {
|
||||
"@now/node-bridge": "^0.1.11-canary.0",
|
||||
"@now/node-bridge": "1.0.0-canary.2",
|
||||
"fs-extra": "7.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
2
packages/now-node/.gitignore
vendored
Normal file
2
packages/now-node/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/dist
|
||||
/src/bridge.d.ts
|
||||
@@ -1 +0,0 @@
|
||||
/test
|
||||
12
packages/now-node/build.sh
Executable file
12
packages/now-node/build.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
bridge_entrypoint="$(node -p 'require.resolve("@now/node-bridge")')"
|
||||
bridge_defs="$(dirname "$bridge_entrypoint")/bridge.d.ts"
|
||||
|
||||
if [ ! -e "$bridge_defs" ]; then
|
||||
yarn install
|
||||
fi
|
||||
|
||||
cp -v "$bridge_defs" src
|
||||
tsc
|
||||
@@ -1,22 +0,0 @@
|
||||
const { Server } = require('http');
|
||||
const { Bridge } = require('./bridge.js');
|
||||
|
||||
const bridge = new Bridge();
|
||||
bridge.port = 3000;
|
||||
let listener;
|
||||
|
||||
try {
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'production';
|
||||
}
|
||||
|
||||
// PLACEHOLDER
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
bridge.userError = error;
|
||||
}
|
||||
|
||||
const server = new Server(listener);
|
||||
server.listen(bridge.port);
|
||||
|
||||
exports.launcher = bridge.launcher;
|
||||
@@ -1,17 +1,27 @@
|
||||
{
|
||||
"name": "@now/node",
|
||||
"version": "0.4.29-canary.4",
|
||||
"version": "0.5.0-canary.5",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zeit/now-builders.git",
|
||||
"directory": "packages/now-node"
|
||||
},
|
||||
"dependencies": {
|
||||
"@now/node-bridge": "^0.1.11-canary.0",
|
||||
"@now/node-bridge": "1.0.0-canary.2",
|
||||
"fs-extra": "7.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./build.sh",
|
||||
"test": "jest"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "11.9.4",
|
||||
"jest": "24.1.0",
|
||||
"typescript": "3.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const FileFsRef = require('@now/build-utils/file-fs-ref.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const path = require('path');
|
||||
const {
|
||||
import { join, dirname } from 'path';
|
||||
import { remove, readFile } from 'fs-extra';
|
||||
import * as glob from '@now/build-utils/fs/glob.js';
|
||||
import * as download from '@now/build-utils/fs/download.js';
|
||||
import * as FileBlob from '@now/build-utils/file-blob.js';
|
||||
import * as FileFsRef from '@now/build-utils/file-fs-ref.js';
|
||||
import { createLambda } from '@now/build-utils/lambda.js';
|
||||
import {
|
||||
runNpmInstall,
|
||||
runPackageJsonScript,
|
||||
} = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
runPackageJsonScript
|
||||
} from '@now/build-utils/fs/run-user-scripts.js';
|
||||
|
||||
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
|
||||
/** @typedef {{[filePath: string]: FileRef}} Files */
|
||||
@@ -27,16 +27,16 @@ const {
|
||||
*/
|
||||
async function downloadInstallAndBundle(
|
||||
{ files, entrypoint, workPath },
|
||||
{ npmArguments = [] } = {},
|
||||
{ npmArguments = [] } = {}
|
||||
) {
|
||||
const userPath = path.join(workPath, 'user');
|
||||
const nccPath = path.join(workPath, 'ncc');
|
||||
const userPath = join(workPath, 'user');
|
||||
const nccPath = join(workPath, 'ncc');
|
||||
|
||||
console.log('downloading user files...');
|
||||
const downloadedFiles = await download(files, userPath);
|
||||
|
||||
console.log("installing dependencies for user's code...");
|
||||
const entrypointFsDirname = path.join(userPath, path.dirname(entrypoint));
|
||||
const entrypointFsDirname = join(userPath, dirname(entrypoint));
|
||||
await runNpmInstall(entrypointFsDirname, npmArguments);
|
||||
|
||||
console.log('writing ncc package.json...');
|
||||
@@ -46,12 +46,12 @@ async function downloadInstallAndBundle(
|
||||
data: JSON.stringify({
|
||||
license: 'UNLICENSED',
|
||||
dependencies: {
|
||||
'@zeit/ncc': '0.15.1',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
'@zeit/ncc': '0.15.2',
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
nccPath,
|
||||
nccPath
|
||||
);
|
||||
|
||||
console.log('installing dependencies for ncc...');
|
||||
@@ -59,43 +59,41 @@ async function downloadInstallAndBundle(
|
||||
return [downloadedFiles, nccPath, entrypointFsDirname];
|
||||
}
|
||||
|
||||
async function compile(workNccPath, downloadedFiles, entrypoint) {
|
||||
async function compile(workNccPath: string, downloadedFiles, entrypoint: string) {
|
||||
const input = downloadedFiles[entrypoint].fsPath;
|
||||
const ncc = require(path.join(workNccPath, 'node_modules/@zeit/ncc'));
|
||||
const ncc = require(join(workNccPath, 'node_modules/@zeit/ncc'));
|
||||
const { code, assets } = await ncc(input);
|
||||
|
||||
const preparedFiles = {};
|
||||
const blob = new FileBlob({ data: code });
|
||||
// move all user code to 'user' subdirectory
|
||||
preparedFiles[path.join('user', entrypoint)] = blob;
|
||||
preparedFiles[join('user', entrypoint)] = blob;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const assetName of Object.keys(assets)) {
|
||||
const { source: data, permissions: mode } = assets[assetName];
|
||||
const blob2 = new FileBlob({ data, mode });
|
||||
preparedFiles[
|
||||
path.join('user', path.dirname(entrypoint), assetName)
|
||||
] = blob2;
|
||||
preparedFiles[join('user', dirname(entrypoint), assetName)] = blob2;
|
||||
}
|
||||
|
||||
return preparedFiles;
|
||||
}
|
||||
|
||||
exports.config = {
|
||||
maxLambdaSize: '5mb',
|
||||
export const config = {
|
||||
maxLambdaSize: '5mb'
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {BuildParamsType} buildParams
|
||||
* @returns {Promise<Files>}
|
||||
*/
|
||||
exports.build = async ({ files, entrypoint, workPath }) => {
|
||||
export async function build({ files, entrypoint, workPath }) {
|
||||
const [
|
||||
downloadedFiles,
|
||||
workNccPath,
|
||||
entrypointFsDirname,
|
||||
entrypointFsDirname
|
||||
] = await downloadInstallAndBundle(
|
||||
{ files, entrypoint, workPath },
|
||||
{ npmArguments: ['--prefer-offline'] },
|
||||
{ npmArguments: ['--prefer-offline'] }
|
||||
);
|
||||
|
||||
console.log('running user script...');
|
||||
@@ -103,36 +101,34 @@ exports.build = async ({ files, entrypoint, workPath }) => {
|
||||
|
||||
console.log('compiling entrypoint with ncc...');
|
||||
const preparedFiles = await compile(workNccPath, downloadedFiles, entrypoint);
|
||||
const launcherPath = path.join(__dirname, 'launcher.js');
|
||||
let launcherData = await fs.readFile(launcherPath, 'utf8');
|
||||
const launcherPath = join(__dirname, 'launcher.js');
|
||||
let launcherData = await readFile(launcherPath, 'utf8');
|
||||
|
||||
launcherData = launcherData.replace(
|
||||
'// PLACEHOLDER',
|
||||
[
|
||||
'process.chdir("./user");',
|
||||
`listener = require("./${path.join('user', entrypoint)}");`,
|
||||
'if (listener.default) listener = listener.default;',
|
||||
].join(' '),
|
||||
`listener = require("./${join('user', entrypoint)}");`,
|
||||
'if (listener.default) listener = listener.default;'
|
||||
].join(' ')
|
||||
);
|
||||
|
||||
const launcherFiles = {
|
||||
'launcher.js': new FileBlob({ data: launcherData }),
|
||||
'bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
|
||||
'bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') })
|
||||
};
|
||||
|
||||
const lambda = await createLambda({
|
||||
files: { ...preparedFiles, ...launcherFiles },
|
||||
handler: 'launcher.launcher',
|
||||
runtime: 'nodejs8.10',
|
||||
runtime: 'nodejs8.10'
|
||||
});
|
||||
|
||||
return { [entrypoint]: lambda };
|
||||
};
|
||||
}
|
||||
|
||||
exports.prepareCache = async ({
|
||||
files, entrypoint, workPath, cachePath,
|
||||
}) => {
|
||||
await fs.remove(workPath);
|
||||
export async function prepareCache({ files, entrypoint, workPath, cachePath }) {
|
||||
await remove(workPath);
|
||||
await downloadInstallAndBundle({ files, entrypoint, workPath: cachePath });
|
||||
|
||||
return {
|
||||
@@ -141,6 +137,6 @@ exports.prepareCache = async ({
|
||||
...(await glob('user/yarn.lock', cachePath)),
|
||||
...(await glob('ncc/node_modules/**', cachePath)),
|
||||
...(await glob('ncc/package-lock.json', cachePath)),
|
||||
...(await glob('ncc/yarn.lock', cachePath)),
|
||||
...(await glob('ncc/yarn.lock', cachePath))
|
||||
};
|
||||
};
|
||||
}
|
||||
16
packages/now-node/src/launcher.ts
Normal file
16
packages/now-node/src/launcher.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Server } from 'http';
|
||||
import { Bridge } from './bridge';
|
||||
|
||||
let listener;
|
||||
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'production';
|
||||
}
|
||||
|
||||
// PLACEHOLDER
|
||||
|
||||
const server = new Server(listener);
|
||||
const bridge = new Bridge(server);
|
||||
bridge.listen();
|
||||
|
||||
exports.launcher = bridge.launcher;
|
||||
15
packages/now-node/tsconfig.json
Normal file
15
packages/now-node/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"sourceMap": false,
|
||||
"declaration": false
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -40,8 +40,16 @@ exports.build = async ({ files, entrypoint }) => {
|
||||
|
||||
await pipInstall(pipPath, srcDir, 'requests');
|
||||
|
||||
if (files['requirements.txt']) {
|
||||
console.log('found "requirements.txt"');
|
||||
const entryDirectory = path.dirname(entrypoint);
|
||||
const requirementsTxt = path.join(entryDirectory, 'requirements.txt');
|
||||
|
||||
if (files[requirementsTxt]) {
|
||||
console.log('found local "requirements.txt"');
|
||||
|
||||
const requirementsTxtPath = files[requirementsTxt].fsPath;
|
||||
await pipInstall(pipPath, srcDir, '-r', requirementsTxtPath);
|
||||
} else if (files['requirements.txt']) {
|
||||
console.log('found global "requirements.txt"');
|
||||
|
||||
const requirementsTxtPath = files['requirements.txt'].fsPath;
|
||||
await pipInstall(pipPath, srcDir, '-r', requirementsTxtPath);
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
import base64
|
||||
from http.server import HTTPServer
|
||||
import json
|
||||
import requests
|
||||
from __NOW_HANDLER_FILENAME import handler
|
||||
import _thread
|
||||
|
||||
|
||||
server = HTTPServer(('', 3000), handler)
|
||||
|
||||
|
||||
def now_handler(event, context):
|
||||
_thread.start_new_thread(server.handle_request, ())
|
||||
|
||||
payload = json.loads(event['body'])
|
||||
path = payload['path']
|
||||
headers = payload['headers']
|
||||
method = payload['method']
|
||||
|
||||
res = requests.request(method, 'http://0.0.0.0:3000' + path, headers=headers)
|
||||
encoding = payload.get('encoding')
|
||||
body = payload.get('body')
|
||||
|
||||
if (
|
||||
(body is not None and len(body) > 0) and
|
||||
(encoding is not None and encoding == 'base64')
|
||||
):
|
||||
body = base64.b64decode(body)
|
||||
|
||||
res = requests.request(method, 'http://0.0.0.0:3000' + path,
|
||||
headers=headers, data=body, allow_redirects=False)
|
||||
|
||||
return {
|
||||
'statusCode': res.status_code,
|
||||
'headers': dict(res.headers),
|
||||
'body': res.text
|
||||
'body': res.text,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/python",
|
||||
"version": "0.0.41-canary.0",
|
||||
"version": "0.0.41-canary.2",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
Reference in New Issue
Block a user