2020 */
2121package eu .openanalytics .containerproxy .util ;
2222
23- import java .io .IOException ;
24- import java .lang .reflect .Field ;
25- import java .net .URI ;
26- import java .nio .ByteBuffer ;
27- import java .util .HashMap ;
28- import java .util .Map ;
29- import java .util .Map .Entry ;
30- import java .util .concurrent .TimeUnit ;
31-
32- import javax .inject .Inject ;
33- import javax .servlet .ServletException ;
34- import javax .servlet .http .HttpServletRequest ;
35- import javax .servlet .http .HttpServletResponse ;
36-
37- import org .springframework .stereotype .Component ;
38-
23+ import eu .openanalytics .containerproxy .service .ProxyService ;
3924import eu .openanalytics .containerproxy .service .hearbeat .HeartbeatService ;
25+ import io .undertow .io .Sender ;
26+ import io .undertow .server .DefaultResponseListener ;
4027import io .undertow .server .HttpHandler ;
4128import io .undertow .server .HttpServerExchange ;
4229import io .undertow .server .handlers .PathHandler ;
4734import io .undertow .server .handlers .proxy .ProxyHandler ;
4835import io .undertow .servlet .handlers .ServletRequestContext ;
4936import io .undertow .util .AttachmentKey ;
37+ import io .undertow .util .Headers ;
5038import io .undertow .util .PathMatcher ;
39+ import io .undertow .util .StatusCodes ;
40+ import org .springframework .stereotype .Component ;
41+
42+ import javax .inject .Inject ;
43+ import javax .servlet .ServletException ;
44+ import javax .servlet .http .HttpServletRequest ;
45+ import javax .servlet .http .HttpServletResponse ;
46+ import java .io .IOException ;
47+ import java .lang .reflect .Field ;
48+ import java .net .URI ;
49+ import java .nio .ByteBuffer ;
50+ import java .util .HashMap ;
51+ import java .util .Map ;
52+ import java .util .Map .Entry ;
53+ import java .util .concurrent .TimeUnit ;
5154
5255/**
5356 * This component keeps track of which proxy mappings (i.e. URL endpoints) are currently registered,
@@ -58,14 +61,18 @@ public class ProxyMappingManager {
5861
5962 private static final String PROXY_INTERNAL_ENDPOINT = "/proxy_endpoint" ;
6063 private static final AttachmentKey <ProxyMappingManager > ATTACHMENT_KEY_DISPATCHER = AttachmentKey .create (ProxyMappingManager .class );
61-
64+ private static final AttachmentKey <ProxyIdAttachment > ATTACHMENT_KEY_PROXY_ID = AttachmentKey .create (ProxyIdAttachment .class );
65+
6266 private PathHandler pathHandler ;
6367
6468 private Map <String , String > mappings = new HashMap <>();
6569
6670 @ Inject
6771 private HeartbeatService heartbeatService ;
68-
72+
73+ @ Inject
74+ private ProxyService proxyService ;
75+
6976 public synchronized HttpHandler createHttpHandler (HttpHandler defaultHandler ) {
7077 if (pathHandler == null ) {
7178 pathHandler = new ProxyPathHandler (defaultHandler );
@@ -118,24 +125,51 @@ public String getProxyId(String mapping) {
118125 *
119126 * Note that clients can never access a proxy handler directly (for security reasons).
120127 * Dispatching is the only allowed method to access proxy handlers.
121- *
128+ *
129+ * @param proxyId The id of the proxy
122130 * @param mapping The target mapping to dispatch to.
123131 * @param request The request to dispatch.
124132 * @param response The response corresponding to the request.
125133 * @throws IOException If the dispatch fails for an I/O reason.
126134 * @throws ServletException If the dispatch fails for any other reason.
127135 */
128- public void dispatchAsync (String mapping , HttpServletRequest request , HttpServletResponse response ) throws IOException , ServletException {
136+ public void dispatchAsync (String proxyId , String mapping , HttpServletRequest request , HttpServletResponse response ) throws IOException , ServletException {
129137 HttpServerExchange exchange = ServletRequestContext .current ().getExchange ();
130138 exchange .putAttachment (ATTACHMENT_KEY_DISPATCHER , this );
139+ exchange .putAttachment (ATTACHMENT_KEY_PROXY_ID , new ProxyIdAttachment (proxyId ));
131140
132141 String queryString = request .getQueryString ();
133142 queryString = (queryString == null ) ? "" : "?" + queryString ;
134143 String targetPath = PROXY_INTERNAL_ENDPOINT + "/" + mapping + queryString ;
135-
144+
145+ exchange .addDefaultResponseListener (defaultResponseListener );
136146 request .startAsync ();
137147 request .getRequestDispatcher (targetPath ).forward (request , response );
138148 }
149+
150+ private final DefaultResponseListener defaultResponseListener = responseExchange -> {
151+ if (!responseExchange .isResponseChannelAvailable ()) {
152+ return false ;
153+ }
154+ if (responseExchange .getStatusCode () == StatusCodes .SERVICE_UNAVAILABLE ) {
155+ final String errorPage = "{\" status\" :\" error\" , \" message\" :\" app_crashed\" }" ;
156+ responseExchange .getResponseHeaders ().put (Headers .CONTENT_LENGTH , "" + errorPage .length ());
157+ responseExchange .getResponseHeaders ().put (Headers .CONTENT_TYPE , "application/json" );
158+ Sender sender = responseExchange .getResponseSender ();
159+ sender .send (errorPage );
160+
161+ ProxyIdAttachment proxyIdAttachment = responseExchange .getAttachment (ATTACHMENT_KEY_PROXY_ID );
162+ if (proxyIdAttachment != null ) {
163+ try {
164+ proxyService .stopCrashedProxy (proxyIdAttachment .proxyId );
165+ } catch (Throwable t ) {
166+ // ignore in order to complete request
167+ }
168+ }
169+ return true ;
170+ }
171+ return false ;
172+ };
139173
140174 private static class ProxyPathHandler extends PathHandler {
141175
@@ -161,4 +195,13 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
161195 }
162196 }
163197 }
198+
199+ private static class ProxyIdAttachment {
200+ final String proxyId ;
201+
202+ public ProxyIdAttachment (String proxyId ) {
203+ this .proxyId = proxyId ;
204+ }
205+ }
206+
164207}
0 commit comments