@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.
Files changed (3) hide show
  1. package/dist/index.js +11280 -11392
  2. package/package.json +3 -4
  3. 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.4",
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": "6.2.1",
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.14"
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
+ }