Netty5 + HTTPS 练习

  本次以《Netty权威指南》第十章里面的例子为基础修改而来

  HttpsFileServerHandler.java

  1 package com.jieli.nettytest.httpsfile;
  2 
  3 import java.io.File;
  4 import java.io.RandomAccessFile;
  5 import java.net.URLDecoder;
  6 import java.util.regex.Pattern;
  7 
  8 import javax.activation.MimetypesFileTypeMap;
  9 
 10 import io.netty.buffer.ByteBuf;
 11 import io.netty.buffer.Unpooled;
 12 import io.netty.channel.ChannelFuture;
 13 import io.netty.channel.ChannelFutureListener;
 14 import io.netty.channel.ChannelHandlerContext;
 15 import io.netty.channel.ChannelProgressiveFuture;
 16 import io.netty.channel.ChannelProgressiveFutureListener;
 17 import io.netty.channel.SimpleChannelInboundHandler;
 18 import io.netty.handler.codec.http.DefaultFullHttpResponse;
 19 import io.netty.handler.codec.http.DefaultHttpResponse;
 20 import io.netty.handler.codec.http.FullHttpRequest;
 21 import io.netty.handler.codec.http.FullHttpResponse;
 22 import io.netty.handler.codec.http.HttpHeaderNames;
 23 import io.netty.handler.codec.http.HttpHeaderUtil;
 24 import io.netty.handler.codec.http.HttpHeaderValues;
 25 import io.netty.handler.codec.http.HttpMethod;
 26 import io.netty.handler.codec.http.HttpResponse;
 27 import io.netty.handler.codec.http.HttpResponseStatus;
 28 import io.netty.handler.codec.http.HttpVersion;
 29 import io.netty.handler.codec.http.LastHttpContent;
 30 import io.netty.handler.stream.ChunkedFile;
 31 import io.netty.util.CharsetUtil;
 32 
 33 public class HttpsFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest>{
 34 
 35     private final String url;
 36     
 37     public HttpsFileServerHandler(String url){
 38         this.url = url;
 39     }
 40     
 41     @Override
 42     protected void messageReceived(ChannelHandlerContext ctx,
 43             FullHttpRequest request) throws Exception {
 44         if(!request.decoderResult().isSuccess()){
 45             sendError(ctx, HttpResponseStatus.BAD_REQUEST);
 46             return ;
 47         }
 48         if(request.method() != HttpMethod.GET){
 49             sendError(ctx, HttpResponseStatus.METHOD_NOT_ALLOWED);
 50             return ;
 51         }
 52         
 53         final String uri = request.uri();
 54         final String path = sanitizeUri(uri);
 55         if(path == null){
 56             sendError(ctx, HttpResponseStatus.FORBIDDEN);
 57             return ;
 58         }
 59         File file = new File(path);
 60         if(file.isHidden() || !file.exists()){
 61             sendError(ctx, HttpResponseStatus.NOT_FOUND);
 62             return ;
 63         }
 64         if(file.isDirectory()){
 65             if(uri.endsWith("/")){
 66                 sendListing(ctx, file);
 67             }else{
 68                 sendRedirect(ctx, uri+'/');
 69             }
 70             return ;
 71         }
 72         if(!file.isFile()){
 73             sendError(ctx, HttpResponseStatus.FORBIDDEN);
 74         }
 75         RandomAccessFile accessFile = null;
 76         try {
 77             accessFile = new RandomAccessFile(file, "r");
 78         } catch (Exception e) {
 79             e.printStackTrace();
 80             sendError(ctx, HttpResponseStatus.NOT_FOUND);
 81             return ;
 82         }
 83         long len = accessFile.length();
 84         HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
 85         HttpHeaderUtil.setContentLength(response, len);
 86         setContentTypeHeader(response, file);
 87         if(HttpHeaderUtil.isKeepAlive(request)){
 88             response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
 89         }
 90         ctx.write(response);
 91         ChannelFuture future;
 92         future = ctx.write(new ChunkedFile(accessFile, 0, len, 8192), ctx.newProgressivePromise());
 93         future.addListener(new ChannelProgressiveFutureListener() {
 94             
 95             @Override
 96             public void operationComplete(ChannelProgressiveFuture arg0)
 97                     throws Exception {
 98                 System.out.println("Transfer complete.");
 99             }
100             
101             @Override
102             public void operationProgressed(ChannelProgressiveFuture future, long progress,
103                     long total) throws Exception {
104                 if(total < 0){
105                     System.err.println("Transfer progress:" + progress);
106                 }else{
107                     System.err.println("Transfer progress:" + progress +"/" +total);
108                 }
109             }
110         });
111         ChannelFuture lastfuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
112         if(!HttpHeaderUtil.isKeepAlive(request)){
113             lastfuture.addListener(ChannelFutureListener.CLOSE);
114         }
115     }
116     
117     @Override
118     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
119             throws Exception {
120         cause.printStackTrace();
121         if(ctx.channel().isActive()){
122             sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
123         }
124     }
125     
126     private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&"].*");
127     private String sanitizeUri(String uri){
128         try {
129             uri = URLDecoder.decode(uri, "UTF-8");
130         } catch (Exception e) {
131             try {
132                 uri = URLDecoder.decode(uri, "ISO-8859-1");
133             } catch (Exception e2) {
134                 throw new Error();
135             }
136         }
137         
138         if(!uri.startsWith(url)){
139             return null;
140         }
141         
142         if(!uri.startsWith("/")){
143             return null;
144         }
145         
146         uri = uri.replace('/', File.separatorChar);
147         if(uri.contains('.'+File.separator) || uri.startsWith(".")
148                 || uri.endsWith(".") || INSECURE_URI.matcher(uri).matches()){
149             return null;
150         }
151         return System.getProperty("user.dir") + File.separator + uri;
152     }
153     
154     private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\.]*");
155     
156     private static void sendListing(ChannelHandlerContext ctx, File dir){
157         FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
158         response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
159         StringBuilder buf = new StringBuilder();
160         String dirPath = dir.getPath();
161         buf.append("<!DOCTYPE html
");
162         buf.append("<html><head><title>");
163         buf.append(dirPath);
164         buf.append(" 目录: ");
165         buf.append("</title></head><body>
");
166         buf.append("<h3>");
167         buf.append(dirPath).append(" 目录 :");
168         buf.append("</h3>");
169         buf.append("<ul>");
170         buf.append("<li>链接: <a href="../">..</a></li>
");
171         for(File f :dir.listFiles()){
172             if(f.isHidden() || !f.canRead()){
173                 continue;
174             }
175             String name = f.getName();
176             if(!ALLOWED_FILE_NAME.matcher(name).matches()){
177                 continue;
178             }
179             buf.append("<li>链接:<a href="");
180             buf.append(name);
181             buf.append("">");
182             buf.append(name);
183             buf.append("</a></li>
");
184         }
185         buf.append("</ul></body></html>
");
186         ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);
187         response.content().writeBytes(buffer);
188         buffer.release();
189         ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
190     }
191     
192     private static void sendRedirect(ChannelHandlerContext ctx, String newuri){
193         FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FOUND);
194         response.headers().set(HttpHeaderNames.LOCATION, newuri);
195         ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
196     }
197     
198     private static void sendError(ChannelHandlerContext ctx, 
199             HttpResponseStatus status){
200         FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,
201                 Unpooled.copiedBuffer("Failure: " + status.toString()+"
", CharsetUtil.UTF_8));
202         response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
203         ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); //异步发送 发送完成后就关闭连接
204     }
205     
206     private static void setContentTypeHeader(HttpResponse response, File file){
207         MimetypesFileTypeMap typeMap = new MimetypesFileTypeMap();
208         response.headers().set(HttpHeaderNames.CONTENT_TYPE, typeMap.getContentType(file.getPath()));
209     }
210 
211 }
View Code

  HttpsFileServer.java

 1 package com.jieli.nettytest.httpsfile;
 2 
 3 import io.netty.bootstrap.ServerBootstrap;
 4 import io.netty.channel.ChannelFuture;
 5 import io.netty.channel.ChannelInitializer;
 6 import io.netty.channel.EventLoopGroup;
 7 import io.netty.channel.nio.NioEventLoopGroup;
 8 import io.netty.channel.socket.SocketChannel;
 9 import io.netty.channel.socket.nio.NioServerSocketChannel;
10 import io.netty.handler.codec.http.HttpObjectAggregator;
11 import io.netty.handler.codec.http.HttpRequestDecoder;
12 import io.netty.handler.codec.http.HttpResponseEncoder;
13 import io.netty.handler.ssl.SslContext;
14 import io.netty.handler.ssl.util.SelfSignedCertificate;
15 import io.netty.handler.stream.ChunkedWriteHandler;
16 
17 public class HttpsFileServer {
18     private static final String DEFAULT_URL = "/src/com/";
19     
20     
21     public void run(final int port, final String url) throws Exception{
22         
23         final SslContext sslCtx;
24         SelfSignedCertificate ssc = new SelfSignedCertificate();
25          //具体场景要通过文件
26         sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
27         
28         EventLoopGroup bossGroup = new NioEventLoopGroup();
29         EventLoopGroup workerGroup = new NioEventLoopGroup();
30         try {
31             ServerBootstrap b = new ServerBootstrap();
32             b.group(bossGroup, workerGroup)
33              .channel(NioServerSocketChannel.class)
34              .childHandler(new ChannelInitializer<SocketChannel>() {
35                  @Override
36                  protected void initChannel(SocketChannel ch) throws Exception {
37                      ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()));
38                      ch.pipeline().addLast("http_decoder", new HttpRequestDecoder())
39                                    .addLast("http-aggregator", new HttpObjectAggregator(65536))
40                                   .addLast("http-encoder", new HttpResponseEncoder())
41                                   .addLast("http-chunked", new ChunkedWriteHandler())
42                                   .addLast("fileserverhandler", new HttpsFileServerHandler(url));
43                 }
44             });
45             
46             ChannelFuture f = b.bind(port).sync();
47             
48             System.out.println("HTTP File Server Start..  http://localhost:"+port+url);
49             f.channel().closeFuture().sync();
50         } catch (Exception e) {
51             e.printStackTrace();
52         } finally {
53             bossGroup.shutdownGracefully();
54             workerGroup.shutdownGracefully();
55         }
56     }
57     
58     public static void main(String[] args) throws Exception {
59         new HttpsFileServer().run(7777, DEFAULT_URL);
60     }
61 }

   运行结果

原文地址:https://www.cnblogs.com/wunaozai/p/5280877.html