@@ -49,9 +49,11 @@ type Proxy struct {
49
49
// url is the active application url.
50
50
url * url.URL
51
51
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.
54
53
* httputil.ReverseProxy
54
+
55
+ // cmd is the current child process of the app.
56
+ cmd * exec.Cmd
55
57
}
56
58
57
59
// New proxy.
@@ -103,7 +105,7 @@ func (p *Proxy) Start() error {
103
105
}
104
106
105
107
p .ReverseProxy = httputil .NewSingleHostReverseProxy (p .url )
106
- p .ReverseProxy .Transport = p . transport
108
+ p .ReverseProxy .Transport = p
107
109
108
110
start := time .Now ()
109
111
timeout := time .Duration (p .config .Proxy .ListenTimeout ) * time .Second
@@ -125,6 +127,12 @@ func (p *Proxy) Restart() error {
125
127
ctx .Warn ("restarting" )
126
128
p .restarts ++
127
129
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
+
128
136
if err := p .Start (); err != nil {
129
137
return err
130
138
}
@@ -133,6 +141,33 @@ func (p *Proxy) Restart() error {
133
141
return nil
134
142
}
135
143
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
+
136
171
// environment returns the server env variables.
137
172
func (p * Proxy ) environment () []string {
138
173
return []string {
@@ -156,32 +191,16 @@ func (p *Proxy) startServer() error {
156
191
p .url = target
157
192
158
193
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 {
161
197
return errors .Wrap (err , "running command" )
162
198
}
163
199
164
- go p .handleExit (cmd )
165
200
ctx .Info ("started app" )
166
-
167
201
return nil
168
202
}
169
203
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
-
185
204
// command returns the command for spawning a server.
186
205
func (p * Proxy ) command (s string , env []string ) * exec.Cmd {
187
206
cmd := exec .Command ("sh" , "-c" , s )
0 commit comments