Skip to content

Commit ac181cc

Browse files
committed
fix crash recovery in lambda
the previous solution (921e818) only worked well locally, but seems to interact poorly with freezing
1 parent 41a861e commit ac181cc

File tree

1 file changed

+41
-22
lines changed

1 file changed

+41
-22
lines changed

http/relay/relay.go

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ type Proxy struct {
4949
// url is the active application url.
5050
url *url.URL
5151

52-
// ReverseProxy is the reverse proxy making the requests
53-
// to the user application.
52+
// ReverseProxy is the reverse proxy making the requests to the app.
5453
*httputil.ReverseProxy
54+
55+
// cmd is the current child process of the app.
56+
cmd *exec.Cmd
5557
}
5658

5759
// New proxy.
@@ -103,7 +105,7 @@ func (p *Proxy) Start() error {
103105
}
104106

105107
p.ReverseProxy = httputil.NewSingleHostReverseProxy(p.url)
106-
p.ReverseProxy.Transport = p.transport
108+
p.ReverseProxy.Transport = p
107109

108110
start := time.Now()
109111
timeout := time.Duration(p.config.Proxy.ListenTimeout) * time.Second
@@ -125,6 +127,12 @@ func (p *Proxy) Restart() error {
125127
ctx.Warn("restarting")
126128
p.restarts++
127129

130+
if p.cmd != nil {
131+
if err := p.cmd.Process.Kill(); err != nil {
132+
ctx.WithError(err).Error("killing application process")
133+
}
134+
}
135+
128136
if err := p.Start(); err != nil {
129137
return err
130138
}
@@ -133,6 +141,33 @@ func (p *Proxy) Restart() error {
133141
return nil
134142
}
135143

144+
// RoundTrip implementation.
145+
func (p *Proxy) RoundTrip(r *http.Request) (*http.Response, error) {
146+
res, err := p.transport.RoundTrip(r)
147+
148+
// temporary error
149+
if e, ok := err.(net.Error); ok && e.Temporary() {
150+
ctx.WithError(err).Warn("request temporary error")
151+
return res, err
152+
}
153+
154+
// timeout error
155+
if e, ok := err.(net.Error); ok && e.Timeout() {
156+
ctx.WithError(err).Warn("request timeout")
157+
return res, err
158+
}
159+
160+
// network error
161+
if err != nil {
162+
ctx.WithError(err).Error("request network error")
163+
if err := p.Restart(); err != nil {
164+
ctx.WithError(err).Error("restarting")
165+
}
166+
}
167+
168+
return res, err
169+
}
170+
136171
// environment returns the server env variables.
137172
func (p *Proxy) environment() []string {
138173
return []string{
@@ -156,32 +191,16 @@ func (p *Proxy) startServer() error {
156191
p.url = target
157192

158193
ctx.WithField("command", p.config.Proxy.Command).WithField("PORT", port).Info("starting app")
159-
cmd := p.command(p.config.Proxy.Command, p.environment())
160-
if err := cmd.Start(); err != nil {
194+
p.cmd = p.command(p.config.Proxy.Command, p.environment())
195+
196+
if err := p.cmd.Start(); err != nil {
161197
return errors.Wrap(err, "running command")
162198
}
163199

164-
go p.handleExit(cmd)
165200
ctx.Info("started app")
166-
167201
return nil
168202
}
169203

170-
// handleExit handles the exit of the application process.
171-
func (p *Proxy) handleExit(cmd *exec.Cmd) {
172-
err := cmd.Wait()
173-
174-
if err == nil {
175-
ctx.Error("app process exited")
176-
} else {
177-
ctx.WithError(err).Error("app process crashed")
178-
}
179-
180-
if err := p.Restart(); err != nil {
181-
ctx.WithError(err).Error("failed to restart")
182-
}
183-
}
184-
185204
// command returns the command for spawning a server.
186205
func (p *Proxy) command(s string, env []string) *exec.Cmd {
187206
cmd := exec.Command("sh", "-c", s)

0 commit comments

Comments
 (0)