Started work on generation/parsing.

This commit is contained in:
Simone Bordet 2013-08-31 00:40:23 +02:00
parent a2324d5adb
commit c65847c89e
11 changed files with 1130 additions and 0 deletions

100
pom.xml Normal file
View File

@ -0,0 +1,100 @@
<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">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-fcgi</artifactId>
<name>Jetty :: FastCGI</name>
<properties>
<bundle-symbolic-name>${project.groupId}.fcgi</bundle-symbolic-name>
</properties>
<build>
<plugins>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>config</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
-->
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Import-Package>javax.net.*,*</Import-Package>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<!-- Required for OSGI -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.fcgi.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,76 @@
//
// ========================================================================
// 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;
public class FCGI
{
private FCGI()
{
}
public enum FrameType
{
BEGIN_REQUEST(1),
ABORT_REQUEST(2),
END_REQUEST(3),
PARAMS(4),
STDIN(5),
STDOUT(6),
STDERR(7),
DATA(8),
GET_VALUES(9),
GET_VALUES_RESULT(10);
public static FrameType from(int code)
{
switch (code)
{
case 1:
return BEGIN_REQUEST;
case 2:
return ABORT_REQUEST;
case 3:
return END_REQUEST;
case 4:
return PARAMS;
case 5:
return STDIN;
case 6:
return STDOUT;
case 7:
return STDERR;
case 8:
return DATA;
case 9:
return GET_VALUES;
case 10:
return GET_VALUES_RESULT;
default:
throw new IllegalArgumentException();
}
}
public final int code;
private FrameType(int code)
{
this.code = code;
}
}
}

View File

@ -0,0 +1,113 @@
//
// ========================================================================
// 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.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Fields;
public class ClientGenerator extends Generator
{
private final ByteBufferPool byteBufferPool;
public ClientGenerator(ByteBufferPool byteBufferPool)
{
this.byteBufferPool = byteBufferPool;
}
public ByteBuffer generateRequestHeaders(int id, Fields fields)
{
id = id & 0xFFFF;
Charset utf8 = Charset.forName("UTF-8");
List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
int fieldsLength = 0;
for (Fields.Field field : fields)
{
String name = field.name();
byte[] nameBytes = name.getBytes(utf8);
bytes.add(nameBytes);
String value = field.value();
byte[] valueBytes = value.getBytes(utf8);
bytes.add(valueBytes);
int nameLength = nameBytes.length;
++fieldsLength;
if (nameLength > 127)
fieldsLength += 3;
int valueLength = valueBytes.length;
++fieldsLength;
if (valueLength > 127)
fieldsLength += 3;
fieldsLength += nameLength;
fieldsLength += valueLength;
}
if (fieldsLength > 0x7F_FF)
throw new IllegalArgumentException(); // TODO: improve this ?
int capacity = 16 + 8 + fieldsLength + 8;
ByteBuffer buffer = byteBufferPool.acquire(capacity, true);
BufferUtil.clearToFill(buffer);
// Generate the FCGI_BEGIN_REQUEST frame
buffer.putInt(0x01_01_00_00 + id);
buffer.putInt(0x00_08_00_00);
buffer.putLong(0x00_01_01_00_00_00_00_00L);
// Generate the FCGI_PARAMS frame
buffer.putInt(0x01_04_00_00 + id);
buffer.putShort((short)fieldsLength);
buffer.putShort((short)0);
for (int i = 0; i < bytes.size(); i += 2)
{
byte[] nameBytes = bytes.get(i);
putParamLength(buffer, nameBytes.length);
byte[] valueBytes = bytes.get(i + 1);
putParamLength(buffer, valueBytes.length);
buffer.put(nameBytes);
buffer.put(valueBytes);
}
// Generate the last FCGI_PARAMS frame
buffer.putInt(0x01_04_00_00 + id);
buffer.putInt(0x00_00_00_00);
buffer.flip();
return buffer;
}
private void putParamLength(ByteBuffer buffer, int length)
{
if (length > 127)
buffer.putInt(length | 0x80_00_00_00);
else
buffer.put((byte)length);
}
}

View File

@ -0,0 +1,31 @@
//
// ========================================================================
// 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 org.eclipse.jetty.util.BufferUtil;
public class Generator
{
public ByteBuffer generateContent(ByteBuffer buffer)
{
return BufferUtil.EMPTY_BUFFER;
}
}

View File

