Implemented response content handling.

Now the implementation can successfully make requests to PHP-FPM and
receive the responses.
This commit is contained in:
Simone Bordet 2013-10-18 19:13:07 +02:00
parent 67a287e5cf
commit 642f8c1ee1
12 changed files with 370 additions and 197 deletions

View File

@ -111,16 +111,17 @@ public class FCGI
public static final String AUTH_TYPE = "AUTH_TYPE";
public static final String CONTENT_LENGTH = "CONTENT_LENGTH";
public static final String CONTENT_TYPE = "CONTENT_TYPE";
public static final String DOCUMENT_ROOT = "DOCUMENT_ROOT";
public static final String GATEWAY_INTERFACE = "GATEWAY_INTERFACE";
public static final String PATH_INFO = "PATH_INFO";
public static final String PATH_TRANSLATED = "PATH_TRANSLATED";
public static final String QUERY_STRING = "QUERY_STRING";
public static final String REMOTE_ADDR = "REMOTE_ADDR";
public static final String REMOTE_HOST = "REMOTE_HOST";
public static final String REMOTE_USER = "REMOTE_USER";
public static final String REMOTE_PORT = "REMOTE_PORT";
public static final String REQUEST_METHOD = "REQUEST_METHOD";
public static final String REQUEST_URI = "REQUEST_URI";
public static final String SCRIPT_FILENAME = "SCRIPT_FILENAME";
public static final String SCRIPT_NAME = "SCRIPT_NAME";
public static final String SERVER_ADDR = "SERVER_ADDR";
public static final String SERVER_NAME = "SERVER_NAME";
public static final String SERVER_PORT = "SERVER_PORT";
public static final String SERVER_PROTOCOL = "SERVER_PROTOCOL";

View File

@ -18,9 +18,11 @@
package org.eclipse.jetty.fcgi.parser;
import java.nio.ByteBuffer;
import java.util.EnumMap;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.http.HttpField;
public class ClientParser extends Parser
{
@ -28,9 +30,11 @@ public class ClientParser extends Parser
public ClientParser(Listener listener)
{
contentParsers.put(FCGI.FrameType.STDOUT, new ResponseContentParser(headerParser, listener));
contentParsers.put(FCGI.FrameType.STDERR, new StreamContentParser(headerParser, FCGI.StreamType.STD_ERR, listener));
contentParsers.put(FCGI.FrameType.END_REQUEST, new EndRequestContentParser(headerParser, listener));
ResponseContentParser stdOutParser = new ResponseContentParser(headerParser, listener);
contentParsers.put(FCGI.FrameType.STDOUT, stdOutParser);
StreamContentParser stdErrParser = new StreamContentParser(headerParser, FCGI.StreamType.STD_ERR, listener);
contentParsers.put(FCGI.FrameType.STDERR, stdErrParser);
contentParsers.put(FCGI.FrameType.END_REQUEST, new EndRequestContentParser(headerParser, new EndRequestListener(listener, stdOutParser, stdErrParser)));
}
@Override
@ -51,4 +55,48 @@ public class ClientParser extends Parser
}
}
}
private class EndRequestListener implements Listener
{
private final Listener listener;
private final StreamContentParser[] streamParsers;
private EndRequestListener(Listener listener, StreamContentParser... streamParsers)
{
this.listener = listener;
this.streamParsers = streamParsers;
}
@Override
public void onBegin(int request, int code, String reason)
{
listener.onBegin(request, code, reason);
}
@Override
public void onHeader(int request, HttpField field)
{
listener.onHeader(request, field);
}
@Override
public void onHeaders(int request)
{
listener.onHeaders(request);
}
@Override
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
{
listener.onContent(request, stream, buffer);
}
@Override
public void onEnd(int request)
{
listener.onEnd(request);
for (StreamContentParser streamParser : streamParsers)
streamParser.end(request);
}
}
}

View File

