diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java
index 79b3fa7..e4a69fa 100644
--- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java
+++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java
@@ -24,8 +24,7 @@
* Interface that defines how an Oblivious HTTP implementation handles the framing of chunks.
*
* Instances of {@link OHttpChunkFramer} are stateless. The state management and encryption is delegated to
- * the {@link Decoder} and {@link Encoder} interfaces, which are typically implemented by
- * {@link OHttpContentParser} and {@link OHttpContentSerializer}, respectively.
+ * the {@link Decoder} and {@link Encoder} interfaces.
*/
public interface OHttpChunkFramer {
diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java
index 6466b86..6883f6b 100644
--- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java
+++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java
@@ -35,7 +35,8 @@ public final class OHttpCiphersuite {
private static final int ENCODED_LENGTH = 7;
- public OHttpCiphersuite(byte keyId, HybridPublicKeyEncryption.KEM kem, HybridPublicKeyEncryption.KDF kdf, HybridPublicKeyEncryption.AEAD aead) {
+ public OHttpCiphersuite(byte keyId, HybridPublicKeyEncryption.KEM kem, HybridPublicKeyEncryption.KDF kdf,
+ HybridPublicKeyEncryption.AEAD aead) {
this.keyId = keyId;
this.kem = requireNonNull(kem, "kem");
this.kdf = requireNonNull(kdf, "kdf");
diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java
index cd4453d..8bdc602 100644
--- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java
+++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java
@@ -36,12 +36,17 @@
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.LastHttpContent;
+import io.netty.incubator.codec.hpke.HybridPublicKeyEncryption;
import io.netty.util.AsciiString;
import io.netty.util.ReferenceCountUtil;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.List;
+import java.util.function.Function;
import static io.netty.handler.codec.ByteToMessageDecoder.MERGE_CUMULATOR;
+import static java.util.Objects.requireNonNull;
/**
* {@link MessageToMessageCodec} that HTTP clients can use to encrypt outgoing HTTP requests into
@@ -50,15 +55,48 @@
*
* Both incoming and outgoing messages are {@link HttpObject}s.
*/
-public abstract class OHttpClientCodec extends MessageToMessageCodec {
+public final class OHttpClientCodec extends MessageToMessageCodec {
- private OHttpContentSerializer serializer;
- private OHttpContentParser parser;
+ private final Deque contextHolders = new ArrayDeque<>();
+
+ private static final class OHttpRequestResponseContextHolder {
+
+ static final OHttpRequestResponseContextHolder NONE = new OHttpRequestResponseContextHolder(null);
+
+ final OHttpRequestResponseContext handler;
+
+ OHttpRequestResponseContextHolder(OHttpRequestResponseContext handler) {
+ this.handler = handler;
+ }
+
+ void destroy() {
+ if (handler != null) {
+ handler.destroy();
+ }
+ }
+ }
+
+ private final HybridPublicKeyEncryption encryption;
+ private final Function encapsulationFunc;
- private OHttpClientContext context;
private ByteBuf cumulationBuffer = Unpooled.EMPTY_BUFFER;
private boolean destroyed;
+ /**
+ * Creates a new instance
+ *
+ * @param encryption the {@link HybridPublicKeyEncryption} to use for all the crypto.
+ * @param encapsulationFunc the {@link Function} that will be used to return the correct
+ * {@link EncapsulationParameters} for a given {@link HttpRequest}.
+ * If {@link Function} returns {@code null} no encapsulation will
+ * take place.
+ */
+ public OHttpClientCodec(HybridPublicKeyEncryption encryption, Function encapsulationFunc) {
+ this.encryption = requireNonNull(encryption, "encryption");
+ this.encapsulationFunc = requireNonNull(encapsulationFunc, "encapsulationFunc");
+ }
+
/**
* Parameters that control the OHTTP encapsulation of an HTTP request.
*/
@@ -75,25 +113,80 @@ public interface EncapsulationParameters {
String outerRequestAuthority();
/**
- * Update outer HTTP request headers, if necessary.
- * @param headers {@link HttpHeaders} to be updated.
+ * Create the headers for the other HTTP request.
+ * @return headers
*/
- default void outerRequestUpdateHeaders(HttpHeaders headers) {
+ default HttpHeaders outerRequestHeaders() {
+ return new DefaultHttpHeaders();
}
/**
- * @return {@link OHttpClientContext}.
+ * Return the {@link OHttpCiphersuite}s to use.
+ *
+ * @return the ciphersuites.
*/
- OHttpClientContext context();
- }
+ OHttpCiphersuite ciphersuite();
- /**
- * Get the parameters to encapsulate a {@link HttpRequest} into OHTTP.
- *
- * @param request outbound {@link HttpRequest} intercepted by the handler.
- * @return {@link EncapsulationParameters} object if OHTTP encapsulation is required, or null otherwise.
- */
- protected abstract EncapsulationParameters encapsulationParameters(HttpRequest request);
+ /**
+ * The public key bytes of the server.
+ *
+ * @return bytes.
+ */
+ byte[] serverPublicKeyBytes();
+
+ /**
+ * The {@link OHttpVersion} to use.
+ *
+ * @return the version.
+ */
+ OHttpVersion version();
+
+ /**
+ * Create a simple {@link EncapsulationParameters} instance.
+ *
+ * @param version the version to use.
+ * @param ciphersuite the suite to use.
+ * @param serverPublicKeyBytes the public key to use.
+ * @param outerRequestUri the outer requst uri.
+ * @param outerRequestAuthority the authority.
+ * @return created params.
+ */
+ static EncapsulationParameters newInstance(OHttpVersion version, OHttpCiphersuite ciphersuite,
+ byte[] serverPublicKeyBytes, String outerRequestUri,
+ String outerRequestAuthority) {
+ requireNonNull(version, "version");
+ requireNonNull(ciphersuite, "ciphersuite");
+ requireNonNull(serverPublicKeyBytes, "serverPublicKeysBytes");
+ requireNonNull(outerRequestUri, "outerRequestUri");
+ requireNonNull(outerRequestAuthority, "outerRequestAuthority");
+ return new EncapsulationParameters() {
+ @Override
+ public String outerRequestUri() {
+ return outerRequestUri;
+ }
+
+ @Override
+ public String outerRequestAuthority() {
+ return outerRequestAuthority;
+ }
+
+ @Override
+ public OHttpCiphersuite ciphersuite() {
+ return ciphersuite;
+ }
+
+ @Override
+ public byte[] serverPublicKeyBytes() {
+ return serverPublicKeyBytes;
+ }
+
+ @Override
+ public OHttpVersion version() {
+ return version;
+ }
+ };
+ }
+ }
@Override
public final boolean isSharable() {
@@ -106,30 +199,35 @@ protected final void decode(ChannelHandlerContext ctx, HttpObject msg, List framer;
-
- private final ContentEncoder contentEncoder = new ContentEncoder();
-
- OHttpContentSerializer(OHttpChunkFramer framer) {
- this.framer = framer;
- }
-
- /**
- * Serialize a {@link HttpObject} into a {@link ByteBuf}.
- * @param msg {@link HttpObject} to serialize.
- * @param out {@link ByteBuf} that serialized bytes are appended to.
- */
- public final void serialize(HttpObject msg, ByteBuf out) throws CryptoException {
- framer.serialize(msg, contentEncoder, out);
- }
-
- /**
- * Encrypt a chunk.
- * @param chunk {@link ByteBuf} to encrypt. The function increases the reader index by chunkLength.
- * @param chunkLength length of chunk.
- * @param isFinal true if this is the last chunk.
- * @param out {@link ByteBuf} into which the encrypted bytes are written.
- * @throws CryptoException if the encryption fails.
- */
- protected abstract void encryptChunk(ByteBuf chunk, int chunkLength, boolean isFinal, ByteBuf out)
- throws CryptoException;
-
- /**
- * Encode the beginning of the HTTP content body.
- * @param out buffer to write the bytes.
- * @throws CryptoException if the prefix cannot be encoded.
- */
- protected abstract void encodePrefixNow(ByteBuf out) throws CryptoException;
-
- private class ContentEncoder implements OHttpChunkFramer.Encoder {
-
- private final BinaryHttpSerializer binaryHttpSerializer = new BinaryHttpSerializer();
-
- private boolean encodedPrefix;
-
- @Override
- public final boolean isPrefixNeeded() {
- return !encodedPrefix;
- }
-
- @Override
- public final void encodeChunk(HttpObject msg, ByteBuf out) throws CryptoException {
- ByteBuf binaryHttpBytes = out.alloc().buffer();
- try {
- boolean isFinal = msg instanceof LastHttpContent;
- binaryHttpSerializer.serialize(msg, binaryHttpBytes);
- encryptChunk(binaryHttpBytes, binaryHttpBytes.readableBytes(), isFinal, out);
- } finally {
- binaryHttpBytes.release();
- }
- }
-
- @Override
- public final void encodePrefix(ByteBuf out) throws CryptoException {
- if (encodedPrefix) {
- throw new IllegalStateException("Prefix already encoded");
- }
- encodePrefixNow(out);
- encodedPrefix = true;
- }
- }
-}
diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java
index d008b38..b473859 100644
--- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java
+++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java
@@ -32,7 +32,6 @@
/**
* {@link OHttpCryptoReceiver} handles all the server-side crypto for an OHTTP request/response.
- * It is used internally by {@link OHttpServerContext}.
*/
public final class OHttpCryptoReceiver {
private final OHttpCryptoConfiguration configuration;
diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java
index 5e76364..b4022a9 100644
--- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java
+++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java
@@ -32,7 +32,6 @@
/**
* {@link OHttpCryptoSender} handles all the client-side crypto for an OHTTP request/response.
- * It is used internally by {@link OHttpClientContext}.
*/
public final class OHttpCryptoSender {
diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpContentParser.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpRequestResponseContext.java
similarity index 58%
rename from codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpContentParser.java
rename to codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpRequestResponseContext.java
index 853aa53..407ef69 100644
--- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpContentParser.java
+++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpRequestResponseContext.java
@@ -15,32 +15,98 @@
*/
package io.netty.incubator.codec.ohttp;
-import io.netty.incubator.codec.bhttp.BinaryHttpParser;
-import io.netty.incubator.codec.hpke.CryptoException;
-import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.http.HttpContent;
+import io.netty.incubator.codec.bhttp.BinaryHttpParser;
+import io.netty.incubator.codec.bhttp.BinaryHttpSerializer;
+import io.netty.incubator.codec.hpke.CryptoException;
+import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpObject;
+import io.netty.handler.codec.http.LastHttpContent;
import java.util.List;
import static io.netty.handler.codec.ByteToMessageDecoder.MERGE_CUMULATOR;
/**
- * Parser that parses {@link ByteBuf}s coming from the content of a OHTTP-encoded message
- * into {@link HttpObject}s.
+ * Handler that parses and serializes {@link HttpObject}s to {@link ByteBuf} that can be used as OHTTP message content
+ * during a request-response cycle.
*/
-abstract class OHttpContentParser {
+abstract class OHttpRequestResponseContext {
+ private final OHttpVersion version;
+ private final ContentEncoder contentEncoder = new ContentEncoder();
+ private ContentDecoder decoder = new ContentDecoder();
- private final OHttpChunkFramer framer;
+ OHttpRequestResponseContext(OHttpVersion version) {
+ this.version = version;
+ }
- private ContentDecoder decoder = new ContentDecoder();
+ final OHttpVersion version() {
+ return version;
+ }
- OHttpContentParser(OHttpChunkFramer framer) {
- this.framer = framer;
+ /**
+ * Serialize a {@link HttpObject} into a {@link ByteBuf}.
+ * @param msg {@link HttpObject} to serialize.
+ * @param out {@link ByteBuf} that serialized bytes are appended to.
+ */
+ final void serialize(HttpObject msg, ByteBuf out) throws CryptoException {
+ version.serialize(msg, contentEncoder, out);
}
+ /**
+ * Encrypt a chunk.
+ * @param chunk {@link ByteBuf} to encrypt. The function increases the reader index by chunkLength.
+ * @param chunkLength length of chunk.
+ * @param isFinal true if this is the last chunk.
+ * @param out {@link ByteBuf} into which the encrypted bytes are written.
+ * @throws CryptoException if the encryption fails.
+ */
+ protected abstract void encryptChunk(ByteBuf chunk, int chunkLength, boolean isFinal, ByteBuf out)
+ throws CryptoException;
+
+ /**
+ * Encode the beginning of the HTTP content body.
+ * @param out buffer to write the bytes.
+ * @throws CryptoException if the prefix cannot be encoded.
+ */
+ protected abstract void encodePrefixNow(ByteBuf out) throws CryptoException;
+
+ private final class ContentEncoder implements OHttpChunkFramer.Encoder {
+
+ private final BinaryHttpSerializer binaryHttpSerializer = new BinaryHttpSerializer();
+
+ private boolean encodedPrefix;
+
+ @Override
+ public boolean isPrefixNeeded() {
+ return !encodedPrefix;
+ }
+
+ @Override
+ public void encodeChunk(HttpObject msg, ByteBuf out) throws CryptoException {
+ ByteBuf binaryHttpBytes = out.alloc().buffer();
+ try {
+ boolean isFinal = msg instanceof LastHttpContent;
+ binaryHttpSerializer.serialize(msg, binaryHttpBytes);
+ encryptChunk(binaryHttpBytes, binaryHttpBytes.readableBytes(), isFinal, out);
+ } finally {
+ binaryHttpBytes.release();
+ }
+ }
+
+ @Override
+ public void encodePrefix(ByteBuf out) throws CryptoException {
+ if (encodedPrefix) {
+ throw new IllegalStateException("Prefix already encoded");
+ }
+ encodePrefixNow(out);
+ encodedPrefix = true;
+ }
+ }
+
+
/**
* Parse OHTTP-encoded HTTP content bytes.
*
@@ -51,12 +117,12 @@ abstract class OHttpContentParser {
* @param completeBodyReceived true if there are no more bytes following in.
* @param out List that produced {@link HttpObject} are appended to.
*/
- public final void parse(ByteBuf in, boolean completeBodyReceived, List