@vercel/go 3.7.1 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/go",
3
- "version": "3.7.1",
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.0"
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