Netty-http协议

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
 * @FileName OrderServer.java
 * @Description: 
 *
 * @Date 2016年3月5日 
 * @author Administroter
 * @version 1.0
 * 
 */
public class HttpFileServer {
 
 private static final String DEFAULT_URL = "/src/main/java/com/lxf/netty/";
 
 public void bind(int port,final String url) throws Exception {
  // 配置服务端的NIO线程组
  EventLoopGroup bossGroup = new NioEventLoopGroup();
  EventLoopGroup workerGroup = new NioEventLoopGroup();
  try {
   ServerBootstrap b = new ServerBootstrap();
   b.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .childHandler(new ChannelInitializer<SocketChannel>() {
     @Override
     protected void initChannel(SocketChannel ch) throws Exception {
      //添加请求消息解码器
      ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
      //将消息转为单一的FullHttpRequest或者FullHttpResponse
      ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
      //添加响应客户端编码器
      ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
      //支持异步发送大的码流,但不会占用过多的内存,防止发生java内存溢出
      ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
      ch.pipeline().addLast("fileServerHandler", new HttpFileServerHandler(url));
     }
    });
   ChannelFuture f = b.bind("127.0.0.1",port).sync();
   System.out.println("Http文件服务器已启动,网址是 : " + http://127.0.0.1:+port+url);
   // 等待服务端监听端口关闭
   f.channel().closeFuture().sync();
  } finally {
   // 优雅退出,释放线程池资源
   bossGroup.shutdownGracefully();
   workerGroup.shutdownGracefully();
  }
 }
 
 
 /**
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  String url = DEFAULT_URL;
  int port = 8080;
  if (args != null && args.length > 0) {
   try {
    port = Integer.valueOf(args[0]);
   } catch (NumberFormatException e) {
    // 采用默认值
   }
  }
  new HttpFileServer().bind(port,url);
 }
}
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.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.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.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.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.stream.ChunkedFile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import com.lxf.netty.common.HttpUrlKit;
/**
 * @FileName HttpFileServerHandler.java
 * @Description:
 *
 * @Date 2016年3月8日
 * @author Administroter
 * @version 1.0
 * 
 */
public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
 private String url;
 public HttpFileServerHandler(String url) {
  this.url = url;
 }
 @Override
 protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
  //如果出现编码错误,跳转404路径错误页面
  if (!request.getDecoderResult().isSuccess()) {
   HttpUrlKit.sendError(ctx, BAD_REQUEST);
   return;
  }
  //如果不是浏览器或者表单发送get请求,跳转405错误
  if (request.getMethod() != GET) {
   HttpUrlKit.sendError(ctx, METHOD_NOT_ALLOWED);
  }
  final String uri = request.getUri();
  //对具体的包装具体的url路径
  final String path = HttpUrlKit.sanitizeUri(uri, url);
  if (path == null) {
   HttpUrlKit.sendError(ctx, FORBIDDEN);
   return;
  }
  //获取文件对象
  File file = new File(path);
  //文件属于隐藏文件或者不存在
  if (file.isHidden() || !file.exists()) {
   HttpUrlKit.sendError(ctx, NOT_FOUND);
   return;
  }
  //查看路径名表示的是否是一个目录,如果是,则发送目录的链接给客户端
  if (file.isDirectory()) {
   if (uri.endsWith("/")) {
    HttpUrlKit.sendListing(ctx, file);
   } else {
    HttpUrlKit.sendRedirect(ctx, uri + "/");
   }
   return;
  }
  //查看路径名表示的文件是否是一个标准文件
  if (!file.isFile()) {
   HttpUrlKit.sendError(ctx, FORBIDDEN);
   return;
  }
  //构建随机访问文件的读取和写入
  RandomAccessFile raf = null;
  try {
   raf = new RandomAccessFile(file, "r");
  } catch (FileNotFoundException e) {
   HttpUrlKit.sendError(ctx, NOT_FOUND);
   return;
  }
  //获取文件的长度,构造http答应消息
  long fileLength = raf.length();
  HttpResponse hresp = new DefaultHttpResponse(HTTP_1_1, OK);
  setContentLength(request, fileLength);
  //设置响应文件的mime类型,即文件的扩展名,而客户端浏览器获取这个类型以后,根绝mime来决定采用什么应用程序来处理数据
  HttpUrlKit.setContentTypeHeader(hresp, file);
  if (isKeepAlive(request)) {
   hresp.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
  }
  //发送消息
  ctx.write(hresp);
  ChannelFuture sendFileFuture;
  //将文件写入到发送缓冲区
  sendFileFuture = ctx.write(new ChunkedFile(raf, 0, fileLength, 8192), ctx.newProgressivePromise());
  //增加ChannelFuture的监听
  sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
   public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
    if (total < 0) {
     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.");
   }
  });
  //发送编码结束的空消息体,标识消息体发送完成,同时小勇flush方法将发送消息缓冲区的消息刷新到SocketChannel
  ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
  //如果非keepalive则标识发送完成,关闭连接
  if (!isKeepAlive(request)) {
   lastContentFuture.addListener(ChannelFutureListener.CLOSE);
  }
 }
 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  cause.printStackTrace();
  if (ctx.channel().isActive()) {
   HttpUrlKit.sendError(ctx, INTERNAL_SERVER_ERROR);
  }
 }
}
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.HttpResponseStatus.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.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.CharsetUtil;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.regex.Pattern;
import javax.activation.MimetypesFileTypeMap;
/**
 * @FileName HttpFileServer.java
 * @Description: 
 *
 * @Date 2016年3月7日 
 * @author Administroter
 * @version 1.0
 * 
 */
