8.netty开发http协议

来源:互联网 发布:java技术培训 编辑:程序博客网 时间:2024/06/08 19:55

1.介绍

http协议:使用httpUrl来确定网络资源(url是一种特殊类型的uri)

http请求包含三个部分:请求行、消息头、请求正文

HttpRequest/HttpResponse详见《Netty权威指南 第2版》p155


2.netty开发http协议

这里使用netty开发一个http文件服务器。可以通过浏览器发送htpp请求访问本工程文件

handler:

package com.tyf.netty;import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;import static io.netty.handler.codec.http.HttpHeaders.setContentLength;import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;import static io.netty.handler.codec.http.HttpHeaders.Names.LOCATION;import static io.netty.handler.codec.http.HttpMethod.GET;import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;import static io.netty.handler.codec.http.HttpResponseStatus.FOUND;import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;import static io.netty.handler.codec.http.HttpResponseStatus.OK;import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelProgressiveFuture;import io.netty.channel.ChannelProgressiveFutureListener;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.handler.codec.http.DefaultFullHttpResponse;import io.netty.handler.codec.http.DefaultHttpResponse;import io.netty.handler.codec.http.FullHttpRequest;import io.netty.handler.codec.http.FullHttpResponse;import io.netty.handler.codec.http.HttpHeaders;import io.netty.handler.codec.http.HttpResponse;import io.netty.handler.codec.http.HttpResponseStatus;import io.netty.handler.codec.http.LastHttpContent;import io.netty.handler.stream.ChunkedFile;import io.netty.util.CharsetUtil;import java.io.File;import java.io.FileNotFoundException;import java.io.RandomAccessFile;import java.io.UnsupportedEncodingException;import java.net.URLDecoder;import java.util.regex.Pattern;import javax.activation.MimetypesFileTypeMap;/** * @author lilinfeng * @date 2014年2月14日 * @version 1.0 */public class MyServerHandler extendsSimpleChannelInboundHandler<FullHttpRequest> {    private final String url;    public MyServerHandler(String url) {this.url = url;    }    @Override    public void messageReceived(ChannelHandlerContext ctx,    FullHttpRequest request) throws Exception {if (!request.getDecoderResult().isSuccess()) {    sendError(ctx, BAD_REQUEST);    return;}if (request.getMethod() != GET) {    sendError(ctx, METHOD_NOT_ALLOWED);    return;}final String uri = request.getUri();final String path = sanitizeUri(uri);if (path == null) {    sendError(ctx, FORBIDDEN);    return;}File file = new File(path);if (file.isHidden() || !file.exists()) {    sendError(ctx, NOT_FOUND);    return;}if (file.isDirectory()) {    if (uri.endsWith("/")) {sendListing(ctx, file);    } else {sendRedirect(ctx, uri + '/');    }    return;}if (!file.isFile()) {    sendError(ctx, FORBIDDEN);    return;}RandomAccessFile randomAccessFile = null;try {    randomAccessFile = new RandomAccessFile(file, "r");// 以只读的方式打开文件} catch (FileNotFoundException fnfe) {    sendError(ctx, NOT_FOUND);    return;}long fileLength = randomAccessFile.length();HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);setContentLength(response, fileLength);setContentTypeHeader(response, file);if (isKeepAlive(request)) {    response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);}ctx.write(response);ChannelFuture sendFileFuture;sendFileFuture = ctx.write(new ChunkedFile(randomAccessFile, 0,fileLength, 8192), ctx.newProgressivePromise());sendFileFuture.addListener(new ChannelProgressiveFutureListener() {    public void operationProgressed(ChannelProgressiveFuture future,    long progress, long total) {if (total < 0) { // total unknown    System.err.println("Transfer progress: " + progress);} else {    System.err.println("Transfer progress: " + progress + " / "    + total);}    }    public void operationComplete(ChannelProgressiveFuture future)    throws Exception {System.out.println("Transfer complete.");    }});ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);if (!isKeepAlive(request)) {    lastContentFuture.addListener(ChannelFutureListener.CLOSE);}    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)    throws Exception {cause.printStackTrace();if (ctx.channel().isActive()) {    sendError(ctx, INTERNAL_SERVER_ERROR);}    }    private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");    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_URI.matcher(uri).matches()) {    return null;}return System.getProperty("user.dir") + File.separator + uri;    }    private static final Pattern ALLOWED_FILE_NAME = Pattern    .compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");        //返回一个文件目录,以及每个文件的路径    private static void sendListing(ChannelHandlerContext ctx, File dir) {FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");StringBuilder buf = new StringBuilder();String dirPath = dir.getPath();buf.append("<!DOCTYPE html>\r\n");buf.append("<html><head><title>");buf.append(dirPath);buf.append(" 目录:");buf.append("</title></head><body>\r\n");buf.append("<h3>");buf.append(dirPath).append(" 目录:");buf.append("</h3>\r\n");buf.append("<ul>");buf.append("<li>链接:<a href=\"../\">..</a></li>\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("<li>链接:<a href=\"");    buf.append(name);    buf.append("\">");    buf.append(name);    buf.append("</a></li>\r\n");}buf.append("</ul></body></html>\r\n");ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);response.content().writeBytes(buffer);buffer.release();ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);    }    //访问成功设置跳转    private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);response.headers().set(LOCATION, newUri);ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);    }    //返回各种错误状态码    private static void sendError(ChannelHandlerContext ctx,    HttpResponseStatus status) {FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,status, Unpooled.copiedBuffer("Failure: " + status.toString()+ "\r\n", CharsetUtil.UTF_8));response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);    }    //设置response_content_type_header    private static void setContentTypeHeader(HttpResponse response, File file) {MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();response.headers().set(CONTENT_TYPE,mimeTypesMap.getContentType(file.getPath()));    }}


server:

package com.tyf.netty;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelHandlerAdapter;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.ChannelHandler.Sharable;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.DelimiterBasedFrameDecoder;import io.netty.handler.codec.FixedLengthFrameDecoder;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.handler.codec.LengthFieldPrepender;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpRequestDecoder;import io.netty.handler.codec.http.HttpResponseDecoder;import io.netty.handler.codec.http.HttpResponseEncoder;import io.netty.handler.codec.protobuf.ProtobufDecoder;import io.netty.handler.codec.protobuf.ProtobufEncoder;import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.stream.ChunkedWriteHandler;public class Server {//创建起步程序public static void  main(String [] argsStrings) throws Exception {//配置服务端NIO线程组(boss线程、worker线程)EventLoopGroup bGroup = new NioEventLoopGroup();EventLoopGroup wGroup = new NioEventLoopGroup();//创建启动辅助类ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bGroup, wGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel channel) throws Exception {//http请求解码器channel.pipeline().addLast(new HttpRequestDecoder());//将多个消息转换为单一fullHttpRequest/Response,因为http解码器在每个消息中会生成多个http对象channel.pipeline().addLast(new HttpObjectAggregator(65536));channel.pipeline().addLast(new HttpResponseEncoder());//这个用于支持大文件传输但是防止java内存溢出channel.pipeline().addLast(new ChunkedWriteHandler());channel.pipeline().addLast(new MyServerHandler("/src/main/java/com/tyf/netty"));} });try {//监听本地端口,同步等待监听结果ChannelFuture future = bootstrap.bind(11111).sync();//等待服务端监听端口关闭,优雅退出future.channel().closeFuture().sync();}finally {bGroup.shutdownGracefully();wGroup.shutdownGracefully();} }}

工程目录:


3.测试

(1)访问正确的地址:http://localhost:11111/src/main/java/com/tyf/netty/

本个文件都是文本文件可以直接在浏览器中打开查看。点击可以下载文件。
(2)访问错误的地址:
状态码403:


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 西安热力公司 太原市热力公司 高德如何打开热力图 和平精英怎么上热力榜 热动 4000张gif福利动图汤不热 嵌入式激烈拍拍动图热米资讯 热勇 火爆热区下载 热卖 淘宝热卖 热炒热卖 地摊货热卖 热卖产品 淘宝热卖网 夜市地摊热卖 淘宝热卖男装品牌 冬季地摊热卖产品 淘宝热卖排行榜 淘宝热卖单品排行榜 地摊热卖玩具 淘宝热卖商品排行榜 地摊热卖小玩具 地摊玩具热卖 淘宝热卖单品报名 春季热卖商品 淘宝热卖类目 淘宝热卖指数 淘宝首页热卖 淘宝热卖女鞋 淘宝热卖鞋子 淘宝热卖的商品 淘宝的掌柜热卖 淘宝手机热卖排行榜 热销 展销会热销产品批发 热销产品 淘宝热销商品排行榜 热销产品排行榜 淘宝右边的掌柜热卖 卡奇特即热式热水器