Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose rawBody #1109

Merged
merged 17 commits into from
Apr 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clever-lizards-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-node': patch
---

Use getRawBody
5 changes: 5 additions & 0 deletions .changeset/cold-buttons-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-cloudflare-workers': patch
---

Pass rawBody to SvelteKit, bundle worker with esbuild
5 changes: 5 additions & 0 deletions .changeset/flat-ducks-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-vercel': patch
---

Fix dependencies
5 changes: 5 additions & 0 deletions .changeset/lemon-ways-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-netlify': patch
---

Fix dependencies
5 changes: 5 additions & 0 deletions .changeset/modern-dryers-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Expose rawBody on request, and expect rawBody from adapters
5 changes: 5 additions & 0 deletions .changeset/nasty-boats-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-netlify': patch
---

Pass rawBody from netlify adapter
5 changes: 5 additions & 0 deletions .changeset/tiny-candles-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Expose getRawBody from kit/http
5 changes: 5 additions & 0 deletions .changeset/unlucky-wasps-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-vercel': patch
---

Use getRawBody in adapter-vercel
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ yarn.lock
.vercel_build_output
.netlify
.svelte
.cloudflare
12 changes: 6 additions & 6 deletions documentation/docs/01-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ type Request<Context = any> = {
path: string;
params: Record<string, string | string[]>;
query: URLSearchParams;
body: string | Buffer | ReadOnlyFormData;
rawBody: string | ArrayBuffer;
body: string | ArrayBuffer | ReadOnlyFormData | any;
context: Context; // see getContext, below
};

