Made first simple GET request working.
This commit is contained in:
parent
07acb25ce0
commit
5427a6d2da
|
@ -0,0 +1,109 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.generator;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
import org.eclipse.jetty.util.ConcurrentArrayQueue;
|
||||||
|
import org.eclipse.jetty.util.IteratingCallback;
|
||||||
|
|
||||||
|
public class Flusher
|
||||||
|
{
|
||||||
|
private final Queue<Generator.Result> queue = new ConcurrentArrayQueue<>();
|
||||||
|
private final Callback flushCallback = new FlushCallback();
|
||||||
|
private final EndPoint endPoint;
|
||||||
|
private boolean flushing;
|
||||||
|
|
||||||
|
public Flusher(EndPoint endPoint)
|
||||||
|
{
|
||||||
|
this.endPoint = endPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush(Generator.Result... results)
|
||||||
|
{
|
||||||
|
synchronized (queue)
|
||||||
|
{
|
||||||
|
for (Generator.Result result : results)
|
||||||
|
queue.offer(result);
|
||||||
|
if (flushing)
|
||||||
|
return;
|
||||||
|
flushing = true;
|
||||||
|
}
|
||||||
|
endPoint.write(flushCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FlushCallback extends IteratingCallback
|
||||||
|
{
|
||||||
|
private Generator.Result active;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean process() throws Exception
|
||||||
|
{
|
||||||
|
// We are flushing, we completed a write, notify
|
||||||
|
if (active != null)
|
||||||
|
{
|
||||||
|
active.succeeded();
|
||||||
|
active = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look if other writes are needed.
|
||||||
|
Generator.Result result;
|
||||||
|
synchronized (queue)
|
||||||
|
{
|
||||||
|
if (queue.isEmpty())
|
||||||
|
{
|
||||||
|
// No more writes to do, switch to non-flushing
|
||||||
|
flushing = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: here is where we want to gather more results to perform gathered writes
|
||||||
|
result = queue.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
active = result;
|
||||||
|
List<ByteBuffer> buffers = result.getByteBuffers();
|
||||||
|
endPoint.write(this, buffers.toArray(new ByteBuffer[buffers.size()]));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void completed()
|
||||||
|
{
|
||||||
|
// Nothing to do, we always return false from process().
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Throwable x)
|
||||||
|
{
|
||||||
|
super.failed(x);
|
||||||
|
|
||||||
|
// TODO: set flushing=false ?
|
||||||
|
|
||||||
|
if (active != null)
|
||||||
|
{
|
||||||
|
active.failed(x);
|
||||||
|
active = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,28 +21,31 @@ package org.eclipse.jetty.fcgi.client.http;
|
||||||
import org.eclipse.jetty.client.HttpChannel;
|
import org.eclipse.jetty.client.HttpChannel;
|
||||||
import org.eclipse.jetty.client.HttpDestination;
|
import org.eclipse.jetty.client.HttpDestination;
|
||||||
import org.eclipse.jetty.client.HttpExchange;
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
|
import org.eclipse.jetty.fcgi.generator.Flusher;
|
||||||
import org.eclipse.jetty.fcgi.generator.Generator;
|
import org.eclipse.jetty.fcgi.generator.Generator;
|
||||||
import org.eclipse.jetty.http.HttpField;
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
|
|
||||||
public class HttpChannelOverFCGI extends HttpChannel
|
public class HttpChannelOverFCGI extends HttpChannel
|
||||||
{
|
{
|
||||||
private final HttpConnectionOverFCGI connection;
|
private final Flusher flusher;
|
||||||
private final int id;
|
private final int request;
|
||||||
private final HttpSenderOverFCGI sender;
|
private final HttpSenderOverFCGI sender;
|
||||||
private final HttpReceiverOverFCGI receiver;
|
private final HttpReceiverOverFCGI receiver;
|
||||||
|
private HttpVersion version;
|
||||||
|
|
||||||
public HttpChannelOverFCGI(HttpDestination destination, HttpConnectionOverFCGI connection, int id)
|
public HttpChannelOverFCGI(HttpDestination destination, Flusher flusher, int request)
|
||||||
{
|
{
|
||||||
super(destination);
|
super(destination);
|
||||||
this.connection = connection;
|
this.flusher = flusher;
|
||||||
this.id = id;
|
this.request = request;
|
||||||
this.sender = new HttpSenderOverFCGI(this);
|
this.sender = new HttpSenderOverFCGI(this);
|
||||||
this.receiver = new HttpReceiverOverFCGI(this);
|
this.receiver = new HttpReceiverOverFCGI(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getId()
|
protected int getRequest()
|
||||||
{
|
{
|
||||||
return id;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -50,25 +53,32 @@ public class HttpChannelOverFCGI extends HttpChannel
|
||||||
{
|
{
|
||||||
HttpExchange exchange = getHttpExchange();
|
HttpExchange exchange = getHttpExchange();
|
||||||
if (exchange != null)
|
if (exchange != null)
|
||||||
|
{
|
||||||
|
version = exchange.getRequest().getVersion();
|
||||||
sender.send(exchange);
|
sender.send(exchange);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void proceed(HttpExchange exchange, boolean proceed)
|
public void proceed(HttpExchange exchange, boolean proceed)
|
||||||
{
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean abort(Throwable cause)
|
public boolean abort(Throwable cause)
|
||||||
{
|
{
|
||||||
return false;
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void responseBegin()
|
protected void responseBegin(int code, String reason)
|
||||||
{
|
{
|
||||||
HttpExchange exchange = getHttpExchange();
|
HttpExchange exchange = getHttpExchange();
|
||||||
if (exchange != null)
|
if (exchange != null)
|
||||||
|
{
|
||||||
|
exchange.getResponse().version(version).status(code).reason(reason);
|
||||||
receiver.responseBegin(exchange);
|
receiver.responseBegin(exchange);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void responseHeader(HttpField field)
|
protected void responseHeader(HttpField field)
|
||||||
|
@ -78,8 +88,22 @@ public class HttpChannelOverFCGI extends HttpChannel
|
||||||
receiver.responseHeader(exchange, field);
|
receiver.responseHeader(exchange, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void write(Generator.Result result)
|
protected void responseHeaders()
|
||||||
{
|
{
|
||||||
connection.write(result);
|
HttpExchange exchange = getHttpExchange();
|
||||||
|
if (exchange != null)
|
||||||
|
receiver.responseHeaders(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void responseSuccess()
|
||||||
|
{
|
||||||
|
HttpExchange exchange = getHttpExchange();
|
||||||
|
if (exchange != null)
|
||||||
|
receiver.responseSuccess(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void flush(Generator.Result result)
|
||||||
|
{
|
||||||
|
flusher.flush(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,7 @@ package org.eclipse.jetty.fcgi.client.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
@ -33,15 +31,12 @@ import org.eclipse.jetty.client.api.Connection;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.fcgi.FCGI;
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
import org.eclipse.jetty.fcgi.generator.Generator;
|
import org.eclipse.jetty.fcgi.generator.Flusher;
|
||||||
import org.eclipse.jetty.fcgi.parser.ClientParser;
|
import org.eclipse.jetty.fcgi.parser.ClientParser;
|
||||||
import org.eclipse.jetty.http.HttpField;
|
import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.io.AbstractConnection;
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.util.Callback;
|
|
||||||
import org.eclipse.jetty.util.ConcurrentArrayQueue;
|
|
||||||
import org.eclipse.jetty.util.IteratingCallback;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
@ -51,16 +46,15 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
|
||||||
|
|
||||||
private final LinkedList<Integer> requests = new LinkedList<>();
|
private final LinkedList<Integer> requests = new LinkedList<>();
|
||||||
private final Map<Integer, HttpChannelOverFCGI> channels = new ConcurrentHashMap<>();
|
private final Map<Integer, HttpChannelOverFCGI> channels = new ConcurrentHashMap<>();
|
||||||
private final Queue<Generator.Result> queue = new ConcurrentArrayQueue<>();
|
private final Flusher flusher;
|
||||||
private final Callback flushCallback = new FlushCallback();
|
|
||||||
private final HttpDestination destination;
|
private final HttpDestination destination;
|
||||||
private final Delegate delegate;
|
private final Delegate delegate;
|
||||||
private final ClientParser parser;
|
private final ClientParser parser;
|
||||||
private boolean flushing;
|
|
||||||
|
|
||||||
public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination)
|
public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination)
|
||||||
{
|
{
|
||||||
super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO());
|
super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO());
|
||||||
|
this.flusher = new Flusher(endPoint);
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
this.delegate = new Delegate(destination);
|
this.delegate = new Delegate(destination);
|
||||||
this.parser = new ClientParser(new ResponseListener());
|
this.parser = new ClientParser(new ResponseListener());
|
||||||
|
@ -137,18 +131,6 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
|
||||||
parser.parse(buffer);
|
parser.parse(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void write(Generator.Result result)
|
|
||||||
{
|
|
||||||
synchronized (queue)
|
|
||||||
{
|
|
||||||
queue.offer(result);
|
|
||||||
if (flushing)
|
|
||||||
return;
|
|
||||||
flushing = true;
|
|
||||||
}
|
|
||||||
getEndPoint().write(flushCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shutdown()
|
private void shutdown()
|
||||||
{
|
{
|
||||||
// TODO: we must signal to the HttpParser that we are at EOF
|
// TODO: we must signal to the HttpParser that we are at EOF
|
||||||
|
@ -207,7 +189,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
|
||||||
|
|
||||||
// FCGI may be multiplexed, so create one channel for each request.
|
// FCGI may be multiplexed, so create one channel for each request.
|
||||||
int id = acquireRequest();
|
int id = acquireRequest();
|
||||||
HttpChannelOverFCGI channel = new HttpChannelOverFCGI(getHttpDestination(), HttpConnectionOverFCGI.this, id);
|
HttpChannelOverFCGI channel = new HttpChannelOverFCGI(getHttpDestination(), flusher, id);
|
||||||
channels.put(id, channel);
|
channels.put(id, channel);
|
||||||
channel.associate(exchange);
|
channel.associate(exchange);
|
||||||
channel.send();
|
channel.send();
|
||||||
|
@ -227,7 +209,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
|
||||||
{
|
{
|
||||||
HttpChannelOverFCGI channel = channels.get(request);
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
channel.responseBegin();
|
channel.responseBegin(code, reason);
|
||||||
else
|
else
|
||||||
noChannel(request);
|
noChannel(request);
|
||||||
}
|
}
|
||||||
|
@ -245,20 +227,33 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
|
||||||
@Override
|
@Override
|
||||||
public void onHeaders(int request)
|
public void onHeaders(int request)
|
||||||
{
|
{
|
||||||
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
|
if (channel != null)
|
||||||
|
channel.responseHeaders();
|
||||||
|
else
|
||||||
|
noChannel(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnd(int request)
|
public void onEnd(int request)
|
||||||
{
|
{
|
||||||
// TODO:
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
|
if (channel != null)
|
||||||
channels.remove(request);
|
{
|
||||||
releaseRequest(request);
|
channel.responseSuccess();
|
||||||
|
channels.remove(request);
|
||||||
|
releaseRequest(request);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
noChannel(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void noChannel(int request)
|
private void noChannel(int request)
|
||||||
|
@ -266,53 +261,4 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
|
||||||
// TODO: what here ?
|
// TODO: what here ?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FlushCallback extends IteratingCallback
|
|
||||||
{
|
|
||||||
private Generator.Result active;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean process() throws Exception
|
|
||||||
{
|
|
||||||
// We are flushing, we completed a write, notify
|
|
||||||
if (active != null)
|
|
||||||
active.succeeded();
|
|
||||||
|
|
||||||
// Look if other writes are needed.
|
|
||||||
Generator.Result result;
|
|
||||||
synchronized (queue)
|
|
||||||
{
|
|
||||||
if (queue.isEmpty())
|
|
||||||
{
|
|
||||||
// No more writes to do, switch to non-flushing
|
|
||||||
flushing = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// TODO: here is where we want to gather more results to perform gathered writes
|
|
||||||
result = queue.poll();
|
|
||||||
}
|
|
||||||
|
|
||||||
active = result;
|
|
||||||
List<ByteBuffer> buffers = result.getByteBuffers();
|
|
||||||
getEndPoint().write(this, buffers.toArray(new ByteBuffer[buffers.size()]));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completed()
|
|
||||||
{
|
|
||||||
active = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable x)
|
|
||||||
{
|
|
||||||
super.failed(x);
|
|
||||||
if (active != null)
|
|
||||||
{
|
|
||||||
active.failed(x);
|
|
||||||
active = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.fcgi.client.http;
|
package org.eclipse.jetty.fcgi.client.http;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpChannel;
|
import org.eclipse.jetty.client.HttpChannel;
|
||||||
import org.eclipse.jetty.client.HttpExchange;
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
import org.eclipse.jetty.client.HttpReceiver;
|
import org.eclipse.jetty.client.HttpReceiver;
|
||||||
|
@ -41,4 +43,28 @@ public class HttpReceiverOverFCGI extends HttpReceiver
|
||||||
{
|
{
|
||||||
return super.responseHeader(exchange, field);
|
return super.responseHeader(exchange, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean responseHeaders(HttpExchange exchange)
|
||||||
|
{
|
||||||
|
return super.responseHeaders(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean responseContent(HttpExchange exchange, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
return super.responseContent(exchange, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean responseSuccess(HttpExchange exchange)
|
||||||
|
{
|
||||||
|
return super.responseSuccess(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean responseFailure(Throwable failure)
|
||||||
|
{
|
||||||
|
return super.responseFailure(failure);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,30 @@ public class HttpSenderOverFCGI extends HttpSender
|
||||||
@Override
|
@Override
|
||||||
protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
|
protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
|
||||||
{
|
{
|
||||||
int request = getHttpChannel().getId();
|
int request = getHttpChannel().getRequest();
|
||||||
Generator.Result result = generator.generateRequestHeaders(request, exchange.getRequest().getHeaders(), callback);
|
boolean noContent = !content.hasContent();
|
||||||
getHttpChannel().write(result);
|
Generator.Result result = generator.generateRequestHeaders(request, exchange.getRequest().getHeaders(),
|
||||||
|
noContent ? new Callback.Adapter() : callback);
|
||||||
|
getHttpChannel().flush(result);
|
||||||
|
if (noContent)
|
||||||
|
{
|
||||||
|
result = generator.generateRequestContent(request, null, true, callback);
|
||||||
|
getHttpChannel().flush(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
|
protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
|
||||||
{
|
{
|
||||||
int request = getHttpChannel().getId();
|
if (content.isConsumed())
|
||||||
Generator.Result result = generator.generateRequestContent(request, content.getByteBuffer(), content.isLast(), callback);
|
{
|
||||||
getHttpChannel().write(result);
|
callback.succeeded();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int request = getHttpChannel().getRequest();
|
||||||
|
Generator.Result result = generator.generateRequestContent(request, content.getByteBuffer(), content.isLast(), callback);
|
||||||
|
getHttpChannel().flush(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,10 @@
|
||||||
package org.eclipse.jetty.fcgi.client.http;
|
package org.eclipse.jetty.fcgi.client.http;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.eclipse.jetty.fcgi.server.FCGIServerConnectionFactory;
|
import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
import org.eclipse.jetty.server.NetworkConnector;
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||||
|
@ -36,7 +36,7 @@ public abstract class AbstractHttpClientServerTest
|
||||||
public final TestTracker tracker = new TestTracker();
|
public final TestTracker tracker = new TestTracker();
|
||||||
|
|
||||||
protected Server server;
|
protected Server server;
|
||||||
protected NetworkConnector connector;
|
protected ServerConnector connector;
|
||||||
protected HttpClient client;
|
protected HttpClient client;
|
||||||
protected String scheme = HttpScheme.HTTP.asString();
|
protected String scheme = HttpScheme.HTTP.asString();
|
||||||
|
|
||||||
|
@ -44,8 +44,9 @@ public abstract class AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
server = new Server();
|
server = new Server();
|
||||||
|
|
||||||
FCGIServerConnectionFactory fcgiConnectionFactory = new FCGIServerConnectionFactory();
|
ServerFCGIConnectionFactory fcgiConnectionFactory = new ServerFCGIConnectionFactory(new HttpConfiguration());
|
||||||
connector = new ServerConnector(server, fcgiConnectionFactory);
|
connector = new ServerConnector(server, fcgiConnectionFactory);
|
||||||
|
connector.setPort(9000);
|
||||||
|
|
||||||
server.addConnector(connector);
|
server.addConnector(connector);
|
||||||
server.setHandler(handler);
|
server.setHandler(handler);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
|
org.eclipse.jetty.client.LEVEL=DEBUG
|
||||||
|
org.eclipse.jetty.fcgi.LEVEL=DEBUG
|
|
@ -13,6 +13,11 @@
|
||||||
<name>Jetty :: FastCGI :: Server</name>
|
<name>Jetty :: FastCGI :: Server</name>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||||
|
<artifactId>fcgi-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-server</artifactId>
|
<artifactId>jetty-server</artifactId>
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.server;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
|
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>
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(HttpChannelOverFCGI.class);
|
||||||
|
private static final String METHOD_HEADER = "REQUEST_METHOD";
|
||||||
|
private static final String URI_HEADER = "REQUEST_URI";
|
||||||
|
private static final String VERSION_HEADER = "SERVER_PROTOCOL";
|
||||||
|
|
||||||
|
private String method;
|
||||||
|
private String uri;
|
||||||
|
private String version;
|
||||||
|
private boolean started;
|
||||||
|
private List<HttpField> fields;
|
||||||
|
|
||||||
|
public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
|
||||||
|
{
|
||||||
|
super(connector, configuration, endPoint, transport, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void header(HttpField field)
|
||||||
|
{
|
||||||
|
LOG.debug("FCGI header {}", field);
|
||||||
|
|
||||||
|
if (METHOD_HEADER.equalsIgnoreCase(field.getName()))
|
||||||
|
{
|
||||||
|
method = field.getValue();
|
||||||
|
if (uri != null && version != null)
|
||||||
|
startRequest();
|
||||||
|
}
|
||||||
|
else if (URI_HEADER.equalsIgnoreCase(field.getName()))
|
||||||
|
{
|
||||||
|
uri = field.getValue();
|
||||||
|
if (method != null && version != null)
|
||||||
|
startRequest();
|
||||||
|
}
|
||||||
|
else if (VERSION_HEADER.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startRequest()
|
||||||
|
{
|
||||||
|
started = true;
|
||||||
|
startRequest(null, method, ByteBuffer.wrap(uri.getBytes(Charset.forName("UTF-8"))), HttpVersion.fromString(version));
|
||||||
|
resumeHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resumeHeaders()
|
||||||
|
{
|
||||||
|
if (fields != null)
|
||||||
|
{
|
||||||
|
for (HttpField field : fields)
|
||||||
|
convertHeader(field);
|
||||||
|
fields = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertHeader(HttpField field)
|
||||||
|
{
|
||||||
|
String name = field.getName();
|
||||||
|
if (name.startsWith("HTTP_"))
|
||||||
|
{
|
||||||
|
// Converts e.g. "HTTP_ACCEPT_ENCODING" to "Accept-Encoding"
|
||||||
|
String[] parts = name.split("_");
|
||||||
|
StringBuilder httpName = new StringBuilder();
|
||||||
|
for (int i = 1; i < parts.length; ++i)
|
||||||
|
{
|
||||||
|
if (i > 1)
|
||||||
|
httpName.append("-");
|
||||||
|
String part = parts[i];
|
||||||
|
httpName.append(Character.toUpperCase(part.charAt(0)));
|
||||||
|
httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH));
|
||||||
|
}
|
||||||
|
field = new HttpField(httpName.toString(), field.getValue());
|
||||||
|
}
|
||||||
|
LOG.debug("HTTP header {}", field);
|
||||||
|
parsedHeader(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispatch()
|
||||||
|
{
|
||||||
|
getConnector().getExecutor().execute(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.server;
|
||||||
|
|
||||||
|
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.io.ByteBufferPool;
|
||||||
|
import org.eclipse.jetty.server.HttpTransport;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
|
||||||
|
public class HttpTransportOverFCGI implements HttpTransport
|
||||||
|
{
|
||||||
|
private final ServerGenerator generator;
|
||||||
|
private final Flusher flusher;
|
||||||
|
private final int request;
|
||||||
|
|
||||||
|
public HttpTransportOverFCGI(ByteBufferPool byteBufferPool, Flusher flusher, int request)
|
||||||
|
{
|
||||||
|
this.generator = new ServerGenerator(byteBufferPool);
|
||||||
|
this.flusher = flusher;
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(ByteBuffer content, boolean lastContent, Callback callback)
|
||||||
|
{
|
||||||
|
Generator.Result result = generator.generateResponseContent(request, content, lastContent, callback);
|
||||||
|
flusher.flush(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completed()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,21 +18,151 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.fcgi.server;
|
package org.eclipse.jetty.fcgi.server;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
|
import org.eclipse.jetty.fcgi.generator.Flusher;
|
||||||
|
import org.eclipse.jetty.fcgi.parser.ServerParser;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.io.AbstractConnection;
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
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.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
public class ServerFCGIConnection extends AbstractConnection
|
public class ServerFCGIConnection extends AbstractConnection
|
||||||
{
|
{
|
||||||
public ServerFCGIConnection(EndPoint endp, Executor executor)
|
private static final Logger LOG = Log.getLogger(ServerFCGIConnection.class);
|
||||||
|
|
||||||
|
private final ConcurrentMap<Integer, HttpChannelOverFCGI> channels = new ConcurrentHashMap<>();
|
||||||
|
private final Connector connector;
|
||||||
|
private final Flusher flusher;
|
||||||
|
private final HttpConfiguration configuration;
|
||||||
|
private final ServerParser parser;
|
||||||
|
|
||||||
|
public ServerFCGIConnection(Connector connector, EndPoint endPoint, HttpConfiguration configuration)
|
||||||
{
|
{
|
||||||
super(endp, executor);
|
super(endPoint, connector.getExecutor());
|
||||||
|
this.connector = connector;
|
||||||
|
this.flusher = new Flusher(endPoint);
|
||||||
|
this.configuration = configuration;
|
||||||
|
this.parser = new ServerParser(new ServerListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen()
|
||||||
|
{
|
||||||
|
super.onOpen();
|
||||||
|
fillInterested();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFillable()
|
public void onFillable()
|
||||||
{
|
{
|
||||||
// TODO: need to create a server-side HttpChannel and feed it with parser events
|
EndPoint endPoint = getEndPoint();
|
||||||
|
ByteBufferPool bufferPool = connector.getByteBufferPool();
|
||||||
|
ByteBuffer buffer = bufferPool.acquire(configuration.getResponseHeaderSize(), true);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int read = endPoint.fill(buffer);
|
||||||
|
if (LOG.isDebugEnabled()) // Avoid boxing of variable 'read'
|
||||||
|
LOG.debug("Read {} bytes from {}", read, endPoint);
|
||||||
|
if (read > 0)
|
||||||
|
{
|
||||||
|
parse(buffer);
|
||||||
|
}
|
||||||
|
else if (read == 0)
|
||||||
|
{
|
||||||
|
fillInterested();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shutdown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception x)
|
||||||
|
{
|
||||||
|
LOG.debug(x);
|
||||||
|
// TODO: fail and close ?
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bufferPool.release(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parse(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
while (buffer.hasRemaining())
|
||||||
|
parser.parse(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdown()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ServerListener implements ServerParser.Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onStart(int request, FCGI.Role role)
|
||||||
|
{
|
||||||
|
HttpChannelOverFCGI channel = new HttpChannelOverFCGI(connector, configuration, getEndPoint(),
|
||||||
|
new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request), new ByteBufferQueuedHttpInput());
|
||||||
|
HttpChannelOverFCGI existing = channels.putIfAbsent(request, channel);
|
||||||
|
if (existing != null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeader(int request, HttpField field)
|
||||||
|
{
|
||||||
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
|
if (channel != null)
|
||||||
|
channel.header(field);
|
||||||
|
else
|
||||||
|
noChannel(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeaders(int request)
|
||||||
|
{
|
||||||
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
|
if (channel != null)
|
||||||
|
channel.headerComplete();
|
||||||
|
else
|
||||||
|
noChannel(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnd(int request)
|
||||||
|
{
|
||||||
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
|
if (channel != null)
|
||||||
|
if (channel.messageComplete())
|
||||||
|
channel.dispatch();
|
||||||
|
else
|
||||||
|
noChannel(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void noChannel(int request)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,17 +22,21 @@ import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.server.AbstractConnectionFactory;
|
import org.eclipse.jetty.server.AbstractConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
|
|
||||||
public class FCGIServerConnectionFactory extends AbstractConnectionFactory
|
public class ServerFCGIConnectionFactory extends AbstractConnectionFactory
|
||||||
{
|
{
|
||||||
public FCGIServerConnectionFactory()
|
private final HttpConfiguration configuration;
|
||||||
|
|
||||||
|
public ServerFCGIConnectionFactory(HttpConfiguration configuration)
|
||||||
{
|
{
|
||||||
super("fcgi/1.0");
|
super("fcgi/1.0");
|
||||||
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection newConnection(Connector connector, EndPoint endPoint)
|
public Connection newConnection(Connector connector, EndPoint endPoint)
|
||||||
{
|
{
|
||||||
return new ServerFCGIConnection(endPoint, connector.getExecutor());
|
return new ServerFCGIConnection(connector, endPoint, configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue