org.glassfish.grizzly.samples.httpserver.blockinghandler.BlockingHttpHandlerSample Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.glassfish.grizzly.samples.httpserver.blockinghandler;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.filterchain.BaseFilter;
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.http.HttpClientFilter;
import org.glassfish.grizzly.http.HttpContent;
import org.glassfish.grizzly.http.HttpRequestPacket;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.grizzly.http.server.Response;
import org.glassfish.grizzly.http.server.ServerConfiguration;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.http.util.HeaderValue;
import org.glassfish.grizzly.impl.FutureImpl;
import org.glassfish.grizzly.impl.SafeFutureImpl;
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.MemoryManager;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;
/**
*
* This example demonstrates the use of a {@link HttpHandler} to echo HTTP
POST
data sent by
* the client, back to the client.
*
*
*
* The is composed of two main parts (as nested classes of BlockingHttpHandlerSample
)
*
* - Client: This is a simple
HTTP
based on the Grizzly {@link HttpClientFilter}. The client uses a
* custom {@link org.glassfish.grizzly.filterchain.Filter} on top of the {@link HttpClientFilter} to send the
* POST
and read, and ultimately display, the response from the server.
* - BlockingEchoHandler: This {@link HttpHandler} is installed to the
* {@link org.glassfish.grizzly.http.server.HttpServer} instance and associated with the path
/echo
. This
* {@link HttpHandler} is fairly simple. The handler uses the {@link java.io.Reader} returned by
* {@link org.glassfish.grizzly.http.server.Request#getReader()} in blocking mode. As data is received, the same data is
* then immediately written to the response.
*
*
*
*/
public class BlockingHttpHandlerSample {
private static final Logger LOGGER = Grizzly.logger(BlockingHttpHandlerSample.class);
public static void main(String[] args) {
// create a basic server that listens on port 8080.
final HttpServer server = HttpServer.createSimpleServer();
final ServerConfiguration config = server.getServerConfiguration();
// Map the path, /echo, to the BlockingEchoHandler
config.addHttpHandler(new BlockingEchoHandler(), "/echo");
try {
server.start();
Client client = new Client();
client.run();
} catch (IOException ioe) {
LOGGER.log(Level.SEVERE, ioe.toString(), ioe);
} finally {
server.shutdownNow();
}
}
// ---------------------------------------------------------- Nested Classes
private static final class Client {
private static final String HOST = "localhost";
private static final int PORT = 8080;
public void run() throws IOException {
final FutureImpl completeFuture = SafeFutureImpl.create();
// Build HTTP client filter chain
FilterChainBuilder clientFilterChainBuilder = FilterChainBuilder.stateless();
// Add transport filter
clientFilterChainBuilder.add(new TransportFilter());
// Add HttpClientFilter, which transforms Buffer <-> HttpContent
clientFilterChainBuilder.add(new HttpClientFilter());
// Add ClientFilter
clientFilterChainBuilder.add(new ClientFilter(completeFuture));
// Initialize Transport
final TCPNIOTransport transport = TCPNIOTransportBuilder.newInstance().build();
transport.setProcessor(clientFilterChainBuilder.build());
try {
// start the transport
transport.start();
Connection connection = null;
// Connecting to a remote Web server
Future connectFuture = transport.connect(HOST, PORT);
try {
// Wait until the client connect operation will be completed
// Once connection has been established, the POST will
// be sent to the server.
connection = connectFuture.get(10, TimeUnit.SECONDS);
// Wait no longer than 30 seconds for the response from the
// server to be complete.
String result = completeFuture.get(30, TimeUnit.SECONDS);
// Display the echoed content
System.out.println("\nEchoed POST Data: " + result + '\n');
} catch (Exception e) {
if (connection == null) {
LOGGER.log(Level.WARNING, "Connection failed. Server is not listening.");
} else {
LOGGER.log(Level.WARNING, "Unexpected error communicating with the server.");
}
} finally {
// Close the client connection
if (connection != null) {
connection.closeSilently();
}
}
} finally {
// stop the transport
transport.shutdownNow();
}
}
// ------------------------------------------------------ Nested Classes
private static final class ClientFilter extends BaseFilter {
private static final HeaderValue HOST_HEADER_VALUE = HeaderValue.newHeaderValue(HOST + ':' + PORT).prepare();
private static final String[] CONTENT = { "contentA-", "contentB-", "contentC-", "contentD" };
private final FutureImpl future;
private final StringBuilder sb = new StringBuilder();
// ---------------------------------------------------- Constructors
private ClientFilter(FutureImpl future) {
this.future = future;
}
// ----------------------------------------- Methods from BaseFilter
@SuppressWarnings({ "unchecked" })
@Override
public NextAction handleConnect(FilterChainContext ctx) throws IOException {
System.out.println("\nClient connected!\n");
HttpRequestPacket.Builder builder = createRequest();
HttpRequestPacket request = builder.build();
request.addHeader(Header.Host, HOST_HEADER_VALUE);
System.out.println("Writing request:\n");
System.out.println(request.toString());
ctx.write(request); // write the request
// for each of the content parts in CONTENT, wrap in a Buffer,
// create the HttpContent to wrap the buffer and write the
// content.
MemoryManager mm = ctx.getConnection().getTransport().getMemoryManager();
for (int i = 0, len = CONTENT.length; i < len; i++) {
HttpContent.Builder contentBuilder = request.httpContentBuilder();
Buffer b = Buffers.wrap(mm, CONTENT[i]);
contentBuilder.content(b);
HttpContent content = contentBuilder.build();
System.out.println(b.toStringContent());
ctx.write(content);
}
// since the request created by createRequest() is chunked,
// we need to write the trailer to signify the end of the
// POST data
ctx.write(request.httpTrailerBuilder().build());
System.out.println("\n");
return ctx.getStopAction(); // discontinue filter chain execution
}
@Override
public NextAction handleRead(FilterChainContext ctx) throws IOException {
HttpContent c = ctx.getMessage();
Buffer b = c.getContent();
if (b.hasRemaining()) {
sb.append(b.toStringContent());
}
// Last content from the server, set the future result so
// the client can display the result and gracefully exit.
if (c.isLast()) {
future.result(sb.toString());
}
return ctx.getStopAction(); // discontinue filter chain execution
}
// ------------------------------------------------- Private Methods
private HttpRequestPacket.Builder createRequest() {
HttpRequestPacket.Builder builder = HttpRequestPacket.builder();
builder.method("POST");
builder.protocol("HTTP/1.1");
builder.uri("/echo");
builder.chunked(true);
return builder;
}
}
} // END Client
/**
* This handler using blocking streams to read POST data and echo it back to the client.
*/
private static class BlockingEchoHandler extends HttpHandler {
// -------------------------------------------- Methods from HttpHandler
@Override
public void service(Request request, Response response) throws Exception {
final char[] buf = new char[128];
Reader in = null;
Writer out = null;
try {
in = request.getReader(); // put the stream in blocking mode
out = response.getWriter();
int read;
while ((read = in.read(buf)) != -1) {
out.write(buf, 0, read); // echo the contents of 'buf' to the client
}
out.flush();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignore) {
}
}
if (out != null) {
try {
out.close();
} catch (IOException ignore) {
}
}
}
}
} // END BlockingEchoHandler
}