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 context) throws IOException, HttpException {
199
200 final AtomicBoolean responseSubmitted = new AtomicBoolean(false);
201 try {
202 final ClassicHttpRequest request = conn.receiveRequestHeader();
203 if (request == null) {
204 conn.close();
205 return;
206 }
207 if (streamListener != null) {
208 streamListener.onRequestHead(conn, request);
209 }
210 conn.receiveRequestEntity(request);
211 final ProtocolVersion transportVersion = request.getVersion();
212 if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
213 throw new UnsupportedHttpVersionException(transportVersion);
214 }
215 context.setProtocolVersion(transportVersion != null ? transportVersion : http1Config.getVersion());
216 context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession());
217 context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails());
218 context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
219 this.processor.process(request, request.getEntity(), context);
220
221 this.requestHandler.handle(request, new HttpServerRequestHandler.ResponseTrigger() {
222
223 @Override
224 public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException {
225 if (responseSubmitted.get()) {
226 throw new HttpException("Response already submitted");
227 }
228 if (response.getCode() >= HttpStatus.SC_SUCCESS) {
229 throw new HttpException("Invalid intermediate response");
230 }
231 if (streamListener != null) {
232 streamListener.onResponseHead(conn, response);
233 }
234 conn.sendResponseHeader(response);
235 conn.flush();
236 }
237
238 @Override
239 public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException {
240 try {
241 final ProtocolVersion transportVersion = response.getVersion();
242 if (transportVersion != null) {
243 if (!transportVersion.lessEquals(http1Config.getVersion())) {
244 throw new UnsupportedHttpVersionException(transportVersion);
245 }
246 context.setProtocolVersion(transportVersion);
247 }
248 context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
249 processor.process(response, response.getEntity(), context);
250
251 responseSubmitted.set(true);
252 conn.sendResponseHeader(response);
253 if (streamListener != null) {
254 streamListener.onResponseHead(conn, response);
255 }
256 if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) {
257 conn.sendResponseEntity(response);
258 }
259
260 EntityUtils.consume(request.getEntity());
261 final boolean keepAlive = connReuseStrategy.keepAlive(request, response, context);
262 if (streamListener != null) {
263 streamListener.onExchangeComplete(conn, keepAlive);
264 }
265 if (!keepAlive) {
266 conn.close();
267 }
268 conn.flush();
269 } finally {
270 response.close();
271 }
272 }
273
274 }, context);
275
276 } catch (final HttpException ex) {
277 if (responseSubmitted.get()) {
278 throw ex;
279 }
280 try (final ClassicHttpResponse errorResponse = new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR)) {
281 handleException(ex, errorResponse);
282 errorResponse.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
283 context.setAttribute(HttpCoreContext.HTTP_RESPONSE, errorResponse);
284 this.processor.process(errorResponse, errorResponse.getEntity(), context);
285
286 conn.sendResponseHeader(errorResponse);
287 if (streamListener != null) {
288 streamListener.onResponseHead(conn, errorResponse);
289 }
290 conn.sendResponseEntity(errorResponse);
291 conn.close();
292 }
293 }
294 }
295
296
297
298
299
300
301
302
303
304 protected void handleException(final HttpException ex, final ClassicHttpResponse response) {
305 response.setCode(toStatusCode(ex));
306 response.setEntity(new StringEntity(ServerSupport.toErrorMessage(ex), ContentType.TEXT_PLAIN));
307 }
308
309 protected int toStatusCode(final Exception ex) {
310 return ServerSupport.toStatusCode(ex);
311 }
312
313
314
315
316
317
318
319 public static Builder builder() {
320 return new Builder();
321 }
322
323
324
325
326
327
328 public static final class Builder {
329
330 private HttpProcessor processor;
331 private HttpServerRequestHandler requestHandler;
332 private Http1Config http1Config;
333 private ConnectionReuseStrategy connReuseStrategy;
334 private Http1StreamListener streamListener;
335
336 private Builder() {}
337
338 public Builder withHttpProcessor(final HttpProcessor processor) {
339 this.processor = processor;
340 return this;
341 }
342
343 public Builder withHttpServerRequestHandler(final HttpServerRequestHandler requestHandler) {
344 this.requestHandler = requestHandler;
345 return this;
346 }
347
348 public Builder withHttp1Config(final Http1Config http1Config) {
349 this.http1Config = http1Config;
350 return this;
351 }
352
353 public Builder withConnectionReuseStrategy(final ConnectionReuseStrategy connReuseStrategy) {
354 this.connReuseStrategy = connReuseStrategy;
355 return this;
356 }
357
358 public Builder withHttp1StreamListener(final Http1StreamListener streamListener) {
359 this.streamListener = streamListener;
360 return this;
361 }
362
363
364
365
366
367
368 public HttpService build() {
369 return new HttpService(
370 processor,
371 requestHandler,
372 http1Config,
373 connReuseStrategy,
374 streamListener);
375 }
376 }
377
378 }