1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.esigate;
17
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22 import java.util.Properties;
23
24 import org.apache.commons.io.output.StringBuilderWriter;
25 import org.apache.commons.lang3.tuple.ImmutablePair;
26 import org.apache.commons.lang3.tuple.Pair;
27 import org.apache.http.HttpEntity;
28 import org.apache.http.HttpStatus;
29 import org.apache.http.ProtocolException;
30 import org.apache.http.client.methods.CloseableHttpResponse;
31 import org.apache.http.entity.ContentType;
32 import org.apache.http.entity.StringEntity;
33 import org.apache.http.message.BasicHttpResponse;
34 import org.apache.http.util.EntityUtils;
35 import org.esigate.RequestExecutor.RequestExecutorBuilder;
36 import org.esigate.api.RedirectStrategy2;
37 import org.esigate.events.EventManager;
38 import org.esigate.events.impl.ProxyEvent;
39 import org.esigate.events.impl.RenderEvent;
40 import org.esigate.extension.ExtensionFactory;
41 import org.esigate.http.BasicCloseableHttpResponse;
42 import org.esigate.http.ContentTypeHelper;
43 import org.esigate.http.HeaderManager;
44 import org.esigate.http.HttpClientRequestExecutor;
45 import org.esigate.http.HttpResponseUtils;
46 import org.esigate.http.IncomingRequest;
47 import org.esigate.http.OutgoingRequest;
48 import org.esigate.http.ResourceUtils;
49 import org.esigate.impl.DriverRequest;
50 import org.esigate.impl.FragmentRedirectStrategy;
51 import org.esigate.impl.UrlRewriter;
52 import org.esigate.vars.VariablesResolver;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56
57
58
59
60
61
62
63
64
65 public final class Driver {
66 private static final String CACHE_RESPONSE_PREFIX = "response_";
67 private static final Logger LOG = LoggerFactory.getLogger(Driver.class);
68 private static final int MAX_REDIRECTS = 50;
69 private DriverConfiguration config;
70 private EventManager eventManager;
71 private RequestExecutor requestExecutor;
72 private ContentTypeHelper contentTypeHelper;
73 private UrlRewriter urlRewriter;
74 private HeaderManager headerManager;
75 private final RedirectStrategy2 redirectStrategy = new FragmentRedirectStrategy();
76
77 public static class DriverBuilder {
78 private Driver driver = new Driver();
79 private String name;
80 private Properties properties;
81 private RequestExecutorBuilder requestExecutorBuilder;
82
83 public Driver build() {
84 if (name == null) {
85 throw new ConfigurationException("name is mandatory");
86 }
87 if (properties == null) {
88 throw new ConfigurationException("properties is mandatory");
89 }
90 if (requestExecutorBuilder == null) {
91 requestExecutorBuilder = HttpClientRequestExecutor.builder();
92 }
93 driver.eventManager = new EventManager(name);
94 driver.config = new DriverConfiguration(name, properties);
95 driver.contentTypeHelper = new ContentTypeHelper(properties);
96
97 ExtensionFactory.getExtensions(properties, Parameters.EXTENSIONS, driver);
98 UrlRewriter urlRewriter = new UrlRewriter();
99 driver.requestExecutor =
100 requestExecutorBuilder.setDriver(driver).setEventManager(driver.eventManager)
101 .setProperties(properties).setContentTypeHelper(driver.contentTypeHelper).build();
102 driver.urlRewriter = urlRewriter;
103 driver.headerManager = new HeaderManager(urlRewriter);
104
105 return driver;
106 }
107
108 public DriverBuilder setName(String n) {
109 this.name = n;
110 return this;
111 }
112
113 public DriverBuilder setProperties(Properties p) {
114 this.properties = p;
115 return this;
116 }
117
118 public DriverBuilder setRequestExecutorBuilder(RequestExecutorBuilder builder) {
119 this.requestExecutorBuilder = builder;
120 return this;
121 }
122
123 }
124
125 protected Driver() {
126 }
127
128 public static DriverBuilder builder() {
129 return new DriverBuilder();
130 }
131
132
133
134
135
136
137 public EventManager getEventManager() {
138 return this.eventManager;
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 public CloseableHttpResponse render(String pageUrl, IncomingRequest incomingRequest, Renderer... renderers)
157 throws IOException, HttpErrorPage {
158 DriverRequest driverRequest = new DriverRequest(incomingRequest, this, pageUrl);
159
160
161
162 String resultingPageUrl = VariablesResolver.replaceAllVariables(pageUrl, driverRequest);
163
164 String targetUrl = ResourceUtils.getHttpUrlWithQueryString(resultingPageUrl, driverRequest, false);
165
166 String currentValue;
167 CloseableHttpResponse response;
168
169
170
171
172
173 String cacheKey = CACHE_RESPONSE_PREFIX + targetUrl;
174 Pair<String, CloseableHttpResponse> cachedValue = incomingRequest.getAttribute(cacheKey);
175
176
177 if (cachedValue == null) {
178 OutgoingRequest outgoingRequest = requestExecutor.createOutgoingRequest(driverRequest, targetUrl, false);
179 headerManager.copyHeaders(driverRequest, outgoingRequest);
180 response = requestExecutor.execute(outgoingRequest);
181 int redirects = MAX_REDIRECTS;
182 try {
183 while (redirects > 0
184 && this.redirectStrategy.isRedirected(outgoingRequest, response, outgoingRequest.getContext())) {
185
186
187 EntityUtils.consumeQuietly(response.getEntity());
188
189 redirects--;
190
191
192 outgoingRequest =
193 this.requestExecutor.createOutgoingRequest(
194 driverRequest,
195 this.redirectStrategy.getLocationURI(outgoingRequest, response,
196 outgoingRequest.getContext()).toString(), false);
197 this.headerManager.copyHeaders(driverRequest, outgoingRequest);
198 response = requestExecutor.execute(outgoingRequest);
199 }
200 } catch (ProtocolException e) {
201 throw new HttpErrorPage(HttpStatus.SC_BAD_GATEWAY, "Invalid response from server", e);
202 }
203 response = this.headerManager.copyHeaders(outgoingRequest, incomingRequest, response);
204 currentValue = HttpResponseUtils.toString(response, this.eventManager);
205
206 cachedValue = new ImmutablePair<>(currentValue, response);
207 incomingRequest.setAttribute(cacheKey, cachedValue);
208 }
209 currentValue = cachedValue.getKey();
210 response = cachedValue.getValue();
211
212 logAction("render", pageUrl, renderers);
213
214
215 currentValue = performRendering(pageUrl, driverRequest, response, currentValue, renderers);
216
217 response.setEntity(new StringEntity(currentValue, HttpResponseUtils.getContentType(response)));
218
219 return response;
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243 private void logAction(String action, String onUrl, Renderer[] renderers) {
244 if (LOG.isInfoEnabled()) {
245 List<String> rendererNames = new ArrayList<>(renderers.length);
246 for (Renderer renderer : renderers) {
247 rendererNames.add(renderer.getClass().getName());
248 }
249 LOG.info("{} provider={} page= {} renderers={}", action, this.config.getInstanceName(), onUrl,
250 rendererNames);
251 }
252 }
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269 public CloseableHttpResponse proxy(String relUrl, IncomingRequest incomingRequest, Renderer... renderers)
270 throws IOException, HttpErrorPage {
271 DriverRequest driverRequest = new DriverRequest(incomingRequest, this, relUrl);
272 driverRequest.setCharacterEncoding(this.config.getUriEncoding());
273
274
275
276
277
278
279 boolean postProxyPerformed = false;
280
281
282 ProxyEvent e = new ProxyEvent(incomingRequest);
283
284
285 this.eventManager.fire(EventManager.EVENT_PROXY_PRE, e);
286
287 if (e.isExit()) {
288 return e.getResponse();
289 }
290
291 logAction("proxy", relUrl, renderers);
292
293 String url = ResourceUtils.getHttpUrlWithQueryString(relUrl, driverRequest, true);
294 OutgoingRequest outgoingRequest = requestExecutor.createOutgoingRequest(driverRequest, url, true);
295 headerManager.copyHeaders(driverRequest, outgoingRequest);
296
297 try {
298 CloseableHttpResponse response = requestExecutor.execute(outgoingRequest);
299
300 response = headerManager.copyHeaders(outgoingRequest, incomingRequest, response);
301
302 e.setResponse(response);
303
304
305 e.setResponse(performRendering(relUrl, driverRequest, e.getResponse(), renderers));
306
307
308
309
310 postProxyPerformed = true;
311 this.eventManager.fire(EventManager.EVENT_PROXY_POST, e);
312
313
314 return e.getResponse();
315
316 } catch (HttpErrorPage errorPage) {
317 e.setErrorPage(errorPage);
318
319
320
321 CloseableHttpResponse response = e.getErrorPage().getHttpResponse();
322 response = headerManager.copyHeaders(outgoingRequest, incomingRequest, response);
323 e.setErrorPage(new HttpErrorPage(performRendering(relUrl, driverRequest, response, renderers)));
324
325
326
327
328 postProxyPerformed = true;
329 this.eventManager.fire(EventManager.EVENT_PROXY_POST, e);
330
331 throw e.getErrorPage();
332 } finally {
333 if (!postProxyPerformed) {
334 this.eventManager.fire(EventManager.EVENT_PROXY_POST, e);
335 }
336 }
337 }
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356 private CloseableHttpResponse performRendering(String pageUrl, DriverRequest originalRequest,
357 CloseableHttpResponse response, Renderer[] renderers) throws HttpErrorPage, IOException {
358
359 if (!contentTypeHelper.isTextContentType(response)) {
360 LOG.debug("'{}' is binary on no transformation to apply: was forwarded without modification.", pageUrl);
361 return response;
362 }
363
364 LOG.debug("'{}' is text : will apply renderers.", pageUrl);
365
366
367 String currentValue = HttpResponseUtils.toString(response, this.eventManager);
368
369
370 currentValue = performRendering(pageUrl, originalRequest, response, currentValue, renderers);
371
372
373 HttpEntity transformedHttpEntity = new StringEntity(currentValue, ContentType.get(response.getEntity()));
374 CloseableHttpResponse transformedResponse =
375 BasicCloseableHttpResponse.adapt(new BasicHttpResponse(response.getStatusLine()));
376 transformedResponse.setHeaders(response.getAllHeaders());
377 transformedResponse.setEntity(transformedHttpEntity);
378 return transformedResponse;
379
380 }
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 private String performRendering(String pageUrl, DriverRequest originalRequest, CloseableHttpResponse response,
400 String body, Renderer[] renderers) throws IOException, HttpErrorPage {
401
402 RenderEvent renderEvent = new RenderEvent(pageUrl, originalRequest, response);
403
404 renderEvent.getRenderers().addAll(Arrays.asList(renderers));
405
406 String currentBody = body;
407
408 this.eventManager.fire(EventManager.EVENT_RENDER_PRE, renderEvent);
409 for (Renderer renderer : renderEvent.getRenderers()) {
410 StringBuilderWriter stringWriter = new StringBuilderWriter(Parameters.DEFAULT_BUFFER_SIZE);
411 renderer.render(originalRequest, currentBody, stringWriter);
412 stringWriter.close();
413 currentBody = stringWriter.toString();
414 }
415 this.eventManager.fire(EventManager.EVENT_RENDER_POST, renderEvent);
416
417 return currentBody;
418 }
419
420
421
422
423
424
425
426
427
428
429
430 public DriverConfiguration getConfiguration() {
431 return this.config;
432 }
433
434 public RequestExecutor getRequestExecutor() {
435 return requestExecutor;
436 }
437
438 @Override
439 public String toString() {
440 return "driver:" + config.getInstanceName();
441 }
442
443 public ContentTypeHelper getContentTypeHelper() {
444 return contentTypeHelper;
445 }
446
447 public UrlRewriter getUrlRewriter() {
448 return urlRewriter;
449 }
450
451 }