netty实战二之HTTP服务端开发

Netty http服务端

文件读取服务

HttpFileServerHandler.class

public class HttpFileServerHandler extends SimpleChannelInboundHandler {

    private static final Pattern INSECURE_URL = Pattern.compile(".*[<>&\"].*");
    private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
    private final String url;

    public HttpFileServerHandler(String url) {
        this.url = url;
    }

    private static void sendListing(ChannelHandlerContext ctx, File dir) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
        StringBuilder buf = new StringBuilder();
        String dirPath = dir.getPath();
        buf.append("\r\n");
        buf.append("");
        buf.append(dirPath);
        buf.append("目录:");
        buf.append("\r\n");
        buf.append("

"); buf.append(dirPath).append("目录:"); buf.append("

\r\n"); buf.append("
    "); buf.append("
  • 链接:..
  • \r\n"); for (File f : dir.listFiles()) { if (f.isHidden() || !f.canRead()) { continue; } String name = f.getName(); if (!ALLOWED_FILE_NAME.matcher(name).matches()) { continue; } buf.append("
  • 链接:"); buf.append(name); buf.append("
  • \r\n"); } buf.append("
\r\n"); ByteBuf byteBuf = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8); response.content().writeBytes(byteBuf); byteBuf.release(); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private static void sendRedirect(ChannelHandlerContext ctx, String newUrl) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FOUND); response.headers().set(HttpHeaderNames.LOCATION, newUrl); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer("Failure:" + status.toString() + "\r\n", CharsetUtil.UTF_8)); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8"); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private static void setContentTypeHeader(HttpResponse response, File file) { MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimetypesFileTypeMap.getContentType(file.getPath())); } public static void main(String[] args) { HttpFileServerHandler handler = new HttpFileServerHandler("/netty/src/main/java/com/oneapm/netty/"); System.out.println(handler.sanitizeUri("/netty/src/main/java/com/oneapm/netty/")); } @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { if (!msg.decoderResult().isSuccess()) { sendError(ctx, HttpResponseStatus.BAD_REQUEST); return; } if (msg.method() != HttpMethod.GET) { sendError(ctx, HttpResponseStatus.METHOD_NOT_ALLOWED); return; } final String uri = msg.uri(); final String path = sanitizeUri(uri); if (path == null) { sendError(ctx, HttpResponseStatus.FORBIDDEN); return; } File file = new File(path); if (file.isHidden() || !file.exists()) { sendError(ctx, HttpResponseStatus.NOT_FOUND); return; } if (file.isDirectory()) { if (uri.endsWith("/")) { sendListing(ctx, file); } else { sendRedirect(ctx, uri + "/"); } return; } if (!file.isFile()) { sendError(ctx, HttpResponseStatus.FORBIDDEN); return; } RandomAccessFile randomAccessFile = null; try { randomAccessFile = new RandomAccessFile(file, "r"); } catch (FileNotFoundException e) { sendError(ctx, HttpResponseStatus.NOT_FOUND); return; } long fileLength = randomAccessFile.length(); HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); HttpUtil.setContentLength(response, fileLength); setContentTypeHeader(response, file); if (HttpUtil.isKeepAlive(msg)) { response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); } ctx.write(response); ChannelFuture sendFileFeture = ctx.write(new ChunkedFile(randomAccessFile, 0, fileLength, 8192), ctx.newProgressivePromise()); sendFileFeture.addListener(new ChannelProgressiveFutureListener() { @Override public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception { if (total < 0) { System.err.println("Transfer progress:" + progress); } else { System.err.println("Transfer progress:" + progress + "/" + total); } } @Override public void operationComplete(ChannelProgressiveFuture future) throws Exception { System.out.println("Transfer complete!"); } }); ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (HttpUtil.isKeepAlive(msg)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); if (ctx.channel().isActive()) { sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR); } } private String sanitizeUri(String uri) { try { uri = URLDecoder.decode(uri, "UTF-8"); } catch (UnsupportedEncodingException e) { try { uri = URLDecoder.decode(uri, "ISO-8859-1"); } catch (UnsupportedEncodingException e1) { throw new Error(); } } if (!uri.startsWith(url)) { return null; } if (!uri.startsWith("/")) { return null; } uri = uri.replace('/', File.separatorChar); if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".") || INSECURE_URL.matcher(uri).matches()) { return null; } return System.getProperty("user.dir") + uri; } }

HttpFileServer.class

public class HttpFileServer {
    private static final String DEFAULT_URL = "/netty/src/main/java/com/oneapm/netty/";

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            try {
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        String url = DEFAULT_URL;
        if (args.length > 1)
            url = args[1];
        new HttpFileServer().run(port, url);
    }

    public void run(final int port, final String url) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                    ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
                    ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                    ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                    ch.pipeline().addLast("fileServerHandler", new HttpFileServerHandler(url));
                }
            });
            ChannelFuture future = bootstrap.bind("localhost", port).sync();
            System.out.println("HTTP文件目录服务器启动,网址是 : " + "http://localhost:"
                    + port + url);
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

运行效果

http服务端文件传输

github地址

https://github.com/engimatic/effectivejava/tree/master/netty/src/main/java/com/oneapm/netty/http

你可能感兴趣的:(netty实战二之HTTP服务端开发)