@SuppressWarnings("restriction")
public class HttpUrlKit {
 
 private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
 private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
 /**
  * @Title: sanitizeUri 
  * @Description:uri路径合法性校验
  * @param uri
  * @param url
  * @return 
  * @author Administroter
  * @date 2016年3月11日
  */
 public static String sanitizeUri(String uri, String url) {
  try {
   //对URL进行解码
   uri = URLDecoder.decode(uri, "UTF-8");
  } catch (UnsupportedEncodingException e) {
   try {
    uri = URLDecoder.decode(uri, "ISO-8859-1");
   } catch (UnsupportedEncodingException e1) {
    throw new Error();
   }
  }
  /**
   * 对uri进行合法性的判断
   */
  if (!uri.startsWith(url)) {
   return null;
  }
  if (!uri.startsWith("/")) {
   return null;
  }
  //将硬编码的文件路径分隔符替换为本地操作系统文件路径分隔符,比如C:\Users\hfgff\Desktop就是将其中的“\”替换成“/”
  uri = uri.replace('/', File.separatorChar);
  //对uri进行第二次合法性验证验证请求的路径当中是否含有"\."或者".\"或者以"."开头或者结尾,再匹配正则规则。
  if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".")
    || INSECURE_URI.matcher(uri).matches()) {
   return null;
  }
  //构造项目目录+uri构造绝对路径返回(System.getProperty("user.dir")表用户当前的工作目录)
  return System.getProperty("user.dir") + File.separator + uri;
 }
 /**
  * @Title: sendListing 
  * @Description:构建请求路径的文件目录
  * @param ctx
  * @param dir 
  * @author Administroter
  * @date 2016年3月11日
  */
 
 public static void sendListing(ChannelHandlerContext ctx, File dir) {
  FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
  //这里显示在浏览器,采用html的格式,设置消息头的类型
  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("Netty学习Dome示例代码目录:");
  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);
  //将缓冲区的消息存放到http答应消息中
  response.content().writeBytes(buffer);
  //释放缓冲区
  buffer.release();
  //将响应消息发送到缓冲区并刷新到SocketChannel中
  ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
 }
 /**
  * @Title: sendRedirect 
  * @Description:
  * @param ctx
  * @param newUri 
  * @author Administroter
  * @date 2016年3月11日
  */
 public 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);
 }
 public 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);
 }
 
 public static void setContentTypeHeader(HttpResponse response, File file) {
  MimetypesFileTypeMap mft = new MimetypesFileTypeMap();
  //获取指定文件的扩展类型,并设置成消息消息头的类型,用于响应客户端后,浏览器根据这个,来决定采用
  //浏览器嵌入的哪个应用程序模块处理响应的数据
  response.headers().set(CONTENT_TYPE, mft.getContentType(file.getPath()));
 }
}

 

你可能感兴趣的:(http协议,netty,netty-http)