Expand Down Expand Up @@ -95,10 +96,10 @@ export async function get({ params }) {

The job of this function is to return a `{status, headers, body}` object representing the response, where `status` is an [HTTP status code](https://httpstatusdogs.com):

* `2xx` — successful response (default is `200`)
* `3xx` — redirection (should be accompanied by a `location` header)
* `4xx` — client error
* `5xx` — server error
- `2xx` — successful response (default is `200`)
- `3xx` — redirection (should be accompanied by a `location` header)
- `4xx` — client error
- `5xx` — server error

> For successful responses, SvelteKit will generate 304s automatically

Expand Down Expand Up @@ -126,7 +127,6 @@ return {
};
```


### Private modules

A filename that has a segment with a leading underscore, such as `src/routes/foo/_Private.svelte` or `src/routes/bar/_utils/cool-util.js`, is hidden from the router, but can be imported by files that are not.
Expand Down
3 changes: 2 additions & 1 deletion documentation/docs/04-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ type Request<Context = any> = {
path: string;
params: Record<string, string>;
query: URLSearchParams;
body: string | Buffer | ReadOnlyFormData;
rawBody: string | ArrayBuffer;
body: string | ArrayBuffer | ReadOnlyFormData | any;
context: Context;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
import { render } from './app.js'; // eslint-disable-line import/no-unresolved
// TODO hardcoding the relative location makes this brittle
import { render } from '../output/server/app.js'; // eslint-disable-line import/no-unresolved
import { getAssetFromKV, NotFoundError } from '@cloudflare/kv-asset-handler'; // eslint-disable-line import/no-unresolved

// From https://developers.cloudflare.com/workers/examples/read-post
async function readRequestBody(request) {
const { headers } = request;
const contentType = headers.get('content-type') || '';
if (contentType.includes('application/json')) {
return await request.json();
} else if (contentType.includes('application/text')) {
return await request.text();
} else if (contentType.includes('text/html')) {
return await request.text();
} else if (contentType.includes('form')) {
return await request.formData();
} else {
const myBlob = await request.blob();
const objectURL = URL.createObjectURL(myBlob);
return objectURL;
}
}

addEventListener('fetch', (event) => {
event.respondWith(handleEvent(event));
event.respondWith(handle(event));
});

async function handleEvent(event) {
//try static files first
async function handle(event) {
// try static files first
if (event.request.method == 'GET') {
try {
// TODO rather than attempting to get an asset,
// use the asset manifest to see if it exists
return await getAssetFromKV(event);
} catch (e) {
if (!(e instanceof NotFoundError)) {
Expand All @@ -38,7 +22,7 @@ async function handleEvent(event) {
}
}

//fall back to an app route
// fall back to an app route
const request = event.request;
const request_url = new URL(request.url);

Expand All @@ -47,17 +31,16 @@ async function handleEvent(event) {
host: request_url.host,
path: request_url.pathname,
query: request_url.searchParams,
body: request.body ? await readRequestBody(request) : null,
rawBody: request.body ? await read(request) : null,
headers: Object.fromEntries(request.headers),
method: request.method
});

if (rendered) {
const response = new Response(rendered.body, {
return new Response(rendered.body, {
status: rendered.status,
headers: rendered.headers
});
return response;
}
} catch (e) {
return new Response('Error rendering route:' + (e.message || e.toString()), { status: 500 });
Expand All @@ -68,3 +51,12 @@ async function handleEvent(event) {
statusText: 'Not Found'
});
}

function read(request) {
const type = request.headers.get('content-type') || '';
if (type.includes('application/octet-stream')) {
return request.arrayBuffer();
}

return request.text();
}
117 changes: 72 additions & 45 deletions packages/adapter-cloudflare-workers/index.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,95 @@
'use strict';

const { exec } = require('child_process');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const esbuild = require('esbuild');
const toml = require('toml');

module.exports = function () {
/** @type {import('@sveltejs/kit').Adapter} */
const adapter = {
name: '@sveltejs/adapter-cloudflare-workers',
async adapt(utils) {
let wrangler_config;

if (fs.existsSync('wrangler.toml')) {
try {
wrangler_config = toml.parse(fs.readFileSync('wrangler.toml', 'utf-8'));
} catch (err) {
err.message = `Error parsing wrangler.toml: ${err.message}`;
throw err;
}
} else {
// TODO offer to create one?
throw new Error(
'Missing a wrangler.toml file. Consult https://developers.cloudflare.com/workers/platform/sites/configuration on how to setup your site'
);
}

if (!wrangler_config.site || !wrangler_config.site.bucket) {
throw new Error(
'You must specify site.bucket in wrangler.toml. Consult https://developers.cloudflare.com/workers/platform/sites/configuration'
);
}

const bucket = path.resolve(wrangler_config.site.bucket);
const entrypoint = path.resolve(wrangler_config.site['entry-point'] ?? 'workers-site');
const { site } = validate_config(utils);

utils.copy_static_files(bucket);
utils.copy_client_files(bucket);
utils.copy_server_files(entrypoint);
const bucket = site.bucket;
const entrypoint = site['entry-point'] || 'workers-site';

utils.rimraf(bucket);
utils.rimraf(entrypoint);

// copy the renderer
utils.copy(path.resolve(__dirname, 'files/render.js'), `${entrypoint}/index.js`);
utils.copy(path.resolve(__dirname, 'files/_package.json'), `${entrypoint}/package.json`);
utils.log.info('Installing worker dependencies...');
utils.copy(`${__dirname}/files/_package.json`, '.svelte/cloudflare-workers/package.json');

// TODO would be cool if we could make this step unnecessary somehow
const stdout = execSync('npm install', { cwd: '.svelte/cloudflare-workers' });
utils.log.info(stdout.toString());

utils.log.minor('Generating worker...');
utils.copy(`${__dirname}/files/entry.js`, '.svelte/cloudflare-workers/entry.js');

await esbuild.build({
entryPoints: ['.svelte/cloudflare-workers/entry.js'],
outfile: `${entrypoint}/index.js`,
bundle: true,
platform: 'node'
});

utils.log.info('Prerendering static pages...');
await utils.prerender({
dest: bucket
});

utils.log.info('Installing Worker Dependencies...');
exec(
'npm install',
{
cwd: entrypoint
},
(error, stdout, stderr) => {
utils.log.info(stderr);
if (error) {
utils.log.error(error);
}
}
);
utils.log.minor('Copying assets...');
utils.copy_static_files(bucket);
utils.copy_client_files(bucket);
}
};

return adapter;
};

function validate_config(utils) {
if (fs.existsSync('wrangler.toml')) {
let wrangler_config;

try {
wrangler_config = toml.parse(fs.readFileSync('wrangler.toml', 'utf-8'));
} catch (err) {
err.message = `Error parsing wrangler.toml: ${err.message}`;
throw err;
}

if (!wrangler_config.site || !wrangler_config.site.bucket) {
throw new Error(
'You must specify site.bucket in wrangler.toml. Consult https://developers.cloudflare.com/workers/platform/sites/configuration'
);
}

return wrangler_config;
}

utils.log.error(
'Consult https://developers.cloudflare.com/workers/platform/sites/configuration on how to setup your site'
);

utils.log(
`
Sample wrangler.toml:

name = "<your-site-name>"
type = "javascript"
account_id = "<your-account-id>"
workers_dev = true
route = ""
zone_id = ""

[site]
bucket = "./.cloudflare/assets"
entry-point = "./.cloudflare/worker"`
.replace(/^\t+/gm, '')
.trim()
);

throw new Error('Missing a wrangler.toml file');
}
1 change: 1 addition & 0 deletions packages/adapter-cloudflare-workers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore"
},
"dependencies": {
"esbuild": "^0.11.12",
"toml": "^3.0.0"
},
"devDependencies": {
Expand Down
12 changes: 3 additions & 9 deletions packages/adapter-netlify/files/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,7 @@ import '@sveltejs/kit/install-fetch'; // eslint-disable-line import/no-unresolve
import { render } from '../output/server/app.js'; // eslint-disable-line import/no-unresolved

export async function handler(event) {
const {
path,
httpMethod,
headers,
queryStringParameters
// body, // TODO pass this to renderer
// isBase64Encoded // TODO is this useful?
} = event;
const { path, httpMethod, headers, queryStringParameters, body, isBase64Encoded } = event;

const query = new URLSearchParams();
for (const k in queryStringParameters) {
Expand All @@ -25,7 +18,8 @@ export async function handler(event) {
method: httpMethod,
headers,
path,
query
query,
rawBody: isBase64Encoded ? new TextEncoder('base64').encode(body).buffer : body
});

if (rendered) {
Expand Down
3 changes: 2 additions & 1 deletion packages/adapter-netlify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
"check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore"
},
"dependencies": {
"@sveltejs/kit": "workspace:*",
"esbuild": "^0.11.12",
"toml": "^3.0.0"
},
"devDependencies": {
"@sveltejs/kit": "workspace:*",
"typescript": "^4.2.3"
}
}
4 changes: 2 additions & 2 deletions packages/adapter-node/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
import compression from 'compression';
import polka from 'polka';
import sirv from 'sirv';
import { get_body } from '@sveltejs/kit/http'; // eslint-disable-line import/no-unresolved
import { getRawBody } from '@sveltejs/kit/http'; // eslint-disable-line import/no-unresolved
import '@sveltejs/kit/install-fetch'; // eslint-disable-line import/no-unresolved

// App is a dynamic file built from the application layer.
Expand Down Expand Up @@ -44,7 +44,7 @@ export function createServer({ render }) {
method: req.method,
headers: req.headers, // TODO: what about repeated headers, i.e. string[]
path: parsed.pathname,
body: await get_body(req),
rawBody: await getRawBody(req),
query: parsed.searchParams
});

Expand Down
Loading