Introduced fcgi-server and fcgi-http-client-transport modules.
This commit is contained in:
parent
9f3a7351fd
commit
07acb25ce0
|
@ -1,4 +1,6 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.eclipse.jetty.fcgi</groupId>
|
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||||
|
|
|
@ -24,10 +24,11 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.fcgi.FCGI;
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.Fields;
|
|
||||||
|
|
||||||
public class ClientGenerator extends Generator
|
public class ClientGenerator extends Generator
|
||||||
{
|
{
|
||||||
|
@ -41,22 +42,22 @@ public class ClientGenerator extends Generator
|
||||||
super(byteBufferPool);
|
super(byteBufferPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result generateRequestHeaders(int request, Fields fields, Callback callback)
|
public Result generateRequestHeaders(int request, HttpFields fields, Callback callback)
|
||||||
{
|
{
|
||||||
request &= 0xFF_FF;
|
request &= 0xFF_FF;
|
||||||
|
|
||||||
Charset utf8 = Charset.forName("UTF-8");
|
Charset utf8 = Charset.forName("UTF-8");
|
||||||
List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
|
List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
|
||||||
int fieldsLength = 0;
|
int fieldsLength = 0;
|
||||||
for (Fields.Field field : fields)
|
for (HttpField field : fields)
|
||||||
{
|
{
|
||||||
String name = field.name();
|
String name = field.getName();
|
||||||
byte[] nameBytes = name.getBytes(utf8);
|
byte[] nameBytes = name.getBytes(utf8);
|
||||||
if (nameBytes.length > MAX_PARAM_LENGTH)
|
if (nameBytes.length > MAX_PARAM_LENGTH)
|
||||||
throw new IllegalArgumentException("Field name " + name + " exceeds max length " + MAX_PARAM_LENGTH);
|
throw new IllegalArgumentException("Field name " + name + " exceeds max length " + MAX_PARAM_LENGTH);
|
||||||
bytes.add(nameBytes);
|
bytes.add(nameBytes);
|
||||||
|
|
||||||
String value = field.value();
|
String value = field.getValue();
|
||||||
byte[] valueBytes = value.getBytes(utf8);
|
byte[] valueBytes = value.getBytes(utf8);
|
||||||
if (valueBytes.length > MAX_PARAM_LENGTH)
|
if (valueBytes.length > MAX_PARAM_LENGTH)
|
||||||
throw new IllegalArgumentException("Field value " + value + " exceeds max length " + MAX_PARAM_LENGTH);
|
throw new IllegalArgumentException("Field value " + value + " exceeds max length " + MAX_PARAM_LENGTH);
|
||||||
|
|
|
@ -24,11 +24,12 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.fcgi.FCGI;
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.Fields;
|
|
||||||
|
|
||||||
public class ServerGenerator extends Generator
|
public class ServerGenerator extends Generator
|
||||||
{
|
{
|
||||||
|
@ -41,7 +42,7 @@ public class ServerGenerator extends Generator
|
||||||
super(byteBufferPool);
|
super(byteBufferPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result generateResponseHeaders(int request, int code, String reason, Fields fields, Callback callback)
|
public Result generateResponseHeaders(int request, int code, String reason, HttpFields fields, Callback callback)
|
||||||
{
|
{
|
||||||
request &= 0xFF_FF;
|
request &= 0xFF_FF;
|
||||||
|
|
||||||
|
@ -59,13 +60,13 @@ public class ServerGenerator extends Generator
|
||||||
length += responseBytes.length + EOL.length;
|
length += responseBytes.length + EOL.length;
|
||||||
|
|
||||||
// Other headers
|
// Other headers
|
||||||
for (Fields.Field field : fields)
|
for (HttpField field : fields)
|
||||||
{
|
{
|
||||||
String name = field.name();
|
String name = field.getName();
|
||||||
byte[] nameBytes = name.getBytes(utf8);
|
byte[] nameBytes = name.getBytes(utf8);
|
||||||
bytes.add(nameBytes);
|
bytes.add(nameBytes);
|
||||||
|
|
||||||
String value = field.value();
|
String value = field.getValue();
|
||||||
byte[] valueBytes = value.getBytes(utf8);
|
byte[] valueBytes = value.getBytes(utf8);
|
||||||
bytes.add(valueBytes);
|
bytes.add(valueBytes);
|
||||||
|
|
||||||
|
|
|
@ -38,4 +38,17 @@ public class ClientParser extends Parser
|
||||||
{
|
{
|
||||||
return contentParsers.get(frameType);
|
return contentParsers.get(frameType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface Listener extends Parser.Listener
|
||||||
|
{
|
||||||
|
public void onBegin(int request, int code, String reason);
|
||||||
|
|
||||||
|
public static class Adapter extends Parser.Listener.Adapter implements Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onBegin(int request, int code, String reason)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.fcgi.parser;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -207,7 +208,7 @@ public class ParamsContentParser extends ContentParser
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.onHeader(getRequest(), name, value);
|
listener.onHeader(getRequest(), new HttpField(name, value));
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.fcgi.parser;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.fcgi.FCGI;
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
|
||||||
public abstract class Parser
|
public abstract class Parser
|
||||||
{
|
{
|
||||||
|
@ -91,7 +92,7 @@ public abstract class Parser
|
||||||
|
|
||||||
public interface Listener
|
public interface Listener
|
||||||
{
|
{
|
||||||
public void onHeader(int request, String name, String value);
|
public void onHeader(int request, HttpField field);
|
||||||
|
|
||||||
public void onHeaders(int request);
|
public void onHeaders(int request);
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ public abstract class Parser
|
||||||
public static class Adapter implements Listener
|
public static class Adapter implements Listener
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeader(int request, String name, String value)
|
public void onHeader(int request, HttpField field)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,18 @@
|
||||||
package org.eclipse.jetty.fcgi.parser;
|
package org.eclipse.jetty.fcgi.parser;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.fcgi.FCGI;
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
import org.eclipse.jetty.http.HttpField;
|
import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpParser;
|
import org.eclipse.jetty.http.HttpParser;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
|
|
||||||
public class ResponseContentParser extends StreamContentParser
|
public class ResponseContentParser extends StreamContentParser
|
||||||
{
|
{
|
||||||
public ResponseContentParser(HeaderParser headerParser, Parser.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, new ResponseListener(headerParser, listener));
|
||||||
}
|
}
|
||||||
|
@ -35,11 +38,13 @@ public class ResponseContentParser extends StreamContentParser
|
||||||
private static class ResponseListener extends Parser.Listener.Adapter implements HttpParser.ResponseHandler<ByteBuffer>
|
private static class ResponseListener extends Parser.Listener.Adapter implements HttpParser.ResponseHandler<ByteBuffer>
|
||||||
{
|
{
|
||||||
private final HeaderParser headerParser;
|
private final HeaderParser headerParser;
|
||||||
private final Parser.Listener listener;
|
private final ClientParser.Listener listener;
|
||||||
private final FCGIHttpParser httpParser;
|
private final FCGIHttpParser httpParser;
|
||||||
private State state = State.HEADERS;
|
private State state = State.HEADERS;
|
||||||
|
private boolean begun;
|
||||||
|
private List<HttpField> fields;
|
||||||
|
|
||||||
public ResponseListener(HeaderParser headerParser, Parser.Listener listener)
|
public ResponseListener(HeaderParser headerParser, ClientParser.Listener listener)
|
||||||
{
|
{
|
||||||
this.headerParser = headerParser;
|
this.headerParser = headerParser;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
@ -77,6 +82,8 @@ public class ResponseContentParser extends StreamContentParser
|
||||||
{
|
{
|
||||||
httpParser.reset();
|
httpParser.reset();
|
||||||
state = State.HEADERS;
|
state = State.HEADERS;
|
||||||
|
begun = false;
|
||||||
|
fields = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,18 +108,46 @@ public class ResponseContentParser extends StreamContentParser
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean parsedHeader(HttpField field)
|
public boolean parsedHeader(HttpField httpField)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ("Status".equalsIgnoreCase(field.getName()))
|
if ("Status".equalsIgnoreCase(httpField.getName()))
|
||||||
{
|
{
|
||||||
// Need to set the response status so the
|
if (!begun)
|
||||||
// HttpParser can handle the content properly.
|
{
|
||||||
int code = Integer.parseInt(field.getValue().split(" ")[0]);
|
begun = true;
|
||||||
httpParser.setResponseStatus(code);
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (begun)
|
||||||
|
{
|
||||||
|
listener.onHeader(headerParser.getRequest(), httpField);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (fields == null)
|
||||||
|
fields = new ArrayList<>();
|
||||||
|
fields.add(httpField);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
listener.onHeader(headerParser.getRequest(), field.getName(), field.getValue());
|
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
|
@ -126,7 +161,15 @@ public class ResponseContentParser extends StreamContentParser
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.onHeaders(headerParser.getRequest());
|
if (begun)
|
||||||
|
{
|
||||||
|
listener.onHeaders(headerParser.getRequest());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fields = null;
|
||||||
|
// TODO: what here ?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,9 +24,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import org.eclipse.jetty.fcgi.FCGI;
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
import org.eclipse.jetty.fcgi.parser.ServerParser;
|
import org.eclipse.jetty.fcgi.parser.ServerParser;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||||
import org.eclipse.jetty.util.Fields;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -35,31 +36,31 @@ public class ClientGeneratorTest
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateRequestHeaders() throws Exception
|
public void testGenerateRequestHeaders() throws Exception
|
||||||
{
|
{
|
||||||
Fields fields = new Fields();
|
HttpFields fields = new HttpFields();
|
||||||
|
|
||||||
// Short name, short value
|
// Short name, short value
|
||||||
final String shortShortName = "REQUEST_METHOD";
|
final String shortShortName = "REQUEST_METHOD";
|
||||||
final String shortShortValue = "GET";
|
final String shortShortValue = "GET";
|
||||||
fields.put(new Fields.Field(shortShortName, shortShortValue));
|
fields.put(new HttpField(shortShortName, shortShortValue));
|
||||||
|
|
||||||
// Short name, long value
|
// Short name, long value
|
||||||
final String shortLongName = "REQUEST_URI";
|
final String shortLongName = "REQUEST_URI";
|
||||||
// Be sure it's longer than 127 chars to test the large value
|
// Be sure it's longer than 127 chars to test the large value
|
||||||
final String shortLongValue = "/api/0.6/map?bbox=-64.217736,-31.456810,-64.187736,-31.432322,filler=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
final String shortLongValue = "/api/0.6/map?bbox=-64.217736,-31.456810,-64.187736,-31.432322,filler=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
||||||
fields.put(new Fields.Field(shortLongName, shortLongValue));
|
fields.put(new HttpField(shortLongName, shortLongValue));
|
||||||
|
|
||||||
// Long name, short value
|
// Long name, short value
|
||||||
// Be sure it's longer than 127 chars to test the large name
|
// Be sure it's longer than 127 chars to test the large name
|
||||||
final String longShortName = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210";
|
final String longShortName = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210";
|
||||||
final String longShortValue = "api.openstreetmap.org";
|
final String longShortValue = "api.openstreetmap.org";
|
||||||
fields.put(new Fields.Field(longShortName, longShortValue));
|
fields.put(new HttpField(longShortName, longShortValue));
|
||||||
|
|
||||||
// Long name, long value
|
// Long name, long value
|
||||||
char[] chars = new char[ClientGenerator.MAX_PARAM_LENGTH];
|
char[] chars = new char[ClientGenerator.MAX_PARAM_LENGTH];
|
||||||
Arrays.fill(chars, 'z');
|
Arrays.fill(chars, 'z');
|
||||||
final String longLongName = new String(chars);
|
final String longLongName = new String(chars);
|
||||||
final String longLongValue = new String(chars);
|
final String longLongValue = new String(chars);
|
||||||
fields.put(new Fields.Field(longLongName, longLongValue));
|
fields.put(new HttpField(longLongName, longLongValue));
|
||||||
|
|
||||||
ByteBufferPool byteBufferPool = new MappedByteBufferPool();
|
ByteBufferPool byteBufferPool = new MappedByteBufferPool();
|
||||||
ClientGenerator generator = new ClientGenerator(byteBufferPool);
|
ClientGenerator generator = new ClientGenerator(byteBufferPool);
|
||||||
|
@ -78,26 +79,26 @@ public class ClientGeneratorTest
|
||||||
ServerParser parser = new ServerParser(new ServerParser.Listener.Adapter()
|
ServerParser parser = new ServerParser(new ServerParser.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeader(int request, String name, String value)
|
public void onHeader(int request, HttpField field)
|
||||||
{
|
{
|
||||||
Assert.assertEquals(id, request);
|
Assert.assertEquals(id, request);
|
||||||
switch (name)
|
switch (field.getName())
|
||||||
{
|
{
|
||||||
case shortShortName:
|
case shortShortName:
|
||||||
Assert.assertEquals(shortShortValue, value);
|
Assert.assertEquals(shortShortValue, field.getValue());
|
||||||
params.set(params.get() * primes[0]);
|
params.set(params.get() * primes[0]);
|
||||||
break;
|
break;
|
||||||
case shortLongName:
|
case shortLongName:
|
||||||
Assert.assertEquals(shortLongValue, value);
|
Assert.assertEquals(shortLongValue, field.getValue());
|
||||||
params.set(params.get() * primes[1]);
|
params.set(params.get() * primes[1]);
|
||||||
break;
|
break;
|
||||||
case longShortName:
|
case longShortName:
|
||||||
Assert.assertEquals(longShortValue, value);
|
Assert.assertEquals(longShortValue, field.getValue());
|
||||||
params.set(params.get() * primes[2]);
|
params.set(params.get() * primes[2]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Assert.assertEquals(longLongName, name);
|
Assert.assertEquals(longLongName, field.getName());
|
||||||
Assert.assertEquals(longLongValue, value);
|
Assert.assertEquals(longLongValue, field.getValue());
|
||||||
params.set(params.get() * primes[3]);
|
params.set(params.get() * primes[3]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
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.Generator;
|
||||||
import org.eclipse.jetty.fcgi.generator.ServerGenerator;
|
import org.eclipse.jetty.fcgi.generator.ServerGenerator;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||||
import org.eclipse.jetty.util.Fields;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ public class ClientParserTest
|
||||||
public void testParseResponseHeaders() throws Exception
|
public void testParseResponseHeaders() throws Exception
|
||||||
{
|
{
|
||||||
final int id = 13;
|
final int id = 13;
|
||||||
Fields fields = new Fields();
|
HttpFields fields = new HttpFields();
|
||||||
|
|
||||||
final String statusName = "Status";
|
final String statusName = "Status";
|
||||||
final int code = 200;
|
final int code = 200;
|
||||||
|
@ -58,20 +59,20 @@ public class ClientParserTest
|
||||||
value *= prime;
|
value *= prime;
|
||||||
|
|
||||||
final AtomicInteger params = new AtomicInteger(1);
|
final AtomicInteger params = new AtomicInteger(1);
|
||||||
ClientParser parser = new ClientParser(new Parser.Listener.Adapter()
|
ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeader(int request, String name, String value)
|
public void onHeader(int request, HttpField field)
|
||||||
{
|
{
|
||||||
Assert.assertEquals(id, request);
|
Assert.assertEquals(id, request);
|
||||||
switch (name)
|
switch (field.getName())
|
||||||
{
|
{
|
||||||
case statusName:
|
case statusName:
|
||||||
Assert.assertTrue(value.startsWith(String.valueOf(code)));
|
Assert.assertTrue(field.getValue().startsWith(String.valueOf(code)));
|
||||||
params.set(params.get() * primes[0]);
|
params.set(params.get() * primes[0]);
|
||||||
break;
|
break;
|
||||||
case contentTypeName:
|
case contentTypeName:
|
||||||
Assert.assertEquals(contentTypeValue, value);
|
Assert.assertEquals(contentTypeValue, field.getValue());
|
||||||
params.set(params.get() * primes[1]);
|
params.set(params.get() * primes[1]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +99,7 @@ public class ClientParserTest
|
||||||
public void testParseNoResponseContent() throws Exception
|
public void testParseNoResponseContent() throws Exception
|
||||||
{
|
{
|
||||||
final int id = 13;
|
final int id = 13;
|
||||||
Fields fields = new Fields();
|
HttpFields fields = new HttpFields();
|
||||||
|
|
||||||
final int code = 200;
|
final int code = 200;
|
||||||
final String contentTypeName = "Content-Length";
|
final String contentTypeName = "Content-Length";
|
||||||
|
@ -111,7 +112,7 @@ public class ClientParserTest
|
||||||
Generator.Result result2 = generator.generateResponseContent(id, null, true, null);
|
Generator.Result result2 = generator.generateResponseContent(id, null, true, null);
|
||||||
|
|
||||||
final AtomicInteger verifier = new AtomicInteger();
|
final AtomicInteger verifier = new AtomicInteger();
|
||||||
ClientParser parser = new ClientParser(new Parser.Listener.Adapter()
|
ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
||||||
|
@ -146,7 +147,7 @@ public class ClientParserTest
|
||||||
public void testParseSmallResponseContent() throws Exception
|
public void testParseSmallResponseContent() throws Exception
|
||||||
{
|
{
|
||||||
final int id = 13;
|
final int id = 13;
|
||||||
Fields fields = new Fields();
|
HttpFields fields = new HttpFields();
|
||||||
|
|
||||||
ByteBuffer content = ByteBuffer.wrap(new byte[1024]);
|
ByteBuffer content = ByteBuffer.wrap(new byte[1024]);
|
||||||
final int contentLength = content.remaining();
|
final int contentLength = content.remaining();
|
||||||
|
@ -162,7 +163,7 @@ public class ClientParserTest
|
||||||
Generator.Result result2 = generator.generateResponseContent(id, content, true, null);
|
Generator.Result result2 = generator.generateResponseContent(id, content, true, null);
|
||||||
|
|
||||||
final AtomicInteger verifier = new AtomicInteger();
|
final AtomicInteger verifier = new AtomicInteger();
|
||||||
ClientParser parser = new ClientParser(new Parser.Listener.Adapter()
|
ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
||||||
|
@ -198,7 +199,7 @@ public class ClientParserTest
|
||||||
public void testParseLargeResponseContent() throws Exception
|
public void testParseLargeResponseContent() throws Exception
|
||||||
{
|
{
|
||||||
final int id = 13;
|
final int id = 13;
|
||||||
Fields fields = new Fields();
|
HttpFields fields = new HttpFields();
|
||||||
|
|
||||||
ByteBuffer content = ByteBuffer.wrap(new byte[128 * 1024]);
|
ByteBuffer content = ByteBuffer.wrap(new byte[128 * 1024]);
|
||||||
final int contentLength = content.remaining();
|
final int contentLength = content.remaining();
|
||||||
|
@ -215,7 +216,7 @@ public class ClientParserTest
|
||||||
|
|
||||||
final AtomicInteger length = new AtomicInteger();
|
final AtomicInteger length = new AtomicInteger();
|
||||||
final AtomicBoolean verifier = new AtomicBoolean();
|
final AtomicBoolean verifier = new AtomicBoolean();
|
||||||
ClientParser parser = new ClientParser(new Parser.Listener.Adapter()
|
ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||||
|
<artifactId>fcgi-parent</artifactId>
|
||||||
|
<version>9.1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>fcgi-http-client-transport</artifactId>
|
||||||
|
<name>Jetty :: FastCGI :: HTTP Client Transport</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||||
|
<artifactId>fcgi-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-client</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||||
|
<artifactId>fcgi-server</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,85 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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 org.eclipse.jetty.client.HttpChannel;
|
||||||
|
import org.eclipse.jetty.client.HttpDestination;
|
||||||
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
|
import org.eclipse.jetty.fcgi.generator.Generator;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
|
||||||
|
public class HttpChannelOverFCGI extends HttpChannel
|
||||||
|
{
|
||||||
|
private final HttpConnectionOverFCGI connection;
|
||||||
|
private final int id;
|
||||||
|
private final HttpSenderOverFCGI sender;
|
||||||
|
private final HttpReceiverOverFCGI receiver;
|
||||||
|
|
||||||
|
public HttpChannelOverFCGI(HttpDestination destination, HttpConnectionOverFCGI connection, int id)
|
||||||
|
{
|
||||||
|
super(destination);
|
||||||
|
this.connection = connection;
|
||||||
|
this.id = id;
|
||||||
|
this.sender = new HttpSenderOverFCGI(this);
|
||||||
|
this.receiver = new HttpReceiverOverFCGI(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send()
|
||||||
|
{
|
||||||
|
HttpExchange exchange = getHttpExchange();
|
||||||
|
if (exchange != null)
|
||||||
|
sender.send(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void proceed(HttpExchange exchange, boolean proceed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean abort(Throwable cause)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void responseBegin()
|
||||||
|
{
|
||||||
|
HttpExchange exchange = getHttpExchange();
|
||||||
|
if (exchange != null)
|
||||||
|
receiver.responseBegin(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void responseHeader(HttpField field)
|
||||||
|
{
|
||||||
|
HttpExchange exchange = getHttpExchange();
|
||||||
|
if (exchange != null)
|
||||||
|
receiver.responseHeader(exchange, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void write(Generator.Result result)
|
||||||
|
{
|
||||||
|
connection.write(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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 org.eclipse.jetty.client.AbstractHttpClientTransport;
|
||||||
|
import org.eclipse.jetty.client.HttpDestination;
|
||||||
|
import org.eclipse.jetty.client.api.Connection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
|
||||||
|
// TODO: add parameter to tell whether use multiplex destinations or not
|
||||||
|
public class HttpClientTransportOverFCGI extends AbstractHttpClientTransport
|
||||||
|
{
|
||||||
|
public HttpClientTransportOverFCGI()
|
||||||
|
{
|
||||||
|
this(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientTransportOverFCGI(int selectors)
|
||||||
|
{
|
||||||
|
super(selectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpDestination newHttpDestination(String scheme, String host, int port)
|
||||||
|
{
|
||||||
|
return new MultiplexHttpDestinationOverFCGI(getHttpClient(), scheme, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, HttpDestination destination)
|
||||||
|
{
|
||||||
|
return new HttpConnectionOverFCGI(endPoint, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection tunnel(Connection connection)
|
||||||
|
{
|
||||||
|
HttpConnectionOverFCGI httpConnection = (HttpConnectionOverFCGI)connection;
|
||||||
|
return tunnel(httpConnection.getEndPoint(), httpConnection.getHttpDestination(), connection);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,318 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.ByteBuffer;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.HttpConnection;
|
||||||
|
import org.eclipse.jetty.client.HttpDestination;
|
||||||
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
|
import org.eclipse.jetty.client.api.Connection;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.fcgi.FCGI;
|
||||||
|
import org.eclipse.jetty.fcgi.generator.Generator;
|
||||||
|
import org.eclipse.jetty.fcgi.parser.ClientParser;
|
||||||
|
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.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.Logger;
|
||||||
|
|
||||||
|
public class HttpConnectionOverFCGI extends AbstractConnection implements Connection
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(HttpConnectionOverFCGI.class);
|
||||||
|
|
||||||
|
private final LinkedList<Integer> requests = new LinkedList<>();
|
||||||
|
private final Map<Integer, HttpChannelOverFCGI> channels = new ConcurrentHashMap<>();
|
||||||
|
private final Queue<Generator.Result> queue = new ConcurrentArrayQueue<>();
|
||||||
|
private final Callback flushCallback = new FlushCallback();
|
||||||
|
private final HttpDestination destination;
|
||||||
|
private final Delegate delegate;
|
||||||
|
private final ClientParser parser;
|
||||||
|
private boolean flushing;
|
||||||
|
|
||||||
|
public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination)
|
||||||
|
{
|
||||||
|
super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO());
|
||||||
|
this.destination = destination;
|
||||||
|
this.delegate = new Delegate(destination);
|
||||||
|
this.parser = new ClientParser(new ResponseListener());
|
||||||
|
requests.addLast(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpDestination getHttpDestination()
|
||||||
|
{
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(Request request, Response.CompleteListener listener)
|
||||||
|
{
|
||||||
|
delegate.send(request, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void send(HttpExchange exchange)
|
||||||
|
{
|
||||||
|
delegate.send(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen()
|
||||||
|
{
|
||||||
|
super.onOpen();
|
||||||
|
fillInterested();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFillable()
|
||||||
|
{
|
||||||
|
EndPoint endPoint = getEndPoint();
|
||||||
|
HttpClient client = destination.getHttpClient();
|
||||||
|
ByteBufferPool bufferPool = client.getByteBufferPool();
|
||||||
|
ByteBuffer buffer = bufferPool.acquire(client.getResponseBufferSize(), 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void write(Generator.Result result)
|
||||||
|
{
|
||||||
|
synchronized (queue)
|
||||||
|
{
|
||||||
|
queue.offer(result);
|
||||||
|
if (flushing)
|
||||||
|
return;
|
||||||
|
flushing = true;
|
||||||
|
}
|
||||||
|
getEndPoint().write(flushCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdown()
|
||||||
|
{
|
||||||
|
// TODO: we must signal to the HttpParser that we are at EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
getEndPoint().shutdownOutput();
|
||||||
|
LOG.debug("{} oshut", this);
|
||||||
|
getEndPoint().close();
|
||||||
|
LOG.debug("{} closed", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int acquireRequest()
|
||||||
|
{
|
||||||
|
synchronized (requests)
|
||||||
|
{
|
||||||
|
int last = requests.getLast();
|
||||||
|
int request = last + 1;
|
||||||
|
requests.addLast(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseRequest(int request)
|
||||||
|
{
|
||||||
|
synchronized (requests)
|
||||||
|
{
|
||||||
|
requests.removeFirstOccurrence(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s@%x(l:%s <-> r:%s)",
|
||||||
|
HttpConnection.class.getSimpleName(),
|
||||||
|
hashCode(),
|
||||||
|
getEndPoint().getLocalAddress(),
|
||||||
|
getEndPoint().getRemoteAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Delegate extends HttpConnection
|
||||||
|
{
|
||||||
|
private Delegate(HttpDestination destination)
|
||||||
|
{
|
||||||
|
super(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void send(HttpExchange exchange)
|
||||||
|
{
|
||||||
|
Request request = exchange.getRequest();
|
||||||
|
normalizeRequest(request);
|
||||||
|
|
||||||
|
// FCGI may be multiplexed, so create one channel for each request.
|
||||||
|
int id = acquireRequest();
|
||||||
|
HttpChannelOverFCGI channel = new HttpChannelOverFCGI(getHttpDestination(), HttpConnectionOverFCGI.this, id);
|
||||||
|
channels.put(id, channel);
|
||||||
|
channel.associate(exchange);
|
||||||
|
channel.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
HttpConnectionOverFCGI.this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ResponseListener implements ClientParser.Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onBegin(int request, int code, String reason)
|
||||||
|
{
|
||||||
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
|
if (channel != null)
|
||||||
|
channel.responseBegin();
|
||||||
|
else
|
||||||
|
noChannel(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeader(int request, HttpField field)
|
||||||
|
{
|
||||||
|
HttpChannelOverFCGI channel = channels.get(request);
|
||||||
|
if (channel != null)
|
||||||
|
channel.responseHeader(field);
|
||||||
|
else
|
||||||
|
noChannel(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeaders(int request)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnd(int request)
|
||||||
|
{
|
||||||
|
// TODO:
|
||||||
|
|
||||||
|
channels.remove(request);
|
||||||
|
releaseRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void noChannel(int request)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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 org.eclipse.jetty.client.HttpChannel;
|
||||||
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
|
import org.eclipse.jetty.client.HttpReceiver;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
|
||||||
|
public class HttpReceiverOverFCGI extends HttpReceiver
|
||||||
|
{
|
||||||
|
public HttpReceiverOverFCGI(HttpChannel channel)
|
||||||
|
{
|
||||||
|
super(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean responseBegin(HttpExchange exchange)
|
||||||
|
{
|
||||||
|
return super.responseBegin(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean responseHeader(HttpExchange exchange, HttpField field)
|
||||||
|
{
|
||||||
|
return super.responseHeader(exchange, field);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.eclipse.jetty.fcgi.client.http;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.HttpChannel;
|
||||||
|
import org.eclipse.jetty.client.HttpContent;
|
||||||
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
|
import org.eclipse.jetty.client.HttpSender;
|
||||||
|
import org.eclipse.jetty.fcgi.generator.ClientGenerator;
|
||||||
|
import org.eclipse.jetty.fcgi.generator.Generator;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
|
||||||
|
public class HttpSenderOverFCGI extends HttpSender
|
||||||
|
{
|
||||||
|
private final ClientGenerator generator;
|
||||||
|
|
||||||
|
public HttpSenderOverFCGI(HttpChannel channel)
|
||||||
|
{
|
||||||
|
super(channel);
|
||||||
|
this.generator = new ClientGenerator(channel.getHttpDestination().getHttpClient().getByteBufferPool());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HttpChannelOverFCGI getHttpChannel()
|
||||||
|
{
|
||||||
|
return (HttpChannelOverFCGI)super.getHttpChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
|
||||||
|
{
|
||||||
|
int request = getHttpChannel().getId();
|
||||||
|
Generator.Result result = generator.generateRequestHeaders(request, exchange.getRequest().getHeaders(), callback);
|
||||||
|
getHttpChannel().write(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
|
||||||
|
{
|
||||||
|
int request = getHttpChannel().getId();
|
||||||
|
Generator.Result result = generator.generateRequestContent(request, content.getByteBuffer(), content.isLast(), callback);
|
||||||
|
getHttpChannel().write(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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 org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
|
import org.eclipse.jetty.client.MultiplexHttpDestination;
|
||||||
|
|
||||||
|
public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination<HttpConnectionOverFCGI>
|
||||||
|
{
|
||||||
|
public MultiplexHttpDestinationOverFCGI(HttpClient client, String scheme, String host, int port)
|
||||||
|
{
|
||||||
|
super(client, scheme, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void send(HttpConnectionOverFCGI connection, HttpExchange exchange)
|
||||||
|
{
|
||||||
|
connection.send(exchange);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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 org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.fcgi.server.FCGIServerConnectionFactory;
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.server.Handler;
|
||||||
|
import org.eclipse.jetty.server.NetworkConnector;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||||
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Rule;
|
||||||
|
|
||||||
|
public abstract class AbstractHttpClientServerTest
|
||||||
|
{
|
||||||
|
@Rule
|
||||||
|
public final TestTracker tracker = new TestTracker();
|
||||||
|
|
||||||
|
protected Server server;
|
||||||
|
protected NetworkConnector connector;
|
||||||
|
protected HttpClient client;
|
||||||
|
protected String scheme = HttpScheme.HTTP.asString();
|
||||||
|
|
||||||
|
public void start(Handler handler) throws Exception
|
||||||
|
{
|
||||||
|
server = new Server();
|
||||||
|
|
||||||
|
FCGIServerConnectionFactory fcgiConnectionFactory = new FCGIServerConnectionFactory();
|
||||||
|
connector = new ServerConnector(server, fcgiConnectionFactory);
|
||||||
|
|
||||||
|
server.addConnector(connector);
|
||||||
|
server.setHandler(handler);
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
QueuedThreadPool executor = new QueuedThreadPool();
|
||||||
|
executor.setName(executor.getName() + "-client");
|
||||||
|
|
||||||
|
client = new HttpClient(new HttpClientTransportOverFCGI(), null);
|
||||||
|
client.setExecutor(executor);
|
||||||
|
client.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void dispose() throws Exception
|
||||||
|
{
|
||||||
|
if (client != null)
|
||||||
|
client.stop();
|
||||||
|
if (server != null)
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.io.IOException;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
|
||||||
|
public class EmptyServerHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,422 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void test_GET_ResponseWithoutContent() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
Response response = client.GET(scheme + "://localhost:" + connector.getLocalPort());
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_GET_ResponseWithContent() throws Exception
|
||||||
|
{
|
||||||
|
final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
response.getOutputStream().write(data);
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort());
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
byte[] content = response.getContent();
|
||||||
|
Assert.assertArrayEquals(data, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_GET_WithParameters_ResponseWithContent() throws Exception
|
||||||
|
{
|
||||||
|
final String paramName1 = "a";
|
||||||
|
final String paramName2 = "b";
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
ServletOutputStream output = response.getOutputStream();
|
||||||
|
String paramValue1 = request.getParameter(paramName1);
|
||||||
|
output.write(paramValue1.getBytes("UTF-8"));
|
||||||
|
String paramValue2 = request.getParameter(paramName2);
|
||||||
|
Assert.assertEquals("", paramValue2);
|
||||||
|
output.write("empty".getBytes("UTF-8"));
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String value1 = "\u20AC";
|
||||||
|
String paramValue1 = URLEncoder.encode(value1, "UTF-8");
|
||||||
|
String query = paramName1 + "=" + paramValue1 + "&" + paramName2;
|
||||||
|
ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query);
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
String content = new String(response.getContent(), "UTF-8");
|
||||||
|
Assert.assertEquals(value1 + "empty", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_GET_WithParametersMultiValued_ResponseWithContent() throws Exception
|
||||||
|
{
|
||||||
|
final String paramName1 = "a";
|
||||||
|
final String paramName2 = "b";
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
ServletOutputStream output = response.getOutputStream();
|
||||||
|
String[] paramValues1 = request.getParameterValues(paramName1);
|
||||||
|
for (String paramValue : paramValues1)
|
||||||
|
output.write(paramValue.getBytes("UTF-8"));
|
||||||
|
String paramValue2 = request.getParameter(paramName2);
|
||||||
|
output.write(paramValue2.getBytes("UTF-8"));
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String value11 = "\u20AC";
|
||||||
|
String value12 = "\u20AA";
|
||||||
|
String value2 = "&";
|
||||||
|
String paramValue11 = URLEncoder.encode(value11, "UTF-8");
|
||||||
|
String paramValue12 = URLEncoder.encode(value12, "UTF-8");
|
||||||
|
String paramValue2 = URLEncoder.encode(value2, "UTF-8");
|
||||||
|
String query = paramName1 + "=" + paramValue11 + "&" + paramName1 + "=" + paramValue12 + "&" + paramName2 + "=" + paramValue2;
|
||||||
|
ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query);
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
String content = new String(response.getContent(), "UTF-8");
|
||||||
|
Assert.assertEquals(value11 + value12 + value2, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_POST_WithParameters() throws Exception
|
||||||
|
{
|
||||||
|
final String paramName = "a";
|
||||||
|
final String paramValue = "\u20AC";
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
String value = request.getParameter(paramName);
|
||||||
|
if (paramValue.equals(value))
|
||||||
|
{
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("text/plain");
|
||||||
|
response.getOutputStream().print(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
|
||||||
|
.param(paramName, paramValue)
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_PUT_WithParameters() throws Exception
|
||||||
|
{
|
||||||
|
final String paramName = "a";
|
||||||
|
final String paramValue = "\u20AC";
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
String value = request.getParameter(paramName);
|
||||||
|
if (paramValue.equals(value))
|
||||||
|
{
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("text/plain");
|
||||||
|
response.getOutputStream().print(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort() + "/path?" + paramName + "=" + paramValue);
|
||||||
|
ContentResponse response = client.newRequest(uri)
|
||||||
|
.method(HttpMethod.PUT)
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_POST_WithParameters_WithContent() throws Exception
|
||||||
|
{
|
||||||
|
final byte[] content = {0, 1, 2, 3};
|
||||||
|
final String paramName = "a";
|
||||||
|
final String paramValue = "\u20AC";
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
String value = request.getParameter(paramName);
|
||||||
|
if (paramValue.equals(value))
|
||||||
|
{
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("application/octet-stream");
|
||||||
|
response.getOutputStream().write(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort() + "/?b=1")
|
||||||
|
.param(paramName, paramValue)
|
||||||
|
.content(new BytesContentProvider(content))
|
||||||
|
.timeout(555, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertArrayEquals(content, response.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_POST_WithContent_NotifiesRequestContentListener() throws Exception
|
||||||
|
{
|
||||||
|
final byte[] content = {0, 1, 2, 3};
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
|
||||||
|
.onRequestContent(new Request.ContentListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onContent(Request request, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[buffer.remaining()];
|
||||||
|
buffer.get(bytes);
|
||||||
|
if (!Arrays.equals(content, bytes))
|
||||||
|
request.abort(new Exception());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.content(new BytesContentProvider(content))
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_POST_WithContent_TracksProgress() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final AtomicInteger progress = new AtomicInteger();
|
||||||
|
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
|
||||||
|
.onRequestContent(new Request.ContentListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onContent(Request request, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[buffer.remaining()];
|
||||||
|
Assert.assertEquals(1, bytes.length);
|
||||||
|
buffer.get(bytes);
|
||||||
|
Assert.assertEquals(bytes[0], progress.getAndIncrement());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertEquals(5, progress.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_GZIP_ContentEncoding() throws Exception
|
||||||
|
{
|
||||||
|
final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
response.setHeader("Content-Encoding", "gzip");
|
||||||
|
GZIPOutputStream gzipOutput = new GZIPOutputStream(response.getOutputStream());
|
||||||
|
gzipOutput.write(data);
|
||||||
|
gzipOutput.finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.scheme(scheme)
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertArrayEquals(data, response.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Slow
|
||||||
|
@Test
|
||||||
|
public void test_Request_IdleTimeout() throws Exception
|
||||||
|
{
|
||||||
|
final long idleTimeout = 1000;
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new ServletException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final String host = "localhost";
|
||||||
|
final int port = connector.getLocalPort();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.newRequest(host, port)
|
||||||
|
.scheme(scheme)
|
||||||
|
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.send();
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
catch (ExecutionException expected)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(expected.getCause() instanceof TimeoutException);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make another request without specifying the idle timeout, should not fail
|
||||||
|
ContentResponse response = client.newRequest(host, port)
|
||||||
|
.scheme(scheme)
|
||||||
|
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSendToIPv6Address() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest("[::1]", connector.getLocalPort())
|
||||||
|
.scheme(scheme)
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_HEAD_With_ResponseContentLength() throws Exception
|
||||||
|
{
|
||||||
|
final int length = 1024;
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
response.getOutputStream().write(new byte[length]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// HEAD requests receive a Content-Length header, but do not
|
||||||
|
// receive the content so they must handle this case properly
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.scheme(scheme)
|
||||||
|
.method(HttpMethod.HEAD)
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertEquals(0, response.getContent().length);
|
||||||
|
|
||||||
|
// Perform a normal GET request to be sure the content is now read
|
||||||
|
response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.scheme(scheme)
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertEquals(length, response.getContent().length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||||
|
<artifactId>fcgi-parent</artifactId>
|
||||||
|
<version>9.1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>fcgi-server</artifactId>
|
||||||
|
<name>Jetty :: FastCGI :: Server</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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 org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.server.AbstractConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
|
||||||
|
public class FCGIServerConnectionFactory extends AbstractConnectionFactory
|
||||||
|
{
|
||||||
|
public FCGIServerConnectionFactory()
|
||||||
|
{
|
||||||
|
super("fcgi/1.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection newConnection(Connector connector, EndPoint endPoint)
|
||||||
|
{
|
||||||
|
return new ServerFCGIConnection(endPoint, connector.getExecutor());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
|
||||||
|
public class ServerFCGIConnection extends AbstractConnection
|
||||||
|
{
|
||||||
|
public ServerFCGIConnection(EndPoint endp, Executor executor)
|
||||||
|
{
|
||||||
|
super(endp, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFillable()
|
||||||
|
{
|
||||||
|
// TODO: need to create a server-side HttpChannel and feed it with parser events
|
||||||
|
}
|
||||||
|
}
|
6
pom.xml
6
pom.xml
|
@ -1,4 +1,6 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
@ -14,6 +16,8 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>fcgi-core</module>
|
<module>fcgi-core</module>
|
||||||
|
<module>fcgi-http-client-transport</module>
|
||||||
|
<module>fcgi-server</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
Loading…
Reference in New Issue