diff --git a/fcgi-core/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java b/fcgi-core/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java index 30e2427a3b9..f25b7ba7603 100644 --- a/fcgi-core/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java +++ b/fcgi-core/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java @@ -45,6 +45,12 @@ public class ResponseContentParser extends StreamContentParser this.listener = listener; } + @Override + public void noContent() + { + // Does nothing, since for responses the end of content is signaled via a FCGI_END_REQUEST frame + } + @Override protected void onContent(ByteBuffer buffer) { diff --git a/fcgi-core/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java b/fcgi-core/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java index bd2f5abf763..6aae78333db 100644 --- a/fcgi-core/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java +++ b/fcgi-core/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java @@ -40,15 +40,15 @@ public class ClientParserTest final int id = 13; HttpFields fields = new HttpFields(); - final String statusName = "Status"; - final int code = 200; + final int statusCode = 200; + final String statusMessage = "OK"; final String contentTypeName = "Content-Type"; final String contentTypeValue = "text/html;charset=utf-8"; fields.put(contentTypeName, contentTypeValue); ByteBufferPool byteBufferPool = new MappedByteBufferPool(); ServerGenerator generator = new ServerGenerator(byteBufferPool); - Generator.Result result = generator.generateResponseHeaders(id, code, "OK", fields, null); + Generator.Result result = generator.generateResponseHeaders(id, statusCode, statusMessage, fields, null); // Use the fundamental theorem of arithmetic to test the results. // This way we know onHeader() has been called the right number of @@ -61,20 +61,26 @@ public class ClientParserTest final AtomicInteger params = new AtomicInteger(1); ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter() { + @Override + public void onBegin(int request, int code, String reason) + { + Assert.assertEquals(statusCode, code); + Assert.assertEquals(statusMessage, reason); + params.set(params.get() * primes[0]); + } + @Override public void onHeader(int request, HttpField field) { Assert.assertEquals(id, request); switch (field.getName()) { - case statusName: - Assert.assertTrue(field.getValue().startsWith(String.valueOf(code))); - params.set(params.get() * primes[0]); - break; case contentTypeName: Assert.assertEquals(contentTypeValue, field.getValue()); params.set(params.get() * primes[1]); break; + default: + break; } } @@ -100,15 +106,11 @@ public class ClientParserTest { final int id = 13; HttpFields fields = new HttpFields(); - - final int code = 200; - final String contentTypeName = "Content-Length"; - final String contentTypeValue = "0"; - fields.put(contentTypeName, contentTypeValue); + fields.put("Content-Length", "0"); ByteBufferPool byteBufferPool = new MappedByteBufferPool(); ServerGenerator generator = new ServerGenerator(byteBufferPool); - Generator.Result result1 = generator.generateResponseHeaders(id, code, "OK", fields, null); + Generator.Result result1 = generator.generateResponseHeaders(id, 200, "OK", fields, null); Generator.Result result2 = generator.generateResponseContent(id, null, true, null); final AtomicInteger verifier = new AtomicInteger(); diff --git a/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index 75a9d3d3639..f0f800ba548 100644 --- a/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -136,9 +136,12 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec private void shutdown() { + // First close then abort, to be sure that the + // connection cannot be reused from an onFailure() + // handler or by blocking code waiting for completion. + close(); for (HttpChannelOverFCGI channel : channels.values()) channel.abort(new EOFException()); - close(); } @Override diff --git a/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java b/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java index 086a71b0825..9d16d5c3eca 100644 --- a/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java +++ b/fcgi-http-client-transport/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java @@ -45,9 +45,9 @@ public class HttpSenderOverFCGI extends HttpSender // FastCGI headers based on the URI URI uri = request.getURI(); - String path = uri.getPath(); + String path = uri.getRawPath(); fcgiHeaders.put(FCGI.Headers.REQUEST_URI, path); - String query = uri.getQuery(); + String query = uri.getRawQuery(); fcgiHeaders.put(FCGI.Headers.QUERY_STRING, query == null ? "" : query); int lastSegment = path.lastIndexOf('/'); String scriptName = lastSegment < 0 ? path : path.substring(lastSegment); diff --git a/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java b/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java index 1f9c060ef7e..dfdcc75b72c 100644 --- a/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java +++ b/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpChannelOverFCGI.java @@ -19,7 +19,7 @@ package org.eclipse.jetty.fcgi.server; import java.nio.ByteBuffer; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; @@ -42,12 +43,12 @@ public class HttpChannelOverFCGI extends HttpChannel { private static final Logger LOG = Log.getLogger(HttpChannelOverFCGI.class); + private final List fields = new ArrayList<>(); private final Dispatcher dispatcher; private String method; - private String uri; + private String path; + private String query; private String version; - private boolean started; - private List fields; public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput input) { @@ -58,57 +59,37 @@ public class HttpChannelOverFCGI extends HttpChannel protected void header(HttpField field) { if (FCGI.Headers.REQUEST_METHOD.equalsIgnoreCase(field.getName())) - { method = field.getValue(); - if (uri != null && version != null) - startRequest(); - } else if (FCGI.Headers.REQUEST_URI.equalsIgnoreCase(field.getName())) - { - uri = field.getValue(); - if (method != null && version != null) - startRequest(); - } + path = field.getValue(); + else if (FCGI.Headers.QUERY_STRING.equalsIgnoreCase(field.getName())) + query = field.getValue(); else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(field.getName())) - { version = field.getValue(); - if (method != null && uri != null) - startRequest(); - } else - { - if (started) - { - resumeHeaders(); - convertHeader(field); - } - else - { - if (fields == null) - fields = new ArrayList<>(); - fields.add(field); - } - } + fields.add(field); } - private void startRequest() + @Override + public boolean headerComplete() { - started = true; - startRequest(null, method, ByteBuffer.wrap(uri.getBytes(Charset.forName("UTF-8"))), HttpVersion.fromString(version)); - resumeHeaders(); - } + 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)); - private void resumeHeaders() - { - if (fields != null) + for (HttpField fcgiField : fields) { - for (HttpField field : fields) - convertHeader(field); - fields = null; + HttpField httpField = convertHeader(fcgiField); + if (httpField != null) + parsedHeader(httpField); } + + return super.headerComplete(); } - private void convertHeader(HttpField field) + private HttpField convertHeader(HttpField field) { String name = field.getName(); if (name.startsWith("HTTP_")) @@ -124,17 +105,9 @@ public class HttpChannelOverFCGI extends HttpChannel httpName.append(Character.toUpperCase(part.charAt(0))); httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH)); } - field = new HttpField(httpName.toString(), field.getValue()); + return new HttpField(httpName.toString(), field.getValue()); } - parsedHeader(field); - } - - @Override - public boolean headerComplete() - { - boolean result = super.headerComplete(); - started = false; - return result; + return null; } protected void dispatch() diff --git a/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java index 26166fa8715..c12d351e668 100644 --- a/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java +++ b/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.fcgi.generator.ServerGenerator; import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.HttpTransport; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; public class HttpTransportOverFCGI implements HttpTransport @@ -33,6 +34,7 @@ public class HttpTransportOverFCGI implements HttpTransport private final ServerGenerator generator; private final Flusher flusher; private final int request; + private volatile boolean head; public HttpTransportOverFCGI(ByteBufferPool byteBufferPool, Flusher flusher, int request) { @@ -44,17 +46,53 @@ public class HttpTransportOverFCGI implements HttpTransport @Override public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback) { - Generator.Result headersResult = generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), - info.getHttpFields(), new Callback.Adapter()); - Generator.Result contentResult = generator.generateResponseContent(request, content, lastContent, callback); - flusher.flush(headersResult, contentResult); + boolean head = this.head = info.isHead(); + if (head) + { + if (lastContent) + { + Generator.Result headersResult = generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), + info.getHttpFields(), new Callback.Adapter()); + Generator.Result contentResult = generator.generateResponseContent(request, BufferUtil.EMPTY_BUFFER, lastContent, callback); + flusher.flush(headersResult, contentResult); + } + else + { + Generator.Result headersResult = generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), + info.getHttpFields(), callback); + flusher.flush(headersResult); + } + } + else + { + Generator.Result headersResult = generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), + info.getHttpFields(), new Callback.Adapter()); + Generator.Result contentResult = generator.generateResponseContent(request, content, lastContent, callback); + flusher.flush(headersResult, contentResult); + } } @Override public void send(ByteBuffer content, boolean lastContent, Callback callback) { - Generator.Result result = generator.generateResponseContent(request, content, lastContent, callback); - flusher.flush(result); + if (head) + { + if (lastContent) + { + Generator.Result result = generator.generateResponseContent(request, BufferUtil.EMPTY_BUFFER, lastContent, callback); + flusher.flush(result); + } + else + { + // Skip content generation + callback.succeeded(); + } + } + else + { + Generator.Result result = generator.generateResponseContent(request, content, lastContent, callback); + flusher.flush(result); + } } @Override