@vercel/go 3.4.6 → 3.4.7

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/package.json +2 -2
  2. package/vc-init.go +48 -11
  3. package/vc-utils.go +32 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/go",
3
- "version": "3.4.6",
3
+ "version": "3.4.7",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -32,7 +32,7 @@
32
32
  "typescript": "4.9.5",
33
33
  "xdg-app-paths": "5.1.0",
34
34
  "yauzl-promise": "2.1.3",
35
- "@vercel/build-utils": "13.8.2"
35
+ "@vercel/build-utils": "13.12.1"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "node ../../utils/build-builder.mjs",
package/vc-init.go CHANGED
@@ -14,6 +14,7 @@ package main
14
14
  import (
15
15
  "context"
16
16
  "encoding/json"
17
+ "errors"
17
18
  "fmt"
18
19
  "net"
19
20
  "net/http"
@@ -53,6 +54,16 @@ type RequestContext struct {
53
54
  RequestID uint64 `json:"requestId"`
54
55
  }
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
+
56
67
  var (
57
68
  ipcConn net.Conn
58
69
  ipcMutex sync.Mutex
@@ -78,6 +89,19 @@ func sendIPCMessage(msg interface{}) error {
78
89
  return err
79
90
  }
80
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
+
81
105
  func connectIPC() error {
82
106
  ipcPath := os.Getenv("VERCEL_IPC_PATH")
83
107
  if ipcPath == "" {
@@ -106,15 +130,13 @@ func main() {
106
130
  // Find a free port for the user's server
107
131
  userPort, err := findFreePort()
108
132
  if err != nil {
109
- fmt.Fprintf(os.Stderr, "Failed to find free port: %v\n", err)
110
- os.Exit(1)
133
+ fatal(1, fmt.Sprintf("Failed to find free port: %v", err))
111
134
  }
112
135
 
113
136
  // Start the user's server binary
114
137
  userBinary := "./user-server"
115
138
  if _, err := os.Stat(userBinary); os.IsNotExist(err) {
116
- fmt.Fprintf(os.Stderr, "User server binary not found: %s\n", userBinary)
117
- os.Exit(1)
139
+ fatal(1, fmt.Sprintf("User server binary not found: %s", userBinary))
118
140
  }
119
141
 
120
142
  ctx, cancel := context.WithCancel(context.Background())
@@ -126,15 +148,30 @@ func main() {
126
148
  cmd.Stderr = os.Stderr
127
149
 
128
150
  if err := cmd.Start(); err != nil {
129
- fmt.Fprintf(os.Stderr, "Failed to start user server: %v\n", err)
130
- os.Exit(1)
151
+ fatal(1, fmt.Sprintf("Failed to start user server: %v", err))
131
152
  }
132
153
 
133
- // Wait for user's server to be ready
134
- if err := waitForServer(userPort, 30*time.Second); err != nil {
135
- fmt.Fprintf(os.Stderr, "User server failed to start: %v\n", err)
136
- cmd.Process.Kill()
137
- os.Exit(1)
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
+ }
138
175
  }
139
176
 
140
177
  // Create reverse proxy to user's server
package/vc-utils.go CHANGED
@@ -3,7 +3,6 @@ package main
3
3
  import (
4
4
  "fmt"
5
5
  "net"
6
- "net/http"
7
6
  "os"
8
7
  "strings"
9
8
  "time"
@@ -20,19 +19,46 @@ func findFreePort() (int, error) {
20
19
  }
21
20
 
22
21
  func waitForServer(port int, timeout time.Duration) error {
22
+ const (
23
+ dialTimeout = 200 * time.Millisecond
24
+ pollInterval = 50 * time.Millisecond
25
+ )
26
+
27
+ addr := fmt.Sprintf("127.0.0.1:%d", port)
23
28
  deadline := time.Now().Add(timeout)
24
- url := fmt.Sprintf("http://127.0.0.1:%d/", port)
29
+ var lastErr error
25
30
 
26
31
  for time.Now().Before(deadline) {
27
- resp, err := http.Get(url)
32
+ remaining := time.Until(deadline)
33
+ if remaining <= 0 {
34
+ break
35
+ }
36
+
37
+ attemptTimeout := dialTimeout
38
+ if remaining < attemptTimeout {
39
+ attemptTimeout = remaining
40
+ }
41
+
42
+ conn, err := net.DialTimeout("tcp", addr, attemptTimeout)
28
43
  if err == nil {
29
- resp.Body.Close()
44
+ conn.Close()
30
45
  return nil
31
46
  }
32
- time.Sleep(50 * time.Millisecond)
47
+
48
+ lastErr = err
49
+ time.Sleep(pollInterval)
50
+ }
51
+
52
+ if lastErr != nil {
53
+ return fmt.Errorf(
54
+ "server did not start listening on %s within %v: %w",
55
+ addr,
56
+ timeout,
57
+ lastErr,
58
+ )
33
59
  }
34
60
 
35
- return fmt.Errorf("server did not start within %v", timeout)
61
+ return fmt.Errorf("server did not start listening on %s within %v", addr, timeout)
36
62
  }
37
63
 
38
64
  func normalizeServiceRoutePrefix(rawPrefix string) string {