@ -19,11 +19,13 @@
package org.eclipse.jetty.fcgi.parser;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
@ -34,46 +36,89 @@ public class ResponseContentParser extends StreamContentParser
{
private static final Logger LOG = Log.getLogger(ResponseContentParser.class);
private final Map<Integer, ResponseParser> parsers = new ConcurrentHashMap<>();
private final ClientParser.Listener listener;
public ResponseContentParser(HeaderParser headerParser, ClientParser.Listener listener)
{
super(headerParser, FCGI.StreamType.STD_OUT, new ResponseListener(headerParser, listener));
super(headerParser, FCGI.StreamType.STD_OUT, listener);
this.listener = listener;
}
private static class ResponseListener extends Parser.Listener.Adapter implements HttpParser.ResponseHandler<ByteBuffer>
@Override
protected void onContent(ByteBuffer buffer)
{
private final HeaderParser headerParser;
private final ClientParser.Listener listener;
int request = getRequest();
ResponseParser parser = parsers.get(request);
if (parser == null)
{
parser = new ResponseParser(listener, request);
parsers.put(request, parser);
}
parser.parse(buffer);
}
@Override
protected void end(int request)
{
super.end(request);
parsers.remove(request);
}
private class ResponseParser implements HttpParser.ResponseHandler<ByteBuffer>
{
private final HttpFields fields = new HttpFields();
private ClientParser.Listener listener;
private final int request;
private final FCGIHttpParser httpParser;
private State state = State.HEADERS;
private boolean begun;
private List<HttpField> fields;
private boolean seenResponseCode;
public ResponseListener(HeaderParser headerParser, ClientParser.Listener listener)
private ResponseParser(ClientParser.Listener listener, int request)
{
this.headerParser = headerParser;
this.listener = listener;
this.request = request;
this.httpParser = new FCGIHttpParser(this);
}
@Override
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
public void parse(ByteBuffer buffer)
{
LOG.debug("Request {} {} content {} {}", request, stream, state, buffer);
LOG.debug("Response {} {} content {} {}", request, FCGI.StreamType.STD_OUT, state, buffer);
while (buffer.hasRemaining())
int remaining = buffer.remaining();
while (remaining > 0)
{
switch (state)
{
case HEADERS:
{
if (httpParser.parseHeaders(buffer))
state = State.CONTENT;
state = State.CONTENT_MODE;
remaining = buffer.remaining();
break;
}
case CONTENT:
case CONTENT_MODE:
{
if (httpParser.parseContent(buffer))
reset();
// If we have no indication of the content, then
// the HTTP parser will assume there is no content
// and will not parse it even if it is provided,
// so we have to parse it raw ourselves here.
boolean rawContent = fields.size() == 0 ||
(fields.get(HttpHeader.CONTENT_LENGTH) == null &&
fields.get(HttpHeader.TRANSFER_ENCODING) == null);
state = rawContent ? State.RAW_CONTENT : State.HTTP_CONTENT;
break;
}
case RAW_CONTENT:
{
notifyContent(buffer);
remaining = 0;
break;
}
case HTTP_CONTENT:
{
httpParser.parseContent(buffer);
remaining = buffer.remaining();
break;
}
default:
@ -84,22 +129,6 @@ public class ResponseContentParser extends StreamContentParser
}
}
private void reset()
{
httpParser.reset();
state = State.HEADERS;
begun = false;
fields = null;
}
@Override
public void onEnd(int request)
{
// We are a STD_OUT stream so the end of the request is
// signaled by a END_REQUEST. Here we just reset the state.
reset();
}
@Override
public int getHeaderCacheSize()
{
@ -119,41 +148,30 @@ public class ResponseContentParser extends StreamContentParser
{
try
{
if ("Status".equalsIgnoreCase(httpField.getName()))
String name = httpField.getName();
if ("Status".equalsIgnoreCase(name))
{
if (!begun)
if (!seenResponseCode)
{
begun = true;
seenResponseCode = true;
// Need to set the response status so the
// HttpParser can handle the content properly.
String[] parts = httpField.getValue().split(" ");
int code = Integer.parseInt(parts[0]);
httpParser.setResponseStatus(code);
String reason = parts.length > 1 ? parts[1] : HttpStatus.getMessage(code);
listener.onBegin(headerParser.getRequest(), code, reason);
if (fields != null)
{
for (HttpField field : fields)
listener.onHeader(headerParser.getRequest(), field);
fields = null;
}
notifyBegin(code, reason);
notifyHeaders(fields);
}
}
else
{
if (begun)
{
listener.onHeader(headerParser.getRequest(), httpField);
}
if (seenResponseCode)
notifyHeader(httpField);
else
{
if (fields == null)
fields = new ArrayList<>();
fields.add(httpField);
}
}
}
catch (Throwable x)
@ -162,48 +180,89 @@ public class ResponseContentParser extends StreamContentParser
}
return false;
}
@Override
public boolean headerComplete()
private void notifyBegin(int code, String reason)
{
try
{
if (begun)
{
listener.onHeaders(headerParser.getRequest());
}
else
{
fields = null;
// TODO: what here ?
}
listener.onBegin(request, code, reason);
}
catch (Throwable x)
{
logger.debug("Exception while invoking listener " + listener, x);
}
}
private void notifyHeader(HttpField httpField)
{
try
{
listener.onHeader(request, httpField);
}
catch (Throwable x)
{
logger.debug("Exception while invoking listener " + listener, x);
}
}
private void notifyHeaders(HttpFields fields)
{
if (fields != null)
{
for (HttpField field : fields)
notifyHeader(field);
}
}
private void notifyHeaders()
{
try
{
listener.onHeaders(request);
}
catch (Throwable x)
{
logger.debug("Exception while invoking listener " + listener, x);
}
}
@Override
public boolean headerComplete()
{
if (!seenResponseCode)
{
// No Status header but we have other headers, assume 200 OK
notifyBegin(200, "OK");
notifyHeaders(fields);
}
notifyHeaders();
// Return from parsing so that we can parse the content
return true;
}
@Override
public boolean content(ByteBuffer buffer)
{
notifyContent(buffer);
return false;
}
private void notifyContent(ByteBuffer buffer)
{
try
{
listener.onContent(headerParser.getRequest(), FCGI.StreamType.STD_OUT, buffer);
listener.onContent(request, FCGI.StreamType.STD_OUT, buffer);
}
catch (Throwable x)
{
logger.debug("Exception while invoking listener " + listener, x);
}
return false;
}
@Override
public boolean messageComplete()
{
// Return from parsing so that we can parse the next headers
// Return from parsing so that we can parse the next headers or the raw content.
// No need to notify the listener because it will be done by FCGI_END_REQUEST.
return true;
}
@ -218,45 +277,45 @@ public class ResponseContentParser extends StreamContentParser
{
// TODO
}
}
// Methods overridden to make them visible here
private static class FCGIHttpParser extends HttpParser
// Methods overridden to make them visible here
private static class FCGIHttpParser extends HttpParser
{
private FCGIHttpParser(ResponseHandler<ByteBuffer> handler)
{
private FCGIHttpParser(ResponseHandler<ByteBuffer> handler)
{
super(handler, 65 * 1024, true);
reset();
}
@Override
public void reset()
{
super.reset();
setState(State.HEADER);
}
@Override
protected boolean parseHeaders(ByteBuffer buffer)
{
return super.parseHeaders(buffer);
}
@Override
protected boolean parseContent(ByteBuffer buffer)
{
return super.parseContent(buffer);
}
@Override
protected void setResponseStatus(int status)
{
super.setResponseStatus(status);
}
super(handler, 65 * 1024, true);
reset();
}
private enum State
@Override
public void reset()
{
HEADERS, CONTENT
super.reset();
setState(State.HEADER);
}
@Override
protected boolean parseHeaders(ByteBuffer buffer)
{
return super.parseHeaders(buffer);
}
@Override
protected boolean parseContent(ByteBuffer buffer)
{
return super.parseContent(buffer);
}
@Override
protected void setResponseStatus(int status)
{
super.setResponseStatus(status);
}
}
private enum State
{
HEADERS, CONTENT_MODE, RAW_CONTENT, HTTP_CONTENT
}
}

View File

@ -80,7 +80,14 @@ public class StreamContentParser extends ContentParser
@Override
public void noContent()
{
onEnd();
try
{
listener.onEnd(getRequest());
}
catch (Throwable x)
{
logger.debug("Exception while invoking listener " + listener, x);
}
}
protected void onContent(ByteBuffer buffer)
@ -95,16 +102,8 @@ public class StreamContentParser extends ContentParser
}
}
protected void onEnd()
protected void end(int request)
{
try
{
listener.onEnd(getRequest());
}
catch (Throwable x)
{
logger.debug("Exception while invoking listener " + listener, x);
}
}
private enum State