@ -0,0 +1,116 @@
//
// ========================================================================
// 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.parser;
import java.nio.ByteBuffer;
public class BeginRequestContentParser extends ContentParser
{
private State state = State.ROLE;
private int cursor;
private int role;
private int flags;
public BeginRequestContentParser(HeaderParser headerParser)
{
super(headerParser);
}
@Override
public boolean parse(ByteBuffer buffer)
{
while (buffer.hasRemaining())
{
switch (state)
{
case ROLE:
{
if (buffer.remaining() >= 2)
{
role = buffer.getShort();
state = State.FLAGS;
}
else
{
state = State.ROLE_BYTES;
cursor = 0;
}
break;
}
case ROLE_BYTES:
{
int halfShort = buffer.get() & 0xFF;
role = (role << (8 * cursor)) + halfShort;
if (++cursor == 2)
state = State.FLAGS;
break;
}
case FLAGS:
{
flags = buffer.get() & 0xFF;
state = State.RESERVED;
break;
}
case RESERVED:
{
if (buffer.remaining() >= 5)
{
buffer.position(buffer.position() + 5);
reset();
return true;
}
else
{
state = State.RESERVED_BYTES;
cursor = 0;
break;
}
}
case RESERVED_BYTES:
{
buffer.get();
if (++cursor == 5)
{
reset();
return true;
}
break;
}
default:
{
throw new IllegalStateException();
}
}
}
return false;
}
private void reset()
{
state = State.ROLE;
cursor = 0;
role = 0;
flags = 0;
}
private enum State
{
ROLE, ROLE_BYTES, FLAGS, RESERVED, RESERVED_BYTES
}
}

View File

@ -0,0 +1,62 @@
//
// ========================================================================
// 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.parser;
import java.util.EnumMap;
import org.eclipse.jetty.fcgi.FCGI;
public class ClientParser extends Parser
{
private final EnumMap<FCGI.FrameType, ContentParser> contentParsers = new EnumMap<>(FCGI.FrameType.class);
private final Listener listener;
public ClientParser(Listener listener)
{
this.listener = listener;
contentParsers.put(FCGI.FrameType.BEGIN_REQUEST, new BeginRequestContentParser(headerParser));
contentParsers.put(FCGI.FrameType.PARAMS, new ParamsContentParser(headerParser, listener));
}
@Override
protected ContentParser findContentParser(FCGI.FrameType frameType)
{
return contentParsers.get(frameType);
}
public interface Listener
{
public void onParam(String name, String value);
public void onParams();
public static class Adapter implements Listener
{
@Override
public void onParam(String name, String value)
{
}
@Override
public void onParams()
{
}
}
}
}

View File

@ -0,0 +1,43 @@
//
// ========================================================================
// 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.parser;
import java.nio.ByteBuffer;
public abstract class ContentParser
{
private final HeaderParser headerParser;
protected ContentParser(HeaderParser headerParser)
{
this.headerParser = headerParser;
}
public abstract boolean parse(ByteBuffer buffer);
public void noContent()
{
throw new IllegalStateException();
}
protected int getContentLength()
{
return headerParser.getContentLength();
}
}

View File

@ -0,0 +1,147 @@
//
// ========================================================================
// 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.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.fcgi.FCGI;
public class HeaderParser
{
private State state = State.VERSION;
private int cursor;
private int version;
private int type;
private int request;
private int length;
private int padding;
public boolean parse(ByteBuffer buffer)
{
while (buffer.hasRemaining())
{
switch (state)
{
case VERSION:
{
version = buffer.get() & 0xFF;
state = State.TYPE;
break;
}
case TYPE:
{
type = buffer.get() & 0xFF;
state = State.REQUEST;
break;
}
case REQUEST:
{
if (buffer.remaining() >= 2)
{
request = buffer.getShort() & 0xFFFF;
state = State.LENGTH;
}
else
{
state = State.REQUEST_BYTES;
cursor = 0;
}
break;
}
case REQUEST_BYTES:
{
int halfShort = buffer.get() & 0xFF;
request = (request << (8 * cursor)) + halfShort;
if (++cursor == 2)
state = State.LENGTH;
break;
}
case LENGTH:
{
if (buffer.remaining() >= 2)
{
length = buffer.getShort() & 0xFFFF;
state = State.PADDING;
}
else
{
state = State.LENGTH_BYTES;
cursor = 0;
}
break;
}
case LENGTH_BYTES:
{
int halfShort = buffer.get() & 0xFF;
length = (length << (8 * cursor)) + halfShort;
if (++cursor == 2)
state = State.PADDING;
break;
}
case PADDING:
{
padding = buffer.get() & 0xFF;
state = State.RESERVED;
break;
}
case RESERVED:
{
buffer.get();
return true;
}
default:
{
throw new IllegalStateException();
}
}
}
return false;
}
public FCGI.FrameType getFrameType()
{
return FCGI.FrameType.from(type);
}
public int getContentLength()
{
return length;
}
public int getPaddingLength()
{
return padding;
}
protected void reset()
{
state = State.VERSION;
cursor = 0;
version = 0;
type = 0;
request = 0;
length = 0;
padding = 0;
}
private enum State
{
VERSION, TYPE, REQUEST, REQUEST_BYTES, LENGTH, LENGTH_BYTES, PADDING, RESERVED
}
}

