Fixed issue: NETTY-330 HttpMessageDecoder cannot decode new handshake messages

* HttpHeaders.getContentLength() returns 10 and 18 for WebSocket handshake messages
* Updated the WebSocket server example to support the new handshake method
This commit is contained in:
Trustin Lee 2010-06-21 09:39:49 +00:00
parent c35c9eb1c3
commit e0545ba583
2 changed files with 75 additions and 6 deletions

View File

@ -22,6 +22,8 @@ import static org.jboss.netty.handler.codec.http.HttpMethod.*;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
import static org.jboss.netty.handler.codec.http.HttpVersion.*;
import java.security.MessageDigest;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
@ -64,7 +66,7 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
}
}
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) {
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
// Allow only GET methods.
if (req.getMethod() != GET) {
sendHttpResponse(
@ -98,11 +100,40 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
res.addHeader(Names.UPGRADE, WEBSOCKET);
res.addHeader(CONNECTION, Values.UPGRADE);
res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
res.addHeader(WEBSOCKET_LOCATION, getWebSocketLocation(req));
String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
if (protocol != null) {
res.addHeader(WEBSOCKET_PROTOCOL, protocol);
// Fill in the headers and contents depending on handshake method.
if (req.containsHeader(SEC_WEBSOCKET_KEY1) &&
req.containsHeader(SEC_WEBSOCKET_KEY2)) {
// New handshake method with a challenge:
res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketLocation(req));
String protocol = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
if (protocol != null) {
res.addHeader(SEC_WEBSOCKET_PROTOCOL, protocol);
}
// Calculate the answer of the challenge.
String key1 = req.getHeader(SEC_WEBSOCKET_KEY1);
String key2 = req.getHeader(SEC_WEBSOCKET_KEY2);
int a = (int) (Long.parseLong(key1.replaceAll("[^0-9]", "")) / key1.replaceAll("[^ ]", "").length());
int b = (int) (Long.parseLong(key2.replaceAll("[^0-9]", "")) / key2.replaceAll("[^ ]", "").length());
long c = req.getContent().readLong();
ChannelBuffer input = ChannelBuffers.buffer(16);
input.writeInt(a);
input.writeInt(b);
input.writeLong(c);
ChannelBuffer output = ChannelBuffers.wrappedBuffer(
MessageDigest.getInstance("MD5").digest(input.array()),
new byte[] { '\r', '\n' });
res.setContent(output);
} else {
// Old handshake method with no challenge:
res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
res.addHeader(WEBSOCKET_LOCATION, getWebSocketLocation(req));
String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
if (protocol != null) {
res.addHeader(WEBSOCKET_PROTOCOL, protocol);
}
}
// Upgrade the connection and send the handshake response.

View File

@ -213,6 +213,26 @@ public class HttpHeaders {
* {@code "Retry-After"}
*/
public static final String RETRY_AFTER = "Retry-After";
/**
* {@code "Sec-WebSocket-Key1"}
*/
public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1";
/**
* {@code "Sec-WebSocket-Key2"}
*/
public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2";
/**
* {@code "Sec-WebSocket-Location"}
*/
public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location";
/**
* {@code "Sec-WebSocket-Origin"}
*/
public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin";
/**
* {@code "Sec-WebSocket-Protocol"}
*/
public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
/**
* {@code "Server"}
*/
@ -604,6 +624,24 @@ public class HttpHeaders {
if (contentLength != null) {
return Long.parseLong(contentLength);
}
// WebSockset messages have constant content-lengths.
if (message instanceof HttpRequest) {
HttpRequest req = (HttpRequest) message;
if (HttpMethod.GET.equals(req.getMethod()) &&
req.containsHeader(Names.SEC_WEBSOCKET_KEY1) &&
req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {
return 10;
}
} else if (message instanceof HttpResponse) {
HttpResponse res = (HttpResponse) message;
if (res.getStatus().getCode() == 101 &&
res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) &&
res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) {
return 18;
}
}
return defaultValue;
}