diff options
Diffstat (limited to 'jetty-fcgi/fcgi-server/src/main/java/org')
5 files changed, 174 insertions, 92 deletions
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java index 62e3825d7c..58b48b0de5 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java @@ -18,41 +18,41 @@ package org.eclipse.jetty.fcgi.server; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.fcgi.FCGI; +import org.eclipse.jetty.http.HostPortHttpField; import org.eclipse.jetty.http.HttpField; -import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpInput; import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer> +public class HttpChannelOverFCGI extends HttpChannel { private static final Logger LOG = Log.getLogger(HttpChannelOverFCGI.class); - private final List<HttpField> fields = new ArrayList<>(); + private final HttpFields fields = new HttpFields(); private final Dispatcher dispatcher; private String method; private String path; private String query; private String version; + private HostPortHttpField hostPort; - public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input) + public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport) { - super(connector, configuration, endPoint, transport, input); + super(connector, configuration, endPoint, transport); this.dispatcher = new Dispatcher(connector.getExecutor(), this); } @@ -67,26 +67,27 @@ public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer> else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(field.getName())) version = field.getValue(); else - fields.add(field); + processField(field); } - @Override - public boolean headerComplete() + private void processField(HttpField field) { - String uri = path; - if (query != null && query.length() > 0) - uri += "?" + query; - startRequest(HttpMethod.fromString(method), method, ByteBuffer.wrap(uri.getBytes(StandardCharsets.UTF_8)), - HttpVersion.fromString(version)); - - for (HttpField fcgiField : fields) + HttpField httpField = convertHeader(field); + if (httpField != null) { - HttpField httpField = convertHeader(fcgiField); - if (httpField != null) - parsedHeader(httpField); + fields.add(httpField); + if (HttpHeader.HOST.is(httpField.getName())) + hostPort = (HostPortHttpField)httpField; } + } - return super.headerComplete(); + public void onRequest() + { + String uri = path; + if (query != null && query.length() > 0) + uri += "?" + query; + // TODO https? + onRequest(new MetaData.Request(method, HttpScheme.HTTP.asString(), hostPort, uri, HttpVersion.fromString(version), fields,Long.MIN_VALUE)); } private HttpField convertHeader(HttpField field) @@ -105,7 +106,11 @@ public class HttpChannelOverFCGI extends HttpChannel<ByteBuffer> httpName.append(Character.toUpperCase(part.charAt(0))); httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH)); } - return new HttpField(httpName.toString(), field.getValue()); + String headerName = httpName.toString(); + if (HttpHeader.HOST.is(headerName)) + return new HostPortHttpField(field.getValue()); + else + return new HttpField(httpName.toString(), field.getValue()); } return null; } diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java index 24210d0028..39bc2ead1e 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java @@ -23,9 +23,9 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.fcgi.generator.Flusher; import org.eclipse.jetty.fcgi.generator.Generator; import org.eclipse.jetty.fcgi.generator.ServerGenerator; -import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; +import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.util.BufferUtil; @@ -48,65 +48,87 @@ public class HttpTransportOverFCGI implements HttpTransport } @Override - public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback) + public boolean isOptimizedForDirectBuffers() { - boolean head = this.head = info.isHead(); - boolean shutdown = this.shutdown = info.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); - - if (head) + return false; + } + + @Override + public void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback) + { + if (info!=null) + commit(info,head,content,lastContent,callback); + else { - if (lastContent) + if (head) { - Generator.Result headersResult = generateResponseHeaders(info, Callback.Adapter.INSTANCE); - Generator.Result contentResult = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback); - flusher.flush(headersResult, contentResult); + if (lastContent) + { + Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback); + flusher.flush(result); + } + else + { + // Skip content generation + callback.succeeded(); + } } else { - Generator.Result headersResult = generateResponseHeaders(info, callback); - flusher.flush(headersResult); + Generator.Result result = generateResponseContent(content, lastContent, callback); + flusher.flush(result); } - } - else - { - Generator.Result headersResult = generateResponseHeaders(info, Callback.Adapter.INSTANCE); - Generator.Result contentResult = generateResponseContent(content, lastContent, callback); - flusher.flush(headersResult, contentResult); - } - if (lastContent && shutdown) - flusher.shutdown(); + if (lastContent && shutdown) + flusher.shutdown(); + } } @Override - public void send(ByteBuffer content, boolean lastContent, Callback callback) + public boolean isPushSupported() + { + return false; + } + + @Override + public void push(org.eclipse.jetty.http.MetaData.Request request) + { + // LOG.debug("ignore push in {}",this); + } + + private void commit(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback) { + this.head = head; + boolean shutdown = this.shutdown = info.getFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); + if (head) { if (lastContent) { - Generator.Result result = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback); - flusher.flush(result); + Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP); + Generator.Result contentResult = generateResponseContent(BufferUtil.EMPTY_BUFFER, true, callback); + flusher.flush(headersResult, contentResult); } else { - // Skip content generation - callback.succeeded(); + Generator.Result headersResult = generateResponseHeaders(info, callback); + flusher.flush(headersResult); } } else { - Generator.Result result = generateResponseContent(content, lastContent, callback); - flusher.flush(result); + Generator.Result headersResult = generateResponseHeaders(info, Callback.NOOP); + Generator.Result contentResult = generateResponseContent(content, lastContent, callback); + flusher.flush(headersResult, contentResult); } if (lastContent && shutdown) flusher.shutdown(); } - protected Generator.Result generateResponseHeaders(HttpGenerator.ResponseInfo info, Callback callback) + protected Generator.Result generateResponseHeaders(MetaData.Response info, Callback callback) { - return generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getHttpFields(), callback); + return generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getFields(), callback); } protected Generator.Result generateResponseContent(ByteBuffer buffer, boolean lastContent, Callback callback) @@ -115,13 +137,13 @@ public class HttpTransportOverFCGI implements HttpTransport } @Override - public void abort() + public void abort(Throwable failure) { aborted = true; } @Override - public void completed() + public void onCompleted() { } } diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java index 9809dd91c8..2b87a91ca9 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java @@ -29,9 +29,9 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.server.ByteBufferQueuedHttpInput; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpInput; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -82,11 +82,13 @@ public class ServerFCGIConnection extends AbstractConnection } else if (read == 0) { + bufferPool.release(buffer); fillInterested(); break; } else { + bufferPool.release(buffer); shutdown(); break; } @@ -96,11 +98,8 @@ public class ServerFCGIConnection extends AbstractConnection { if (LOG.isDebugEnabled()) LOG.debug(x); - // TODO: fail and close ? - } - finally - { bufferPool.release(buffer); + // TODO: fail and close ? } } @@ -122,8 +121,7 @@ public class ServerFCGIConnection extends AbstractConnection { // TODO: handle flags HttpChannelOverFCGI channel = new HttpChannelOverFCGI(connector, configuration, getEndPoint(), - new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200), - new ByteBufferQueuedHttpInput()); + new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200)); HttpChannelOverFCGI existing = channels.putIfAbsent(request, channel); if (existing != null) throw new IllegalStateException(); @@ -149,8 +147,8 @@ public class ServerFCGIConnection extends AbstractConnection LOG.debug("Request {} headers on {}", request, channel); if (channel != null) { - if (channel.headerComplete()) - channel.dispatch(); + channel.onRequest(); + channel.dispatch(); } } @@ -162,8 +160,9 @@ public class ServerFCGIConnection extends AbstractConnection LOG.debug("Request {} {} content {} on {}", request, stream, buffer, channel); if (channel != null) { - if (channel.content(buffer)) - channel.dispatch(); + ByteBuffer copy = ByteBuffer.allocate(buffer.remaining()); + copy.put(buffer).flip(); + channel.onContent(new HttpInput.Content(copy)); } return false; } @@ -176,8 +175,7 @@ public class ServerFCGIConnection extends AbstractConnection LOG.debug("Request {} end on {}", request, channel); if (channel != null) { - if (channel.messageComplete()) - channel.dispatch(); + channel.onRequestComplete(); } } @@ -189,7 +187,7 @@ public class ServerFCGIConnection extends AbstractConnection LOG.debug("Request {} failure on {}: {}", request, channel, failure); if (channel != null) { - channel.badMessage(400, failure.toString()); + channel.onBadMessage(400, failure.toString()); } } } diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java index b725ccfb9b..8587042304 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java @@ -19,29 +19,35 @@ package org.eclipse.jetty.fcgi.server.proxy; import java.net.URI; +import java.util.List; +import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; + import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI; +import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.proxy.AsyncProxyServlet; -import org.eclipse.jetty.proxy.ProxyServlet; /** - * Specific implementation of {@link ProxyServlet.Transparent} for FastCGI. - * <p /> + * Specific implementation of {@link org.eclipse.jetty.proxy.AsyncProxyServlet.Transparent} for FastCGI. + * <p> * This servlet accepts a HTTP request and transforms it into a FastCGI request * that is sent to the FastCGI server specified in the <code>proxyTo</code> * init-param. - * <p /> + * <p> * This servlet accepts two additional init-params: * <ul> * <li><code>scriptRoot</code>, mandatory, that must be set to the directory where @@ -64,6 +70,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent { public static final String SCRIPT_ROOT_INIT_PARAM = "scriptRoot"; public static final String SCRIPT_PATTERN_INIT_PARAM = "scriptPattern"; + public static final String ORIGINAL_URI_ATTRIBUTE_INIT_PARAM = "originalURIAttribute"; public static final String FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS"; private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr"; @@ -75,6 +82,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent private static final String REQUEST_URI_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestURI"; private Pattern scriptPattern; + private String originalURIAttribute; private boolean fcgiHTTPS; @Override @@ -87,6 +95,8 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent value = "(.+?\\.php)"; scriptPattern = Pattern.compile(value); + originalURIAttribute = getInitParameter(ORIGINAL_URI_ATTRIBUTE_INIT_PARAM); + fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM)); } @@ -101,33 +111,69 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent } @Override - protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request) + protected void sendProxyRequest(HttpServletRequest request, HttpServletResponse proxyResponse, Request proxyRequest) { proxyRequest.attribute(REMOTE_ADDR_ATTRIBUTE, request.getRemoteAddr()); proxyRequest.attribute(REMOTE_PORT_ATTRIBUTE, String.valueOf(request.getRemotePort())); proxyRequest.attribute(SERVER_NAME_ATTRIBUTE, request.getServerName()); proxyRequest.attribute(SERVER_ADDR_ATTRIBUTE, request.getLocalAddr()); proxyRequest.attribute(SERVER_PORT_ATTRIBUTE, String.valueOf(request.getLocalPort())); - proxyRequest.attribute(SCHEME_ATTRIBUTE, request.getScheme()); - // If we are forwarded or included, retain the original request URI. - String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); - String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING); - if (originalPath == null) + // Has the original URI been rewritten ? + String originalURI = null; + if (originalURIAttribute != null) + originalURI = (String)request.getAttribute(originalURIAttribute); + + if (originalURI == null) { - originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); - originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING); + // If we are forwarded or included, retain the original request URI. + String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); + String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING); + if (originalPath == null) + { + originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); + originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING); + } + if (originalPath != null) + { + originalURI = originalPath; + if (originalQuery != null) + originalURI += "?" + originalQuery; + } } - if (originalPath != null) - { - String originalURI = originalPath; - if (originalQuery != null) - originalURI += "?" + originalQuery; + + if (originalURI != null) proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI); + + // If the Host header is missing, add it. + if (!proxyRequest.getHeaders().containsKey(HttpHeader.HOST.asString())) + { + String host = request.getServerName(); + int port = request.getServerPort(); + if (!getHttpClient().isDefaultPort(request.getScheme(), port)) + host += ":" + port; + proxyRequest.header(HttpHeader.HOST, host); + proxyRequest.header(HttpHeader.X_FORWARDED_HOST, host); + } + + // PHP does not like multiple Cookie headers, coalesce into one. + List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE.asString()); + if (cookies.size() > 1) + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < cookies.size(); ++i) + { + if (i > 0) + builder.append("; "); + String cookie = cookies.get(i); + builder.append(cookie); + } + proxyRequest.header(HttpHeader.COOKIE, null); + proxyRequest.header(HttpHeader.COOKIE, builder.toString()); } - super.customizeProxyRequest(proxyRequest, request); + super.sendProxyRequest(request, proxyResponse, proxyRequest); } protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields fastCGIHeaders) @@ -183,6 +229,16 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent { super.customize(request, fastCGIHeaders); customizeFastCGIHeaders(request, fastCGIHeaders); + if (_log.isDebugEnabled()) + { + TreeMap<String, String> fcgi = new TreeMap<>(); + for (HttpField field : fastCGIHeaders) + fcgi.put(field.getName(), field.getValue()); + String eol = System.lineSeparator(); + _log.debug("FastCGI variables{}{}", eol, fcgi.entrySet().stream() + .map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue())) + .collect(Collectors.joining(eol))); + } } } } diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java index 5cdfa1df89..715d65cfc1 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java @@ -24,6 +24,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; + import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -36,11 +37,11 @@ import javax.servlet.http.HttpServletResponse; /** * Inspired by nginx's try_files functionality. - * <p /> + * <p> * This filter accepts the <code>files</code> init-param as a list of space-separated * file URIs. The special token <code>$path</code> represents the current request URL's * path (the portion after the context path). - * <p /> + * <p> * Typical example of how this filter can be configured is the following: * <pre> * <filter> @@ -58,7 +59,7 @@ import javax.servlet.http.HttpServletResponse; * failing that it will forward the request to <code>index.php?p=/path/to/resource.ext</code>. * The last file URI specified in the list is therefore the "fallback" to which the request * is forwarded to in case no previous files can be found. - * <p /> + * <p> * The files are resolved using {@link ServletContext#getResource(String)} to make sure * that only files visible to the application are served. * |