From f80f1f79a6c27767590df4733c730e7f54b3b77c Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Sun, 7 Jul 2019 06:19:54 -0700 Subject: [PATCH] [now dev] Add `etag` response header for Lambda invocations (#2502) This matches the behavior in production. --- src/util/dev/builder-cache.ts | 8 +------- src/util/dev/builder.ts | 2 ++ src/util/dev/server.ts | 3 ++- src/util/dev/types.ts | 1 + src/util/sha.ts | 7 +++++++ test/dev-server.unit.js | 1 + 6 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 src/util/sha.ts diff --git a/src/util/dev/builder-cache.ts b/src/util/dev/builder-cache.ts index 69dec387d..4dbdc9506 100644 --- a/src/util/dev/builder-cache.ts +++ b/src/util/dev/builder-cache.ts @@ -1,7 +1,6 @@ import chalk from 'chalk'; import execa from 'execa'; import npa from 'npm-package-arg'; -import { createHash } from 'crypto'; import { join, resolve } from 'path'; import { funCacheDir } from '@zeit/fun'; import cacheDirectory from 'cache-or-tmp-directory'; @@ -12,6 +11,7 @@ import { BuilderCacheCleanError } from '../errors-ts'; import wait from '../output/wait'; +import { getSha } from '../sha'; import { Output } from '../output'; import * as staticBuilder from './static-builder'; @@ -215,9 +215,3 @@ function getPackageName( } return null; } - -function getSha(buffer: Buffer): string { - const hash = createHash('sha256'); - hash.update(buffer); - return hash.digest('hex'); -} diff --git a/src/util/dev/builder.ts b/src/util/dev/builder.ts index 185f9690f..cca9fc122 100644 --- a/src/util/dev/builder.ts +++ b/src/util/dev/builder.ts @@ -11,6 +11,7 @@ import chalk from 'chalk'; import which from 'which'; import ora, { Ora } from 'ora'; +import { getSha } from '../sha'; import { Output } from '../output'; import { relative } from '../path-helpers'; import { LambdaSizeExceededError } from '../errors-ts'; @@ -354,6 +355,7 @@ export async function executeBuild( } } }); + asset.sha = getSha(asset.zipBuffer, 'sha1'); } match.buildTimestamp = Date.now(); diff --git a/src/util/dev/server.ts b/src/util/dev/server.ts index 3318f9c54..d376e429a 100644 --- a/src/util/dev/server.ts +++ b/src/util/dev/server.ts @@ -839,7 +839,7 @@ export default class DevServer { return; case 'Lambda': - if (!asset.fn) { + if (!asset.fn || !asset.sha) { // This is mostly to appease TypeScript since `fn` is an optional prop, // but this shouldn't really ever happen since we run the builds before // responding to HTTP requests. @@ -894,6 +894,7 @@ export default class DevServer { res.statusCode = result.statusCode; this.setResponseHeaders(res, nowRequestId, result.headers); + res.setHeader('etag', `W/"${asset.sha}"`); let resBody: Buffer | string | undefined; if (result.encoding === 'base64' && typeof result.body === 'string') { diff --git a/src/util/dev/types.ts b/src/util/dev/types.ts index f6ad0de3c..a4aeca983 100644 --- a/src/util/dev/types.ts +++ b/src/util/dev/types.ts @@ -59,6 +59,7 @@ export interface BuilderInputs { export type BuiltLambda = Lambda & { fn?: FunLambda; + sha?: string; }; export type BuilderOutput = BuiltLambda | FileFsRef | FileBlob; diff --git a/src/util/sha.ts b/src/util/sha.ts new file mode 100644 index 000000000..94bc5daa5 --- /dev/null +++ b/src/util/sha.ts @@ -0,0 +1,7 @@ +import { createHash } from 'crypto'; + +export function getSha(buffer: Buffer, cypher: string = 'sha256'): string { + const hash = createHash(cypher); + hash.update(buffer); + return hash.digest('hex'); +} diff --git a/test/dev-server.unit.js b/test/dev-server.unit.js index 642269bac..734a056f6 100644 --- a/test/dev-server.unit.js +++ b/test/dev-server.unit.js @@ -44,6 +44,7 @@ function testFixture(name, fn) { function validateResponseHeaders(t, res) { t.is(res.headers.get('cache-control'), 'public, max-age=0, must-revalidate'); t.is(res.headers.get('x-now-trace'), 'dev1'); + t.truthy(/^W\/"[0-9a-f]{40}"$/.test(res.headers.get('etag'))); t.truthy( /^dev1:[0-9a-z]{5}-[1-9][0-9]+-[a-f0-9]{12}$/.test( res.headers.get('x-now-id')