* +doc #20192 explain need of draining entities in server/client HTTP * missing javadsl for Connection header * Update HttpClientExampleDocTest.java
This commit is contained in:
parent
9683e4bc58
commit
60fb163331
20 changed files with 766 additions and 16 deletions
|
|
@ -4,31 +4,137 @@
|
|||
|
||||
package docs.http.javadsl;
|
||||
|
||||
import akka.Done;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.javadsl.ConnectHttp;
|
||||
import akka.http.javadsl.HostConnectionPool;
|
||||
import akka.japi.Pair;
|
||||
|
||||
import akka.japi.pf.ReceiveBuilder;
|
||||
import akka.stream.Materializer;
|
||||
import akka.util.ByteString;
|
||||
import scala.compat.java8.FutureConverters;
|
||||
import scala.concurrent.ExecutionContextExecutor;
|
||||
import scala.concurrent.Future;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.javadsl.*;
|
||||
import akka.http.javadsl.OutgoingConnection;
|
||||
import akka.http.javadsl.model.*;
|
||||
import akka.http.javadsl.Http;
|
||||
import scala.util.Try;
|
||||
|
||||
import static akka.http.javadsl.ConnectHttp.toHost;
|
||||
import static akka.pattern.PatternsCS.*;
|
||||
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
//#manual-entity-consume-example-1
|
||||
import java.io.File;
|
||||
import akka.actor.ActorSystem;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.javadsl.Framing;
|
||||
import akka.http.javadsl.model.*;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
import scala.util.Try;
|
||||
//#manual-entity-consume-example-1
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class HttpClientExampleDocTest {
|
||||
|
||||
HttpResponse responseFromSomewhere() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void manualEntityComsumeExample() {
|
||||
//#manual-entity-consume-example-1
|
||||
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final HttpResponse response = responseFromSomewhere();
|
||||
|
||||
final Function<ByteString, ByteString> transformEachLine = line -> line /* some transformation here */;
|
||||
|
||||
final int maximumFrameLength = 256;
|
||||
|
||||
response.entity().getDataBytes()
|
||||
.via(Framing.delimiter(ByteString.fromString("\n"), maximumFrameLength, FramingTruncation.ALLOW))
|
||||
.map(transformEachLine::apply)
|
||||
.runWith(FileIO.toPath(new File("/tmp/example.out").toPath()), materializer);
|
||||
//#manual-entity-consume-example-1
|
||||
}
|
||||
|
||||
private
|
||||
//#manual-entity-consume-example-2
|
||||
final class ExamplePerson {
|
||||
final String name;
|
||||
public ExamplePerson(String name) { this.name = name; }
|
||||
}
|
||||
|
||||
public ExamplePerson parse(ByteString line) {
|
||||
return new ExamplePerson(line.utf8String());
|
||||
}
|
||||
//#manual-entity-consume-example-2
|
||||
|
||||
void manualEntityConsumeExample2() {
|
||||
//#manual-entity-consume-example-2
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final HttpResponse response = responseFromSomewhere();
|
||||
|
||||
// toStrict to enforce all data be loaded into memory from the connection
|
||||
final CompletionStage<HttpEntity.Strict> strictEntity = response.entity()
|
||||
.toStrict(FiniteDuration.create(3, TimeUnit.SECONDS).toMillis(), materializer);
|
||||
|
||||
// while API remains the same to consume dataBytes, now they're in memory already:
|
||||
|
||||
final CompletionStage<ExamplePerson> person =
|
||||
strictEntity
|
||||
.thenCompose(strict ->
|
||||
strict.getDataBytes()
|
||||
.runFold(ByteString.empty(), (acc, b) -> acc.concat(b), materializer)
|
||||
.thenApply(this::parse)
|
||||
);
|
||||
|
||||
//#manual-entity-consume-example-2
|
||||
}
|
||||
|
||||
void manualEntityDiscardExample1() {
|
||||
//#manual-entity-discard-example-1
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final HttpResponse response = responseFromSomewhere();
|
||||
|
||||
final HttpMessage.DiscardedEntity discarded = response.discardEntityBytes(materializer);
|
||||
|
||||
discarded.completionStage().whenComplete((done, ex) -> {
|
||||
System.out.println("Entity discarded completely!");
|
||||
});
|
||||
//#manual-entity-discard-example-1
|
||||
}
|
||||
|
||||
void manualEntityDiscardExample2() {
|
||||
//#manual-entity-discard-example-2
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final HttpResponse response = responseFromSomewhere();
|
||||
|
||||
final CompletionStage<Done> discardingComplete = response.entity().getDataBytes().runWith(Sink.ignore(), materializer);
|
||||
|
||||
discardingComplete.whenComplete((done, ex) -> {
|
||||
System.out.println("Entity discarded completely!");
|
||||
});
|
||||
//#manual-entity-discard-example-2
|
||||
}
|
||||
|
||||
|
||||
// compile only test
|
||||
public void testConstructRequest() {
|
||||
//#outgoing-connection-example
|
||||
|
|
|
|||
|
|
@ -4,26 +4,37 @@
|
|||
|
||||
package docs.http.javadsl.server;
|
||||
|
||||
import akka.Done;
|
||||
import akka.NotUsed;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.javadsl.ConnectHttp;
|
||||
import akka.http.javadsl.Http;
|
||||
import akka.http.javadsl.IncomingConnection;
|
||||
import akka.http.javadsl.ServerBinding;
|
||||
import akka.http.javadsl.marshallers.jackson.Jackson;
|
||||
import akka.http.javadsl.model.*;
|
||||
import akka.http.javadsl.model.headers.Connection;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.server.Unmarshaller;
|
||||
import akka.japi.function.Function;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.IOResult;
|
||||
import akka.stream.Materializer;
|
||||
import akka.stream.javadsl.FileIO;
|
||||
import akka.stream.javadsl.Flow;
|
||||
import akka.stream.javadsl.Sink;
|
||||
import akka.stream.javadsl.Source;
|
||||
import akka.util.ByteString;
|
||||
import scala.concurrent.ExecutionContextExecutor;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static akka.http.javadsl.server.Directives.*;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class HttpServerExampleDocTest {
|
||||
|
||||
|
|
@ -205,4 +216,113 @@ public class HttpServerExampleDocTest {
|
|||
public static void main(String[] args) throws Exception {
|
||||
fullServerExample();
|
||||
}
|
||||
|
||||
|
||||
//#consume-entity-directive
|
||||
class Bid {
|
||||
final String userId;
|
||||
final int bid;
|
||||
|
||||
Bid(String userId, int bid) {
|
||||
this.userId = userId;
|
||||
this.bid = bid;
|
||||
}
|
||||
}
|
||||
//#consume-entity-directive
|
||||
|
||||
void consumeEntityUsingEntityDirective() {
|
||||
//#consume-entity-directive
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final Unmarshaller<HttpEntity, Bid> asBid = Jackson.unmarshaller(Bid.class);
|
||||
|
||||
final Route s = path("bid", () ->
|
||||
put(() ->
|
||||
entity(asBid, bid ->
|
||||
// incoming entity is fully consumed and converted into a Bid
|
||||
complete("The bid was: " + bid)
|
||||
)
|
||||
)
|
||||
);
|
||||
//#consume-entity-directive
|
||||
}
|
||||
|
||||
void consumeEntityUsingRawDataBytes() {
|
||||
//#consume-raw-dataBytes
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final Route s =
|
||||
put(() ->
|
||||
path("lines", () ->
|
||||
withoutSizeLimit(() ->
|
||||
extractDataBytes(bytes -> {
|
||||
final CompletionStage<IOResult> res = bytes.runWith(FileIO.toPath(new File("/tmp/example.out").toPath()), materializer);
|
||||
|
||||
return onComplete(() -> res, ioResult ->
|
||||
// we only want to respond once the incoming data has been handled:
|
||||
complete("Finished writing data :" + ioResult));
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
//#consume-raw-dataBytes
|
||||
}
|
||||
|
||||
void discardEntityUsingRawBytes() {
|
||||
//#discard-discardEntityBytes
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final Route s =
|
||||
put(() ->
|
||||
path("lines", () ->
|
||||
withoutSizeLimit(() ->
|
||||
extractRequest(r -> {
|
||||
final CompletionStage<Done> res = r.discardEntityBytes(materializer).completionStage();
|
||||
|
||||
return onComplete(() -> res, done ->
|
||||
// we only want to respond once the incoming data has been handled:
|
||||
complete("Finished writing data :" + done));
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
//#discard-discardEntityBytes
|
||||
}
|
||||
|
||||
void discardEntityManuallyCloseConnections() {
|
||||
//#discard-close-connections
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ExecutionContextExecutor dispatcher = system.dispatcher();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final Route s =
|
||||
put(() ->
|
||||
path("lines", () ->
|
||||
withoutSizeLimit(() ->
|
||||
extractDataBytes(bytes -> {
|
||||
// Closing connections, method 1 (eager):
|
||||
// we deem this request as illegal, and close the connection right away:
|
||||
bytes.runWith(Sink.cancelled(), materializer); // "brutally" closes the connection
|
||||
|
||||
// Closing connections, method 2 (graceful):
|
||||
// consider draining connection and replying with `Connection: Close` header
|
||||
// if you want the client to close after this request/reply cycle instead:
|
||||
return respondWithHeader(Connection.create("close"), () ->
|
||||
complete(StatusCodes.FORBIDDEN, "Not allowed!")
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
//#discard-close-connections
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue