1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.fcgi.server.proxy;
20
21 import java.net.URI;
22 import java.util.List;
23 import java.util.TreeMap;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26 import java.util.stream.Collectors;
27
28 import javax.servlet.RequestDispatcher;
29 import javax.servlet.ServletConfig;
30 import javax.servlet.ServletException;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33
34 import org.eclipse.jetty.client.HttpClient;
35 import org.eclipse.jetty.client.api.Request;
36 import org.eclipse.jetty.fcgi.FCGI;
37 import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
38 import org.eclipse.jetty.http.HttpField;
39 import org.eclipse.jetty.http.HttpFields;
40 import org.eclipse.jetty.http.HttpHeader;
41 import org.eclipse.jetty.http.HttpScheme;
42 import org.eclipse.jetty.proxy.AsyncProxyServlet;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
70 {
71 public static final String SCRIPT_ROOT_INIT_PARAM = "scriptRoot";
72 public static final String SCRIPT_PATTERN_INIT_PARAM = "scriptPattern";
73 public static final String ORIGINAL_URI_ATTRIBUTE_INIT_PARAM = "originalURIAttribute";
74 public static final String ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM = "originalQueryAttribute";
75 public static final String FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS";
76
77 private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr";
78 private static final String REMOTE_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remotePort";
79 private static final String SERVER_NAME_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverName";
80 private static final String SERVER_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverAddr";
81 private static final String SERVER_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverPort";
82 private static final String SCHEME_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".scheme";
83 private static final String REQUEST_URI_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestURI";
84 private static final String REQUEST_QUERY_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestQuery";
85
86 private Pattern scriptPattern;
87 private String originalURIAttribute;
88 private String originalQueryAttribute;
89 private boolean fcgiHTTPS;
90
91 @Override
92 public void init() throws ServletException
93 {
94 super.init();
95
96 String value = getInitParameter(SCRIPT_PATTERN_INIT_PARAM);
97 if (value == null)
98 value = "(.+?\\.php)";
99 scriptPattern = Pattern.compile(value);
100
101 originalURIAttribute = getInitParameter(ORIGINAL_URI_ATTRIBUTE_INIT_PARAM);
102 originalQueryAttribute = getInitParameter(ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM);
103
104 fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM));
105 }
106
107 @Override
108 protected HttpClient newHttpClient()
109 {
110 ServletConfig config = getServletConfig();
111 String scriptRoot = config.getInitParameter(SCRIPT_ROOT_INIT_PARAM);
112 if (scriptRoot == null)
113 throw new IllegalArgumentException("Mandatory parameter '" + SCRIPT_ROOT_INIT_PARAM + "' not configured");
114 return new HttpClient(new ProxyHttpClientTransportOverFCGI(scriptRoot), null);
115 }
116
117 @Override
118 protected void sendProxyRequest(HttpServletRequest request, HttpServletResponse proxyResponse, Request proxyRequest)
119 {
120 proxyRequest.attribute(REMOTE_ADDR_ATTRIBUTE, request.getRemoteAddr());
121 proxyRequest.attribute(REMOTE_PORT_ATTRIBUTE, String.valueOf(request.getRemotePort()));
122 proxyRequest.attribute(SERVER_NAME_ATTRIBUTE, request.getServerName());
123 proxyRequest.attribute(SERVER_ADDR_ATTRIBUTE, request.getLocalAddr());
124 proxyRequest.attribute(SERVER_PORT_ATTRIBUTE, String.valueOf(request.getLocalPort()));
125 proxyRequest.attribute(SCHEME_ATTRIBUTE, request.getScheme());
126
127
128 String originalURI = null;
129 String originalQuery = null;
130 if (originalURIAttribute != null)
131 originalURI = (String)request.getAttribute(originalURIAttribute);
132 if (originalURI != null && originalQueryAttribute != null)
133 {
134 originalQuery = (String)request.getAttribute(originalQueryAttribute);
135 if (originalQuery != null)
136 originalURI += "?" + originalQuery;
137 }
138
139 if (originalURI == null)
140 {
141
142 String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
143 originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
144 if (originalPath == null)
145 {
146 originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
147 originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
148 }
149 if (originalPath != null)
150 {
151 originalURI = originalPath;
152 if (originalQuery != null)
153 originalURI += "?" + originalQuery;
154 }
155 }
156
157 if (originalURI != null)
158 proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI);
159 if (originalQuery != null)
160 proxyRequest.attribute(REQUEST_QUERY_ATTRIBUTE, originalQuery);
161
162
163 if (!proxyRequest.getHeaders().containsKey(HttpHeader.HOST.asString()))
164 {
165 String host = request.getServerName();
166 int port = request.getServerPort();
167 if (!getHttpClient().isDefaultPort(request.getScheme(), port))
168 host += ":" + port;
169 proxyRequest.header(HttpHeader.HOST, host);
170 proxyRequest.header(HttpHeader.X_FORWARDED_HOST, host);
171 }
172
173
174 List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE);
175 if (cookies.size() > 1)
176 {
177 StringBuilder builder = new StringBuilder();
178 for (int i = 0; i < cookies.size(); ++i)
179 {
180 if (i > 0)
181 builder.append("; ");
182 String cookie = cookies.get(i);
183 builder.append(cookie);
184 }
185 proxyRequest.header(HttpHeader.COOKIE, null);
186 proxyRequest.header(HttpHeader.COOKIE, builder.toString());
187 }
188
189 super.sendProxyRequest(request, proxyResponse, proxyRequest);
190 }
191
192 protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields fastCGIHeaders)
193 {
194 fastCGIHeaders.remove("HTTP_PROXY");
195
196 fastCGIHeaders.put(FCGI.Headers.REMOTE_ADDR, (String)proxyRequest.getAttributes().get(REMOTE_ADDR_ATTRIBUTE));
197 fastCGIHeaders.put(FCGI.Headers.REMOTE_PORT, (String)proxyRequest.getAttributes().get(REMOTE_PORT_ATTRIBUTE));
198 fastCGIHeaders.put(FCGI.Headers.SERVER_NAME, (String)proxyRequest.getAttributes().get(SERVER_NAME_ATTRIBUTE));
199 fastCGIHeaders.put(FCGI.Headers.SERVER_ADDR, (String)proxyRequest.getAttributes().get(SERVER_ADDR_ATTRIBUTE));
200 fastCGIHeaders.put(FCGI.Headers.SERVER_PORT, (String)proxyRequest.getAttributes().get(SERVER_PORT_ATTRIBUTE));
201
202 if (fcgiHTTPS || HttpScheme.HTTPS.is((String)proxyRequest.getAttributes().get(SCHEME_ATTRIBUTE)))
203 fastCGIHeaders.put(FCGI.Headers.HTTPS, "on");
204
205 URI proxyRequestURI = proxyRequest.getURI();
206 String rawPath = proxyRequestURI == null ? proxyRequest.getPath() : proxyRequestURI.getRawPath();
207 String rawQuery = proxyRequestURI == null ? null : proxyRequestURI.getRawQuery();
208
209 String requestURI = (String)proxyRequest.getAttributes().get(REQUEST_URI_ATTRIBUTE);
210 if (requestURI == null)
211 {
212 requestURI = rawPath;
213 if (rawQuery != null)
214 requestURI += "?" + rawQuery;
215 }
216 fastCGIHeaders.put(FCGI.Headers.REQUEST_URI, requestURI);
217
218 String requestQuery = (String)proxyRequest.getAttributes().get(REQUEST_QUERY_ATTRIBUTE);
219 if (requestQuery != null)
220 fastCGIHeaders.put(FCGI.Headers.QUERY_STRING, requestQuery);
221
222 String scriptName = rawPath;
223 Matcher matcher = scriptPattern.matcher(rawPath);
224 if (matcher.matches())
225 {
226
227 scriptName = matcher.group(1);
228
229
230 if (matcher.groupCount() > 1)
231 fastCGIHeaders.put(FCGI.Headers.PATH_INFO, matcher.group(2));
232 }
233 fastCGIHeaders.put(FCGI.Headers.SCRIPT_NAME, scriptName);
234
235 String root = fastCGIHeaders.get(FCGI.Headers.DOCUMENT_ROOT);
236 fastCGIHeaders.put(FCGI.Headers.SCRIPT_FILENAME, root + scriptName);
237 }
238
239 private class ProxyHttpClientTransportOverFCGI extends HttpClientTransportOverFCGI
240 {
241 public ProxyHttpClientTransportOverFCGI(String scriptRoot)
242 {
243 super(scriptRoot);
244 }
245
246 @Override
247 protected void customize(Request request, HttpFields fastCGIHeaders)
248 {
249 super.customize(request, fastCGIHeaders);
250 customizeFastCGIHeaders(request, fastCGIHeaders);
251 if (_log.isDebugEnabled())
252 {
253 TreeMap<String, String> fcgi = new TreeMap<>();
254 for (HttpField field : fastCGIHeaders)
255 fcgi.put(field.getName(), field.getValue());
256 String eol = System.lineSeparator();
257 _log.debug("FastCGI variables{}{}", eol, fcgi.entrySet().stream()
258 .map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue()))
259 .collect(Collectors.joining(eol)));
260 }
261 }
262 }
263 }