View File

@ -154,21 +154,21 @@ public class ClientGeneratorTest
final int id = 13;
Generator.Result result = generator.generateRequestContent(id, content, true, null);
final AtomicInteger length = new AtomicInteger();
final AtomicInteger totalLength = new AtomicInteger();
ServerParser parser = new ServerParser(new ServerParser.Listener.Adapter()
{
@Override
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
{
Assert.assertEquals(id, request);
length.addAndGet(buffer.remaining());
totalLength.addAndGet(buffer.remaining());
}
@Override
public void onEnd(int request)
{
Assert.assertEquals(id, request);
Assert.assertEquals(contentLength, length.get());
Assert.assertEquals(contentLength, totalLength.get());
}
});

View File

@ -214,7 +214,7 @@ public class ClientParserTest
Generator.Result result1 = generator.generateResponseHeaders(id, code, "OK", fields, null);
Generator.Result result2 = generator.generateResponseContent(id, content, true, null);
final AtomicInteger length = new AtomicInteger();
final AtomicInteger totalLength = new AtomicInteger();
final AtomicBoolean verifier = new AtomicBoolean();
ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter()
{
@ -222,14 +222,14 @@ public class ClientParserTest
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
{
Assert.assertEquals(id, request);
length.addAndGet(buffer.remaining());
totalLength.addAndGet(buffer.remaining());
}
@Override
public void onEnd(int request)
{
Assert.assertEquals(id, request);
Assert.assertEquals(contentLength, length.get());
Assert.assertEquals(contentLength, totalLength.get());
verifier.set(true);
}
});