View File

@ -0,0 +1,231 @@
//
// ========================================================================
// 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.parser;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class ParamsContentParser extends ContentParser
{
private final ClientParser.Listener listener;
private State state = State.LENGTH;
private int cursor;
private int length;
private int nameLength;
private int valueLength;
private byte[] nameBytes;
private byte[] valueBytes;
public ParamsContentParser(HeaderParser headerParser, ClientParser.Listener listener)
{
super(headerParser);
this.listener = listener;
}
@Override
public boolean parse(ByteBuffer buffer)
{
while (buffer.hasRemaining())
{
switch (state)
{
case LENGTH:
{
length = getContentLength();
state = State.NAME_LENGTH;
break;
}
case NAME_LENGTH:
{
if (isLargeLength(buffer))
{
if (buffer.remaining() >= 4)
{
nameLength = buffer.getInt() & 0x7F_FF;
state = State.VALUE_LENGTH;
length -= 4;
}
else
{
state = State.NAME_LENGTH_BYTES;
cursor = 0;
}
}
else
{
nameLength = buffer.get() & 0xFF;
state = State.VALUE_LENGTH;
--length;
}
break;
}
case NAME_LENGTH_BYTES:
{
int quarterInt = buffer.get() & 0xFF;
nameLength = (nameLength << (8 * cursor)) + quarterInt;
--length;
if (++cursor == 4)
state = State.VALUE_LENGTH;
break;
}
case VALUE_LENGTH:
{
if (isLargeLength(buffer))
{
if (buffer.remaining() >= 4)
{
valueLength = buffer.getInt() & 0x7F_FF;
state = State.NAME;
length -= 4;
}
else
{
state = State.VALUE_LENGTH_BYTES;
cursor = 0;
}
}
else
{
valueLength = buffer.get() & 0xFF;
state = State.NAME;
--length;
}
break;
}
case VALUE_LENGTH_BYTES:
{
int quarterInt = buffer.get() & 0xFF;
valueLength = (valueLength << (8 * cursor)) + quarterInt;
--length;
if (++cursor == 4)
state = State.NAME;
break;
}
case NAME:
{
nameBytes = new byte[nameLength];
if (buffer.remaining() >= nameLength)
{
buffer.get(nameBytes);
state = State.VALUE;
length -= nameLength;
}
else
{
state = State.NAME_BYTES;
cursor = 0;
}
break;
}
case NAME_BYTES:
{
nameBytes[cursor] = buffer.get();
--length;
if (++cursor == nameLength)
state = State.VALUE;
break;
}
case VALUE:
{
valueBytes = new byte[valueLength];
if (buffer.remaining() >= valueLength)
{
buffer.get(valueBytes);
state = State.PARAM;
length -= valueLength;
}
else
{
state = State.VALUE_BYTES;
cursor = 0;
}
break;
}
case VALUE_BYTES:
{
valueBytes[cursor] = buffer.get();
--length;
if (++cursor == valueLength)
state = State.PARAM;
break;
}
case PARAM:
{
Charset utf8 = Charset.forName("UTF-8");
onParam(new String(nameBytes, utf8), new String(valueBytes, utf8));
partialReset();
if (length == 0)
{
reset();
return true;
}
break;
}
default:
{
throw new IllegalStateException();
}
}
}
return false;
}
protected void onParam(String name, String value)
{
listener.onParam(name, value);
}
protected void onParams()
{
listener.onParams();
}
@Override
public void noContent()
{
onParams();
}
private boolean isLargeLength(ByteBuffer buffer)
{
return (buffer.get(buffer.position()) & 0x80) == 0x80;
}
private void partialReset()
{
state = State.NAME_LENGTH;
cursor = 0;
nameLength = 0;
valueLength = 0;
nameBytes = null;
valueBytes = null;
}
private void reset()
{
partialReset();
state = State.LENGTH;
length = 0;
}
private enum State
{
LENGTH, NAME_LENGTH, NAME_LENGTH_BYTES, VALUE_LENGTH, VALUE_LENGTH_BYTES, NAME, NAME_BYTES, VALUE, VALUE_BYTES, PARAM
}
}

