1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.example.stomp.websocket;
17
18 import io.netty.channel.ChannelFutureListener;
19 import io.netty.channel.ChannelHandler.Sharable;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.DefaultFileRegion;
22 import io.netty.channel.SimpleChannelInboundHandler;
23 import io.netty.handler.codec.http.DefaultFullHttpResponse;
24 import io.netty.handler.codec.http.DefaultHttpResponse;
25 import io.netty.handler.codec.http.FullHttpRequest;
26 import io.netty.handler.codec.http.FullHttpResponse;
27 import io.netty.handler.codec.http.HttpHeaderNames;
28 import io.netty.handler.codec.http.HttpHeaderValues;
29 import io.netty.handler.codec.http.HttpResponse;
30 import io.netty.handler.codec.http.HttpUtil;
31 import io.netty.handler.codec.http.LastHttpContent;
32 import io.netty.util.CharsetUtil;
33
34 import java.io.FileNotFoundException;
35 import java.io.IOException;
36 import java.io.RandomAccessFile;
37 import java.net.URL;
38
39 import static io.netty.handler.codec.http.HttpHeaderNames.*;
40 import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
41 import static io.netty.handler.codec.http.HttpHeaderValues.*;
42 import static io.netty.handler.codec.http.HttpResponseStatus.*;
43 import static io.netty.handler.codec.http.HttpVersion.*;
44
45 @Sharable
46 public final class StompWebSocketClientPageHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
47
48 static final StompWebSocketClientPageHandler INSTANCE = new StompWebSocketClientPageHandler();
49
50 private StompWebSocketClientPageHandler() {
51 }
52
53 @Override
54 protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
55 if (request.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true)) {
56 ctx.fireChannelRead(request.retain());
57 return;
58 }
59
60 if (request.decoderResult().isFailure()) {
61 FullHttpResponse badRequest = new DefaultFullHttpResponse(request.protocolVersion(), BAD_REQUEST);
62 sendResponse(badRequest, ctx, true);
63 return;
64 }
65
66 if (!sendResource(request, ctx)) {
67 FullHttpResponse notFound = new DefaultFullHttpResponse(request.protocolVersion(), NOT_FOUND);
68 notFound.headers().set(CONTENT_TYPE, TEXT_PLAIN);
69 String payload = "Requested resource " + request.uri() + " not found";
70 notFound.content().writeCharSequence(payload, CharsetUtil.UTF_8);
71 HttpUtil.setContentLength(notFound, notFound.content().readableBytes());
72 sendResponse(notFound, ctx, true);
73 }
74 }
75
76 private static boolean sendResource(FullHttpRequest request, ChannelHandlerContext ctx) {
77 if (request.uri().isEmpty() || !request.uri().startsWith("/")) {
78 return false;
79 }
80
81 String requestResource = request.uri().substring(1);
82 if (requestResource.isEmpty()) {
83 requestResource = "index.html";
84 }
85
86 URL resourceUrl = INSTANCE.getClass().getResource(requestResource);
87 if (resourceUrl == null) {
88 return false;
89 }
90
91 RandomAccessFile raf = null;
92 long fileLength = -1L;
93 try {
94 raf = new RandomAccessFile(resourceUrl.getFile(), "r");
95 fileLength = raf.length();
96 } catch (FileNotFoundException fne) {
97 System.out.println("File not found " + fne.getMessage());
98 return false;
99 } catch (IOException io) {
100 System.out.println("Cannot read file length " + io.getMessage());
101 return false;
102 } finally {
103 if (fileLength < 0 && raf != null) {
104 try {
105 raf.close();
106 } catch (IOException io) {
107
108 }
109 }
110 }
111
112 HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), OK);
113 HttpUtil.setContentLength(response, fileLength);
114
115 String contentType = "application/octet-stream";
116 if (requestResource.endsWith("html")) {
117 contentType = "text/html; charset=UTF-8";
118 } else if (requestResource.endsWith("css")) {
119 contentType = "text/css; charset=UTF-8";
120 } else if (requestResource.endsWith("js")) {
121 contentType = "application/javascript";
122 }
123
124 response.headers().set(CONTENT_TYPE, contentType);
125 sendResponse(response, ctx, false);
126 ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength));
127 ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
128 return true;
129 }
130
131 private static void sendResponse(HttpResponse response, ChannelHandlerContext ctx, boolean autoFlush) {
132 if (HttpUtil.isKeepAlive(response)) {
133 if (response.protocolVersion().equals(HTTP_1_0)) {
134 response.headers().set(CONNECTION, KEEP_ALIVE);
135 }
136 ctx.write(response);
137 } else {
138 response.headers().set(CONNECTION, CLOSE);
139 ctx.write(response).addListener(ChannelFutureListener.CLOSE);
140 }
141
142 if (autoFlush) {
143 ctx.flush();
144 }
145 }
146 }