View File

@ -111,7 +111,7 @@ public class HttpChannelOverFCGI extends HttpChannel
receiver.responseSuccess(exchange);
}
protected void flush(Generator.Result result)
protected void flush(Generator.Result... result)
{
flusher.flush(result);
}

View File

@ -18,50 +18,56 @@
package org.eclipse.jetty.fcgi.client.http;
import java.util.regex.Pattern;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jetty.client.AbstractHttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Promise;
// TODO: add parameter to tell whether use multiplex destinations or not
public class HttpClientTransportOverFCGI extends AbstractHttpClientTransport
{
private final Pattern uriPattern;
private final String scriptRoot;
public HttpClientTransportOverFCGI(Pattern uriPattern)
public HttpClientTransportOverFCGI(String scriptRoot)
{
this(1, uriPattern);
this(Runtime.getRuntime().availableProcessors() / 2 + 1, scriptRoot);
}
public HttpClientTransportOverFCGI(int selectors, Pattern uriPattern)
public HttpClientTransportOverFCGI(int selectors, String scriptRoot)
{
super(selectors);
this.uriPattern = uriPattern;
}
public Pattern getURIPattern()
{
return uriPattern;
this.scriptRoot = scriptRoot;
}
@Override
public HttpDestination newHttpDestination(String scheme, String host, int port)
public HttpDestination newHttpDestination(Origin origin)
{
return new MultiplexHttpDestinationOverFCGI(getHttpClient(), scheme, host, port);
return new MultiplexHttpDestinationOverFCGI(getHttpClient(), origin);
}
@Override
protected org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, HttpDestination destination)
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
return new HttpConnectionOverFCGI(endPoint, destination);
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
HttpConnectionOverFCGI connection = new HttpConnectionOverFCGI(endPoint, destination);
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.succeeded(connection);
return connection;
}
@Override
public Connection tunnel(Connection connection)
protected void customize(Request request, HttpFields fastCGIHeaders)
{
HttpConnectionOverFCGI httpConnection = (HttpConnectionOverFCGI)connection;
return tunnel(httpConnection.getEndPoint(), httpConnection.getHttpDestination(), connection);
String scriptName = fastCGIHeaders.get(FCGI.Headers.SCRIPT_NAME);
fastCGIHeaders.put(FCGI.Headers.SCRIPT_FILENAME, scriptRoot + scriptName);
fastCGIHeaders.put(FCGI.Headers.DOCUMENT_ROOT, scriptRoot);
}
}