View File

@ -0,0 +1,96 @@
//
// ========================================================================
// 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.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.fcgi.FCGI;
public abstract class Parser
{
protected final HeaderParser headerParser = new HeaderParser();
private State state = State.HEADER;
private int padding;
public void parse(ByteBuffer buffer)
{
while (true)
{
switch (state)
{
case HEADER:
{
if (!headerParser.parse(buffer))
return;
state = State.CONTENT;
break;
}
case CONTENT:
{
ContentParser contentParser = findContentParser(headerParser.getFrameType());
if (headerParser.getContentLength() == 0)
{
contentParser.noContent();
}
else
{
if (!contentParser.parse(buffer))
return;
}
padding = headerParser.getPaddingLength();
state = State.PADDING;
break;
}
case PADDING:
{
if (buffer.remaining() >= padding)
{
buffer.position(buffer.position() + padding);
reset();
break;
}
else
{
padding -= buffer.remaining();
buffer.position(buffer.limit());
return;
}
}
default:
{
throw new IllegalStateException();
}
}
}
}
protected abstract ContentParser findContentParser(FCGI.FrameType frameType);
private void reset()
{
headerParser.reset();
state = State.HEADER;
padding = 0;
}
private enum State
{
HEADER, CONTENT, PADDING
}
}

View File

@ -0,0 +1,115 @@
//
// ========================================================================
// 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.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.fcgi.parser.ClientParser;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.Fields;
import org.junit.Assert;
import org.junit.Test;
public class ClientGeneratorTest
{
@Test
public void testGenerateRequestWithoutContent() throws Exception
{
Fields fields = new Fields();
final String methodParamName = "REQUEST_METHOD";
final String methodParamValue = "GET";
fields.put(new Fields.Field(methodParamName, methodParamValue));
final String uriParamName = "REQUEST_URI";
// Be sure it's longer than 127 chars to test the large value
final String uriParamValue = "/api/0.6/map?bbox=-64.217736,-31.456810,-64.187736,-31.432322,filler=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
fields.put(new Fields.Field(uriParamName, uriParamValue));
final String protocolParamName = "SERVER_PROTOCOL";
final String protocolParamValue = "HTTP/1.1";
fields.put(new Fields.Field(protocolParamName, protocolParamValue));
// Be sure it's longer than 127 chars to test the large name
final String hostParamName = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210";
final String hostParamValue = "api.openstreetmap.org";
fields.put(new Fields.Field(hostParamName, hostParamValue));
ByteBufferPool byteBufferPool = new MappedByteBufferPool();
ClientGenerator generator = new ClientGenerator(byteBufferPool);
ByteBuffer buffer = generator.generateRequestHeaders(13, fields);
// Use the fundamental theorem of arithmetic to test the results.
// This way we know onParam() has been called the right number of
// times with the right arguments, and so onParams().
final int[] primes = new int[]{2, 3, 5, 7, 11};
int result = 1;
for (int prime : primes)
result *= prime;
final AtomicInteger params = new AtomicInteger(1);
ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter()
{
@Override
public void onParam(String name, String value)
{
switch (name)
{
case methodParamName:
Assert.assertEquals(methodParamValue, value);
params.set(params.get() * primes[0]);
break;
case uriParamName:
Assert.assertEquals(uriParamValue, value);
params.set(params.get() * primes[1]);
break;
case protocolParamName:
Assert.assertEquals(protocolParamValue, value);
params.set(params.get() * primes[2]);
break;
case hostParamName:
Assert.assertEquals(hostParamValue, value);
params.set(params.get() * primes[3]);
break;
default:
throw new IllegalStateException();
}
}
@Override
public void onParams()
{
params.set(params.get() * primes[4]);
}
});
parser.parse(buffer);
Assert.assertFalse(buffer.hasRemaining());
Assert.assertEquals(result, params.get());
// Parse again byte by byte
buffer.flip();
params.set(1);
while (buffer.hasRemaining())
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
Assert.assertFalse(buffer.hasRemaining());
Assert.assertEquals(result, params.get());
}
}