Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'HttpLiveRequest/HttpLiveResponses' s aggregate method to plugin dev docs #465

Merged
merged 1 commit into from
Oct 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ public Transformer newBuilder() {
* value according to your application requirements and heap size. When the content
* size stream exceeds the {@code maxContentBytes}, a @{link ContentOverflowException}
* is emitted on the returned observable.
* <p>Please note that {@code HttpLiveRequest} will not be valid anymore after aggregation,
* and thus the returned {@link HttpRequest} should be used instead.
*
* @param maxContentBytes maximum expected content size
* @return a {@link Eventual}
Expand Down
18 changes: 11 additions & 7 deletions docs/developer-guide/plugins-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ into the response headers.
## HTTP Content Transformations

Styx exposes HTTP messages to interceptors as streaming `LiveHttpRequest` and `LiveHttpResponse` messages.
In this form, the interceptors can process the content in a streaming fashion. That is, they they
In this form, the interceptors can process the content in a streaming fashion. That is, they
can look into, and modify the content as it streams through.

Alternatively, live messages can be aggregated to `HttpRequest` or `HttpResponse`
Expand All @@ -181,6 +181,8 @@ aggregation is always an asynchronous operation. This is because the streaming H
exposing the content, in byte buffers, as it arrives from the network, and Styx must wait until
all content has been received.

**Note that once the content is aggregated by using the `aggregate()` method, the `LiveHttpRequest`/ `LiveHttpResponse`
object is not valid anymore and thus the `HttpRequest`/`HttpResponse` returned by `aggregate()` should be used.**

### Aggregating Content into Full Messages

Expand All @@ -195,18 +197,20 @@ public class RequestAggregationPlugin implements Plugin {

@Override
public Eventual<LiveHttpResponse> intercept(LiveHttpRequest request, Chain chain) {
return request.toFullRequest(MAX_CONTENT_LENGTH)
.map(fullRequest -> fullRequest.newBuilder()
.body(new byte[0], true)
.build())
.flatMap(fullRequest -> chain.proceed(fullRequest.toStreamingRequest()));
return request.aggregate(MAX_CONTENT_LENGTH)
.map(fullRequest -> {
String body = fullRequest.bodyAs(UTF_8);
return fullRequest.newBuilder()
.body(body + "EXTRA TEXT", UTF_8)
.build().stream();
}).flatMap(chain::proceed);
}
}
```

`maxContentBytes` is a safety valve that prevents Styx from accumulating too much content
and running out of memory. Styx only accumulates up to `maxContentBytes` of content.
`toFullRequest` fails when the content stream exceeds this amount, and Styx emits a
`aggregate()` fails when the content stream exceeds this amount, and Styx emits a
`ContentOverflowException`,


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
Copyright (C) 2013-2019 Expedia Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.hotels.styx;

import com.hotels.styx.api.Eventual;
import com.hotels.styx.api.HttpRequest;
import com.hotels.styx.api.LiveHttpRequest;
import com.hotels.styx.api.LiveHttpResponse;
import com.hotels.styx.api.plugins.spi.Plugin;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;

/**
* Example to show how we can read the whole content and aggregate it in memory.
* Remember that the original {@link LiveHttpRequest} will not be valid anymore after aggregating the content,
* and thus you must operate on the {@link HttpRequest} object returned by aggregate.
*
* For example, if the message content contains a JSON object and you need to modify the object somehow.
* You can aggregate the live HTTP message into a full HTTP message. Transform the content into a full message context
* and convert the results back to live HTTP message
*
*This can be used when you need the entire content in order to operate on it.
*Please note that this uses more heap space as the full request is transiently stored in the heap.
*
*/

public class AggregateRequestBodyExamplePlugin implements Plugin {
private final Config config;

public AggregateRequestBodyExamplePlugin(Config config) {
this.config = requireNonNull(config);
}

@Override
public Eventual<LiveHttpResponse> intercept(LiveHttpRequest request, Chain chain) {

return request.aggregate(10000)
.map(fullRequest -> {
String body = fullRequest.bodyAs(UTF_8);
return fullRequest.newBuilder()
.body(body + config.extraText(), UTF_8)
.build().stream();
}).flatMap(chain::proceed);
}

/**
* Config for example plugin.
*/
public static class Config {
private final String extraText;

public Config(String extraText) {
this.extraText = requireNonNull(extraText);
}

public String extraText() {
return extraText;
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright (C) 2013-2019 Expedia Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.hotels.styx;

import com.hotels.styx.AggregateRequestBodyExamplePlugin.Config;
import com.hotels.styx.api.ByteStream;
import com.hotels.styx.api.Eventual;
import com.hotels.styx.api.HttpInterceptor;
import com.hotels.styx.api.LiveHttpRequest;
import com.hotels.styx.api.LiveHttpResponse;
import org.testng.annotations.Test;
import reactor.core.publisher.Mono;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;


public class AggregateRequestBodyExamplePluginTest {

@Test
public void contentIsModified() {
// Set up
Config config = new Config("MyExtraText");
String originalBody = "OriginalBody";
AggregateRequestBodyExamplePlugin plugin = new AggregateRequestBodyExamplePlugin(config);
LiveHttpRequest request = LiveHttpRequest.post("/", ByteStream.from(originalBody, UTF_8)).build();

// Our implementation of "chain.proceed()" verifies the content of the request
HttpInterceptor.Chain chain = intRequest -> {
String requestBody = Mono.from(intRequest.aggregate(100)).block().bodyAs(UTF_8);
assertThat(requestBody, is(originalBody + config.extraText()));
return Eventual.of(LiveHttpResponse.response().build());
};
// Invoke plugin and wait until it finishes execution
Mono.from(plugin.intercept(request, chain)).block();
}
}