View File

@ -1,8 +1,7 @@
package org.eclipse.jetty.fcgi.client.http;
import java.net.URI;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Locale;
import org.eclipse.jetty.client.HttpChannel;
import org.eclipse.jetty.client.HttpContent;
@ -16,6 +15,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Jetty;
public class HttpSenderOverFCGI extends HttpSender
{
@ -36,59 +36,66 @@ public class HttpSenderOverFCGI extends HttpSender
@Override
protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
{
Request httpRequest = exchange.getRequest();
URI uri = httpRequest.getURI();
HttpFields headers = httpRequest.getHeaders();
Request request = exchange.getRequest();
// Copy the request headers to be able to convert them properly
HttpFields headers = new HttpFields();
for (HttpField field : request.getHeaders())
headers.put(field);
HttpFields fcgiHeaders = new HttpFields();
HttpField field = headers.remove(HttpHeader.AUTHORIZATION);
if (field != null)
headers.put(FCGI.Headers.AUTH_TYPE, field.getValue());
// FastCGI headers based on the URI
URI uri = request.getURI();
String path = uri.getPath();
fcgiHeaders.put(FCGI.Headers.REQUEST_URI, path);
String query = uri.getQuery();
fcgiHeaders.put(FCGI.Headers.QUERY_STRING, query == null ? "" : query);
int lastSegment = path.lastIndexOf('/');
String scriptName = lastSegment < 0 ? path : path.substring(lastSegment);
fcgiHeaders.put(FCGI.Headers.SCRIPT_NAME, scriptName);
field = headers.remove(HttpHeader.CONTENT_LENGTH);
if (field != null)
headers.put(FCGI.Headers.CONTENT_LENGTH, field.getValue());
// FastCGI headers based on HTTP headers
HttpField httpField = headers.remove(HttpHeader.AUTHORIZATION);
if (httpField != null)
fcgiHeaders.put(FCGI.Headers.AUTH_TYPE, httpField.getValue());
httpField = headers.remove(HttpHeader.CONTENT_LENGTH);
fcgiHeaders.put(FCGI.Headers.CONTENT_LENGTH, httpField == null ? "" : httpField.getValue());
httpField = headers.remove(HttpHeader.CONTENT_TYPE);
fcgiHeaders.put(FCGI.Headers.CONTENT_TYPE, httpField == null ? "" : httpField.getValue());
field = headers.remove(HttpHeader.CONTENT_TYPE);
if (field != null)
headers.put(FCGI.Headers.CONTENT_TYPE, field.getValue());
// FastCGI headers that are not based on HTTP headers nor URI
fcgiHeaders.put(FCGI.Headers.REQUEST_METHOD, request.getMethod());
fcgiHeaders.put(FCGI.Headers.SERVER_PROTOCOL, request.getVersion().asString());
fcgiHeaders.put(FCGI.Headers.GATEWAY_INTERFACE, "CGI/1.1");
fcgiHeaders.put(FCGI.Headers.SERVER_SOFTWARE, "Jetty/" + Jetty.VERSION);
// TODO: need to pass SERVER_NAME, SERVER_ADDR, SERVER_PORT, REMOTE_ADDR, REMOTE_PORT
// TODO: if the FCGI transport is used within a ProxyServlet, they must have certain values
// TODO: for example the remote address is taken from the ProxyServlet request
// TODO: if used standalone, they must have other values.
headers.put(FCGI.Headers.GATEWAY_INTERFACE, "CGI/1.1");
HttpClientTransportOverFCGI transport = (HttpClientTransportOverFCGI)getHttpChannel().getHttpDestination().getHttpClient().getTransport();
Pattern uriPattern = transport.getURIPattern();
Matcher matcher = uriPattern.matcher(uri.toString());
// TODO: what if the URI does not match ? Here is kinda too late to abort the request ?
// TODO: perhaps this works in conjuntion with the ProxyServlet, which is mapped to the same URI regexp
// TODO: so that if the call arrives here, we are sure it matches.
// headers.put(Headers.PATH_INFO, ???);
// headers.put(Headers.PATH_TRANSLATED, ???);
headers.put(FCGI.Headers.QUERY_STRING, uri.getQuery());
// TODO: the fields below are probably provided by ProxyServlet as X-Forwarded-*
// headers.put(Headers.REMOTE_ADDR, ???);
// headers.put(Headers.REMOTE_HOST, ???);
// headers.put(Headers.REMOTE_USER, ???);
headers.put(FCGI.Headers.REQUEST_METHOD, httpRequest.getMethod());
headers.put(FCGI.Headers.REQUEST_URI, uri.toString());
headers.put(FCGI.Headers.SERVER_PROTOCOL, httpRequest.getVersion().asString());
// TODO: translate remaining HTTP header into the HTTP_* format
int request = getHttpChannel().getRequest();
boolean hasContent = content.hasContent();
Generator.Result result = generator.generateRequestHeaders(request, headers,
hasContent ? callback : new Callback.Adapter());
getHttpChannel().flush(result);
if (!hasContent)
// Translate remaining HTTP header into the HTTP_* format
for (HttpField field : headers)
{
result = generator.generateRequestContent(request, null, true, callback);
getHttpChannel().flush(result);
String name = field.getName();
String fcgiName = "HTTP_" + name.replaceAll("-", "_").toUpperCase(Locale.ENGLISH);
fcgiHeaders.add(fcgiName, field.getValue());
}
// Give a chance to the transport implementation to customize the FastCGI headers
HttpClientTransportOverFCGI transport = (HttpClientTransportOverFCGI)getHttpChannel().getHttpDestination().getHttpClient().getTransport();
transport.customize(request, fcgiHeaders);
int id = getHttpChannel().getRequest();
boolean hasContent = content.hasContent();
Generator.Result headersResult = generator.generateRequestHeaders(id, fcgiHeaders,
hasContent ? callback : new Callback.Adapter());
if (hasContent)
{
getHttpChannel().flush(headersResult);
}
else
{
Generator.Result noContentResult = generator.generateRequestContent(id, null, true, callback);
getHttpChannel().flush(headersResult, noContentResult);
}
}

View File

@ -21,12 +21,13 @@ package org.eclipse.jetty.fcgi.client.http;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.MultiplexHttpDestination;
import org.eclipse.jetty.client.Origin;
public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination<HttpConnectionOverFCGI>
{
public MultiplexHttpDestinationOverFCGI(HttpClient client, String scheme, String host, int port)
public MultiplexHttpDestinationOverFCGI(HttpClient client, Origin origin)
{
super(client, scheme, host, port);
super(client, origin);
}
@Override

View File

@ -55,7 +55,7 @@ public abstract class AbstractHttpClientServerTest
QueuedThreadPool executor = new QueuedThreadPool();
executor.setName(executor.getName() + "-client");
client = new HttpClient(new HttpClientTransportOverFCGI(), null);
client = new HttpClient(new HttpClientTransportOverFCGI(""), null);
client.setExecutor(executor);
client.start();
}

View File

@ -0,0 +1,52 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.fcgi.client.http;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
public class ExternalFastCGIServerTest
{
@Test
@Ignore("Relies on an external server")
public void testExternalFastCGIServer() throws Exception
{
// Assume a FastCGI server is listening on port 9000
HttpClient client = new HttpClient(new HttpClientTransportOverFCGI("/var/www/php-fcgi"), null);
client.start();
ContentResponse response = client.newRequest("localhost", 9000)
.path("/index.php")
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
Files.write(Paths.get(System.getProperty("java.io.tmpdir"), "fcgi_response.html"), response.getContent(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
}
}