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 (streamListener != null) {
180 streamListener.onRequestHead(conn, request);
181 }
182 conn.receiveRequestEntity(request);
183 final ProtocolVersion transportVersion = request.getVersion();
184 context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1);
185 context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession());
186 context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails());
187 context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
188 this.processor.process(request, request.getEntity(), context);
189
190 this.requestHandler.handle(request, new HttpServerRequestHandler.ResponseTrigger() {
191
192 @Override
193 public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException {
194 if (responseSubmitted.get()) {
195 throw new HttpException("Response already submitted");
196 }
197 if (response.getCode() >= HttpStatus.SC_SUCCESS) {
198 throw new HttpException("Invalid intermediate response");
199 }
200 if (streamListener != null) {
201 streamListener.onResponseHead(conn, response);
202 }
203 conn.sendResponseHeader(response);
204 conn.flush();
205 }
206
207 @Override
208 public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException {
209 try {
210 final ProtocolVersion transportVersion = response.getVersion();
211 if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
212 throw new UnsupportedHttpVersionException(transportVersion);
213 }
214 ServerSupport.validateResponse(response, response.getEntity());
215 context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1);
216 context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
217 processor.process(response, response.getEntity(), context);
218
219 responseSubmitted.set(true);
220 conn.sendResponseHeader(response);
221 if (streamListener != null) {
222 streamListener.onResponseHead(conn, response);
223 }
224 if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) {
225 conn.sendResponseEntity(response);
226 }
227
228 EntityUtils.consume(request.getEntity());
229 final boolean keepAlive = connReuseStrategy.keepAlive(request, response, context);
230 if (streamListener != null) {
231 streamListener.onExchangeComplete(conn, keepAlive);
232 }
233 if (!keepAlive) {
234 conn.close();
235 }
236 conn.flush();
237 } finally {
238 response.close();
239 }
240 }
241
242 }, context);
243
244 } catch (final HttpException ex) {
245 if (responseSubmitted.get()) {
246 throw ex;
247 }
248 try (final ClassicHttpResponse errorResponse = new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR)) {
249 handleException(ex, errorResponse);
250 errorResponse.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
251 context.setAttribute(HttpCoreContext.HTTP_RESPONSE, errorResponse);
252 this.processor.process(errorResponse, errorResponse.getEntity(), context);
253
254 conn.sendResponseHeader(errorResponse);
255 if (streamListener != null) {
256 streamListener.onResponseHead(conn, errorResponse);
257 }
258 conn.sendResponseEntity(errorResponse);
259 conn.close();
260 }
261 }
262 }
263
264
265
266
267
268
269
270
271
272 protected void handleException(final HttpException ex, final ClassicHttpResponse response) {
273 response.setCode(toStatusCode(ex));
274 response.setEntity(new StringEntity(ServerSupport.toErrorMessage(ex), ContentType.TEXT_PLAIN));
275 }
276
277 protected int toStatusCode(final Exception ex) {
278 return ServerSupport.toStatusCode(ex);
279 }
280
281 }