1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.http.impl.io;
29
30 import java.io.IOException;
31 import java.util.concurrent.atomic.AtomicBoolean;
32
33 import org.apache.hc.core5.annotation.Contract;
34 import org.apache.hc.core5.annotation.ThreadingBehavior;
35 import org.apache.hc.core5.http.ClassicHttpRequest;
36 import org.apache.hc.core5.http.ClassicHttpResponse;
37 import org.apache.hc.core5.http.ConnectionReuseStrategy;
38 import org.apache.hc.core5.http.ContentType;
39 import org.apache.hc.core5.http.HeaderElements;
40 import org.apache.hc.core5.http.HttpException;
41 import org.apache.hc.core5.http.HttpHeaders;
42 import org.apache.hc.core5.http.HttpRequestMapper;
43 import org.apache.hc.core5.http.HttpResponseFactory;
44 import org.apache.hc.core5.http.HttpStatus;
45 import org.apache.hc.core5.http.HttpVersion;
46 import org.apache.hc.core5.http.ProtocolVersion;
47 import org.apache.hc.core5.http.UnsupportedHttpVersionException;
48 import org.apache.hc.core5.http.config.Http1Config;
49 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
50 import org.apache.hc.core5.http.impl.Http1StreamListener;
51 import org.apache.hc.core5.http.impl.ServerSupport;
52 import org.apache.hc.core5.http.io.HttpRequestHandler;
53 import org.apache.hc.core5.http.io.HttpServerConnection;
54 import org.apache.hc.core5.http.io.HttpServerRequestHandler;
55 import org.apache.hc.core5.http.io.entity.EntityUtils;
56 import org.apache.hc.core5.http.io.entity.StringEntity;
57 import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator;
58 import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler;
59 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
60 import org.apache.hc.core5.http.message.MessageSupport;
61 import org.apache.hc.core5.http.protocol.HttpContext;
62 import org.apache.hc.core5.http.protocol.HttpCoreContext;
63 import org.apache.hc.core5.http.protocol.HttpProcessor;
64 import org.apache.hc.core5.util.Args;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
83 public class HttpService {
84
85 private final HttpProcessor processor;
86 private final Http1Config http1Config;
87 private final HttpServerRequestHandler requestHandler;
88 private final ConnectionReuseStrategy connReuseStrategy;
89 private final Http1StreamListener streamListener;
90
91
92
93
94
95
96
97
98
99
100
101 public HttpService(
102 final HttpProcessor processor,
103 final HttpRequestMapper<HttpRequestHandler> handlerMapper,
104 final ConnectionReuseStrategy connReuseStrategy,
105 final HttpResponseFactory<ClassicHttpResponse> responseFactory,
106 final Http1StreamListener streamListener) {
107 this(processor,
108 new BasicHttpServerExpectationDecorator(new BasicHttpServerRequestHandler(handlerMapper, responseFactory)),
109 Http1Config.DEFAULT,
110 connReuseStrategy,
111 streamListener);
112 }
113
114
115
116
117
118
119
120
121
122
123
124 public HttpService(
125 final HttpProcessor processor,
126 final HttpRequestMapper<HttpRequestHandler> handlerMapper,
127 final ConnectionReuseStrategy connReuseStrategy,
128 final HttpResponseFactory<ClassicHttpResponse> responseFactory) {
129 this(processor, handlerMapper, connReuseStrategy, responseFactory, null);
130 }
131
132
133
134
135
136
137
138
139
140
141 public HttpService(
142 final HttpProcessor processor,
143 final HttpServerRequestHandler requestHandler,
144 final ConnectionReuseStrategy connReuseStrategy,
145 final Http1StreamListener streamListener) {
146 this(processor, requestHandler, Http1Config.DEFAULT, connReuseStrategy, streamListener);
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160
161 public HttpService(
162 final HttpProcessor processor,
163 final HttpServerRequestHandler requestHandler,
164 final Http1Config http1Config,
165 final ConnectionReuseStrategy connReuseStrategy,
166 final Http1StreamListener streamListener) {
167 super();
168 this.processor = Args.notNull(processor, "HTTP processor");
169 this.requestHandler = Args.notNull(requestHandler, "Request handler");
170 this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT;
171 this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE;
172 this.streamListener = streamListener;
173 }
174
175
176
177
178
179
180
181 public HttpService(
182 final HttpProcessor processor, final HttpServerRequestHandler requestHandler) {
183 this(processor, requestHandler, Http1Config.DEFAULT, null, null);
184 }
185
186
187
188
189
190
191
192
193
194
195
196 public void handleRequest(
197 final HttpServerConnection conn,
198 final HttpContext localContext) throws IOException, HttpException {
199
200 final AtomicBoolean responseSubmitted = new AtomicBoolean(false);
201 final HttpCoreContext context = HttpCoreContext.cast(localContext);
202 try {
203 final ClassicHttpRequest request = conn.receiveRequestHeader();
204 if (request == null) {
205 conn.close();
206 return;
207 }
208 if (streamListener != null) {
209 streamListener.onRequestHead(conn, request);
210 }
211 conn.receiveRequestEntity(request);
212 final ProtocolVersion transportVersion = request.getVersion();
213 if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
214 throw new UnsupportedHttpVersionException(transportVersion);
215 }
216 context.setProtocolVersion(transportVersion != null ? transportVersion : http1Config.getVersion());
217 context.setRequest(request);
218 context.setSSLSession(conn.getSSLSession());
219 context.setEndpointDetails(conn.getEndpointDetails());
220 this.processor.process(request, request.getEntity(), context);
221
222 this.requestHandler.handle(request, new HttpServerRequestHandler.ResponseTrigger() {
223
224 @Override
225 public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException {
226 if (responseSubmitted.get()) {
227 throw new HttpException("Response already submitted");
228 }
229 if (response.getCode() >= HttpStatus.SC_SUCCESS) {
230 throw new HttpException("Invalid intermediate response");
231 }
232 if (streamListener != null) {
233 streamListener.onResponseHead(conn, response);
234 }
235 conn.sendResponseHeader(response);
236 conn.flush();
237 }
238
239 @Override
240 public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException {
241 try {
242 final ProtocolVersion transportVersion = response.getVersion();
243 if (transportVersion != null) {
244 if (!transportVersion.lessEquals(http1Config.getVersion())) {
245 throw new UnsupportedHttpVersionException(transportVersion);
246 }
247 context.setProtocolVersion(transportVersion);
248 }
249 context.setResponse(response);
250 processor.process(response, response.getEntity(), context);
251
252 responseSubmitted.set(true);
253 conn.sendResponseHeader(response);
254 if (streamListener != null) {
255 streamListener.onResponseHead(conn, response);
256 }
257 if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) {
258 conn.sendResponseEntity(response);
259 }
260
261 EntityUtils.consume(request.getEntity());
262 final boolean keepAlive = connReuseStrategy.keepAlive(request, response, context);
263 if (streamListener != null) {
264 streamListener.onExchangeComplete(conn, keepAlive);
265 }
266 if (!keepAlive) {
267 conn.close();
268 }
269 conn.flush();
270 } finally {
271 response.close();
272 }
273 }
274
275 }, context);
276
277 } catch (final HttpException ex) {
278 if (responseSubmitted.get()) {
279 throw ex;
280 }
281 try (final ClassicHttpResponse errorResponse = new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR)) {
282 handleException(ex, errorResponse);
283 errorResponse.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
284 context.setResponse(errorResponse);
285 this.processor.process(errorResponse, errorResponse.getEntity(), context);
286
287 conn.sendResponseHeader(errorResponse);
288 if (streamListener != null) {
289 streamListener.onResponseHead(conn, errorResponse);
290 }
291 conn.sendResponseEntity(errorResponse);
292 conn.close();
293 }
294 }
295 }
296
297
298
299
300
301
302
303
304
305 protected void handleException(final HttpException ex, final ClassicHttpResponse response) {
306 response.setCode(toStatusCode(ex));
307 response.setEntity(new StringEntity(ServerSupport.toErrorMessage(ex), ContentType.TEXT_PLAIN));
308 }
309
310 protected int toStatusCode(final Exception ex) {
311 return ServerSupport.toStatusCode(ex);
312 }
313
314
315
316
317
318
319
320 public static Builder builder() {
321 return new Builder();
322 }
323
324
325
326
327
328
329 public static final class Builder {
330
331 private HttpProcessor processor;
332 private HttpServerRequestHandler requestHandler;
333 private Http1Config http1Config;
334 private ConnectionReuseStrategy connReuseStrategy;
335 private Http1StreamListener streamListener;
336
337 private Builder() {}
338
339 public Builder withHttpProcessor(final HttpProcessor processor) {
340 this.processor = processor;
341 return this;
342 }
343
344 public Builder withHttpServerRequestHandler(final HttpServerRequestHandler requestHandler) {
345 this.requestHandler = requestHandler;
346 return this;
347 }
348
349 public Builder withHttp1Config(final Http1Config http1Config) {
350 this.http1Config = http1Config;
351 return this;
352 }
353
354 public Builder withConnectionReuseStrategy(final ConnectionReuseStrategy connReuseStrategy) {
355 this.connReuseStrategy = connReuseStrategy;
356 return this;
357 }
358
359 public Builder withHttp1StreamListener(final Http1StreamListener streamListener) {
360 this.streamListener = streamListener;
361 return this;
362 }
363
364
365
366
367
368
369 public HttpService build() {
370 return new HttpService(
371 processor,
372 requestHandler,
373 http1Config,
374 connReuseStrategy,
375 streamListener);
376 }
377 }
378
379 }