@vercel/go 3.3.4 → 3.4.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/dist/index.js +11280 -11392
- package/package.json +3 -4
- package/vc-init.go +259 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/go",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/index",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
"@types/jest": "28.1.6",
|
|
22
22
|
"@types/node": "20.11.0",
|
|
23
23
|
"@types/node-fetch": "^2.3.0",
|
|
24
|
-
"@types/tar": "6.1.5",
|
|
25
24
|
"@types/yauzl-promise": "2.1.0",
|
|
26
25
|
"async-retry": "1.3.3",
|
|
27
26
|
"execa": "^1.0.0",
|
|
@@ -29,11 +28,11 @@
|
|
|
29
28
|
"jest-junit": "16.0.0",
|
|
30
29
|
"node-fetch": "^2.2.1",
|
|
31
30
|
"string-argv": "0.3.1",
|
|
32
|
-
"tar": "
|
|
31
|
+
"tar": "7.5.7",
|
|
33
32
|
"typescript": "4.3.4",
|
|
34
33
|
"xdg-app-paths": "5.1.0",
|
|
35
34
|
"yauzl-promise": "2.1.3",
|
|
36
|
-
"@vercel/build-utils": "13.2
|
|
35
|
+
"@vercel/build-utils": "13.3.2"
|
|
37
36
|
},
|
|
38
37
|
"scripts": {
|
|
39
38
|
"build": "node ../../utils/build-builder.mjs",
|
package/vc-init.go
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
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
|
+
"fmt"
|
|
18
|
+
"net"
|
|
19
|
+
"net/http"
|
|
20
|
+
"net/http/httputil"
|
|
21
|
+
"net/url"
|
|
22
|
+
"os"
|
|
23
|
+
"os/exec"
|
|
24
|
+
"strconv"
|
|
25
|
+
"strings"
|
|
26
|
+
"sync"
|
|
27
|
+
"time"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
// IPC message types
|
|
31
|
+
type StartMessage struct {
|
|
32
|
+
Type string `json:"type"`
|
|
33
|
+
Payload StartPayload `json:"payload"`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type StartPayload struct {
|
|
37
|
+
InitDuration int `json:"initDuration"`
|
|
38
|
+
HTTPPort int `json:"httpPort"`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type EndMessage struct {
|
|
42
|
+
Type string `json:"type"`
|
|
43
|
+
Payload EndPayload `json:"payload"`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type EndPayload struct {
|
|
47
|
+
Context RequestContext `json:"context"`
|
|
48
|
+
Error interface{} `json:"error,omitempty"`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type RequestContext struct {
|
|
52
|
+
InvocationID string `json:"invocationId"`
|
|
53
|
+
RequestID uint64 `json:"requestId"`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var (
|
|
57
|
+
ipcConn net.Conn
|
|
58
|
+
ipcMutex sync.Mutex
|
|
59
|
+
ipcReady bool
|
|
60
|
+
startTime time.Time
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
func sendIPCMessage(msg interface{}) error {
|
|
64
|
+
if ipcConn == nil {
|
|
65
|
+
return nil
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ipcMutex.Lock()
|
|
69
|
+
defer ipcMutex.Unlock()
|
|
70
|
+
|
|
71
|
+
data, err := json.Marshal(msg)
|
|
72
|
+
if err != nil {
|
|
73
|
+
return err
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// IPC messages are JSON followed by null byte
|
|
77
|
+
_, err = ipcConn.Write(append(data, 0))
|
|
78
|
+
return err
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func connectIPC() error {
|
|
82
|
+
ipcPath := os.Getenv("VERCEL_IPC_PATH")
|
|
83
|
+
if ipcPath == "" {
|
|
84
|
+
// No IPC path - running in dev mode or locally
|
|
85
|
+
return nil
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
conn, err := net.Dial("unix", ipcPath)
|
|
89
|
+
if err != nil {
|
|
90
|
+
return fmt.Errorf("failed to connect to IPC socket: %w", err)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
ipcConn = conn
|
|
94
|
+
return nil
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
func findFreePort() (int, error) {
|
|
98
|
+
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
99
|
+
if err != nil {
|
|
100
|
+
return 0, err
|
|
101
|
+
}
|
|
102
|
+
port := listener.Addr().(*net.TCPAddr).Port
|
|
103
|
+
listener.Close()
|
|
104
|
+
return port, nil
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
func waitForServer(port int, timeout time.Duration) error {
|
|
108
|
+
deadline := time.Now().Add(timeout)
|
|
109
|
+
url := fmt.Sprintf("http://127.0.0.1:%d/", port)
|
|
110
|
+
|
|
111
|
+
for time.Now().Before(deadline) {
|
|
112
|
+
resp, err := http.Get(url)
|
|
113
|
+
if err == nil {
|
|
114
|
+
resp.Body.Close()
|
|
115
|
+
return nil
|
|
116
|
+
}
|
|
117
|
+
time.Sleep(50 * time.Millisecond)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return fmt.Errorf("server did not start within %v", timeout)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func main() {
|
|
124
|
+
startTime = time.Now()
|
|
125
|
+
|
|
126
|
+
// Connect to IPC socket
|
|
127
|
+
if err := connectIPC(); err != nil {
|
|
128
|
+
fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Find a free port for the user's server
|
|
132
|
+
userPort, err := findFreePort()
|
|
133
|
+
if err != nil {
|
|
134
|
+
fmt.Fprintf(os.Stderr, "Failed to find free port: %v\n", err)
|
|
135
|
+
os.Exit(1)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Start the user's server binary
|
|
139
|
+
userBinary := "./user-server"
|
|
140
|
+
if _, err := os.Stat(userBinary); os.IsNotExist(err) {
|
|
141
|
+
fmt.Fprintf(os.Stderr, "User server binary not found: %s\n", userBinary)
|
|
142
|
+
os.Exit(1)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
ctx, cancel := context.WithCancel(context.Background())
|
|
146
|
+
defer cancel()
|
|
147
|
+
|
|
148
|
+
cmd := exec.CommandContext(ctx, userBinary)
|
|
149
|
+
cmd.Env = append(os.Environ(), fmt.Sprintf("PORT=%d", userPort))
|
|
150
|
+
cmd.Stdout = os.Stdout
|
|
151
|
+
cmd.Stderr = os.Stderr
|
|
152
|
+
|
|
153
|
+
if err := cmd.Start(); err != nil {
|
|
154
|
+
fmt.Fprintf(os.Stderr, "Failed to start user server: %v\n", err)
|
|
155
|
+
os.Exit(1)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Wait for user's server to be ready
|
|
159
|
+
if err := waitForServer(userPort, 30*time.Second); err != nil {
|
|
160
|
+
fmt.Fprintf(os.Stderr, "User server failed to start: %v\n", err)
|
|
161
|
+
cmd.Process.Kill()
|
|
162
|
+
os.Exit(1)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Create reverse proxy to user's server
|
|
166
|
+
targetURL, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", userPort))
|
|
167
|
+
proxy := httputil.NewSingleHostReverseProxy(targetURL)
|
|
168
|
+
|
|
169
|
+
// Customize the proxy director to preserve headers
|
|
170
|
+
originalDirector := proxy.Director
|
|
171
|
+
proxy.Director = func(req *http.Request) {
|
|
172
|
+
originalDirector(req)
|
|
173
|
+
// Preserve the original Host header
|
|
174
|
+
if host := req.Header.Get("X-Forwarded-Host"); host != "" {
|
|
175
|
+
req.Host = host
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// The port we'll listen on (Vercel will route traffic here)
|
|
180
|
+
listenPort := 3000
|
|
181
|
+
|
|
182
|
+
// Create HTTP server with IPC-aware handler
|
|
183
|
+
server := &http.Server{
|
|
184
|
+
Addr: fmt.Sprintf("127.0.0.1:%d", listenPort),
|
|
185
|
+
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
186
|
+
// Handle Vercel health check
|
|
187
|
+
if r.URL.Path == "/_vercel/ping" {
|
|
188
|
+
w.WriteHeader(http.StatusOK)
|
|
189
|
+
w.Write([]byte("OK"))
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Extract Vercel internal headers
|
|
194
|
+
invocationID := r.Header.Get("X-Vercel-Internal-Invocation-Id")
|
|
195
|
+
requestIDStr := r.Header.Get("X-Vercel-Internal-Request-Id")
|
|
196
|
+
requestID, _ := strconv.ParseUint(requestIDStr, 10, 64)
|
|
197
|
+
|
|
198
|
+
// Remove internal headers before forwarding
|
|
199
|
+
for key := range r.Header {
|
|
200
|
+
if strings.HasPrefix(strings.ToLower(key), "x-vercel-internal-") {
|
|
201
|
+
r.Header.Del(key)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Forward request to user's server
|
|
206
|
+
proxy.ServeHTTP(w, r)
|
|
207
|
+
|
|
208
|
+
// Send end message via IPC
|
|
209
|
+
if ipcConn != nil && invocationID != "" {
|
|
210
|
+
endMsg := EndMessage{
|
|
211
|
+
Type: "end",
|
|
212
|
+
Payload: EndPayload{
|
|
213
|
+
Context: RequestContext{
|
|
214
|
+
InvocationID: invocationID,
|
|
215
|
+
RequestID: requestID,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
}
|
|
219
|
+
sendIPCMessage(endMsg)
|
|
220
|
+
}
|
|
221
|
+
}),
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Send server-started IPC message
|
|
225
|
+
initDuration := int(time.Since(startTime).Milliseconds())
|
|
226
|
+
startMsg := StartMessage{
|
|
227
|
+
Type: "server-started",
|
|
228
|
+
Payload: StartPayload{
|
|
229
|
+
InitDuration: initDuration,
|
|
230
|
+
HTTPPort: listenPort,
|
|
231
|
+
},
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if err := sendIPCMessage(startMsg); err != nil {
|
|
235
|
+
fmt.Fprintf(os.Stderr, "Warning: Failed to send IPC start message: %v\n", err)
|
|
236
|
+
} else {
|
|
237
|
+
ipcReady = true
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// If no IPC, print the port for local development
|
|
241
|
+
if ipcConn == nil {
|
|
242
|
+
fmt.Printf("Server listening on port %d (proxying to user server on port %d)\n", listenPort, userPort)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Start the proxy server
|
|
246
|
+
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
247
|
+
fmt.Fprintf(os.Stderr, "Server error: %v\n", err)
|
|
248
|
+
os.Exit(1)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Clean up
|
|
252
|
+
cmd.Process.Kill()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Ensure we close IPC connection on exit
|
|
256
|
+
func init() {
|
|
257
|
+
// Note: Using a deferred cleanup in main() is preferred,
|
|
258
|
+
// but this provides a fallback
|
|
259
|
+
}
|