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