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 FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS";
75
76 private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr";
77 private static final String REMOTE_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remotePort";
78 private static final String SERVER_NAME_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverName";
79 private static final String SERVER_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverAddr";
80 private static final String SERVER_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverPort";
81 private static final String SCHEME_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".scheme";
82 private static final String REQUEST_URI_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestURI";
83
84 private Pattern scriptPattern;
85 private String originalURIAttribute;
86 private boolean fcgiHTTPS;
87
88 @Override
89 public void init() throws ServletException
90 {
91 super.init();
92
93 String value = getInitParameter(SCRIPT_PATTERN_INIT_PARAM);
94 if (value == null)
95 value = "(.+?\\.php)";
96 scriptPattern = Pattern.compile(value);
97
98 originalURIAttribute = getInitParameter(ORIGINAL_URI_ATTRIBUTE_INIT_PARAM);
99
100 fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM));
101 }
102
103 @Override
104 protected HttpClient newHttpClient()
105 {
106 ServletConfig config = getServletConfig();
107 String scriptRoot = config.getInitParameter(SCRIPT_ROOT_INIT_PARAM);
108 if (scriptRoot == null)
109 throw new IllegalArgumentException("Mandatory parameter '" + SCRIPT_ROOT_INIT_PARAM + "' not configured");
110 return new HttpClient(new ProxyHttpClientTransportOverFCGI(scriptRoot), null);
111 }
112
113 @Override
114 protected void sendProxyRequest(HttpServletRequest request, HttpServletResponse proxyResponse, Request proxyRequest)
115 {
116 proxyRequest.attribute(REMOTE_ADDR_ATTRIBUTE, request.getRemoteAddr());
117 proxyRequest.attribute(REMOTE_PORT_ATTRIBUTE, String.valueOf(request.getRemotePort()));
118 proxyRequest.attribute(SERVER_NAME_ATTRIBUTE, request.getServerName());
119 proxyRequest.attribute(SERVER_ADDR_ATTRIBUTE, request.getLocalAddr());
120 proxyRequest.attribute(SERVER_PORT_ATTRIBUTE, String.valueOf(request.getLocalPort()));
121 proxyRequest.attribute(SCHEME_ATTRIBUTE, request.getScheme());
122
123
124 String originalURI = null;
125 if (originalURIAttribute != null)
126 originalURI = (String)request.getAttribute(originalURIAttribute);
127
128 if (originalURI == null)
129 {
130
131 String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
132 String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
133 if (originalPath == null)
134 {
135 originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
136 originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
137 }
138 if (originalPath != null)
139 {
140 originalURI = originalPath;
141 if (originalQuery != null)
142 originalURI += "?" + originalQuery;
143 }
144 }
145
146 if (originalURI != null)
147 proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI);
148
149
150 if (!proxyRequest.getHeaders().containsKey(HttpHeader.HOST.asString()))
151 {
152 String host = request.getServerName();
153 int port = request.getServerPort();
154 if (!getHttpClient().isDefaultPort(request.getScheme(), port))
155 host += ":" + port;
156 proxyRequest.header(HttpHeader.HOST, host);
157 proxyRequest.header(HttpHeader.X_FORWARDED_HOST, host);
158 }
159
160
161 List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE.asString());
162 if (cookies.size() > 1)
163 {
164 StringBuilder builder = new StringBuilder();
165 for (int i = 0; i < cookies.size(); ++i)
166 {
167 if (i > 0)
168 builder.append("; ");
169 String cookie = cookies.get(i);
170 builder.append(cookie);
171 }
172 proxyRequest.header(HttpHeader.COOKIE, null);
173 proxyRequest.header(HttpHeader.COOKIE, builder.toString());
174 }
175
176 super.sendProxyRequest(request, proxyResponse, proxyRequest);
177 }
178
179 protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields fastCGIHeaders)
180 {
181 fastCGIHeaders.put(FCGI.Headers.REMOTE_ADDR, (String)proxyRequest.getAttributes().get(REMOTE_ADDR_ATTRIBUTE));
182 fastCGIHeaders.put(FCGI.Headers.REMOTE_PORT, (String)proxyRequest.getAttributes().get(REMOTE_PORT_ATTRIBUTE));
183 fastCGIHeaders.put(FCGI.Headers.SERVER_NAME, (String)proxyRequest.getAttributes().get(SERVER_NAME_ATTRIBUTE));
184 fastCGIHeaders.put(FCGI.Headers.SERVER_ADDR, (String)proxyRequest.getAttributes().get(SERVER_ADDR_ATTRIBUTE));
185 fastCGIHeaders.put(FCGI.Headers.SERVER_PORT, (String)proxyRequest.getAttributes().get(SERVER_PORT_ATTRIBUTE));
186
187 if (fcgiHTTPS || HttpScheme.HTTPS.is((String)proxyRequest.getAttributes().get(SCHEME_ATTRIBUTE)))
188 fastCGIHeaders.put(FCGI.Headers.HTTPS, "on");
189
190 URI proxyRequestURI = proxyRequest.getURI();
191 String rawPath = proxyRequestURI.getRawPath();
192 String rawQuery = proxyRequestURI.getRawQuery();
193
194 String requestURI = (String)proxyRequest.getAttributes().get(REQUEST_URI_ATTRIBUTE);
195 if (requestURI == null)
196 {
197 requestURI = rawPath;
198 if (rawQuery != null)
199 requestURI += "?" + rawQuery;
200 }
201 fastCGIHeaders.put(FCGI.Headers.REQUEST_URI, requestURI);
202
203 String scriptName = rawPath;
204 Matcher matcher = scriptPattern.matcher(rawPath);
205 if (matcher.matches())
206 {
207
208 scriptName = matcher.group(1);
209
210
211 if (matcher.groupCount() > 1)
212 fastCGIHeaders.put(FCGI.Headers.PATH_INFO, matcher.group(2));
213 }
214 fastCGIHeaders.put(FCGI.Headers.SCRIPT_NAME, scriptName);
215
216 String root = fastCGIHeaders.get(FCGI.Headers.DOCUMENT_ROOT);
217 fastCGIHeaders.put(FCGI.Headers.SCRIPT_FILENAME, root + scriptName);
218 }
219
220 private class ProxyHttpClientTransportOverFCGI extends HttpClientTransportOverFCGI
221 {
222 public ProxyHttpClientTransportOverFCGI(String scriptRoot)
223 {
224 super(scriptRoot);
225 }
226
227 @Override
228 protected void customize(Request request, HttpFields fastCGIHeaders)
229 {
230 super.customize(request, fastCGIHeaders);
231 customizeFastCGIHeaders(request, fastCGIHeaders);
232 if (_log.isDebugEnabled())
233 {
234 TreeMap<String, String> fcgi = new TreeMap<>();
235 for (HttpField field : fastCGIHeaders)
236 fcgi.put(field.getName(), field.getValue());
237 String eol = System.lineSeparator();
238 _log.debug("FastCGI variables{}{}", eol, fcgi.entrySet().stream()
239 .map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue()))
240 .collect(Collectors.joining(eol)));
241 }
242 }
243 }
244 }