@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/bin/proxy-linux-amd64 +0 -0
- package/bin/proxy-linux-arm64 +0 -0
- package/dist/index.js +768 -422
- package/package.json +5 -3
- package/bootstrap/go.mod +0 -3
- package/bootstrap/vc-init.go +0 -274
- /package/bootstrap/{vc-utils.go → utils.go} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/go",
|
|
3
|
-
"version": "3.
|
|
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/
|
|
35
|
+
"@vercel-internals/ipc-proxy": "1.0.0",
|
|
36
|
+
"@vercel/build-utils": "13.29.1"
|
|
35
37
|
},
|
|
36
38
|
"scripts": {
|
|
37
|
-
"build": "node
|
|
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
package/bootstrap/vc-init.go
DELETED
|
@@ -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
|