@vercel/go 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
Binary file
package/dist/index.js CHANGED
@@ -1917,9 +1917,9 @@ var require_end_of_stream = __commonJS({
1917
1917
  }
1918
1918
  });
1919
1919
 
1920
- // ../../node_modules/.pnpm/pump@3.0.2/node_modules/pump/index.js
1920
+ // ../../node_modules/.pnpm/pump@3.0.4/node_modules/pump/index.js
1921
1921
  var require_pump = __commonJS({
1922
- "../../node_modules/.pnpm/pump@3.0.2/node_modules/pump/index.js"(exports, module2) {
1922
+ "../../node_modules/.pnpm/pump@3.0.4/node_modules/pump/index.js"(exports, module2) {
1923
1923
  var once2 = require_once();
1924
1924
  var eos = require_end_of_stream();
1925
1925
  var fs3;
@@ -1929,7 +1929,7 @@ var require_pump = __commonJS({
1929
1929
  }
1930
1930
  var noop = function() {
1931
1931
  };
1932
- var ancient = /^v?\.0/.test(process.version);
1932
+ var ancient = typeof process === "undefined" ? false : /^v?\.0/.test(process.version);
1933
1933
  var isFn = function(fn2) {
1934
1934
  return typeof fn2 === "function";
1935
1935
  };
@@ -3453,13 +3453,13 @@ var require_clone = __commonJS({
3453
3453
  if (obj === null || typeof obj !== "object")
3454
3454
  return obj;
3455
3455
  if (obj instanceof Object)
3456
- var copy4 = { __proto__: getPrototypeOf(obj) };
3456
+ var copy3 = { __proto__: getPrototypeOf(obj) };
3457
3457
  else
3458
- var copy4 = /* @__PURE__ */ Object.create(null);
3458
+ var copy3 = /* @__PURE__ */ Object.create(null);
3459
3459
  Object.getOwnPropertyNames(obj).forEach(function(key) {
3460
- Object.defineProperty(copy4, key, Object.getOwnPropertyDescriptor(obj, key));
3460
+ Object.defineProperty(copy3, key, Object.getOwnPropertyDescriptor(obj, key));
3461
3461
  });
3462
- return copy4;
3462
+ return copy3;
3463
3463
  }
3464
3464
  }
3465
3465
  });
@@ -3549,8 +3549,8 @@ var require_graceful_fs = __commonJS({
3549
3549
  fs4.createReadStream = createReadStream;
3550
3550
  fs4.createWriteStream = createWriteStream2;
3551
3551
  var fs$readFile = fs4.readFile;
3552
- fs4.readFile = readFile4;
3553
- function readFile4(path, options, cb) {
3552
+ fs4.readFile = readFile3;
3553
+ function readFile3(path, options, cb) {
3554
3554
  if (typeof options === "function")
3555
3555
  cb = options, options = null;
3556
3556
  return go$readFile(path, options, cb);
@@ -4376,7 +4376,7 @@ var require_copy = __commonJS({
4376
4376
  var pathExists5 = require_path_exists().pathExists;
4377
4377
  var utimes = require_utimes().utimesMillis;
4378
4378
  var notExist = Symbol("notExist");
4379
- function copy4(src, dest, opts, cb) {
4379
+ function copy3(src, dest, opts, cb) {
4380
4380
  if (typeof opts === "function" && !cb) {
4381
4381
  cb = opts;
4382
4382
  opts = {};
@@ -4602,7 +4602,7 @@ var require_copy = __commonJS({
4602
4602
  return cb(null, destStat);
4603
4603
  });
4604
4604
  }
4605
- module2.exports = copy4;
4605
+ module2.exports = copy3;
4606
4606
  }
4607
4607
  });
4608
4608
 
@@ -5274,7 +5274,7 @@ var require_jsonfile = __commonJS({
5274
5274
  } catch (_2) {
5275
5275
  _fs = require("fs");
5276
5276
  }
5277
- function readFile4(file, options, callback) {
5277
+ function readFile3(file, options, callback) {
5278
5278
  if (callback == null) {
5279
5279
  callback = options;
5280
5280
  options = {};
@@ -5373,7 +5373,7 @@ var require_jsonfile = __commonJS({
5373
5373
  return content;
5374
5374
  }
5375
5375
  var jsonfile = {
5376
- readFile: readFile4,
5376
+ readFile: readFile3,
5377
5377
  readFileSync,
5378
5378
  writeFile: writeFile2,
5379
5379
  writeFileSync
@@ -5573,7 +5573,7 @@ var require_move = __commonJS({
5573
5573
  var u2 = require_universalify().fromCallback;
5574
5574
  var fs3 = require_graceful_fs();
5575
5575
  var path = require("path");
5576
- var copy4 = require_copy2().copy;
5576
+ var copy3 = require_copy2().copy;
5577
5577
  var remove3 = require_remove().remove;
5578
5578
  var mkdirp3 = require_mkdirs2().mkdirp;
5579
5579
  var pathExists5 = require_path_exists().pathExists;
@@ -5630,7 +5630,7 @@ var require_move = __commonJS({
5630
5630
  overwrite,
5631
5631
  errorOnExist: true
5632
5632
  };
5633
- copy4(src, dest, opts, (err) => {
5633
+ copy3(src, dest, opts, (err) => {
5634
5634
  if (err)
5635
5635
  return cb(err);
5636
5636
  return remove3(src, cb);
@@ -7498,9 +7498,9 @@ var require_public_api = __commonJS({
7498
7498
  }
7499
7499
  });
7500
7500
 
7501
- // ../../node_modules/.pnpm/node-fetch@2.6.7/node_modules/node-fetch/lib/index.js
7501
+ // ../../node_modules/.pnpm/node-fetch@2.6.9/node_modules/node-fetch/lib/index.js
7502
7502
  var require_lib4 = __commonJS({
7503
- "../../node_modules/.pnpm/node-fetch@2.6.7/node_modules/node-fetch/lib/index.js"(exports, module2) {
7503
+ "../../node_modules/.pnpm/node-fetch@2.6.9/node_modules/node-fetch/lib/index.js"(exports, module2) {
7504
7504
  "use strict";
7505
7505
  Object.defineProperty(exports, "__esModule", { value: true });
7506
7506
  function _interopDefault(ex) {
@@ -8495,6 +8495,11 @@ var require_lib4 = __commonJS({
8495
8495
  const dest = new URL$1(destination).hostname;
8496
8496
  return orig === dest || orig[orig.length - dest.length - 1] === "." && orig.endsWith(dest);
8497
8497
  };
8498
+ var isSameProtocol = function isSameProtocol2(destination, original) {
8499
+ const orig = new URL$1(original).protocol;
8500
+ const dest = new URL$1(destination).protocol;
8501
+ return orig === dest;
8502
+ };
8498
8503
  function fetch(url, opts) {
8499
8504
  if (!fetch.Promise) {
8500
8505
  throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
@@ -8510,7 +8515,7 @@ var require_lib4 = __commonJS({
8510
8515
  let error = new AbortError("The user aborted a request.");
8511
8516
  reject(error);
8512
8517
  if (request.body && request.body instanceof Stream.Readable) {
8513
- request.body.destroy(error);
8518
+ destroyStream(request.body, error);
8514
8519
  }
8515
8520
  if (!response || !response.body)
8516
8521
  return;
@@ -8545,8 +8550,31 @@ var require_lib4 = __commonJS({
8545
8550
  }
8546
8551
  req.on("error", function(err) {
8547
8552
  reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, "system", err));
8553
+ if (response && response.body) {
8554
+ destroyStream(response.body, err);
8555
+ }
8548
8556
  finalize();
8549
8557
  });
8558
+ fixResponseChunkedTransferBadEnding(req, function(err) {
8559
+ if (signal && signal.aborted) {
8560
+ return;
8561
+ }
8562
+ if (response && response.body) {
8563
+ destroyStream(response.body, err);
8564
+ }
8565
+ });
8566
+ if (parseInt(process.version.substring(1)) < 14) {
8567
+ req.on("socket", function(s3) {
8568
+ s3.addListener("close", function(hadError) {
8569
+ const hasDataListener = s3.listenerCount("data") > 0;
8570
+ if (response && hasDataListener && !hadError && !(signal && signal.aborted)) {
8571
+ const err = new Error("Premature close");
8572
+ err.code = "ERR_STREAM_PREMATURE_CLOSE";
8573
+ response.body.emit("error", err);
8574
+ }
8575
+ });
8576
+ });
8577
+ }
8550
8578
  req.on("response", function(res) {
8551
8579
  clearTimeout(reqTimeout);
8552
8580
  const headers = createHeadersLenient(res.headers);
@@ -8597,7 +8625,7 @@ var require_lib4 = __commonJS({
8597
8625
  timeout: request.timeout,
8598
8626
  size: request.size
8599
8627
  };
8600
- if (!isDomainOrSubdomain(request.url, locationURL)) {
8628
+ if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) {
8601
8629
  for (const name of ["authorization", "www-authenticate", "cookie", "cookie2"]) {
8602
8630
  requestOpts.headers.delete(name);
8603
8631
  }
@@ -8658,6 +8686,12 @@ var require_lib4 = __commonJS({
8658
8686
  response = new Response(body, response_options);
8659
8687
  resolve(response);
8660
8688
  });
8689
+ raw.on("end", function() {
8690
+ if (!response) {
8691
+ response = new Response(body, response_options);
8692
+ resolve(response);
8693
+ }
8694
+ });
8661
8695
  return;
8662
8696
  }
8663
8697
  if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
@@ -8672,6 +8706,33 @@ var require_lib4 = __commonJS({
8672
8706
  writeToStream(req, request);
8673
8707
  });
8674
8708
  }
8709
+ function fixResponseChunkedTransferBadEnding(request, errorCallback) {
8710
+ let socket;
8711
+ request.on("socket", function(s3) {
8712
+ socket = s3;
8713
+ });
8714
+ request.on("response", function(response) {
8715
+ const headers = response.headers;
8716
+ if (headers["transfer-encoding"] === "chunked" && !headers["content-length"]) {
8717
+ response.once("close", function(hadError) {
8718
+ const hasDataListener = socket.listenerCount("data") > 0;
8719
+ if (hasDataListener && !hadError) {
8720
+ const err = new Error("Premature close");
8721
+ err.code = "ERR_STREAM_PREMATURE_CLOSE";
8722
+ errorCallback(err);
8723
+ }
8724
+ });
8725
+ }
8726
+ });
8727
+ }
8728
+ function destroyStream(stream, err) {
8729
+ if (stream.destroy) {
8730
+ stream.destroy(err);
8731
+ } else {
8732
+ stream.emit("error", err);
8733
+ stream.end();
8734
+ }
8735
+ }
8675
8736
  fetch.isRedirect = function(code) {
8676
8737
  return code === 301 || code === 302 || code === 303 || code === 307 || code === 308;
8677
8738
  };
@@ -10877,6 +10938,84 @@ var require_xdg_app_paths = __commonJS({
10877
10938
  }
10878
10939
  });
10879
10940
 
10941
+ // ../../internals/ipc-proxy/dist/index.js
10942
+ var require_dist2 = __commonJS({
10943
+ "../../internals/ipc-proxy/dist/index.js"(exports, module2) {
10944
+ "use strict";
10945
+ var __defProp2 = Object.defineProperty;
10946
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
10947
+ var __getOwnPropNames2 = Object.getOwnPropertyNames;
10948
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
10949
+ var __export2 = (target, all) => {
10950
+ for (var name in all)
10951
+ __defProp2(target, name, { get: all[name], enumerable: true });
10952
+ };
10953
+ var __copyProps2 = (to2, from, except, desc) => {
10954
+ if (from && typeof from === "object" || typeof from === "function") {
10955
+ for (let key of __getOwnPropNames2(from))
10956
+ if (!__hasOwnProp2.call(to2, key) && key !== except)
10957
+ __defProp2(to2, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
10958
+ }
10959
+ return to2;
10960
+ };
10961
+ var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
10962
+ var src_exports2 = {};
10963
+ __export2(src_exports2, {
10964
+ createStandaloneLambda: () => createStandaloneLambda2,
10965
+ getBootstrapDir: () => getBootstrapDir,
10966
+ getProxyBinaryPath: () => getProxyBinaryPath
10967
+ });
10968
+ module2.exports = __toCommonJS2(src_exports2);
10969
+ var import_node_path10 = require("path");
10970
+ var import_node_fs7 = require("fs");
10971
+ var import_promises2 = require("fs/promises");
10972
+ var import_build_utils6 = require("@vercel/build-utils");
10973
+ function proxyBinaryName(architecture) {
10974
+ return architecture === "arm64" ? "proxy-linux-arm64" : "proxy-linux-amd64";
10975
+ }
10976
+ function getProxyBinaryPath(architecture) {
10977
+ const binPath = (0, import_node_path10.join)(__dirname, "..", "bin", proxyBinaryName(architecture));
10978
+ if (!(0, import_node_fs7.existsSync)(binPath)) {
10979
+ throw new Error(
10980
+ `IPC proxy binary not found for architecture "${architecture}" at ${binPath}. Ensure @vercel-internals/ipc-proxy has been built and its prebuilt binaries copied alongside the consumer's dist output.`
10981
+ );
10982
+ }
10983
+ return binPath;
10984
+ }
10985
+ function getBootstrapDir() {
10986
+ return (0, import_node_path10.join)(__dirname, "..", "bootstrap");
10987
+ }
10988
+ async function createStandaloneLambda2(options) {
10989
+ const {
10990
+ userServerPath,
10991
+ architecture,
10992
+ lambdaOptions,
10993
+ includedFiles,
10994
+ runtimeLanguage,
10995
+ supportsResponseStreaming = true
10996
+ } = options;
10997
+ const proxyPath = getProxyBinaryPath(architecture);
10998
+ const [proxyData, userServerData] = await Promise.all([
10999
+ (0, import_promises2.readFile)(proxyPath),
11000
+ (0, import_promises2.readFile)(userServerPath)
11001
+ ]);
11002
+ return new import_build_utils6.Lambda({
11003
+ ...lambdaOptions,
11004
+ files: {
11005
+ ...includedFiles,
11006
+ executable: new import_build_utils6.FileBlob({ mode: 493, data: proxyData }),
11007
+ "user-server": new import_build_utils6.FileBlob({ mode: 493, data: userServerData })
11008
+ },
11009
+ handler: "executable",
11010
+ runtime: "executable",
11011
+ supportsResponseStreaming,
11012
+ architecture,
11013
+ runtimeLanguage
11014
+ });
11015
+ }
11016
+ }
11017
+ });
11018
+
10880
11019
  // src/index.ts
10881
11020
  var src_exports = {};
10882
11021
  __export(src_exports, {
@@ -14668,6 +14807,7 @@ var import_child_process = require("child_process");
14668
14807
  var import_path6 = require("path");
14669
14808
  var import_fs_extra3 = __toESM(require_lib2());
14670
14809
  var import_build_utils4 = require("@vercel/build-utils");
14810
+ var import_ipc_proxy = __toESM(require_dist2());
14671
14811
 
14672
14812
  // src/diagnostics.ts
14673
14813
  var import_build_utils3 = require("@vercel/build-utils");
@@ -14802,7 +14942,6 @@ async function buildStandaloneServer({
14802
14942
  });
14803
14943
  const outDir = await (0, import_build_utils4.getWriteableDirectory)();
14804
14944
  const userServerPath = (0, import_path6.join)(outDir, "user-server");
14805
- const bootstrapPath = (0, import_path6.join)(outDir, "executable");
14806
14945
  const vendorModulesPath = (0, import_path6.join)(workPath, "vendor", "modules.txt");
14807
14946
  const isVendored = await (0, import_fs_extra3.pathExists)(vendorModulesPath);
14808
14947
  if (isVendored) {
@@ -14829,35 +14968,6 @@ async function buildStandaloneServer({
14829
14968
  throw err;
14830
14969
  }
14831
14970
  }
14832
- const bootstrapDir = (0, import_path6.join)(__dirname, "../bootstrap");
14833
- const bootstrapSrc = (0, import_path6.join)(bootstrapDir, "vc-init.go");
14834
- const utilsSrc = (0, import_path6.join)(bootstrapDir, "vc-utils.go");
14835
- const bootstrapGoModSrc = (0, import_path6.join)(bootstrapDir, "go.mod");
14836
- (0, import_build_utils4.debug)(`Building bootstrap wrapper: ${bootstrapSrc} -> ${bootstrapPath}`);
14837
- try {
14838
- const bootstrapBuildDir = await (0, import_build_utils4.getWriteableDirectory)();
14839
- const bootstrapGoFile = (0, import_path6.join)(bootstrapBuildDir, "main.go");
14840
- await (0, import_fs_extra3.copy)(bootstrapSrc, bootstrapGoFile);
14841
- await (0, import_fs_extra3.copy)(utilsSrc, (0, import_path6.join)(bootstrapBuildDir, "vc-utils.go"));
14842
- await (0, import_fs_extra3.copy)(bootstrapGoModSrc, (0, import_path6.join)(bootstrapBuildDir, "go.mod"));
14843
- const bootstrapEnv = {
14844
- PATH: process.env.PATH || "",
14845
- HOME: process.env.HOME || "",
14846
- TMPDIR: process.env.TMPDIR || "",
14847
- GOARCH: architecture === "arm64" ? "arm64" : "amd64",
14848
- GOOS: "linux",
14849
- CGO_ENABLED: "0"
14850
- };
14851
- const bootstrapGo = await createGo({
14852
- modulePath: bootstrapBuildDir,
14853
- opts: { cwd: bootstrapBuildDir, env: bootstrapEnv },
14854
- workPath: bootstrapBuildDir
14855
- });
14856
- await bootstrapGo.build(".", bootstrapPath);
14857
- } catch (err) {
14858
- console.error("Failed to build bootstrap wrapper");
14859
- throw err;
14860
- }
14861
14971
  const includedFiles = {};
14862
14972
  if (config && config.includeFiles) {
14863
14973
  const patterns = Array.isArray(config.includeFiles) ? config.includeFiles : [config.includeFiles];
@@ -14868,24 +14978,13 @@ async function buildStandaloneServer({
14868
14978
  }
14869
14979
  }
14870
14980
  }
14871
- const [userServerData, bootstrapData] = await Promise.all([
14872
- (0, import_fs_extra3.readFile)(userServerPath),
14873
- (0, import_fs_extra3.readFile)(bootstrapPath)
14874
- ]);
14875
- const lambda = new import_build_utils4.Lambda({
14876
- ...lambdaOptions,
14877
- files: {
14878
- ...includedFiles,
14879
- // Bootstrap is the main entrypoint (handles IPC protocol)
14880
- executable: new import_build_utils4.FileBlob({ mode: 493, data: bootstrapData }),
14881
- // User's server is spawned by bootstrap
14882
- "user-server": new import_build_utils4.FileBlob({ mode: 493, data: userServerData })
14883
- },
14884
- handler: "executable",
14885
- runtime: "executable",
14886
- supportsResponseStreaming: true,
14981
+ const lambda = await (0, import_ipc_proxy.createStandaloneLambda)({
14982
+ userServerPath,
14887
14983
  architecture,
14888
- runtimeLanguage: "go"
14984
+ lambdaOptions,
14985
+ includedFiles,
14986
+ runtimeLanguage: "go",
14987
+ supportsResponseStreaming: true
14889
14988
  });
14890
14989
  const preDeployCommand = config?.preDeployCommand;
14891
14990
  if (registerPreDeploy && typeof preDeployCommand === "string") {
@@ -14917,7 +15016,7 @@ async function startStandaloneDevServer(opts, resolvedEntrypoint) {
14917
15016
  });
14918
15017
  const runTarget = resolvedEntrypoint === "main.go" ? "." : "./" + (0, import_path6.dirname)(resolvedEntrypoint);
14919
15018
  const devWrapper = (0, import_path6.join)(__dirname, "../bootstrap/vc-init-dev.go");
14920
- const devUtils = (0, import_path6.join)(__dirname, "../bootstrap/vc-utils.go");
15019
+ const devUtils = (0, import_path6.join)(__dirname, "../bootstrap/utils.go");
14921
15020
  (0, import_build_utils4.debug)(
14922
15021
  `Starting standalone Go dev server wrapper: go run ${devWrapper} (target ${runTarget}, port ${port})`
14923
15022
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/go",
3
- "version": "3.8.0",
3
+ "version": "3.9.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -12,6 +12,7 @@
12
12
  "files": [
13
13
  "dist",
14
14
  "*.go",
15
+ "bin",
15
16
  "bootstrap"
16
17
  ],
17
18
  "devDependencies": {
@@ -31,10 +32,11 @@
31
32
  "vitest": "2.0.3",
32
33
  "xdg-app-paths": "5.1.0",
33
34
  "yauzl-promise": "2.1.3",
34
- "@vercel/build-utils": "13.26.2"
35
+ "@vercel-internals/ipc-proxy": "1.0.0",
36
+ "@vercel/build-utils": "13.29.1"
35
37
  },
36
38
  "scripts": {
37
- "build": "node ../../utils/build-builder.mjs",
39
+ "build": "node build.mjs",
38
40
  "test": "vitest run --config ../../vitest.config.mts",
39
41
  "test-e2e": "pnpm test test/integration-*",
40
42
  "type-check": "tsc --noEmit",
package/bootstrap/go.mod DELETED
@@ -1,3 +0,0 @@
1
- module vc-init
2
-
3
- go 1.23
@@ -1,274 +0,0 @@
1
- // vc-init.go - Bootstrap wrapper for standalone Go servers on Vercel
2
- // This handles the IPC protocol required for executable runtime mode.
3
- //
4
- // The bootstrap:
5
- // 1. Connects to VERCEL_IPC_PATH Unix socket
6
- // 2. Starts the user's server on an internal port
7
- // 3. Sends "server-started" IPC message
8
- // 4. Reverse proxies requests to user's server
9
- // 5. Handles /_vercel/ping health check
10
- // 6. Sends "end" IPC message after each request
11
-
12
- package main
13
-
14
- import (
15
- "context"
16
- "encoding/json"
17
- "errors"
18
- "fmt"
19
- "net"
20
- "net/http"
21
- "net/http/httputil"
22
- "net/url"
23
- "os"
24
- "os/exec"
25
- "strconv"
26
- "strings"
27
- "sync"
28
- "time"
29
- )
30
-
31
- // IPC message types
32
- type StartMessage struct {
33
- Type string `json:"type"`
34
- Payload StartPayload `json:"payload"`
35
- }
36
-
37
- type StartPayload struct {
38
- InitDuration int `json:"initDuration"`
39
- HTTPPort int `json:"httpPort"`
40
- }
41
-
42
- type EndMessage struct {
43
- Type string `json:"type"`
44
- Payload EndPayload `json:"payload"`
45
- }
46
-
47
- type EndPayload struct {
48
- Context RequestContext `json:"context"`
49
- Error interface{} `json:"error,omitempty"`
50
- }
51
-
52
- type RequestContext struct {
53
- InvocationID string `json:"invocationId"`
54
- RequestID uint64 `json:"requestId"`
55
- }
56
-
57
- type UnrecoverableErrorMessage struct {
58
- Type string `json:"type"`
59
- Payload UnrecoverableErrorPayload `json:"payload"`
60
- }
61
-
62
- type UnrecoverableErrorPayload struct {
63
- ExitCode int `json:"exitCode"`
64
- Message string `json:"message"`
65
- }
66
-
67
- var (
68
- ipcConn net.Conn
69
- ipcMutex sync.Mutex
70
- ipcReady bool
71
- startTime time.Time
72
- )
73
-
74
- func sendIPCMessage(msg interface{}) error {
75
- if ipcConn == nil {
76
- return nil
77
- }
78
-
79
- ipcMutex.Lock()
80
- defer ipcMutex.Unlock()
81
-
82
- data, err := json.Marshal(msg)
83
- if err != nil {
84
- return err
85
- }
86
-
87
- // IPC messages are JSON followed by null byte
88
- _, err = ipcConn.Write(append(data, 0))
89
- return err
90
- }
91
-
92
- // fatal reports a fatal init error via IPC and exits.
93
- func fatal(exitCode int, msg string) {
94
- fmt.Fprintln(os.Stderr, msg)
95
- sendIPCMessage(UnrecoverableErrorMessage{
96
- Type: "unrecoverable-error",
97
- Payload: UnrecoverableErrorPayload{
98
- ExitCode: exitCode,
99
- Message: msg,
100
- },
101
- })
102
- os.Exit(exitCode)
103
- }
104
-
105
- func connectIPC() error {
106
- ipcPath := os.Getenv("VERCEL_IPC_PATH")
107
- if ipcPath == "" {
108
- // No IPC path - running in dev mode or locally
109
- return nil
110
- }
111
-
112
- conn, err := net.Dial("unix", ipcPath)
113
- if err != nil {
114
- return fmt.Errorf("failed to connect to IPC socket: %w", err)
115
- }
116
-
117
- ipcConn = conn
118
- return nil
119
- }
120
-
121
- func main() {
122
- startTime = time.Now()
123
- serviceRoutePrefix := resolveServiceRoutePrefix()
124
-
125
- // Connect to IPC socket
126
- if err := connectIPC(); err != nil {
127
- fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
128
- }
129
-
130
- // Find a free port for the user's server
131
- userPort, err := findFreePort()
132
- if err != nil {
133
- fatal(1, fmt.Sprintf("Failed to find free port: %v", err))
134
- }
135
-
136
- // Start the user's server binary
137
- userBinary := "./user-server"
138
- if _, err := os.Stat(userBinary); os.IsNotExist(err) {
139
- fatal(1, fmt.Sprintf("User server binary not found: %s", userBinary))
140
- }
141
-
142
- ctx, cancel := context.WithCancel(context.Background())
143
- defer cancel()
144
-
145
- cmd := exec.CommandContext(ctx, userBinary)
146
- cmd.Env = append(os.Environ(), fmt.Sprintf("PORT=%d", userPort))
147
- cmd.Stdout = os.Stdout
148
- cmd.Stderr = os.Stderr
149
-
150
- if err := cmd.Start(); err != nil {
151
- fatal(1, fmt.Sprintf("Failed to start user server: %v", err))
152
- }
153
-
154
- // Race server readiness against early child death.
155
- childDone := make(chan error, 1)
156
- go func() { childDone <- cmd.Wait() }()
157
-
158
- serverReady := make(chan error, 1)
159
- go func() { serverReady <- waitForServer(userPort, 30*time.Second) }()
160
-
161
- select {
162
- case waitErr := <-childDone:
163
- // Child exited before the server became ready.
164
- exitCode := 1
165
- var exitErr *exec.ExitError
166
- if errors.As(waitErr, &exitErr) {
167
- exitCode = exitErr.ExitCode()
168
- }
169
- fatal(exitCode, "User server exited during startup")
170
- case err := <-serverReady:
171
- if err != nil {
172
- cmd.Process.Kill()
173
- fatal(1, fmt.Sprintf("User server failed to start: %v", err))
174
- }
175
- }
176
-
177
- // Create reverse proxy to user's server
178
- targetURL, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", userPort))
179
- proxy := httputil.NewSingleHostReverseProxy(targetURL)
180
-
181
- // Customize the proxy director to preserve headers
182
- originalDirector := proxy.Director
183
- proxy.Director = func(req *http.Request) {
184
- originalDirector(req)
185
- // Preserve the original Host header
186
- if host := req.Header.Get("X-Forwarded-Host"); host != "" {
187
- req.Host = host
188
- }
189
- }
190
-
191
- // The port we'll listen on (Vercel will route traffic here)
192
- listenPort := 3000
193
-
194
- // Create HTTP server with IPC-aware handler
195
- server := &http.Server{
196
- Addr: fmt.Sprintf("127.0.0.1:%d", listenPort),
197
- Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
198
- // Handle Vercel health check
199
- if r.URL.Path == "/_vercel/ping" {
200
- w.WriteHeader(http.StatusOK)
201
- w.Write([]byte("OK"))
202
- return
203
- }
204
-
205
- // Extract Vercel internal headers
206
- invocationID := r.Header.Get("X-Vercel-Internal-Invocation-Id")
207
- requestIDStr := r.Header.Get("X-Vercel-Internal-Request-Id")
208
- requestID, _ := strconv.ParseUint(requestIDStr, 10, 64)
209
-
210
- // Remove internal headers before forwarding
211
- for key := range r.Header {
212
- if strings.HasPrefix(strings.ToLower(key), "x-vercel-internal-") {
213
- r.Header.Del(key)
214
- }
215
- }
216
-
217
- if r.URL != nil {
218
- originalPath := r.URL.Path
219
- r.URL.Path = stripServiceRoutePrefix(r.URL.Path, serviceRoutePrefix)
220
- if r.URL.Path != originalPath {
221
- // Keep URL path encoding fields consistent after rewrite.
222
- r.URL.RawPath = ""
223
- }
224
- }
225
-
226
- // Forward request to user's server
227
- proxy.ServeHTTP(w, r)
228
-
229
- // Send end message via IPC
230
- if ipcConn != nil && invocationID != "" {
231
- endMsg := EndMessage{
232
- Type: "end",
233
- Payload: EndPayload{
234
- Context: RequestContext{
235
- InvocationID: invocationID,
236
- RequestID: requestID,
237
- },
238
- },
239
- }
240
- sendIPCMessage(endMsg)
241
- }
242
- }),
243
- }
244
-
245
- // Send server-started IPC message
246
- initDuration := int(time.Since(startTime).Milliseconds())
247
- startMsg := StartMessage{
248
- Type: "server-started",
249
- Payload: StartPayload{
250
- InitDuration: initDuration,
251
- HTTPPort: listenPort,
252
- },
253
- }
254
-
255
- if err := sendIPCMessage(startMsg); err != nil {
256
- fmt.Fprintf(os.Stderr, "Warning: Failed to send IPC start message: %v\n", err)
257
- } else {
258
- ipcReady = true
259
- }
260
-
261
- // If no IPC, print the port for local development
262
- if ipcConn == nil {
263
- fmt.Printf("Server listening on port %d (proxying to user server on port %d)\n", listenPort, userPort)
264
- }
265
-
266
- // Start the proxy server
267
- if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
268
- fmt.Fprintf(os.Stderr, "Server error: %v\n", err)
269
- os.Exit(1)
270
- }
271
-
272
- // Clean up
273
- cmd.Process.Kill()
274
- }
File without changes