Skip to content

Commit d21940f

Browse files
committed
Body transformer should accept and produce a media type
Motivation: Currently body transformer are applied on the media type whatsoever. A body transformer should declare the list of media type it produces as well as indicate which media type it consumes. These should be taken in account when applying the body transformer or not on the proxied stream. Changes: Add a body transformer produces/consumes method. The generic proxy interceptor uses the produces/consumes to decide whether or not to apply the transformer based on the response accept header. The Body interface has been added a media type that indicates the media type of the body entity which can be used to match against transformers and also let the transformer set a body with a different media type.
1 parent 1b1e0e7 commit d21940f

19 files changed

+1148
-128
lines changed

src/main/java/examples/HttpProxyExamples.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ public void bodyInterceptorTransformer(HttpProxy proxy) {
122122
ProxyInterceptor
123123
.builder()
124124
.transformingResponseBody(
125+
MediaType.APPLICATION_JSON,
126+
MediaType.APPLICATION_OCTET_STREAM,
125127
buffer -> {
126128
// Apply some transformation
127129
return buffer;

src/main/java/io/vertx/httpproxy/Body.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,32 @@ public interface Body {
3131
* @return a reference to this, so the API can be used fluently
3232
*/
3333
static Body body(ReadStream<Buffer> stream, long len) {
34+
return body(stream, len, MediaType.APPLICATION_OCTET_STREAM);
35+
}
36+
37+
static Body body(ReadStream<Buffer> stream, long len, MediaType mediatype) {
38+
return new Body() {
39+
@Override
40+
public String mediaType() {
41+
return mediatype != null ? mediatype.toString() : null;
42+
}
43+
@Override
44+
public long length() {
45+
return len;
46+
}
47+
@Override
48+
public ReadStream<Buffer> stream() {
49+
return stream;
50+
}
51+
};
52+
}
53+
54+
static Body body(ReadStream<Buffer> stream, long len, String mediatype) {
3455
return new Body() {
56+
@Override
57+
public String mediaType() {
58+
return mediatype;
59+
}
3560
@Override
3661
public long length() {
3762
return len;
@@ -52,15 +77,24 @@ public ReadStream<Buffer> stream() {
5277
static Body body(ReadStream<Buffer> stream) {
5378
return body(stream, -1L);
5479
}
55-
80+
81+
static Body body(Buffer buffer) {
82+
return body(buffer, MediaType.APPLICATION_OCTET_STREAM);
83+
}
84+
5685
/**
5786
* Create a new {@code Body} instance.
5887
*
5988
* @param buffer the {@link Buffer} of the body
89+
* @param mediaType the body media type
6090
* @return a reference to this, so the API can be used fluently
6191
*/
62-
static Body body(Buffer buffer) {
92+
static Body body(Buffer buffer, MediaType mediaType) {
6393
return new Body() {
94+
@Override
95+
public String mediaType() {
96+
return mediaType == null ? null : mediaType.toString();
97+
}
6498
@Override
6599
public long length() {
66100
return buffer.length();
@@ -72,6 +106,11 @@ public ReadStream<Buffer> stream() {
72106
};
73107
}
74108

109+
/**
110+
* @return the media type of this body
111+
*/
112+
String mediaType();
113+
75114
/**
76115
*
77116
* Get length of the {@code Body}.

src/main/java/io/vertx/httpproxy/BodyTransformer.java

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,91 @@
11
package io.vertx.httpproxy;
22

3+
import io.vertx.codegen.annotations.GenIgnore;
34
import io.vertx.codegen.annotations.Unstable;
45
import io.vertx.codegen.annotations.VertxGen;
56
import io.vertx.core.buffer.Buffer;
67
import io.vertx.core.json.JsonArray;
78
import io.vertx.core.json.JsonObject;
89
import io.vertx.httpproxy.impl.BodyTransformerImpl;
910

11+
import java.util.Collections;
12+
import java.util.List;
1013
import java.util.function.Function;
1114

1215
/**
1316
* A synchronous function that transforms an HTTP body entity.
1417
*/
1518
@VertxGen
1619
@Unstable
17-
public interface BodyTransformer extends Function<Buffer, Buffer> {
20+
public interface BodyTransformer {
1821

1922
/**
20-
* Create a callback for transform JsonObject.
23+
* @return whether this transformer consumes the {@code mediaType}, {@code mediaType} can be {@code null}
24+
* when the HTTP head does not present a body, the default implementation returns {@code false}
25+
*/
26+
default boolean consumes(MediaType mediaType) {
27+
return false;
28+
}
29+
30+
/**
31+
* @return the media type produced by this transformer, the default implementation returns {@code application/octet-stream}
32+
*/
33+
default MediaType produces(MediaType mediaType) {
34+
return mediaType;
35+
}
36+
37+
@GenIgnore
38+
Function<Buffer, Buffer> transformer(MediaType mediaType);
39+
40+
@GenIgnore(GenIgnore.PERMITTED_TYPE)
41+
static BodyTransformer transformer(MediaType consumedMediaType, MediaType producedMediaType, Function<Buffer, Buffer> transformer) {
42+
return new BodyTransformerImpl(transformer, consumedMediaType, producedMediaType);
43+
}
44+
45+
/**
46+
* Create a transformer that transforms JSON object to JSON object, the transformer accepts and produces {@code application/json}.
2147
*
22-
* @param transformer the operation to transform data
23-
* @return the built callback
48+
* @param fn the operation to transform data
49+
* @return the transformer instance
2450
*/
25-
static BodyTransformer transformJsonObject(Function<JsonObject, JsonObject> transformer) {
26-
return BodyTransformerImpl.transformJsonObject(transformer);
51+
static BodyTransformer transformJsonObject(Function<JsonObject, JsonObject> fn) {
52+
return BodyTransformerImpl.transformJsonObject(fn);
2753
}
2854

2955
/**
30-
* Create a callback for transform JsonArray.
56+
* Create a transformer that transforms JSON array to JSON array, the transformer accepts and produces {@code application/json}.
3157
*
32-
* @param transformer the operation to transform data
33-
* @return the built callback
58+
* @param fn the operation to transform data
59+
* @return the transformer instance
3460
*/
35-
static BodyTransformer transformJsonArray(Function<JsonArray, JsonArray> transformer) {
36-
return BodyTransformerImpl.transformJsonArray(transformer);
61+
static BodyTransformer transformJsonArray(Function<JsonArray, JsonArray> fn) {
62+
return BodyTransformerImpl.transformJsonArray(fn);
3763
}
3864

3965
/**
40-
* Create a callback for transform json with unknown shape.
66+
* Create a transformer that transforms JSON value to JSON value, the transformer accepts and produces {@code application/json}.
4167
*
42-
* @param transformer the operation to transform data
43-
* @return the built callback
68+
* @param fn the operation to transform data
69+
* @return the transformer instance
4470
*/
45-
static BodyTransformer transformJson(Function<Object, Object> transformer) {
46-
return BodyTransformerImpl.transformJson(transformer);
71+
static BodyTransformer transformJson(Function<Object, Object> fn) {
72+
return BodyTransformerImpl.transformJson(fn);
4773
}
4874

4975
/**
50-
* Create a callback for transform texts.
76+
* Create a transformer that transforms text to text, the transformer accepts and produces {@code text/plain}.
5177
*
52-
* @param transformer the operation to transform data
53-
* @return the built callback
78+
* @param fn the operation to transform data
79+
* @return the transformer instance
5480
*/
55-
static BodyTransformer transformText(Function<String, String> transformer, String encoding) {
56-
return BodyTransformerImpl.transformText(transformer, encoding);
81+
static BodyTransformer transformText(Function<String, String> fn, String encoding) {
82+
return BodyTransformerImpl.transformText(fn, encoding);
5783
}
5884

5985
/**
60-
* Create a callback to discard the body.
86+
* Create a transformer that discards the body, the transformer accepts any media type.
6187
*
62-
* @return the built callback
88+
* @return the transformer instance
6389
*/
6490
static BodyTransformer discard() {
6591
return BodyTransformerImpl.discard();
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.vertx.httpproxy;
2+
3+
import io.vertx.codegen.annotations.DataObject;
4+
import io.vertx.codegen.annotations.VertxGen;
5+
import io.vertx.httpproxy.impl.MediaTypeImpl;
6+
7+
import java.util.List;
8+
9+
/**
10+
* Represent a Media type (rfc6838).
11+
*
12+
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
13+
*/
14+
@DataObject
15+
public interface MediaType {
16+
17+
MediaType ANY = MediaType.parse("*/*");
18+
MediaType APPLICATION = MediaType.parse("application/*");
19+
MediaType APPLICATION_OCTET_STREAM = MediaType.parse("application/octet-stream");
20+
MediaType APPLICATION_JSON = MediaType.parse("application/json");
21+
MediaType TEXT = MediaType.parse("text/*");
22+
MediaType TEXT_PLAIN = MediaType.parse("text/plain");
23+
24+
/**
25+
* Parse an accept header which is a list of media types
26+
*
27+
* @param header the header
28+
* @return the list of media types
29+
* @throws IllegalArgumentException when the list is not valid
30+
*/
31+
static List<MediaType> parseAcceptHeader(String header) throws IllegalArgumentException {
32+
return MediaTypeImpl.parseCommaSeparatedList(header, 0);
33+
}
34+
35+
/**
36+
* Parse a media type.
37+
*
38+
* @param s the string representation
39+
* @return the parsed media type or {@code null} when the string to parse is not valid
40+
*/
41+
static MediaType parse(String s) {
42+
return MediaTypeImpl.parseMediaType(s, 0);
43+
}
44+
45+
/**
46+
* @return the type or {@code null} when everything is matched (*)
47+
*/
48+
String type();
49+
50+
/**
51+
* @return the sub type or {@code null} when everything is matched (*)
52+
*/
53+
String subType();
54+
55+
/**
56+
* @return whether the {@code other} mime type is accepted
57+
*/
58+
boolean accepts(MediaType other);
59+
60+
/**
61+
* Return a media type parameter
62+
*
63+
* @param name the parameter name
64+
* @return the value or {@code null}
65+
*/
66+
String parameter(String name);
67+
68+
}

src/main/java/io/vertx/httpproxy/ProxyInterceptorBuilder.java

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import io.vertx.codegen.annotations.VertxGen;
1818
import io.vertx.core.Handler;
1919
import io.vertx.core.MultiMap;
20+
import io.vertx.core.buffer.Buffer;
21+
import io.vertx.httpproxy.impl.BodyTransformerImpl;
2022

2123
import java.util.Set;
2224
import java.util.function.Function;
@@ -31,7 +33,7 @@
3133
* of configuration, that goes for operations on request headers, response headers and query parameters.</p>
3234
*
3335
* <p>Body transformation can be achieved with {@link #transformingResponseBody(BodyTransformer)} and
34-
* {@link #transformingRequestBody(BodyTransformer)}. Body transformation buffer the body content and then apply
36+
* {@link #transformingRequestBody(BodyTransformer)}. Body transformation buffer the body content and then applies
3537
* a transforming function before sending the response.</p>
3638
*/
3739
@VertxGen
@@ -149,7 +151,7 @@ default ProxyInterceptorBuilder transformingRequestBody(BodyTransformer requestT
149151
/**
150152
* <p>Apply a transformation to change the request body when the proxy receives it.</p>
151153
*
152-
* <p>The interceptor fully buffers the request body and then apply the transformation.</p>
154+
* <p>The interceptor fully buffers the request body and then applies the transformation.</p>
153155
*
154156
* @param requestTransformer the operation to apply to the request body
155157
* @param maxBufferedSize the maximum number of buffered bytes, when the buffered amount exceeds an HTTP error is sent
@@ -158,6 +160,30 @@ default ProxyInterceptorBuilder transformingRequestBody(BodyTransformer requestT
158160
@Fluent
159161
ProxyInterceptorBuilder transformingRequestBody(BodyTransformer requestTransformer, long maxBufferedSize);
160162

163+
/**
164+
* Like {@link #transformingRequestBody(MediaType, MediaType, Function, long)} with {@code maxBufferedSize} = {@link #DEFAULT_MAX_BUFFERED_SIZE}
165+
*/
166+
@Fluent
167+
default ProxyInterceptorBuilder transformingRequestBody(MediaType consumedMediaType, MediaType producedMediaType, Function<Buffer, Buffer> requestTransformer) {
168+
return transformingRequestBody(consumedMediaType, producedMediaType, requestTransformer, DEFAULT_MAX_BUFFERED_SIZE);
169+
}
170+
171+
/**
172+
* <p>Apply a transformation to change the request body when the proxy receives it.</p>
173+
*
174+
* <p>The interceptor fully buffers the request body and then applies the transformation.</p>
175+
*
176+
* @param consumedMediaType the media type this transformer understand
177+
* @param producedMediaType the media type this transformer produces
178+
* @param requestTransformer the operation to apply to the request body
179+
* @param maxBufferedSize the maximum number of buffered bytes, when the buffered amount exceeds an HTTP error is sent
180+
* @return the created interceptor
181+
*/
182+
@Fluent
183+
default ProxyInterceptorBuilder transformingRequestBody(MediaType consumedMediaType, MediaType producedMediaType, Function<Buffer, Buffer> requestTransformer, long maxBufferedSize) {
184+
return transformingRequestBody(new BodyTransformerImpl(requestTransformer, consumedMediaType, producedMediaType), maxBufferedSize);
185+
}
186+
161187
/**
162188
* Like {@link #transformingResponseBody(BodyTransformer, long)} with {@code maxBufferedSize} = {@link #DEFAULT_MAX_BUFFERED_SIZE}
163189
*/
@@ -169,7 +195,7 @@ default ProxyInterceptorBuilder transformingResponseBody(BodyTransformer respons
169195
/**
170196
* <p>Apply a transformation to change the response body when the proxy receives it.</p>
171197
*
172-
* <p>The interceptor fully buffers the response body and then apply the transformation.</p>
198+
* <p>The interceptor fully buffers the response body and then applies the transformation.</p>
173199
*
174200
* @param responseTransformer the operation to apply to the response body
175201
* @param maxBufferedSize the maximum number of buffered bytes, when the buffered amount exceeds an HTTP error is sent
@@ -178,4 +204,28 @@ default ProxyInterceptorBuilder transformingResponseBody(BodyTransformer respons
178204
@Fluent
179205
ProxyInterceptorBuilder transformingResponseBody(BodyTransformer responseTransformer, long maxBufferedSize);
180206

207+
/**
208+
* Like {@link #transformingResponseBody(MediaType, MediaType, Function, long)} with {@code maxBufferedSize} = {@link #DEFAULT_MAX_BUFFERED_SIZE}
209+
*/
210+
@Fluent
211+
default ProxyInterceptorBuilder transformingResponseBody(MediaType consumedMediaType, MediaType producedMediaType, Function<Buffer, Buffer> responseTransformer) {
212+
return transformingResponseBody(consumedMediaType, producedMediaType, responseTransformer, DEFAULT_MAX_BUFFERED_SIZE);
213+
}
214+
215+
/**
216+
* <p>Apply a transformation to change the response body when the proxy receives it.</p>
217+
*
218+
* <p>The interceptor fully buffers the response body and then applies the transformation.</p>
219+
*
220+
* @param consumedMediaType the media type this transformer understand
221+
* @param producedMediaType the media type this transformer produces
222+
* @param responseTransformer the operation to apply to the response body
223+
* @param maxBufferedSize the maximum number of buffered bytes, when the buffered amount exceeds an HTTP error is sent
224+
* @return the created interceptor
225+
*/
226+
@Fluent
227+
default ProxyInterceptorBuilder transformingResponseBody(MediaType consumedMediaType, MediaType producedMediaType, Function<Buffer, Buffer> responseTransformer, long maxBufferedSize) {
228+
return transformingResponseBody(new BodyTransformerImpl(responseTransformer, consumedMediaType, producedMediaType), maxBufferedSize);
229+
}
230+
181231
}

src/main/java/io/vertx/httpproxy/ProxyResponse.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,7 @@
1515
import io.vertx.codegen.annotations.VertxGen;
1616
import io.vertx.core.Future;
1717
import io.vertx.core.MultiMap;
18-
import io.vertx.core.buffer.Buffer;
19-
import io.vertx.core.http.HttpClient;
2018
import io.vertx.core.http.HttpClientResponse;
21-
import io.vertx.core.http.HttpServerRequest;
22-
import io.vertx.core.http.HttpServerResponse;
23-
import io.vertx.core.streams.ReadStream;
24-
25-
import java.util.function.Function;
2619

2720
/**
2821
*

0 commit comments

Comments
 (0)