mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-22 17:43:59 +00:00
Compare commits
59 Commits
@now/lambd
...
@now/wordp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38ba8a36fc | ||
|
|
0323c505a3 | ||
|
|
17ee07f4f6 | ||
|
|
0a6ada77ac | ||
|
|
4d817dd67d | ||
|
|
9682a7cc0b | ||
|
|
3456f23b3e | ||
|
|
800ca2cb0e | ||
|
|
ba54b4d706 | ||
|
|
e9482d66a9 | ||
|
|
401b669363 | ||
|
|
a2a0ede1f6 | ||
|
|
3c9fcff743 | ||
|
|
e5aa526583 | ||
|
|
822b0ee3de | ||
|
|
d612e46233 | ||
|
|
77ee10cead | ||
|
|
fb2029c464 | ||
|
|
3b15755054 | ||
|
|
4f65cc3aa8 | ||
|
|
9936e35280 | ||
|
|
a04fd242b8 | ||
|
|
17bc6174a9 | ||
|
|
a7c2d9648a | ||
|
|
faa5ea9e21 | ||
|
|
c52f30c898 | ||
|
|
d675edf1dc | ||
|
|
f85c4f496f | ||
|
|
d52d7904c2 | ||
|
|
79232024bd | ||
|
|
660b787bc3 | ||
|
|
2dbf983ddb | ||
|
|
0866ba9391 | ||
|
|
d259a722a0 | ||
|
|
bf77c51f64 | ||
|
|
062b78130c | ||
|
|
fa70bc50cb | ||
|
|
08e22b35d1 | ||
|
|
9d8f3315a1 | ||
|
|
a737a99a9d | ||
|
|
ee92d92c9f | ||
|
|
34d3ebd289 | ||
|
|
785f187e5d | ||
|
|
44449f474e | ||
|
|
f44dae7f39 | ||
|
|
06f973f641 | ||
|
|
47bb47804e | ||
|
|
5df692979a | ||
|
|
cd02d5390f | ||
|
|
c93fd416c4 | ||
|
|
431db7a62d | ||
|
|
6f86c70313 | ||
|
|
0923fc9200 | ||
|
|
787675f462 | ||
|
|
977615720a | ||
|
|
1a4e64cd27 | ||
|
|
c9fc255002 | ||
|
|
e83d4d4249 | ||
|
|
d2ca763079 |
@@ -31,6 +31,7 @@
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/multistream": "^2.1.1",
|
||||
"@types/node": "^10.12.8",
|
||||
"async-retry": "1.2.3",
|
||||
"buffer-replace": "^1.0.0",
|
||||
"eslint": "^5.9.0",
|
||||
"eslint-config-airbnb-base": "^13.1.0",
|
||||
|
||||
@@ -33,9 +33,9 @@ if declare -f build > /dev/null; then
|
||||
build "$@"
|
||||
fi
|
||||
|
||||
# Ensure the entrypoint defined a `serve` function
|
||||
if ! declare -f serve > /dev/null; then
|
||||
echo "ERROR: A \`serve\` function must be defined in \"$ENTRYPOINT\"!" >&2
|
||||
# Ensure the entrypoint defined a `handler` function
|
||||
if ! declare -f handler > /dev/null; then
|
||||
echo "ERROR: A \`handler\` function must be defined in \"$ENTRYPOINT\"!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/bash",
|
||||
"version": "0.0.3-canary.0",
|
||||
"version": "0.0.4-canary.0",
|
||||
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
|
||||
"main": "index.js",
|
||||
"author": "Nathan Rajlich <nate@zeit.co>",
|
||||
|
||||
@@ -49,7 +49,7 @@ _lambda_runtime_next() {
|
||||
local exit_code=0
|
||||
REQUEST="$event"
|
||||
|
||||
# Stdin of the `serve` function is the HTTP request body.
|
||||
# Stdin of the `handler` function is the HTTP request body.
|
||||
# Need to use a fifo here instead of bash <() because Lambda
|
||||
# errors with "/dev/fd/63 not found" for some reason :/
|
||||
local stdin
|
||||
@@ -57,7 +57,7 @@ _lambda_runtime_next() {
|
||||
mkfifo "$stdin"
|
||||
_lambda_runtime_body "$event" > "$stdin" &
|
||||
|
||||
serve "$event" < "$stdin" > "$body" || exit_code="$?"
|
||||
handler "$event" < "$stdin" > "$body" || exit_code="$?"
|
||||
rm -f "$event" "$stdin"
|
||||
|
||||
if [ "$exit_code" -eq 0 ]; then
|
||||
|
||||
32
packages/now-build-utils/fs/bootstrap-yarn.js
vendored
32
packages/now-build-utils/fs/bootstrap-yarn.js
vendored
@@ -14,15 +14,15 @@ const cachePath = spawnSync(yarnPath, ['cache', 'dir'])
|
||||
spawnSync(yarnPath, ['cache', 'clean']);
|
||||
const vfs = new MemoryFileSystem();
|
||||
|
||||
function isOutsideCachePath(filename) {
|
||||
function isInsideCachePath(filename) {
|
||||
const relative = path.relative(cachePath, filename);
|
||||
return relative.startsWith('..');
|
||||
return !relative.startsWith('..');
|
||||
}
|
||||
|
||||
const saveCreateWriteStream = fs.createWriteStream;
|
||||
fs.createWriteStream = (...args) => {
|
||||
const filename = args[0];
|
||||
if (isOutsideCachePath(filename)) {
|
||||
if (!isInsideCachePath(filename)) {
|
||||
return saveCreateWriteStream.call(fs, ...args);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ fs.createWriteStream = (...args) => {
|
||||
const saveReadFile = fs.readFile;
|
||||
fs.readFile = (...args) => {
|
||||
const filename = args[0];
|
||||
if (isOutsideCachePath(filename)) {
|
||||
if (!isInsideCachePath(filename)) {
|
||||
return saveReadFile.call(fs, ...args);
|
||||
}
|
||||
|
||||
@@ -60,23 +60,33 @@ fs.readFile = (...args) => {
|
||||
const saveCopyFile = fs.copyFile;
|
||||
fs.copyFile = (...args) => {
|
||||
const src = args[0];
|
||||
if (isOutsideCachePath(src)) {
|
||||
return saveCopyFile.call(fs, ...args);
|
||||
}
|
||||
|
||||
const dest = args[1];
|
||||
const callback = args[args.length - 1];
|
||||
const buffer = vfs.readFileSync(src);
|
||||
return fs.writeFile(dest, buffer, callback);
|
||||
|
||||
if (isInsideCachePath(src) && !isInsideCachePath(dest)) {
|
||||
const buffer = vfs.readFileSync(src);
|
||||
return fs.writeFile(dest, buffer, callback);
|
||||
} else
|
||||
if (!isInsideCachePath(src) && isInsideCachePath(dest)) {
|
||||
const buffer = fs.readFileSync(src);
|
||||
|
||||
vfs.mkdirpSync(path.dirname(dest));
|
||||
fs.writeFileSync(dest, Buffer.alloc(0));
|
||||
return vfs.writeFile(dest, buffer, callback);
|
||||
} else {
|
||||
return saveCopyFile.call(fs, ...args);
|
||||
}
|
||||
};
|
||||
|
||||
const saveWriteFile = fs.writeFile;
|
||||
fs.writeFile = (...args) => {
|
||||
const filename = args[0];
|
||||
if (isOutsideCachePath(filename)) {
|
||||
if (!isInsideCachePath(filename)) {
|
||||
return saveWriteFile.call(fs, ...args);
|
||||
}
|
||||
|
||||
vfs.mkdirpSync(path.dirname(filename));
|
||||
fs.writeFileSync(filename, Buffer.alloc(0));
|
||||
return vfs.writeFile(...args);
|
||||
};
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ async function scanParentDirs(destPath, scriptName) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const packageJson = JSON.parse(await fs.readFile(packageJsonPath));
|
||||
hasScript = Boolean(
|
||||
packageJson.scripts && packageJson.scripts[scriptName],
|
||||
packageJson.scripts && scriptName && packageJson.scripts[scriptName],
|
||||
);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
hasPackageLockJson = await fs.exists(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const assert = require('assert');
|
||||
const Sema = require('async-sema');
|
||||
const { ZipFile } = require('yazl');
|
||||
const streamToBuffer = require('./fs/stream-to-buffer.js');
|
||||
|
||||
@@ -14,6 +15,7 @@ class Lambda {
|
||||
}
|
||||
}
|
||||
|
||||
const sema = new Sema(10);
|
||||
const mtime = new Date(1540000000000);
|
||||
|
||||
async function createLambda({
|
||||
@@ -23,24 +25,33 @@ async function createLambda({
|
||||
assert(typeof handler === 'string', '"handler" is not a string');
|
||||
assert(typeof runtime === 'string', '"runtime" is not a string');
|
||||
assert(typeof environment === 'object', '"environment" is not an object');
|
||||
const zipFile = new ZipFile();
|
||||
|
||||
Object.keys(files)
|
||||
.sort()
|
||||
.forEach((name) => {
|
||||
const file = files[name];
|
||||
const stream = file.toStream();
|
||||
zipFile.addReadStream(stream, name, { mode: file.mode, mtime });
|
||||
await sema.acquire();
|
||||
try {
|
||||
const zipFile = new ZipFile();
|
||||
const zipBuffer = await new Promise((resolve, reject) => {
|
||||
Object.keys(files)
|
||||
.sort()
|
||||
.forEach((name) => {
|
||||
const file = files[name];
|
||||
const stream = file.toStream();
|
||||
stream.on('error', reject);
|
||||
zipFile.addReadStream(stream, name, { mode: file.mode, mtime });
|
||||
});
|
||||
|
||||
zipFile.end();
|
||||
streamToBuffer(zipFile.outputStream).then(resolve).catch(reject);
|
||||
});
|
||||
|
||||
zipFile.end();
|
||||
const zipBuffer = await streamToBuffer(zipFile.outputStream);
|
||||
return new Lambda({
|
||||
zipBuffer,
|
||||
handler,
|
||||
runtime,
|
||||
environment,
|
||||
});
|
||||
return new Lambda({
|
||||
zipBuffer,
|
||||
handler,
|
||||
runtime,
|
||||
environment,
|
||||
});
|
||||
} finally {
|
||||
sema.release();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/build-utils",
|
||||
"version": "0.4.31-canary.2",
|
||||
"version": "0.4.32-canary.2",
|
||||
"dependencies": {
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "2.1.4",
|
||||
|
||||
5
packages/now-build-utils/test/fixtures/07-cross-install/api/index.js
vendored
Normal file
5
packages/now-build-utils/test/fixtures/07-cross-install/api/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
const cowsay = require('cowsay').say;
|
||||
|
||||
module.exports = (req, resp) => {
|
||||
resp.end(cowsay({ text: 'cross-cow:RANDOMNESS_PLACEHOLDER' }));
|
||||
};
|
||||
5
packages/now-build-utils/test/fixtures/07-cross-install/api/package.json
vendored
Normal file
5
packages/now-build-utils/test/fixtures/07-cross-install/api/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"lib": "../lib"
|
||||
}
|
||||
}
|
||||
7
packages/now-build-utils/test/fixtures/07-cross-install/lib/package.json
vendored
Normal file
7
packages/now-build-utils/test/fixtures/07-cross-install/lib/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "lib",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"cowsay": "*"
|
||||
}
|
||||
}
|
||||
9
packages/now-build-utils/test/fixtures/07-cross-install/now.json
vendored
Normal file
9
packages/now-build-utils/test/fixtures/07-cross-install/now.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{ "src": "api/index.js", "use": "@now/node" }
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/api/index.js", "mustContain": "cross-cow:RANDOMNESS_PLACEHOLDER" }
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/cgi",
|
||||
"version": "0.0.14-canary.0",
|
||||
"version": "0.0.14",
|
||||
"scripts": {
|
||||
"test": "best -I test/*.js",
|
||||
"prepublish": "./build.sh"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/go",
|
||||
"version": "0.2.11-canary.1",
|
||||
"version": "0.2.11",
|
||||
"scripts": {
|
||||
"test": "best -I test/*.js",
|
||||
"prepublish": "./build.sh"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/lambda",
|
||||
"version": "0.4.8-canary.1",
|
||||
"version": "0.4.8",
|
||||
"peerDependencies": {
|
||||
"@now/build-utils": ">=0.0.1"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/md",
|
||||
"version": "0.4.8-canary.2",
|
||||
"version": "0.4.8",
|
||||
"dependencies": {
|
||||
"rehype-document": "^2.2.0",
|
||||
"rehype-format": "^2.3.0",
|
||||
|
||||
@@ -6,8 +6,6 @@ const glob = require('@now/build-utils/fs/glob.js');
|
||||
const path = require('path');
|
||||
const { runNpmInstall } = require('@now/build-utils/fs/run-user-scripts.js');
|
||||
|
||||
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
|
||||
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
|
||||
exports.build = async ({ files, entrypoint, workPath }) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/mdx-deck",
|
||||
"version": "0.4.17-canary.3",
|
||||
"version": "0.4.18-canary.0",
|
||||
"peerDependencies": {
|
||||
"@now/build-utils": ">=0.0.1"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/next",
|
||||
"version": "0.0.81-canary.1",
|
||||
"version": "0.0.82-canary.0",
|
||||
"dependencies": {
|
||||
"@now/node-bridge": "0.1.4",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -164,7 +164,7 @@ function normalizePackageJson(defaultPackageJson = {}) {
|
||||
},
|
||||
scripts: {
|
||||
...defaultPackageJson.scripts,
|
||||
'now-build': 'next build --lambdas',
|
||||
'now-build': 'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ async function downloadInstallAndBundle(
|
||||
'package.json': new FileBlob({
|
||||
data: JSON.stringify({
|
||||
dependencies: {
|
||||
'@zeit/ncc': '0.4.1',
|
||||
'@zeit/ncc': '0.6.0',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -12,7 +12,10 @@ Server.prototype.listen = function listen(...args) {
|
||||
};
|
||||
|
||||
try {
|
||||
process.env.NODE_ENV = 'production';
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'production';
|
||||
}
|
||||
|
||||
// PLACEHOLDER
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/node-server",
|
||||
"version": "0.4.25-canary.3",
|
||||
"version": "0.4.26-canary.4",
|
||||
"dependencies": {
|
||||
"@now/node-bridge": "^0.1.9",
|
||||
"fs-extra": "7.0.1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const cowsay = require('cowsay/build/cowsay.umd.js').say;
|
||||
const cowsay = require('cowsay').say;
|
||||
const http = require('http');
|
||||
|
||||
// test that process.env is not replaced by webpack
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"cowsay": "^1.3.1"
|
||||
"cowsay": "1.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const yodasay = require('yodasay/build/yodasay.umd.js').say;
|
||||
const yodasay = require('yodasay').say;
|
||||
const http = require('http');
|
||||
|
||||
// test that process.env is not replaced by webpack
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"yodasay": "^1.1.6"
|
||||
"yodasay": "1.1.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{ "src": "without-bundle/index.js", "use": "@now/node-server", "config": { "bundle": false } }
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/with-bundle", "mustContain": "RANDOMNESS_PLACEHOLDER" },
|
||||
{ "path": "/without-bundle", "mustContain": "RANDOMNESS_PLACEHOLDER" }
|
||||
{ "path": "/with-bundle", "mustContain": "RANDOMNESS_PLACEHOLDER:with-bundle" },
|
||||
{ "path": "/without-bundle", "mustContain": "RANDOMNESS_PLACEHOLDER:without-bundle" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ const http = require('http');
|
||||
const isBundled = require('./is-bundled.js');
|
||||
|
||||
const server = http.createServer((req, resp) => {
|
||||
resp.end(isBundled() ? 'RANDOMNESS_PLACEHOLDER' : '');
|
||||
resp.end(isBundled() ? 'RANDOMNESS_PLACEHOLDER:with-bundle' : '');
|
||||
});
|
||||
|
||||
server.listen();
|
||||
|
||||
@@ -2,7 +2,7 @@ const http = require('http');
|
||||
const isBundled = require('./is-bundled.js');
|
||||
|
||||
const server = http.createServer((req, resp) => {
|
||||
resp.end(isBundled() ? '' : 'RANDOMNESS_PLACEHOLDER');
|
||||
resp.end(isBundled() ? '' : 'RANDOMNESS_PLACEHOLDER:without-bundle');
|
||||
});
|
||||
|
||||
server.listen();
|
||||
|
||||
@@ -45,7 +45,7 @@ async function downloadInstallAndBundle(
|
||||
'package.json': new FileBlob({
|
||||
data: JSON.stringify({
|
||||
dependencies: {
|
||||
'@zeit/ncc': '0.4.1',
|
||||
'@zeit/ncc': '0.6.0',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,10 @@ bridge.port = 3000;
|
||||
let listener;
|
||||
|
||||
try {
|
||||
process.env.NODE_ENV = 'production';
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'production';
|
||||
}
|
||||
|
||||
// PLACEHOLDER
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/node",
|
||||
"version": "0.4.27-canary.3",
|
||||
"version": "0.4.28-canary.3",
|
||||
"dependencies": {
|
||||
"@now/node-bridge": "^0.1.9",
|
||||
"fs-extra": "7.0.1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const cowsay = require('cowsay/build/cowsay.umd.js').say;
|
||||
const cowsay = require('cowsay').say;
|
||||
|
||||
// test that process.env is not replaced by webpack
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"cowsay": "^1.3.1"
|
||||
"cowsay": "1.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const yodasay = require('yodasay/build/yodasay.umd.js').say;
|
||||
const yodasay = require('yodasay').say;
|
||||
|
||||
// test that process.env is not replaced by webpack
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"yodasay": "^1.1.6"
|
||||
"yodasay": "1.1.9"
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/now-php-bridge/.npmignore
Normal file
1
packages/now-php-bridge/.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
20
packages/now-php-bridge/build/Dockerfile
Normal file
20
packages/now-php-bridge/build/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM library/centos:6.8
|
||||
RUN yum -y install wget git
|
||||
RUN rpm -Uvh https://mirror.webtatic.com/yum/el6/latest.rpm
|
||||
RUN yum -y install php71w-cli php71w-fpm php71w-mbstring php71w-mysql php71w-opcache
|
||||
RUN yum -y install epel-release
|
||||
RUN yum -y install patchelf
|
||||
|
||||
RUN mkdir -p /root/app/public
|
||||
|
||||
WORKDIR /root/app
|
||||
COPY ./php.ini /root/app/php.ini
|
||||
COPY ./php-fpm.ini /root/app/php-fpm.ini
|
||||
COPY ./test.php /root/app/test.php
|
||||
COPY ./test.sh /root/app/test.sh
|
||||
|
||||
RUN patchelf --set-rpath '$ORIGIN' /usr/bin/php
|
||||
RUN patchelf --set-rpath '$ORIGIN' /usr/sbin/php-fpm
|
||||
RUN patchelf --set-rpath '$ORIGIN' /usr/lib64/php/modules/mysqli.so
|
||||
|
||||
CMD ["/bin/bash", "test.sh"]
|
||||
15
packages/now-php-bridge/build/build.sh
Executable file
15
packages/now-php-bridge/build/build.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
rm -rf ../native
|
||||
mkdir -p ../native/modules
|
||||
docker rmi now-php-docker-image --force
|
||||
docker build . -t now-php-docker-image
|
||||
docker run now-php-docker-image
|
||||
docker run now-php-docker-image /bin/cat /usr/sbin/php-fpm > ../native/php-fpm
|
||||
docker run now-php-docker-image /bin/cat /root/app/php.ini > ../native/php.ini
|
||||
docker run now-php-docker-image /bin/cat /root/app/php-fpm.ini > ../native/php-fpm.ini
|
||||
docker run now-php-docker-image /bin/cat /usr/lib64/php/modules/curl.so > ../native/modules/curl.so
|
||||
docker run now-php-docker-image /bin/cat /usr/lib64/php/modules/json.so > ../native/modules/json.so
|
||||
docker run now-php-docker-image /bin/cat /usr/lib64/php/modules/mbstring.so > ../native/modules/mbstring.so
|
||||
docker run now-php-docker-image /bin/cat /usr/lib64/php/modules/mysqli.so > ../native/modules/mysqli.so
|
||||
docker run now-php-docker-image /bin/cat /usr/lib64/mysql/libmysqlclient.so.16 > ../native/modules/libmysqlclient.so.16
|
||||
docker run now-php-docker-image /bin/cat /usr/lib64/php/modules/opcache.so > ../native/modules/opcache.so
|
||||
chmod +x ../native/php-fpm
|
||||
9
packages/now-php-bridge/build/php-fpm.ini
Normal file
9
packages/now-php-bridge/build/php-fpm.ini
Normal file
@@ -0,0 +1,9 @@
|
||||
[global]
|
||||
error_log=/tmp/fpm-error.log
|
||||
|
||||
[www]
|
||||
listen=0.0.0.0:9000
|
||||
pm=static
|
||||
pm.max_children=1
|
||||
catch_workers_output=yes
|
||||
clear_env=no
|
||||
8
packages/now-php-bridge/build/php.ini
Normal file
8
packages/now-php-bridge/build/php.ini
Normal file
@@ -0,0 +1,8 @@
|
||||
extension=/root/app/modules/curl.so
|
||||
extension=/root/app/modules/json.so
|
||||
extension=/root/app/modules/mbstring.so
|
||||
extension=/root/app/modules/mysqli.so
|
||||
zend_extension=/root/app/modules/opcache.so
|
||||
opcache.enable_cli=1
|
||||
mysqli.max_links=10
|
||||
mysqli.max_persistent=10
|
||||
4
packages/now-php-bridge/build/test.php
Normal file
4
packages/now-php-bridge/build/test.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
mysqli_connect();
|
||||
print('php_sapi_name=' . php_sapi_name() . PHP_EOL);
|
||||
print('opcache_enabled=' . opcache_get_status()['opcache_enabled'] . PHP_EOL);
|
||||
18
packages/now-php-bridge/build/test.sh
Normal file
18
packages/now-php-bridge/build/test.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
mkdir -p /root/app/modules
|
||||
cp /usr/bin/php /root/app/php
|
||||
cp /usr/sbin/php-fpm /root/app/php-fpm
|
||||
cp /usr/lib64/php/modules/curl.so /root/app/modules/curl.so
|
||||
cp /usr/lib64/php/modules/json.so /root/app/modules/json.so
|
||||
cp /usr/lib64/php/modules/mbstring.so /root/app/modules/mbstring.so
|
||||
cp /usr/lib64/php/modules/mysqli.so /root/app/modules/mysqli.so
|
||||
cp /usr/lib64/mysql/libmysqlclient.so.16 /root/app/modules/libmysqlclient.so.16
|
||||
cp /usr/lib64/php/modules/opcache.so /root/app/modules/opcache.so
|
||||
rm -rf $(which php)
|
||||
rm -rf $(which php-fpm)
|
||||
rm -rf /usr/lib64/php
|
||||
rm -rf /usr/lib64/mysql
|
||||
rm -rf /etc/php.d
|
||||
./php-fpm --help
|
||||
./php -c php.ini test.php
|
||||
echo "if you see 'can't connect to local mysql' - it is good - mysql library is found and used"
|
||||
echo "if you see 'call to undefined function mysqli_connect' - it is bad - something went wrong"
|
||||
149
packages/now-php-bridge/fastcgi/index.js
Normal file
149
packages/now-php-bridge/fastcgi/index.js
Normal file
@@ -0,0 +1,149 @@
|
||||
/* eslint-disable no-bitwise,no-use-before-define */
|
||||
|
||||
const assert = require('assert');
|
||||
const { freeParser } = require('_http_common');
|
||||
const { spawn } = require('child_process');
|
||||
const createConnection = require('./connection.js');
|
||||
const { MSG_TYPE, PROTOCOL_STATUS } = require('./consts.js');
|
||||
const { whenPortOpens } = require('./port.js');
|
||||
|
||||
const { HTTPParser } = process.binding('http_parser');
|
||||
const BEGIN_REQUEST_DATA_KEEP_CONN = Buffer.from('\0\x01\x01\0\0\0\0\0'); // FCGI_ROLE_RESPONDER && FCGI_KEEP_CONN
|
||||
const MESSAGE_FCGI_STDOUT = `message-${MSG_TYPE.FCGI_STDOUT}`;
|
||||
const MESSAGE_FCGI_STDERR = `message-${MSG_TYPE.FCGI_STDERR}`;
|
||||
const MESSAGE_FCGI_END_REQUEST = `message-${MSG_TYPE.FCGI_END_REQUEST}`;
|
||||
|
||||
let curReqId = 0;
|
||||
let connection;
|
||||
|
||||
async function startPhp() {
|
||||
assert(!connection);
|
||||
|
||||
const child = spawn(
|
||||
'./php-fpm',
|
||||
['-c', 'php.ini',
|
||||
'--fpm-config', 'php-fpm.ini',
|
||||
'--nodaemonize'],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
cwd: '/var/task/native',
|
||||
},
|
||||
);
|
||||
|
||||
child.on('exit', () => {
|
||||
console.error('php exited');
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
await whenPortOpens(9000, 400);
|
||||
|
||||
const newConnection = createConnection({
|
||||
_host: '127.0.0.1',
|
||||
_port: 9000,
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
function onError() {
|
||||
cleanup();
|
||||
reject();
|
||||
}
|
||||
function onConnect() {
|
||||
connection = newConnection;
|
||||
cleanup();
|
||||
resolve();
|
||||
}
|
||||
|
||||
newConnection.on('error', onError);
|
||||
newConnection.on('connect', onConnect);
|
||||
function cleanup() {
|
||||
newConnection.removeListener('error', onError);
|
||||
newConnection.removeListener('connect', onConnect);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function query({ params, stdin }) {
|
||||
if (!connection) {
|
||||
await startPhp();
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
assert(connection);
|
||||
|
||||
const chunks = [
|
||||
Buffer.from('HTTP/1.1 200 MAKES-PARSER-WORK\n'),
|
||||
];
|
||||
|
||||
function onError(error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
function onStdout(reqId, data) {
|
||||
chunks.push(data);
|
||||
}
|
||||
function onStderr(reqId, data) {
|
||||
console.error(data.toString().trim());
|
||||
}
|
||||
function onEndRequest(reqId, data) {
|
||||
const protocolStatus = data.readUInt8(4, true);
|
||||
if (protocolStatus !== PROTOCOL_STATUS.FCGI_REQUEST_COMPLETE) {
|
||||
console.error('protocolStatus', protocolStatus);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const response = Buffer.concat(chunks);
|
||||
const parser = new HTTPParser(HTTPParser.RESPONSE);
|
||||
|
||||
let tuples = [];
|
||||
parser[HTTPParser.kOnHeadersComplete | 0] = (major, minor, t) => {
|
||||
tuples = t;
|
||||
};
|
||||
|
||||
let body;
|
||||
parser[HTTPParser.kOnBody | 0] = (b, start, len) => {
|
||||
body = b.slice(start, start + len);
|
||||
};
|
||||
|
||||
parser.execute(response);
|
||||
freeParser(parser);
|
||||
cleanup();
|
||||
resolve({ tuples, body });
|
||||
}
|
||||
|
||||
connection.on('error', onError);
|
||||
connection.on(MESSAGE_FCGI_STDOUT, onStdout);
|
||||
connection.on(MESSAGE_FCGI_STDERR, onStderr);
|
||||
connection.on(MESSAGE_FCGI_END_REQUEST, onEndRequest);
|
||||
function cleanup() {
|
||||
connection.removeListener('error', onError);
|
||||
connection.removeListener(MESSAGE_FCGI_STDOUT, onStdout);
|
||||
connection.removeListener(MESSAGE_FCGI_STDERR, onStderr);
|
||||
connection.removeListener(MESSAGE_FCGI_END_REQUEST, onEndRequest);
|
||||
}
|
||||
|
||||
curReqId += 1;
|
||||
// TODO these things have callbacks. what to do with them?
|
||||
connection.send(MSG_TYPE.FCGI_BEGIN_REQUEST, curReqId, BEGIN_REQUEST_DATA_KEEP_CONN);
|
||||
connection.send(MSG_TYPE.FCGI_PARAMS, curReqId, params);
|
||||
connection.send(MSG_TYPE.FCGI_PARAMS, curReqId, null);
|
||||
if (stdin) connection.send(MSG_TYPE.FCGI_STDIN, curReqId, stdin);
|
||||
connection.send(MSG_TYPE.FCGI_STDIN, curReqId, null);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
query,
|
||||
};
|
||||
|
||||
/*
|
||||
(async function() {
|
||||
console.log(await query({ params: {
|
||||
REQUEST_METHOD: 'GET', SCRIPT_FILENAME: '/phpinfo.php'
|
||||
} }));
|
||||
})();
|
||||
*/
|
||||
41
packages/now-php-bridge/fastcgi/port.js
Normal file
41
packages/now-php-bridge/fastcgi/port.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/* eslint-disable consistent-return */
|
||||
|
||||
const net = require('net');
|
||||
|
||||
function whenPortOpensCallback(port, attempts, cb) {
|
||||
const client = net.connect(port, '127.0.0.1');
|
||||
client.on('error', (error) => {
|
||||
if (!attempts) return cb(error);
|
||||
setTimeout(() => {
|
||||
whenPortOpensCallback(port, attempts - 1, cb);
|
||||
}, 50);
|
||||
});
|
||||
client.on('connect', () => {
|
||||
client.destroy();
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
function isPortOpen(port) {
|
||||
return new Promise((resolve) => {
|
||||
whenPortOpensCallback(port, 0, (error) => {
|
||||
if (error) return resolve(false);
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function whenPortOpens(port, attempts) {
|
||||
return new Promise((resolve, reject) => {
|
||||
whenPortOpensCallback(port, attempts, (error) => {
|
||||
if (error) return reject(error);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isPortOpen,
|
||||
whenPortOpensCallback,
|
||||
whenPortOpens,
|
||||
};
|
||||
28
packages/now-php-bridge/index.js
Normal file
28
packages/now-php-bridge/index.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const FileBlob = require('@now/build-utils/file-blob.js');
|
||||
const FileFsRef = require('@now/build-utils/file-fs-ref.js');
|
||||
const glob = require('@now/build-utils/fs/glob.js');
|
||||
const path = require('path');
|
||||
|
||||
async function getFiles() {
|
||||
const files = await glob('native/**', __dirname);
|
||||
|
||||
const phpConfig = await FileBlob.fromStream({ stream: files['native/php.ini'].toStream() });
|
||||
phpConfig.data = phpConfig.data.toString()
|
||||
.replace(/\/root\/app\/modules/g, '/var/task/native/modules');
|
||||
files['native/php.ini'] = phpConfig;
|
||||
|
||||
Object.assign(files, {
|
||||
'fastcgi/connection.js': new FileFsRef({ fsPath: require.resolve('fastcgi-client/lib/connection.js') }),
|
||||
'fastcgi/consts.js': new FileFsRef({ fsPath: require.resolve('fastcgi-client/lib/consts.js') }),
|
||||
'fastcgi/stringifykv.js': new FileFsRef({ fsPath: require.resolve('fastcgi-client/lib/stringifykv.js') }),
|
||||
'fastcgi/index.js': new FileFsRef({ fsPath: path.join(__dirname, 'fastcgi/index.js') }),
|
||||
'fastcgi/port.js': new FileFsRef({ fsPath: path.join(__dirname, 'fastcgi/port.js') }),
|
||||
'launcher.js': new FileFsRef({ fsPath: path.join(__dirname, 'launcher.js') }),
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getFiles,
|
||||
};
|
||||
142
packages/now-php-bridge/launcher.js
Normal file
142
packages/now-php-bridge/launcher.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/* eslint-disable prefer-template */
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const { join: pathJoin } = require('path');
|
||||
const { parse: parseUrl } = require('url');
|
||||
const { query } = require('./fastcgi/index.js');
|
||||
|
||||
function normalizeEvent(event) {
|
||||
if (event.Action === 'Invoke') {
|
||||
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 {
|
||||
method, path, headers, body,
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
httpMethod: method, path, headers, body,
|
||||
} = event;
|
||||
|
||||
return {
|
||||
method, path, headers, body,
|
||||
};
|
||||
}
|
||||
|
||||
function isDirectory(p) {
|
||||
return new Promise((resolve) => {
|
||||
fs.stat(p, (error, s) => {
|
||||
if (error) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.isDirectory()) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function transformFromAwsRequest({
|
||||
method, path, headers, body,
|
||||
}) {
|
||||
const { pathname, search, query: queryString } = parseUrl(path);
|
||||
let requestUri = pathname + (search || '');
|
||||
|
||||
let filename = pathJoin('/var/task/user', pathname);
|
||||
if (await isDirectory(filename)) {
|
||||
if (!filename.endsWith('/')) {
|
||||
filename += '/';
|
||||
requestUri = pathname + '/' + (search || '');
|
||||
}
|
||||
filename += 'index.php';
|
||||
}
|
||||
|
||||
const params = {};
|
||||
params.REQUEST_METHOD = method;
|
||||
params.REQUEST_URI = requestUri;
|
||||
params.QUERY_STRING = queryString || ''; // can be null
|
||||
params.SCRIPT_FILENAME = filename;
|
||||
params.SERVER_PROTOCOL = 'HTTP/1.1';
|
||||
params.SERVER_PORT = 443;
|
||||
params.HTTPS = 'on';
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [k, v] of Object.entries(headers)) {
|
||||
const camel = k.toUpperCase().replace(/-/g, '_');
|
||||
params[`HTTP_${camel}`] = v;
|
||||
if (camel === 'HOST') {
|
||||
params.SERVER_NAME = v;
|
||||
} else
|
||||
if (['CONTENT_TYPE', 'CONTENT_LENGTH'].includes(camel)) {
|
||||
params[camel] = v; // without HOST_ prepended
|
||||
}
|
||||
}
|
||||
|
||||
return { params, stdin: body };
|
||||
}
|
||||
|
||||
function transformToAwsResponse({ tuples, body }) {
|
||||
let statusCode = 200;
|
||||
const headers = {};
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
if (!body) body = Buffer.alloc(0);
|
||||
assert(Buffer.isBuffer(body));
|
||||
|
||||
for (let i = 0; i < tuples.length; i += 2) {
|
||||
const k = tuples[i].toLowerCase();
|
||||
const v = tuples[i + 1];
|
||||
if (k === 'status') {
|
||||
statusCode = Number(v.split(' ')[0]); // '408 Request Timeout'
|
||||
} else {
|
||||
if (!headers[k]) headers[k] = [];
|
||||
headers[k].push(v);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode,
|
||||
headers,
|
||||
body: body.toString('base64'),
|
||||
encoding: 'base64',
|
||||
};
|
||||
}
|
||||
|
||||
async function launcher(event) {
|
||||
const awsRequest = normalizeEvent(event);
|
||||
const input = await transformFromAwsRequest(awsRequest);
|
||||
const output = await query(input);
|
||||
return transformToAwsResponse(output);
|
||||
}
|
||||
|
||||
exports.launcher = launcher;
|
||||
|
||||
/*
|
||||
(async function() {
|
||||
console.log(await launcher({
|
||||
httpMethod: 'GET',
|
||||
path: '/phpinfo.php'
|
||||
}));
|
||||
})();
|
||||
*/
|
||||
Binary file not shown.
Binary file not shown.
BIN
packages/now-php-bridge/native/modules/libmysqlclient.so.16
Normal file
BIN
packages/now-php-bridge/native/modules/libmysqlclient.so.16
Normal file
Binary file not shown.
Binary file not shown.
BIN
packages/now-php-bridge/native/modules/mysqli.so
Normal file
BIN
packages/now-php-bridge/native/modules/mysqli.so
Normal file
Binary file not shown.
BIN
packages/now-php-bridge/native/modules/opcache.so
Normal file
BIN
packages/now-php-bridge/native/modules/opcache.so
Normal file
Binary file not shown.
BIN
packages/now-php-bridge/native/php-fpm
Executable file
BIN
packages/now-php-bridge/native/php-fpm
Executable file
Binary file not shown.
9
packages/now-php-bridge/native/php-fpm.ini
Normal file
9
packages/now-php-bridge/native/php-fpm.ini
Normal file
@@ -0,0 +1,9 @@
|
||||
[global]
|
||||
error_log=/tmp/fpm-error.log
|
||||
|
||||
[www]
|
||||
listen=0.0.0.0:9000
|
||||
pm=static
|
||||
pm.max_children=1
|
||||
catch_workers_output=yes
|
||||
clear_env=no
|
||||
8
packages/now-php-bridge/native/php.ini
Normal file
8
packages/now-php-bridge/native/php.ini
Normal file
@@ -0,0 +1,8 @@
|
||||
extension=/root/app/modules/curl.so
|
||||
extension=/root/app/modules/json.so
|
||||
extension=/root/app/modules/mbstring.so
|
||||
extension=/root/app/modules/mysqli.so
|
||||
zend_extension=/root/app/modules/opcache.so
|
||||
opcache.enable_cli=1
|
||||
mysqli.max_links=10
|
||||
mysqli.max_persistent=10
|
||||
10
packages/now-php-bridge/package.json
Normal file
10
packages/now-php-bridge/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@now/php-bridge",
|
||||
"version": "0.4.13-canary.0",
|
||||
"dependencies": {
|
||||
"fastcgi-client": "0.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@now/build-utils": ">=0.0.1"
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
/launcher
|
||||
/test
|
||||
|
||||
BIN
packages/now-php/dist/launcher
vendored
BIN
packages/now-php/dist/launcher
vendored
Binary file not shown.
BIN
packages/now-php/dist/libphp7-7.1.so
vendored
BIN
packages/now-php/dist/libphp7-7.1.so
vendored
Binary file not shown.
3
packages/now-php/dist/php.ini
vendored
3
packages/now-php/dist/php.ini
vendored
@@ -1,3 +0,0 @@
|
||||
extension=modules/curl.so
|
||||
extension=modules/json.so
|
||||
extension=modules/mbstring.so
|
||||
@@ -1,7 +1,7 @@
|
||||
const { createLambda } = require('@now/build-utils/lambda.js');
|
||||
const glob = require('@now/build-utils/fs/glob.js');
|
||||
const path = require('path');
|
||||
const rename = require('@now/build-utils/fs/rename.js');
|
||||
const { getFiles } = require('@now/php-bridge');
|
||||
|
||||
exports.config = {
|
||||
maxLambdaSize: '10mb',
|
||||
@@ -10,17 +10,16 @@ exports.config = {
|
||||
exports.build = async ({ files, entrypoint }) => {
|
||||
// move all user code to 'user' subdirectory
|
||||
const userFiles = rename(files, name => path.join('user', name));
|
||||
const launcherFiles = await glob('**', path.join(__dirname, 'dist'));
|
||||
const zipFiles = { ...userFiles, ...launcherFiles };
|
||||
const bridgeFiles = await getFiles();
|
||||
|
||||
// TODO config.extensions. OR php.ini from user
|
||||
delete bridgeFiles['native/modules/mysqli.so'];
|
||||
delete bridgeFiles['native/modules/libmysqlclient.so.16'];
|
||||
|
||||
const lambda = await createLambda({
|
||||
files: zipFiles,
|
||||
handler: 'launcher',
|
||||
runtime: 'go1.x',
|
||||
environment: {
|
||||
SCRIPT_NAME: path.join('/', entrypoint),
|
||||
NOW_PHP_SCRIPT: path.join('user', entrypoint),
|
||||
},
|
||||
files: { ...userFiles, ...bridgeFiles },
|
||||
handler: 'launcher.launcher',
|
||||
runtime: 'nodejs8.10',
|
||||
});
|
||||
|
||||
return { [entrypoint]: lambda };
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
FROM library/centos:6.8
|
||||
RUN yum -y install wget git
|
||||
RUN rpm -Uvh https://mirror.webtatic.com/yum/el6/latest.rpm
|
||||
RUN yum -y install php71w-cli php71w-devel php71w-embedded php71w-mbstring
|
||||
RUN yum -y install epel-release
|
||||
RUN yum -y install golang
|
||||
|
||||
RUN mkdir -p /root/go/app/public
|
||||
|
||||
RUN go get -v github.com/aws/aws-lambda-go/lambda
|
||||
RUN go get -v github.com/aws/aws-lambda-go/events
|
||||
RUN go get -v github.com/deuill/go-php
|
||||
|
||||
WORKDIR /root/go/app
|
||||
COPY ./utils/bridge.go /root/go/app/utils/bridge.go
|
||||
COPY ./launcher.go /root/go/app/launcher.go
|
||||
COPY ./php.ini /root/go/app/php.ini
|
||||
COPY ./test.go /root/go/app/test.go
|
||||
COPY ./test.php /root/go/app/public/test.php
|
||||
COPY ./test.sh /root/go/app/test.sh
|
||||
|
||||
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o launcher launcher.go
|
||||
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o test test.go
|
||||
|
||||
RUN strip launcher
|
||||
RUN strip test
|
||||
|
||||
CMD ["/bin/bash", "test.sh"]
|
||||
@@ -1,15 +0,0 @@
|
||||
rm -rf ../dist
|
||||
mkdir -p ../dist/modules
|
||||
mkdir ./utils
|
||||
cp ../../../utils/go/bridge/bridge.go ./utils/bridge.go
|
||||
docker rmi go-php-builder --force
|
||||
docker build . -t go-php-builder
|
||||
docker run go-php-builder
|
||||
docker run go-php-builder /bin/cat /root/go/app/launcher > ../dist/launcher
|
||||
docker run go-php-builder /bin/cat /root/go/app/php.ini > ../dist/php.ini
|
||||
docker run go-php-builder /bin/cat /usr/lib64/libphp7-7.1.so > ../dist/libphp7-7.1.so
|
||||
docker run go-php-builder /bin/cat /usr/lib64/php/modules/curl.so > ../dist/modules/curl.so
|
||||
docker run go-php-builder /bin/cat /usr/lib64/php/modules/json.so > ../dist/modules/json.so
|
||||
docker run go-php-builder /bin/cat /usr/lib64/php/modules/mbstring.so > ../dist/modules/mbstring.so
|
||||
chmod +x ../dist/launcher
|
||||
rm -rf ./utils
|
||||
@@ -1,80 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
now "./utils"
|
||||
"bytes"
|
||||
php "github.com/deuill/go-php"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PhpHandler struct {
|
||||
http.Handler
|
||||
ScriptFull string
|
||||
}
|
||||
|
||||
func (h *PhpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
engine, _ := php.New()
|
||||
context, _ := engine.NewContext()
|
||||
|
||||
var query = r.URL.Query()
|
||||
getMap := make(map[string]string)
|
||||
for k, v := range query {
|
||||
for _, s := range v {
|
||||
getMap[k] = s
|
||||
}
|
||||
}
|
||||
context.Bind("_GET", getMap)
|
||||
|
||||
r.ParseForm()
|
||||
postMap := make(map[string]string)
|
||||
for k, v := range r.PostForm {
|
||||
for _, s := range v {
|
||||
postMap[k] = s
|
||||
}
|
||||
}
|
||||
context.Bind("_POST", postMap)
|
||||
|
||||
envMap := make(map[string]string)
|
||||
for _, e := range os.Environ() {
|
||||
pair := strings.Split(e, "=")
|
||||
envMap[pair[0]] = pair[1]
|
||||
}
|
||||
context.Bind("_ENV", envMap)
|
||||
|
||||
context.Eval("$_SERVER[\"SERVER_NAME\"]=\"" + r.Host + "\";")
|
||||
context.Eval("$_SERVER[\"SERVER_PORT\"]=\"443\";")
|
||||
context.Eval("$_SERVER[\"HTTPS\"]=\"on\";")
|
||||
context.Eval("http_response_code(200);")
|
||||
|
||||
var stdout bytes.Buffer
|
||||
context.Output = &stdout
|
||||
context.Exec(h.ScriptFull)
|
||||
|
||||
statusCodeVal, _ := context.Eval("return http_response_code();")
|
||||
w.WriteHeader(int(statusCodeVal.Int()))
|
||||
|
||||
headers := w.Header()
|
||||
headers.Add("content-type", "text/html")
|
||||
for k, v := range context.Header {
|
||||
for _, s := range v {
|
||||
headers.Add(k, s)
|
||||
}
|
||||
}
|
||||
|
||||
w.Write(stdout.Bytes())
|
||||
|
||||
engine.Destroy()
|
||||
}
|
||||
|
||||
func main() {
|
||||
ex, _ := os.Executable()
|
||||
handler := &PhpHandler{
|
||||
nil,
|
||||
path.Join(filepath.Dir(ex), os.Getenv("NOW_PHP_SCRIPT")),
|
||||
}
|
||||
now.Start(handler)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
extension=modules/curl.so
|
||||
extension=modules/json.so
|
||||
extension=modules/mbstring.so
|
||||
@@ -1,54 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
php "github.com/deuill/go-php"
|
||||
)
|
||||
|
||||
var public = ""
|
||||
|
||||
func handler() {
|
||||
engine, _ := php.New()
|
||||
context, _ := engine.NewContext()
|
||||
|
||||
var bodyReader = strings.NewReader("Message=from test.go")
|
||||
var httpReq, _ = http.NewRequest("POST", "/dummy/path", bodyReader)
|
||||
httpReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
httpReq.ParseForm()
|
||||
|
||||
postMap := make(map[string]string)
|
||||
for k, v := range httpReq.PostForm {
|
||||
for _, s := range v {
|
||||
postMap[k] = s
|
||||
}
|
||||
}
|
||||
|
||||
context.Bind("_POST", postMap)
|
||||
var stdout bytes.Buffer
|
||||
context.Output = &stdout
|
||||
context.Exec(path.Join(public, "test.php"))
|
||||
|
||||
for k, v := range context.Header {
|
||||
// see https://golang.org/src/net/http/header.go function writeSubset
|
||||
for _, s := range v {
|
||||
fmt.Printf("%s: %s\n", k, s)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
fmt.Println(stdout.String())
|
||||
engine.Destroy()
|
||||
}
|
||||
|
||||
func main() {
|
||||
ex, _ := os.Executable()
|
||||
public = path.Join(filepath.Dir(ex), "public")
|
||||
fmt.Printf("public %s\n", path.Join(filepath.Dir(ex), "public"))
|
||||
handler()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
header('X-See-You: Tomorrow');
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
phpinfo();
|
||||
?>
|
||||
@@ -1,8 +0,0 @@
|
||||
ldd launcher
|
||||
# echo Message=hi | REQUEST_METHOD=POST REDIRECT_STATUS=true SCRIPT_FILENAME=index.php CONTENT_LENGTH=10 CONTENT_TYPE=application\/x-www-form-urlencoded php-cgi
|
||||
mkdir -p /root/go/app/modules
|
||||
cp /usr/lib64/php/modules/curl.so /root/go/app/modules/curl.so
|
||||
cp /usr/lib64/php/modules/json.so /root/go/app/modules/json.so
|
||||
cp /usr/lib64/php/modules/mbstring.so /root/go/app/modules/mbstring.so
|
||||
rm -rf /etc/php.d
|
||||
./test | head
|
||||
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"name": "@now/php",
|
||||
"version": "0.4.12-canary.3",
|
||||
"version": "0.4.13-canary.1",
|
||||
"dependencies": {
|
||||
"@now/php-bridge": "^0.4.13-canary.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@now/build-utils": ">=0.0.1"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
<?php
|
||||
print "cow:RANDOMNESS_PLACEHOLDER";
|
||||
?>
|
||||
print('cow:RANDOMNESS_PLACEHOLDER');
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
<?php
|
||||
print "yoda:RANDOMNESS_PLACEHOLDER";
|
||||
?>
|
||||
print('yoda:RANDOMNESS_PLACEHOLDER');
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
<?php
|
||||
print $_ENV["RANDOMNESS_ENV_VAR"] . ":env";
|
||||
?>
|
||||
print($_ENV['RANDOMNESS_ENV_VAR'] . ':env');
|
||||
|
||||
28
packages/now-php/test/fixtures/11-globals/index.php
vendored
Normal file
28
packages/now-php/test/fixtures/11-globals/index.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
header('Content-Type: text/plain');
|
||||
print($_SERVER['SCRIPT_FILENAME'] . PHP_EOL);
|
||||
print($_SERVER['REQUEST_METHOD'] . PHP_EOL);
|
||||
print($_SERVER['REQUEST_URI'] . PHP_EOL);
|
||||
print($_SERVER['HTTP_HOST'] . PHP_EOL);
|
||||
print($_SERVER['HTTP_X_SOME_HEADER'] . PHP_EOL);
|
||||
print($_SERVER['SERVER_PROTOCOL'] . PHP_EOL);
|
||||
print($_SERVER['SERVER_NAME'] . PHP_EOL);
|
||||
print($_SERVER['SERVER_PORT'] . PHP_EOL);
|
||||
print($_SERVER['HTTPS'] . PHP_EOL);
|
||||
|
||||
print($_GET['get1'] . PHP_EOL);
|
||||
var_dump($_GET['get2']);
|
||||
print($_POST['post1'] . PHP_EOL);
|
||||
var_dump($_POST['post2']);
|
||||
print($_COOKIE['cookie1'] . PHP_EOL);
|
||||
var_dump($_COOKIE['cookie2']);
|
||||
|
||||
print($_REQUEST['get1'] . PHP_EOL);
|
||||
var_dump($_REQUEST['get2']);
|
||||
print($_REQUEST['post1'] . PHP_EOL);
|
||||
var_dump($_REQUEST['post2']);
|
||||
print($_REQUEST['cookie1'] . PHP_EOL);
|
||||
var_dump($_REQUEST['cookie2']);
|
||||
|
||||
print(file_get_contents('php://input') . PHP_EOL);
|
||||
print('end' . PHP_EOL);
|
||||
6
packages/now-php/test/fixtures/11-globals/now.json
vendored
Normal file
6
packages/now-php/test/fixtures/11-globals/now.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{ "src": "index.php", "use": "@now/php" }
|
||||
]
|
||||
}
|
||||
157
packages/now-php/test/fixtures/11-globals/probe.js
vendored
Normal file
157
packages/now-php/test/fixtures/11-globals/probe.js
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
const assert = require('assert');
|
||||
|
||||
async function test1({ deploymentUrl, fetch }) {
|
||||
const resp = await fetch(
|
||||
`https://${deploymentUrl}/index.php?get1=foo&get1=bar&get2[]=bim&get2[]=bom`, {
|
||||
headers: {
|
||||
'X-Some-Header': 'x-some-header-value',
|
||||
},
|
||||
},
|
||||
);
|
||||
assert(resp.status === 200);
|
||||
const text = await resp.text();
|
||||
const lines = text.trim().split('\n');
|
||||
|
||||
assert.deepEqual(lines, [
|
||||
'/var/task/user/index.php',
|
||||
'GET',
|
||||
'/index.php?get1=foo&get1=bar&get2%5B%5D=bim&get2%5B%5D=bom', // TODO fake news, must be unescaped
|
||||
deploymentUrl, // example 'test-19phw91ph.now.sh'
|
||||
'x-some-header-value',
|
||||
'HTTP/1.1',
|
||||
deploymentUrl, // example 'test-19phw91ph.now.sh'
|
||||
'443',
|
||||
'on',
|
||||
'bar',
|
||||
'array(2) {',
|
||||
' [0]=>',
|
||||
' string(3) "bim"',
|
||||
' [1]=>',
|
||||
' string(3) "bom"',
|
||||
'}',
|
||||
'',
|
||||
'NULL',
|
||||
'',
|
||||
'NULL',
|
||||
'bar',
|
||||
'array(2) {',
|
||||
' [0]=>',
|
||||
' string(3) "bim"',
|
||||
' [1]=>',
|
||||
' string(3) "bom"',
|
||||
'}',
|
||||
'',
|
||||
'NULL',
|
||||
'',
|
||||
'NULL',
|
||||
'',
|
||||
'end',
|
||||
]);
|
||||
}
|
||||
|
||||
async function test2({ deploymentUrl, fetch }) {
|
||||
const resp = await fetch(
|
||||
`https://${deploymentUrl}/index.php`, {
|
||||
method: 'POST',
|
||||
body: 'post1=baz&post1=bat&post2[]=pim&post2[]=pom',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
},
|
||||
);
|
||||
assert(resp.status === 200);
|
||||
const text = await resp.text();
|
||||
const lines = text.trim().split('\n');
|
||||
|
||||
assert.deepEqual(lines, [
|
||||
'/var/task/user/index.php',
|
||||
'POST',
|
||||
'/index.php',
|
||||
deploymentUrl, // example 'test-19phw91ph.now.sh'
|
||||
'',
|
||||
'HTTP/1.1',
|
||||
deploymentUrl, // example 'test-19phw91ph.now.sh'
|
||||
'443',
|
||||
'on',
|
||||
'',
|
||||
'NULL',
|
||||
'bat',
|
||||
'array(2) {',
|
||||
' [0]=>',
|
||||
' string(3) "pim"',
|
||||
' [1]=>',
|
||||
' string(3) "pom"',
|
||||
'}',
|
||||
'',
|
||||
'NULL',
|
||||
'',
|
||||
'NULL',
|
||||
'bat',
|
||||
'array(2) {',
|
||||
' [0]=>',
|
||||
' string(3) "pim"',
|
||||
' [1]=>',
|
||||
' string(3) "pom"',
|
||||
'}',
|
||||
'',
|
||||
'NULL',
|
||||
'post1=baz&post1=bat&post2[]=pim&post2[]=pom',
|
||||
'end',
|
||||
]);
|
||||
}
|
||||
|
||||
async function test3({ deploymentUrl, fetch }) {
|
||||
const resp = await fetch(
|
||||
`https://${deploymentUrl}/index.php`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Cookie: `cookie1=foo; cookie1=${escape('bar|bar')}; ${escape('cookie2[]')}=dim; ${escape('cookie2[]')}=${escape('dom|dom')}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
assert(resp.status === 200);
|
||||
const text = await resp.text();
|
||||
const lines = text.trim().split('\n');
|
||||
|
||||
assert.deepEqual(lines, [
|
||||
'/var/task/user/index.php',
|
||||
'GET',
|
||||
'/index.php',
|
||||
deploymentUrl, // example 'test-19phw91ph.now.sh'
|
||||
'',
|
||||
'HTTP/1.1',
|
||||
deploymentUrl, // example 'test-19phw91ph.now.sh'
|
||||
'443',
|
||||
'on',
|
||||
'',
|
||||
'NULL',
|
||||
'',
|
||||
'NULL',
|
||||
'foo',
|
||||
'array(2) {',
|
||||
' [0]=>',
|
||||
' string(3) "dim"',
|
||||
' [1]=>',
|
||||
' string(7) "dom|dom"',
|
||||
'}',
|
||||
'',
|
||||
'NULL',
|
||||
'',
|
||||
'NULL',
|
||||
'foo',
|
||||
'array(2) {',
|
||||
' [0]=>',
|
||||
' string(3) "dim"',
|
||||
' [1]=>',
|
||||
' string(7) "dom|dom"',
|
||||
'}',
|
||||
'',
|
||||
'end',
|
||||
]);
|
||||
}
|
||||
|
||||
module.exports = async (opts) => {
|
||||
await test1(opts);
|
||||
await test2(opts);
|
||||
await test3(opts);
|
||||
};
|
||||
5
packages/now-php/test/fixtures/12-setcookie/index.php
vendored
Normal file
5
packages/now-php/test/fixtures/12-setcookie/index.php
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
header('Content-Type: text/plain');
|
||||
header('Content-Type: text/plain; charset=UTF-16');
|
||||
setcookie('cookie1', 'cookie1value');
|
||||
setcookie('cookie2', 'cookie2value');
|
||||
6
packages/now-php/test/fixtures/12-setcookie/now.json
vendored
Normal file
6
packages/now-php/test/fixtures/12-setcookie/now.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{ "src": "index.php", "use": "@now/php" }
|
||||
]
|
||||
}
|
||||
9
packages/now-php/test/fixtures/12-setcookie/probe.js
vendored
Normal file
9
packages/now-php/test/fixtures/12-setcookie/probe.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
const assert = require('assert');
|
||||
|
||||
module.exports = async ({ deploymentUrl, fetch }) => {
|
||||
const resp = await fetch(`https://${deploymentUrl}/index.php`);
|
||||
assert(resp.status === 200);
|
||||
assert.equal(resp.headers.get('content-type'), 'text/plain; charset=UTF-16');
|
||||
assert(resp.headers.get('set-cookie').includes('cookie1=cookie1value'));
|
||||
assert(resp.headers.get('set-cookie').includes('cookie2=cookie2value'));
|
||||
};
|
||||
10
packages/now-php/test/fixtures/13-function/index.php
vendored
Normal file
10
packages/now-php/test/fixtures/13-function/index.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// regression test for go-php engine reusage. on failure prints
|
||||
// Fatal error: Cannot redeclare some_function() (previously declared in /var/task/user/index.php:7)
|
||||
|
||||
function some_function() {
|
||||
print("paskantamasaari");
|
||||
}
|
||||
|
||||
some_function();
|
||||
6
packages/now-php/test/fixtures/13-function/now.json
vendored
Normal file
6
packages/now-php/test/fixtures/13-function/now.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{ "src": "index.php", "use": "@now/php" }
|
||||
]
|
||||
}
|
||||
8
packages/now-php/test/fixtures/13-function/probe.js
vendored
Normal file
8
packages/now-php/test/fixtures/13-function/probe.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
const assert = require('assert');
|
||||
|
||||
module.exports = async ({ deploymentUrl, fetch }) => {
|
||||
const resp1 = await fetch(`https://${deploymentUrl}/index.php`);
|
||||
assert.equal(await resp1.text(), 'paskantamasaari');
|
||||
const resp2 = await fetch(`https://${deploymentUrl}/index.php`);
|
||||
assert.equal(await resp2.text(), 'paskantamasaari');
|
||||
};
|
||||
2
packages/now-php/test/fixtures/18-opcache/index.php
vendored
Normal file
2
packages/now-php/test/fixtures/18-opcache/index.php
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
var_dump(opcache_compile_file(__FILE__));
|
||||
6
packages/now-php/test/fixtures/18-opcache/now.json
vendored
Normal file
6
packages/now-php/test/fixtures/18-opcache/now.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{ "src": "index.php", "use": "@now/php" }
|
||||
]
|
||||
}
|
||||
6
packages/now-php/test/fixtures/18-opcache/probe.js
vendored
Normal file
6
packages/now-php/test/fixtures/18-opcache/probe.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
const assert = require('assert');
|
||||
|
||||
module.exports = async ({ deploymentUrl, fetch }) => {
|
||||
const resp = await fetch(`https://${deploymentUrl}/index.php`);
|
||||
assert.equal(await resp.text(), 'bool(true)\n');
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/static-build",
|
||||
"version": "0.4.16-canary.2",
|
||||
"version": "0.4.16",
|
||||
"peerDependencies": {
|
||||
"@now/build-utils": ">=0.0.1"
|
||||
},
|
||||
|
||||
1
packages/now-wordpress/.npmignore
Normal file
1
packages/now-wordpress/.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
/test
|
||||
112
packages/now-wordpress/index.js
Normal file
112
packages/now-wordpress/index.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const assert = require('assert');
|
||||
const { createLambda } = require('@now/build-utils/lambda.js');
|
||||
const fetch = require('node-fetch');
|
||||
const FileBlob = require('@now/build-utils/file-blob.js');
|
||||
const { getFiles } = require('@now/php-bridge');
|
||||
const path = require('path');
|
||||
const rename = require('@now/build-utils/fs/rename.js');
|
||||
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js');
|
||||
const yauzl = require('yauzl');
|
||||
|
||||
exports.config = {
|
||||
maxLambdaSize: '20mb',
|
||||
};
|
||||
|
||||
async function readReleaseUrl(releaseUrl) {
|
||||
const resp = await fetch(releaseUrl);
|
||||
|
||||
if (!resp.ok) {
|
||||
throw new Error(`Failed to download ${releaseUrl}. Status code is ${resp.status}`);
|
||||
}
|
||||
|
||||
return resp.buffer();
|
||||
}
|
||||
|
||||
const prefixRegexp = /^wordpress\//;
|
||||
|
||||
function decompressBuffer(buffer, mountpoint) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const files = {};
|
||||
|
||||
yauzl.fromBuffer(buffer, { lazyEntries: true }, (error, zipfile) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
zipfile.readEntry();
|
||||
zipfile.on('entry', (entry) => {
|
||||
const { fileName } = entry;
|
||||
|
||||
if (/\/$/.test(fileName)) {
|
||||
zipfile.readEntry();
|
||||
return;
|
||||
}
|
||||
|
||||
zipfile.openReadStream(entry, (error2, readStream) => {
|
||||
if (error2) {
|
||||
reject(error2);
|
||||
return;
|
||||
}
|
||||
|
||||
streamToBuffer(readStream).then((data) => {
|
||||
assert(prefixRegexp.test(fileName), fileName);
|
||||
const fileName2 = fileName.replace(prefixRegexp, '');
|
||||
const fileName3 = path.join(mountpoint, fileName2);
|
||||
files[fileName3] = new FileBlob({ data });
|
||||
zipfile.readEntry();
|
||||
}).catch(reject);
|
||||
});
|
||||
});
|
||||
|
||||
zipfile.on('end', () => resolve(files));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const staticRegexps = [
|
||||
/\.css$/, /\.gif$/, /\.ico$/, /\.js$/, /\.jpg$/, /\.png$/, /\.svg$/, /\.woff$/, /\.woff2$/,
|
||||
];
|
||||
|
||||
exports.build = async ({ files, entrypoint, config }) => {
|
||||
if (path.basename(entrypoint) !== 'wp-config.php') {
|
||||
throw new Error(`Entrypoint file name must be "wp-config.php". Currently it is ${entrypoint}`);
|
||||
}
|
||||
|
||||
const { releaseUrl = 'https://wordpress.org/latest.zip' } = config;
|
||||
console.log(`downloading release url ${releaseUrl}...`);
|
||||
const releaseBuffer = await readReleaseUrl(releaseUrl);
|
||||
console.log('decompressing release url...');
|
||||
const mountpoint = path.dirname(entrypoint);
|
||||
const releaseFiles = await decompressBuffer(releaseBuffer, mountpoint);
|
||||
const mergedFiles = { ...releaseFiles, ...files };
|
||||
|
||||
if (config.patchForPersistentConnections) {
|
||||
const wpDbPhp = path.join(mountpoint, 'wp-includes/wp-db.php');
|
||||
const wpDbPhpBlob = mergedFiles[wpDbPhp];
|
||||
wpDbPhpBlob.data = wpDbPhpBlob.data.toString()
|
||||
.replace(/mysqli_real_connect\( \$this->dbh, \$host,/g,
|
||||
'mysqli_real_connect( $this->dbh, \'p:\' . $host,');
|
||||
}
|
||||
|
||||
const staticFiles = {};
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [k, v] of Object.entries(mergedFiles)) {
|
||||
if (staticRegexps.some(r => r.test(k))) {
|
||||
staticFiles[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
// move all code to 'user' subdirectory
|
||||
const userFiles = rename(mergedFiles, name => path.join('user', name));
|
||||
const bridgeFiles = await getFiles();
|
||||
|
||||
const lambda = await createLambda({
|
||||
files: { ...userFiles, ...bridgeFiles },
|
||||
handler: 'launcher.launcher',
|
||||
runtime: 'nodejs8.10',
|
||||
});
|
||||
|
||||
const indexPhp = path.join(mountpoint, 'index.php');
|
||||
return { ...staticFiles, ...{ [indexPhp]: lambda } };
|
||||
};
|
||||
15
packages/now-wordpress/package.json
Normal file
15
packages/now-wordpress/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "@now/wordpress",
|
||||
"version": "0.4.14-canary.0",
|
||||
"dependencies": {
|
||||
"@now/php-bridge": "^0.4.13-canary.0",
|
||||
"node-fetch": "2.3.0",
|
||||
"yauzl": "2.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@now/build-utils": ">=0.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
20
packages/now-wordpress/test/fixtures/20-minimal/ca.pem
vendored
Normal file
20
packages/now-wordpress/test/fixtures/20-minimal/ca.pem
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDTTCCAjWgAwIBAgIJAIG+rCVyV4UMMA0GCSqGSIb3DQEBCwUAMDwxFTATBgNV
|
||||
BAMMDHNjYWxlZ3JpZC5pbzEWMBQGA1UECgwNU2NhbGVncmlkLmlvLjELMAkGA1UE
|
||||
BhMCVVMwIBcNMTgxMjA2MjMyMDM1WhgPMjA2ODAzMTgyMzIwMzVaMDwxFTATBgNV
|
||||
BAMMDHNjYWxlZ3JpZC5pbzEWMBQGA1UECgwNU2NhbGVncmlkLmlvLjELMAkGA1UE
|
||||
BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7syPdlrIOs0hC
|
||||
zH879Mnxd2KUu7v3qPqz3Z4gGmYf1sVBDhjGfR8OqoL7eDiSOpkb82eevOhVJTND
|
||||
tGCAQvhE8hA5GWP7KEfbI6BZ4bnOeWCQM4NG9xLNvJvJ4DlwjEUkbzjTbXGMwBp+
|
||||
E08//20l6yUllot1vCQFzu32pvoi7JQiEpSZexWSrYQ19XIQ4UxI32zSPUmHN1N0
|
||||
NcC33vw9vZOg3eoJuEEmQ0cvaXZcAcwf9+Wzu3S0JHALYlCw1S8/vPcR/I7tHxUX
|
||||
RUcBcqF7mcNSoV7FpFUeUZqgThW/goHojLyb97ezpKeVEzIoIG4+fV8MUDijRqz8
|
||||
OofLWs2HAgMBAAGjUDBOMB0GA1UdDgQWBBQnXYv6KeIEiQIYik5JDBEF+ehQjTAf
|
||||
BgNVHSMEGDAWgBQnXYv6KeIEiQIYik5JDBEF+ehQjTAMBgNVHRMEBTADAQH/MA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQCR7PvEylrSkSbTJ7I5d70r3y88U1O50RXJ2TJHdTC2
|
||||
vmZg8PGZr4tDP4ItwjWQRxJQ/44tCTy8J0zPEZ8IsTOKgs+hIad6wgBZql9YhUSM
|
||||
a6Nz8d+92lS9n77wXbyqzU8iROeMZzCK9tx1ivcOnUWym1DjpSP0WZh0KyBE4dlt
|
||||
EUSdihrmvFasWfAhNLeBufG1YjuT2FL23zlf/ktXNSQZsH6yV080QpVA85zAgT2+
|
||||
bRFbjOSoZ8Tx1KpaN2HG9tlvO8yMyEYyPeUx+cvxkRQwnGl2+XTDw6PS7m/xvYQB
|
||||
7bikuWPnE7JMxj+57lA9F2QlKZfIve419z7y3bZyKyLf
|
||||
-----END CERTIFICATE-----
|
||||
18
packages/now-wordpress/test/fixtures/20-minimal/now.json
vendored
Normal file
18
packages/now-wordpress/test/fixtures/20-minimal/now.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{ "src": "wp-config.php", "use": "@now/wordpress",
|
||||
"config": { "releaseUrl": "https://wordpress.org/wordpress-5.0.1.zip",
|
||||
"patchForPersistentConnections": true } }
|
||||
],
|
||||
"routes": [
|
||||
{ "src": "/wp-admin/?", "dest": "index.php" },
|
||||
{ "src": ".*\\.php$", "dest": "index.php" }
|
||||
],
|
||||
"env": {
|
||||
"DB_NAME": "@wordpress_db_name",
|
||||
"DB_USER": "@wordpress_db_user",
|
||||
"DB_PASSWORD": "@wordpress_db_password",
|
||||
"DB_HOST": "@wordpress_db_host"
|
||||
}
|
||||
}
|
||||
96
packages/now-wordpress/test/fixtures/20-minimal/wp-config.php
vendored
Normal file
96
packages/now-wordpress/test/fixtures/20-minimal/wp-config.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* The base configuration for WordPress
|
||||
*
|
||||
* The wp-config.php creation script uses this file during the
|
||||
* installation. You don't have to use the web site, you can
|
||||
* copy this file to "wp-config.php" and fill in the values.
|
||||
*
|
||||
* This file contains the following configurations:
|
||||
*
|
||||
* * MySQL settings
|
||||
* * Secret keys
|
||||
* * Database table prefix
|
||||
* * ABSPATH
|
||||
*
|
||||
* @link https://codex.wordpress.org/Editing_wp-config.php
|
||||
*
|
||||
* @package WordPress
|
||||
*/
|
||||
|
||||
// ** MySQL settings - You can get this info from your web host ** //
|
||||
/** The name of the database for WordPress */
|
||||
define('DB_NAME', $_ENV['DB_NAME']);
|
||||
|
||||
/** MySQL database username */
|
||||
define('DB_USER', $_ENV['DB_USER']);
|
||||
|
||||
/** MySQL database password */
|
||||
define('DB_PASSWORD', $_ENV['DB_PASSWORD']);
|
||||
|
||||
/** MySQL hostname */
|
||||
define('DB_HOST', $_ENV['DB_HOST']);
|
||||
|
||||
/** Database Charset to use in creating database tables. */
|
||||
define('DB_CHARSET', 'utf8');
|
||||
|
||||
/** The Database Collate type. Don't change this if in doubt. */
|
||||
define('DB_COLLATE', '');
|
||||
|
||||
/**#@+
|
||||
* Authentication Unique Keys and Salts.
|
||||
*
|
||||
* Change these to different unique phrases!
|
||||
* You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
|
||||
* You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
define('AUTH_KEY', 'abracadabra');
|
||||
define('SECURE_AUTH_KEY', 'abracadabra');
|
||||
define('LOGGED_IN_KEY', 'abracadabra');
|
||||
define('NONCE_KEY', 'abracadabra');
|
||||
define('AUTH_SALT', 'abracadabra');
|
||||
define('SECURE_AUTH_SALT', 'abracadabra');
|
||||
define('LOGGED_IN_SALT', 'abracadabra');
|
||||
define('NONCE_SALT', 'abracadabra');
|
||||
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* WordPress Database Table prefix.
|
||||
*
|
||||
* You can have multiple installations in one database if you give each
|
||||
* a unique prefix. Only numbers, letters, and underscores please!
|
||||
*/
|
||||
$table_prefix = 'wp_';
|
||||
|
||||
/**
|
||||
* For developers: WordPress debugging mode.
|
||||
*
|
||||
* Change this to true to enable the display of notices during development.
|
||||
* It is strongly recommended that plugin and theme developers use WP_DEBUG
|
||||
* in their development environments.
|
||||
*
|
||||
* For information on other constants that can be used for debugging,
|
||||
* visit the Codex.
|
||||
*
|
||||
* @link https://codex.wordpress.org/Debugging_in_WordPress
|
||||
*/
|
||||
define('WP_DEBUG', false);
|
||||
|
||||
define( 'WP_SITEURL', 'https://' . $_SERVER['HTTP_HOST'] );
|
||||
define( 'WP_HOME', 'https://' . $_SERVER['HTTP_HOST'] );
|
||||
define( 'WP_CONTENT_URL', 'https://' . $_SERVER['HTTP_HOST'] . '/wp-content' );
|
||||
|
||||
/* That's all, stop editing! Happy blogging. */
|
||||
|
||||
/** Absolute path to the WordPress directory. */
|
||||
if ( !defined('ABSPATH') )
|
||||
define('ABSPATH', dirname(__FILE__) . '/');
|
||||
|
||||
define( 'MYSQL_SSL_CA', ABSPATH . 'ca.pem' );
|
||||
define( 'MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL );
|
||||
|
||||
/** Sets up WordPress vars and included files. */
|
||||
require_once(ABSPATH . 'wp-settings.php');
|
||||
23
test/lib/deployment/fetch-retry.js
Normal file
23
test/lib/deployment/fetch-retry.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const fetch = require('node-fetch');
|
||||
const retryBailByDefault = require('./retry-bail-by-default.js');
|
||||
|
||||
async function fetchRetry (...args) {
|
||||
return await retryBailByDefault(async (canRetry) => {
|
||||
try {
|
||||
return await fetch(...args);
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOTFOUND') {
|
||||
// getaddrinfo ENOTFOUND api.zeit.co like some transient dns issue
|
||||
throw canRetry(error);
|
||||
} else
|
||||
if (error.code === 'ETIMEDOUT') {
|
||||
// request to https://api-gru1.zeit.co/v3/now/deployments/dpl_FBWWhpQomjgwjJLu396snLrGZYCm failed, reason:
|
||||
// connect ETIMEDOUT 18.228.143.224:443
|
||||
throw canRetry(error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}, { factor: 1, retries: 3 });
|
||||
}
|
||||
|
||||
module.exports = fetchRetry;
|
||||
@@ -1,8 +1,8 @@
|
||||
const assert = require('assert');
|
||||
const { createHash } = require('crypto');
|
||||
const fetch = require('node-fetch');
|
||||
const { homedir } = require('os');
|
||||
const path = require('path');
|
||||
const fetch = require('./fetch-retry.js');
|
||||
|
||||
async function nowDeploy (bodies, randomness) {
|
||||
const files = Object.keys(bodies)
|
||||
@@ -18,7 +18,7 @@ async function nowDeploy (bodies, randomness) {
|
||||
|
||||
const nowDeployPayload = {
|
||||
version: 2,
|
||||
env: { RANDOMNESS_ENV_VAR: randomness },
|
||||
env: Object.assign({}, nowJson.env, { RANDOMNESS_ENV_VAR: randomness }),
|
||||
build: { env: { RANDOMNESS_BUILD_ENV_VAR: randomness } },
|
||||
name: 'test',
|
||||
files,
|
||||
@@ -27,12 +27,13 @@ async function nowDeploy (bodies, randomness) {
|
||||
meta: {}
|
||||
};
|
||||
|
||||
console.log(`posting ${files.length} files`);
|
||||
|
||||
for (const { file: filename } of files) {
|
||||
const json = await filePost(
|
||||
await filePost(
|
||||
bodies[filename],
|
||||
digestOfFile(bodies[filename])
|
||||
);
|
||||
if (json.error) throw new Error(json.error.message);
|
||||
}
|
||||
|
||||
let deploymentId;
|
||||
@@ -45,6 +46,8 @@ async function nowDeploy (bodies, randomness) {
|
||||
deploymentUrl = json.url;
|
||||
}
|
||||
|
||||
console.log('id', deploymentId);
|
||||
|
||||
for (let i = 0; i < 500; i += 1) {
|
||||
const { state } = await deploymentGet(deploymentId);
|
||||
if (state === 'ERROR') throw new Error(`State of ${deploymentUrl} is ${state}`);
|
||||
@@ -76,8 +79,13 @@ async function filePost (body, digest) {
|
||||
headers,
|
||||
body
|
||||
});
|
||||
const json = await resp.json();
|
||||
|
||||
return await resp.json();
|
||||
if (json.error) {
|
||||
console.log('headers', resp.headers);
|
||||
throw new Error(json.error.message);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
async function deploymentPost (payload) {
|
||||
@@ -86,7 +94,11 @@ async function deploymentPost (payload) {
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.error) throw new Error(json.error.message);
|
||||
|
||||
if (json.error) {
|
||||
console.log('headers', resp.headers);
|
||||
throw new Error(json.error.message);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -96,33 +108,43 @@ async function deploymentGet (deploymentId) {
|
||||
}
|
||||
|
||||
async function fetchWithAuth (url, opts = {}) {
|
||||
const apiHost = process.env.API_HOST || 'api.zeit.co';
|
||||
const urlWithHost = `https://${apiHost}${url}`;
|
||||
if (!opts.headers) opts.headers = {};
|
||||
let token;
|
||||
|
||||
if (process.env.NOW_AUTH_TOKENS) {
|
||||
const tokens = process.env.NOW_AUTH_TOKENS.split(',');
|
||||
if (process.env.CIRCLE_BUILD_NUM) {
|
||||
token = tokens[Number(process.env.CIRCLE_BUILD_NUM) % tokens.length];
|
||||
if (!opts.headers.Authorization) {
|
||||
let token;
|
||||
if (process.env.NOW_AUTH_TOKENS) {
|
||||
const tokens = process.env.NOW_AUTH_TOKENS.split(',');
|
||||
if (process.env.CIRCLE_BUILD_NUM) {
|
||||
token = tokens[Number(process.env.CIRCLE_BUILD_NUM) % tokens.length];
|
||||
} else {
|
||||
token = tokens[Math.floor(Math.random() * tokens.length)];
|
||||
}
|
||||
} else {
|
||||
token = tokens[Math.floor(Math.random() * tokens.length)];
|
||||
const authJsonPath = path.join(homedir(), '.now/auth.json');
|
||||
token = require(authJsonPath).token;
|
||||
}
|
||||
} else {
|
||||
const authJsonPath = path.join(homedir(), '.now/auth.json');
|
||||
token = require(authJsonPath).token;
|
||||
|
||||
opts.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
opts.headers.Authorization = `Bearer ${token}`;
|
||||
return await fetchApiWithChecks(urlWithHost, opts);
|
||||
return await fetchApi(url, opts);
|
||||
}
|
||||
|
||||
async function fetchApiWithChecks (url, opts = {}) {
|
||||
// const { method = 'GET', body } = opts;
|
||||
// console.log('fetch', method, url);
|
||||
// if (body) console.log(encodeURIComponent(body).slice(0, 80));
|
||||
const resp = await fetch(url, opts);
|
||||
return resp;
|
||||
async function fetchApi (url, opts = {}) {
|
||||
const apiHost = process.env.API_HOST || 'api.zeit.co';
|
||||
const urlWithHost = `https://${apiHost}${url}`;
|
||||
const { method = 'GET', body } = opts;
|
||||
|
||||
if (process.env.VERBOSE) {
|
||||
console.log('fetch', method, url);
|
||||
if (body) console.log(encodeURIComponent(body).slice(0, 80));
|
||||
}
|
||||
|
||||
return await fetch(urlWithHost, opts);
|
||||
}
|
||||
|
||||
module.exports = nowDeploy;
|
||||
module.exports = {
|
||||
fetchApi,
|
||||
fetchWithAuth,
|
||||
nowDeploy
|
||||
};
|
||||
|
||||
23
test/lib/deployment/retry-bail-by-default.js
Normal file
23
test/lib/deployment/retry-bail-by-default.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const retry = require('async-retry');
|
||||
|
||||
function canRetry (error) {
|
||||
error.dontBail = true;
|
||||
return error;
|
||||
}
|
||||
|
||||
async function retryBailByDefault (fn, opts) {
|
||||
return await retry(async () => {
|
||||
try {
|
||||
return await fn(canRetry);
|
||||
} catch (error) {
|
||||
if (error.dontBail) {
|
||||
delete error.dontBail;
|
||||
} else {
|
||||
error.bail = true;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}, opts);
|
||||
}
|
||||
|
||||
module.exports = retryBailByDefault;
|
||||
@@ -1,11 +1,11 @@
|
||||
const assert = require('assert');
|
||||
const bufferReplace = require('buffer-replace');
|
||||
const fetch = require('node-fetch');
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('util').promisify(require('glob'));
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const nowDeploy = require('./now-deploy.js');
|
||||
const fetch = require('./fetch-retry.js');
|
||||
const { nowDeploy } = require('./now-deploy.js');
|
||||
|
||||
async function packAndDeploy (builderPath) {
|
||||
const tgzName = (await spawnAsync('npm', [ '--loglevel', 'warn', 'pack' ], {
|
||||
@@ -66,10 +66,11 @@ async function testDeployment ({ builderUrl, buildUtilsUrl }, fixturePath) {
|
||||
}
|
||||
|
||||
bodies['now.json'] = Buffer.from(JSON.stringify(nowJson));
|
||||
delete bodies['probe.js'];
|
||||
const { deploymentId, deploymentUrl } = await nowDeploy(bodies, randomness);
|
||||
console.log('deploymentUrl', deploymentUrl);
|
||||
|
||||
for (const probe of nowJson.probes) {
|
||||
for (const probe of nowJson.probes || []) {
|
||||
console.log('testing', JSON.stringify(probe));
|
||||
const probeUrl = `https://${deploymentUrl}${probe.path}`;
|
||||
const text = await fetchDeploymentUrl(probeUrl, {
|
||||
@@ -91,6 +92,11 @@ async function testDeployment ({ builderUrl, buildUtilsUrl }, fixturePath) {
|
||||
}
|
||||
}
|
||||
|
||||
const probeJsFullPath = path.resolve(fixturePath, 'probe.js');
|
||||
if (await fs.exists(probeJsFullPath)) {
|
||||
await require(probeJsFullPath)({ deploymentUrl, fetch });
|
||||
}
|
||||
|
||||
return { deploymentId, deploymentUrl };
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +216,8 @@ describe('normalizePackageJson', () => {
|
||||
next: 'canary',
|
||||
},
|
||||
scripts: {
|
||||
'now-build': 'next build --lambdas',
|
||||
'now-build':
|
||||
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -246,7 +247,8 @@ describe('normalizePackageJson', () => {
|
||||
next: 'canary',
|
||||
},
|
||||
scripts: {
|
||||
'now-build': 'next build --lambdas',
|
||||
'now-build':
|
||||
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -270,7 +272,8 @@ describe('normalizePackageJson', () => {
|
||||
next: 'canary',
|
||||
},
|
||||
scripts: {
|
||||
'now-build': 'next build --lambdas',
|
||||
'now-build':
|
||||
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -294,7 +297,8 @@ describe('normalizePackageJson', () => {
|
||||
next: 'canary',
|
||||
},
|
||||
scripts: {
|
||||
'now-build': 'next build --lambdas',
|
||||
'now-build':
|
||||
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -318,7 +322,8 @@ describe('normalizePackageJson', () => {
|
||||
next: 'canary',
|
||||
},
|
||||
scripts: {
|
||||
'now-build': 'next build --lambdas',
|
||||
'now-build':
|
||||
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -380,7 +385,8 @@ describe('normalizePackageJson', () => {
|
||||
scripts: {
|
||||
dev: 'next',
|
||||
build: 'next build',
|
||||
'now-build': 'next build --lambdas',
|
||||
'now-build':
|
||||
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
|
||||
start: 'next start',
|
||||
test: "xo && stylelint './pages/**/*.js' && jest",
|
||||
},
|
||||
|
||||
@@ -21,10 +21,10 @@ type Request struct {
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
StatusCode int `json:"statusCode"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
Encoding string `json:"encoding,omitemtpy"`
|
||||
Body string `json:"body"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Encoding string `json:"encoding,omitemtpy"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type ResponseWriter struct {
|
||||
@@ -68,20 +68,17 @@ func Serve(handler http.Handler, req *Request) (res Response, err error) {
|
||||
for k, v := range req.Headers {
|
||||
r.Header.Add(k, v)
|
||||
switch strings.ToLower(k) {
|
||||
case "host":
|
||||
r.Host = v
|
||||
case "content-length":
|
||||
contentLength, _ := strconv.ParseInt(v, 10, 64)
|
||||
r.ContentLength = contentLength
|
||||
case "x-forwarded-for":
|
||||
case "x-real-ip":
|
||||
r.RemoteAddr = v
|
||||
}
|
||||
if strings.ToLower(k) == "host" {
|
||||
// we need to set `Host` in the request
|
||||
// because Go likes to ignore the `Host` header
|
||||
// see https://github.com/golang/go/issues/7682
|
||||
r.Host = v
|
||||
case "host":
|
||||
// we need to set `Host` in the request
|
||||
// because Go likes to ignore the `Host` header
|
||||
// see https://github.com/golang/go/issues/7682
|
||||
r.Host = v
|
||||
case "content-length":
|
||||
contentLength, _ := strconv.ParseInt(v, 10, 64)
|
||||
r.ContentLength = contentLength
|
||||
case "x-forwarded-for":
|
||||
case "x-real-ip":
|
||||
r.RemoteAddr = v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,10 +93,10 @@ func Serve(handler http.Handler, req *Request) (res Response, err error) {
|
||||
handler.ServeHTTP(w, r)
|
||||
defer r.Body.Close()
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers := make(map[string][]string)
|
||||
for k, v := range w.headers {
|
||||
for _, s := range v {
|
||||
headers[k] = s
|
||||
headers[k] = append(headers[k], s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
yarn.lock
16
yarn.lock
@@ -2839,6 +2839,11 @@ fast-stream-to-buffer@1.0.0:
|
||||
dependencies:
|
||||
end-of-stream "^1.4.1"
|
||||
|
||||
fastcgi-client@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fastcgi-client/-/fastcgi-client-0.0.1.tgz#1046d42ff2cee2a9ac03fea04695b3ef7311861c"
|
||||
integrity sha1-EEbUL/LO4qmsA/6gRpWz73MRhhw=
|
||||
|
||||
fb-watchman@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
|
||||
@@ -5712,7 +5717,7 @@ node-fetch@2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.2.0.tgz#4ee79bde909262f9775f731e3656d0db55ced5b5"
|
||||
integrity sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==
|
||||
|
||||
node-fetch@^2.2.0, node-fetch@^2.2.1, node-fetch@^2.3.0:
|
||||
node-fetch@2.3.0, node-fetch@^2.2.0, node-fetch@^2.2.1, node-fetch@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
|
||||
integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==
|
||||
@@ -7246,6 +7251,13 @@ smart-buffer@^4.0.1:
|
||||
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3"
|
||||
integrity sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==
|
||||
|
||||
snake-case@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
|
||||
integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
snapdragon-node@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
|
||||
@@ -8586,7 +8598,7 @@ yargs@^12.0.1:
|
||||
y18n "^3.2.1 || ^4.0.0"
|
||||
yargs-parser "^11.1.1"
|
||||
|
||||
yauzl@^2.2.1:
|
||||
yauzl@2.10.0, yauzl@^2.2.1:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
|
||||
Reference in New Issue
Block a user