34
34
import com .google .apphosting .runtime .RequestRunner .EagerRunner ;
35
35
import com .google .apphosting .runtime .ResponseAPIData ;
36
36
import com .google .apphosting .runtime .ServletEngineAdapter ;
37
+ import com .google .apphosting .runtime .anyrpc .AnyRpcServerContext ;
37
38
import com .google .common .flogger .GoogleLogger ;
38
39
import java .io .IOException ;
39
40
import java .io .PrintWriter ;
44
45
import javax .servlet .http .HttpServletRequest ;
45
46
import javax .servlet .http .HttpServletResponse ;
46
47
import org .eclipse .jetty .server .Handler ;
48
+ import org .eclipse .jetty .server .HttpChannel ;
47
49
import org .eclipse .jetty .server .Request ;
48
50
import org .eclipse .jetty .server .Server ;
51
+ import org .eclipse .jetty .server .ServerConnector ;
49
52
import org .eclipse .jetty .server .handler .HandlerWrapper ;
50
53
51
54
/**
62
65
public class JettyHttpHandler extends HandlerWrapper {
63
66
private static final GoogleLogger logger = GoogleLogger .forEnclosingClass ();
64
67
68
+ public static final String FINISH_REQUEST_ATTRIBUTE =
69
+ "com.google.apphosting.runtime.jetty9.finishRequestAttribute" ;
70
+
65
71
private final boolean passThroughPrivateHeaders ;
66
72
private final AppInfoFactory appInfoFactory ;
67
73
private final AppVersionKey appVersionKey ;
@@ -73,7 +79,8 @@ public JettyHttpHandler(
73
79
ServletEngineAdapter .Config runtimeOptions ,
74
80
AppVersion appVersion ,
75
81
AppVersionKey appVersionKey ,
76
- AppInfoFactory appInfoFactory ) {
82
+ AppInfoFactory appInfoFactory ,
83
+ ServerConnector connector ) {
77
84
this .passThroughPrivateHeaders = runtimeOptions .passThroughPrivateHeaders ();
78
85
this .appInfoFactory = appInfoFactory ;
79
86
this .appVersionKey = appVersionKey ;
@@ -82,6 +89,7 @@ public JettyHttpHandler(
82
89
ApiProxyImpl apiProxyImpl = (ApiProxyImpl ) ApiProxy .getDelegate ();
83
90
coordinator = apiProxyImpl .getBackgroundRequestCoordinator ();
84
91
requestManager = (RequestManager ) apiProxyImpl .getRequestThreadManager ();
92
+ connector .addBean (new CompletionListener ());
85
93
}
86
94
87
95
@ Override
@@ -113,6 +121,10 @@ public void handle(
113
121
ApiProxy .Environment currentEnvironment = ApiProxy .getCurrentEnvironment ();
114
122
request .setAttribute (AppEngineConstants .ENVIRONMENT_ATTR , currentEnvironment );
115
123
124
+ Runnable finishRequest =
125
+ () -> finishRequest (currentEnvironment , requestToken , genericResponse , context );
126
+ baseRequest .setAttribute (FINISH_REQUEST_ATTRIBUTE , finishRequest );
127
+
116
128
try {
117
129
dispatchRequest (target , requestToken , genericRequest , genericResponse );
118
130
if (!baseRequest .isHandled ()) {
@@ -128,17 +140,32 @@ public void handle(
128
140
handleException (ex , requestToken , genericResponse );
129
141
response .sendError (HttpServletResponse .SC_INTERNAL_SERVER_ERROR , ex .getMessage ());
130
142
} finally {
131
- requestManager .finishRequest (requestToken );
143
+ // We don't want threads used for background requests to go back
144
+ // in the thread pool, because users may have stashed references
145
+ // to them or may be expecting them to exit. Setting the
146
+ // interrupt bit causes the pool to drop them.
147
+ if (genericRequest .getRequestType () == RuntimePb .UPRequest .RequestType .BACKGROUND ) {
148
+ Thread .currentThread ().interrupt ();
149
+ }
132
150
}
133
- // Do not put this in a final block. If we propagate an
134
- // exception the callback will be invoked automatically.
135
- genericResponse .finishWithResponse (context );
136
- // We don't want threads used for background requests to go back
137
- // in the thread pool, because users may have stashed references
138
- // to them or may be expecting them to exit. Setting the
139
- // interrupt bit causes the pool to drop them.
140
- if (genericRequest .getRequestType () == RuntimePb .UPRequest .RequestType .BACKGROUND ) {
141
- Thread .currentThread ().interrupt ();
151
+ }
152
+
153
+ private void finishRequest (
154
+ ApiProxy .Environment env ,
155
+ RequestManager .RequestToken requestToken ,
156
+ JettyResponseAPIData response ,
157
+ AnyRpcServerContext context ) {
158
+
159
+ ApiProxy .Environment oldEnv = ApiProxy .getCurrentEnvironment ();
160
+ try {
161
+ ApiProxy .setEnvironmentForCurrentThread (env );
162
+ requestManager .finishRequest (requestToken );
163
+
164
+ // Do not put this in a final block. If we propagate an
165
+ // exception the callback will be invoked automatically.
166
+ response .finishWithResponse (context );
167
+ } finally {
168
+ ApiProxy .setEnvironmentForCurrentThread (oldEnv );
142
169
}
143
170
}
144
171
@@ -288,4 +315,15 @@ private String getBackgroundRequestId(JettyRequestAPIData upRequest) {
288
315
}
289
316
return backgroundRequestId ;
290
317
}
318
+
319
+ private static class CompletionListener implements HttpChannel .Listener {
320
+ @ Override
321
+ public void onComplete (Request request ) {
322
+ Runnable finishRequest =
323
+ (Runnable ) request .getAttribute (JettyHttpHandler .FINISH_REQUEST_ATTRIBUTE );
324
+ if (finishRequest != null ) {
325
+ finishRequest .run ();
326
+ }
327
+ }
328
+ }
291
329
}
0 commit comments