SPDY client example
Demonstrates the usage of SPDY from a client perspective. One can also use a SPDY-enabled browser as a client, but it’s easier to understand the internals of the protocol from a client point-of-view if you have some code you can debug.
This commit is contained in:
parent
5c4063b6a9
commit
20d2fb8c2e
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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 io.netty.example.spdyclient;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.example.http.snoop.HttpSnoopClientHandler;
|
||||||
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
import io.netty.handler.codec.http.HttpObject;
|
||||||
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a modified version of {@link HttpSnoopClientHandler} that uses a {@link BlockingQueue} to wait until an
|
||||||
|
* HTTPResponse is received.
|
||||||
|
*/
|
||||||
|
public class HttpResponseClientHandler extends SimpleChannelInboundHandler<HttpObject> {
|
||||||
|
|
||||||
|
private final BlockingQueue<ChannelFuture> queue = new LinkedBlockingQueue<ChannelFuture>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
|
||||||
|
if (msg instanceof HttpResponse) {
|
||||||
|
HttpResponse response = (HttpResponse) msg;
|
||||||
|
|
||||||
|
System.out.println("STATUS: " + response.getStatus());
|
||||||
|
System.out.println("VERSION: " + response.getProtocolVersion());
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
if (!response.headers().isEmpty()) {
|
||||||
|
for (String name : response.headers().names()) {
|
||||||
|
for (String value : response.headers().getAll(name)) {
|
||||||
|
System.out.println("HEADER: " + name + " = " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HttpHeaders.isTransferEncodingChunked(response)) {
|
||||||
|
System.out.println("CHUNKED CONTENT {");
|
||||||
|
} else {
|
||||||
|
System.out.println("CONTENT {");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg instanceof HttpContent) {
|
||||||
|
HttpContent content = (HttpContent) msg;
|
||||||
|
|
||||||
|
System.out.print(content.content().toString(CharsetUtil.UTF_8));
|
||||||
|
System.out.flush();
|
||||||
|
|
||||||
|
if (content instanceof LastHttpContent) {
|
||||||
|
System.out.println("} END OF CONTENT");
|
||||||
|
this.queue.add(ctx.channel().newSucceededFuture());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
|
this.queue.add(ctx.channel().newFailedFuture(cause));
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockingQueue<ChannelFuture> queue() {
|
||||||
|
return this.queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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 io.netty.example.spdyclient;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An SPDY client that allows you to send HTTP GET to a SPDY server.
|
||||||
|
* <p>
|
||||||
|
* This class must be run with the JVM parameter: {@code java -Xbootclasspath/p:<path_to_npn_boot_jar> ...}. The
|
||||||
|
* "path_to_npn_boot_jar" is the path on the file system for the NPN Boot Jar file which can be downloaded from Maven at
|
||||||
|
* coordinates org.mortbay.jetty.npn:npn-boot. Different versions applies to different OpenJDK versions. See
|
||||||
|
* {@link http://www.eclipse.org/jetty/documentation/current/npn-chapter.html Jetty docs} for more information.
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public class SpdyClient {
|
||||||
|
|
||||||
|
private final String host;
|
||||||
|
private final int port;
|
||||||
|
private final HttpResponseClientHandler httpResponseHandler;
|
||||||
|
private Channel channel;
|
||||||
|
private EventLoopGroup workerGroup;
|
||||||
|
|
||||||
|
public SpdyClient(String host, int port) {
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
|
this.httpResponseHandler = new HttpResponseClientHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
if (this.channel != null) {
|
||||||
|
System.out.println("Already running!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.workerGroup = new NioEventLoopGroup();
|
||||||
|
|
||||||
|
Bootstrap b = new Bootstrap();
|
||||||
|
b.group(workerGroup);
|
||||||
|
b.channel(NioSocketChannel.class);
|
||||||
|
b.option(ChannelOption.SO_KEEPALIVE, true);
|
||||||
|
b.remoteAddress(new InetSocketAddress(this.host, this.port));
|
||||||
|
b.handler(new SpdyClientInitializer(this.httpResponseHandler));
|
||||||
|
|
||||||
|
// Start the client.
|
||||||
|
this.channel = b.connect().syncUninterruptibly().channel();
|
||||||
|
System.out.println("Connected to [" + this.host + ":" + this.port + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
try {
|
||||||
|
// Wait until the connection is closed.
|
||||||
|
this.channel.close().syncUninterruptibly();
|
||||||
|
} finally {
|
||||||
|
if (this.workerGroup != null) {
|
||||||
|
this.workerGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelFuture send(HttpRequest request) {
|
||||||
|
// Sends the HTTP request.
|
||||||
|
return this.channel.writeAndFlush(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest get() {
|
||||||
|
HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "");
|
||||||
|
request.headers().set(HttpHeaders.Names.HOST, this.host);
|
||||||
|
request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockingQueue<ChannelFuture> httpResponseQueue() {
|
||||||
|
return this.httpResponseHandler.queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
int port;
|
||||||
|
if (args.length > 0) {
|
||||||
|
port = Integer.parseInt(args[0]);
|
||||||
|
} else {
|
||||||
|
port = 8443;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SpdyClient client = new SpdyClient("localhost", port);
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.start();
|
||||||
|
ChannelFuture requestFuture = client.send(client.get()).sync();
|
||||||
|
|
||||||
|
if (!requestFuture.isSuccess()) {
|
||||||
|
requestFuture.cause().printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waits for the complete HTTP response
|
||||||
|
ChannelFuture responseFuture = client.httpResponseQueue().poll(5, SECONDS);
|
||||||
|
|
||||||
|
if (!responseFuture.isSuccess()) {
|
||||||
|
responseFuture.cause().printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Finished SPDY HTTP GET");
|
||||||
|
} finally {
|
||||||
|
client.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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 io.netty.example.spdyclient;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.spdy.SpdyVersion.SPDY_3_1;
|
||||||
|
import static io.netty.util.internal.logging.InternalLogLevel.INFO;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.example.securechat.SecureChatSslContextFactory;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyFrameDecoder;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyFrameEncoder;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyHttpDecoder;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyHttpEncoder;
|
||||||
|
import io.netty.handler.codec.spdy.SpdySessionHandler;
|
||||||
|
import io.netty.handler.ssl.SslHandler;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.npn.NextProtoNego;
|
||||||
|
|
||||||
|
public class SpdyClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
|
|
||||||
|
private final HttpResponseClientHandler httpResponseHandler;
|
||||||
|
|
||||||
|
public SpdyClientInitializer(HttpResponseClientHandler httpResponseHandler) {
|
||||||
|
this.httpResponseHandler = httpResponseHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int MAX_SPDY_CONTENT_LENGTH = 1024 * 1024; // 1 MB
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
SSLEngine engine = SecureChatSslContextFactory.getClientContext().createSSLEngine();
|
||||||
|
engine.setUseClientMode(true);
|
||||||
|
NextProtoNego.put(engine, new SpdyClientProvider());
|
||||||
|
NextProtoNego.debug = true;
|
||||||
|
|
||||||
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
|
|
||||||
|
pipeline.addLast("ssl", new SslHandler(engine));
|
||||||
|
pipeline.addLast("spdyEncoder", new SpdyFrameEncoder(SPDY_3_1));
|
||||||
|
pipeline.addLast("spdyDecoder", new SpdyFrameDecoder(SPDY_3_1));
|
||||||
|
pipeline.addLast("spdyFrameLogger", new SpdyFrameLogger(INFO));
|
||||||
|
pipeline.addLast("spdySessionHandler", new SpdySessionHandler(SPDY_3_1, false));
|
||||||
|
pipeline.addLast("spdyHttpEncoder", new SpdyHttpEncoder(SPDY_3_1));
|
||||||
|
pipeline.addLast("spdyHttpDecoder", new SpdyHttpDecoder(SPDY_3_1, MAX_SPDY_CONTENT_LENGTH));
|
||||||
|
pipeline.addLast("spdyStreamIdHandler", new SpdyClientStreamIdHandler());
|
||||||
|
pipeline.addLast("httpHandler", httpResponseHandler);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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 io.netty.example.spdyclient;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol.HTTP_1_1;
|
||||||
|
import static io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol.SPDY_3_1;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Jetty project provides an implementation of the Transport Layer Security (TLS) extension for Next Protocol
|
||||||
|
* Negotiation (NPN) for OpenJDK 7 or greater. NPN allows the application layer to negotiate which protocol to use
|
||||||
|
* over the secure connection.
|
||||||
|
* <p>
|
||||||
|
* This NPN service provider negotiates using SPDY.
|
||||||
|
* <p>
|
||||||
|
* To enable NPN support, start the JVM with: {@code java -Xbootclasspath/p:<path_to_npn_boot_jar> ...}. The
|
||||||
|
* "path_to_npn_boot_jar" is the path on the file system for the NPN Boot Jar file which can be downloaded from Maven
|
||||||
|
* at coordinates org.mortbay.jetty.npn:npn-boot. Different versions applies to different OpenJDK versions.
|
||||||
|
*
|
||||||
|
* @see http://www.eclipse.org/jetty/documentation/current/npn-chapter.html
|
||||||
|
*/
|
||||||
|
public class SpdyClientProvider implements ClientProvider {
|
||||||
|
|
||||||
|
private String selectedProtocol;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String selectProtocol(List<String> protocols) {
|
||||||
|
if (protocols.contains(SPDY_3_1.protocolName())) {
|
||||||
|
return SPDY_3_1.protocolName();
|
||||||
|
}
|
||||||
|
return selectedProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unsupported() {
|
||||||
|
this.selectedProtocol = HTTP_1_1.protocolName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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 io.netty.example.spdyclient;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPromise;
|
||||||
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyHttpHeaders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a unique client stream ID to the SPDY header. Client stream IDs MUST be odd.
|
||||||
|
*/
|
||||||
|
public class SpdyClientStreamIdHandler extends ChannelHandlerAdapter {
|
||||||
|
|
||||||
|
private int currentStreamId = 1;
|
||||||
|
|
||||||
|
public boolean acceptOutboundMessage(Object msg) throws Exception {
|
||||||
|
return msg instanceof HttpMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
|
if (acceptOutboundMessage(msg)) {
|
||||||
|
HttpMessage httpMsg = (HttpMessage) msg;
|
||||||
|
if (!httpMsg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) {
|
||||||
|
SpdyHttpHeaders.setStreamId(httpMsg, this.currentStreamId);
|
||||||
|
// Client stream IDs are always odd
|
||||||
|
currentStreamId += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.write(ctx, msg, promise);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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 io.netty.example.spdyclient;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPromise;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyFrame;
|
||||||
|
import io.netty.util.internal.logging.InternalLogLevel;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs SPDY frames for debugging purposes.
|
||||||
|
*/
|
||||||
|
public class SpdyFrameLogger extends ChannelHandlerAdapter {
|
||||||
|
|
||||||
|
private enum Direction {
|
||||||
|
INBOUND, OUTBOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final InternalLogger logger;
|
||||||
|
private final InternalLogLevel level;
|
||||||
|
|
||||||
|
public SpdyFrameLogger(InternalLogLevel level) {
|
||||||
|
if (level == null) {
|
||||||
|
throw new NullPointerException("level");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger = InternalLoggerFactory.getInstance(getClass());
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
if (acceptMessage(msg)) {
|
||||||
|
log((SpdyFrame) msg, Direction.INBOUND);
|
||||||
|
}
|
||||||
|
super.channelRead(ctx, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
|
if (acceptMessage(msg)) {
|
||||||
|
log((SpdyFrame) msg, Direction.OUTBOUND);
|
||||||
|
}
|
||||||
|
super.write(ctx, msg, promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean acceptMessage(Object msg) throws Exception {
|
||||||
|
return msg instanceof SpdyFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void log(SpdyFrame msg, Direction d) {
|
||||||
|
if (logger.isEnabled(this.level)) {
|
||||||
|
StringBuilder b = new StringBuilder("\n----------------").append(d.name()).append("--------------------\n");
|
||||||
|
b.append(msg.toString());
|
||||||
|
b.append("\n------------------------------------");
|
||||||
|
logger.log(this.level, b.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains an example SPDY HTTP client. It will behave like a SPDY-enabled browser and you can see the
|
||||||
|
* SPDY frames flowing in and out using the {@link io.netty.example.spdyclient.SpdyFrameLogger}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This package relies on the Jetty project's implementation of the Transport Layer Security (TLS) extension for Next
|
||||||
|
* Protocol Negotiation (NPN) for OpenJDK 7 is required. NPN allows the application layer to negotiate which
|
||||||
|
* protocol, SPDY or HTTP, to use.
|
||||||
|
* <p>
|
||||||
|
* To start, run {@link io.netty.example.spdy.SpdyServer} with the JVM parameter:
|
||||||
|
* {@code java -Xbootclasspath/p:<path_to_npn_boot_jar> ...}.
|
||||||
|
* The "path_to_npn_boot_jar" is the path on the file system for the NPN Boot Jar file which can be downloaded from
|
||||||
|
* Maven at coordinates org.mortbay.jetty.npn:npn-boot. Different versions applies to different OpenJDK versions.
|
||||||
|
* See {@link http://www.eclipse.org/jetty/documentation/current/npn-chapter.html Jetty docs} for more information.
|
||||||
|
* <p>
|
||||||
|
* After that, you can run {@link io.netty.example.spdyclient.SpdyClient}, also settings the JVM parameter
|
||||||
|
* mentioned above.
|
||||||
|
*/
|
||||||
|
package io.netty.example.spdyclient;
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user