用Netty实现的一个简单的HTTP服务器,可以处理静态文件,例子中的注释也比较全。主要是对HTTP的理解,接下来的文章中我也会更新一些HTTP相关的文章以及对例子的进一步完善,由浅到深,记录一些我的学习过程!
1.Server
01 | public class HttpServer { |
02 | public static void main(String[] args) { |
03 | ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( |
04 | Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); |
06 | bootstrap.setPipelineFactory( new HttpServerPipelineFactory()); |
08 | bootstrap.bind( new InetSocketAddress(8080)); |
09 | System.out.println( "服务器已经启动,请访问http://127.0.0.1:8080/index.html进行测试!\n\n" ); |
2.Pipeline
01 | public class HttpServerPipelineFactory implements ChannelPipelineFactory { |
02 | public ChannelPipeline getPipeline() throws Exception { |
04 | ChannelPipeline pipeline = pipeline(); |
12 | pipeline.addLast( "decoder" , new HttpRequestDecoder()); |
15 | pipeline.addLast( "encoder" , new HttpResponseEncoder()); |
19 | pipeline.addLast( "chunkedWriter" , new ChunkedWriteHandler()); |
20 | pipeline.addLast( "deflater" , new HttpContentCompressor()); |
21 | pipeline.addLast( "handler" , new HttpRequestHandler()); |
3.handler类
001 | import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected; |
003 | import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive; |
004 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; |
005 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.COOKIE; |
006 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE; |
007 | import static org.jboss.netty.handler.codec.http.HttpResponseStatus.CONTINUE; |
008 | import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK; |
009 | import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; |
012 | import java.io.RandomAccessFile; |
013 | import java.util.List; |
014 | import java.util.Map; |
015 | import java.util.Map.Entry; |
016 | import java.util.Set; |
018 | import org.jboss.netty.buffer.ChannelBuffer; |
019 | import org.jboss.netty.channel.Channel; |
020 | import org.jboss.netty.channel.ChannelFutureListener; |
021 | import org.jboss.netty.channel.ChannelHandlerContext; |
022 | import org.jboss.netty.channel.ExceptionEvent; |
023 | import org.jboss.netty.channel.MessageEvent; |
024 | import org.jboss.netty.channel.SimpleChannelUpstreamHandler; |
025 | import org.jboss.netty.handler.codec.http.Cookie; |
026 | import org.jboss.netty.handler.codec.http.CookieDecoder; |
027 | import org.jboss.netty.handler.codec.http.CookieEncoder; |
028 | import org.jboss.netty.handler.codec.http.DefaultHttpResponse; |
029 | import org.jboss.netty.handler.codec.http.HttpChunk; |
030 | import org.jboss.netty.handler.codec.http.HttpChunkTrailer; |
031 | import org.jboss.netty.handler.codec.http.HttpHeaders; |
032 | import org.jboss.netty.handler.codec.http.HttpMethod; |
033 | import org.jboss.netty.handler.codec.http.HttpRequest; |
034 | import org.jboss.netty.handler.codec.http.HttpResponse; |
035 | import org.jboss.netty.handler.codec.http.HttpResponseStatus; |
036 | import org.jboss.netty.handler.codec.http.QueryStringDecoder; |
037 | import org.jboss.netty.handler.stream.ChunkedFile; |
038 | import org.jboss.netty.util.CharsetUtil; |
040 | public class HttpRequestHandler extends SimpleChannelUpstreamHandler { |
042 | private HttpRequest request; |
043 | private boolean readingChunks; |
046 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { |
047 | if (!readingChunks) { |
048 | HttpRequest request = this .request = (HttpRequest) e.getMessage(); |
049 | String uri = request.getUri(); |
050 | System.out.println( "-----------------------------------------------------------------" ); |
051 | System.out.println( "uri:" +uri); |
052 | System.out.println( "-----------------------------------------------------------------" ); |
059 | if (is100ContinueExpected(request)) { |
063 | for (Map.Entry<String, String> h : request.getHeaders()) { |
064 | System.out.println( "HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n" ); |
067 | QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri()); |
068 | Map<String, List<String>> params = queryStringDecoder.getParameters(); |
069 | if (!params.isEmpty()) { |
070 | for (Entry<String, List<String>> p : params.entrySet()) { |
071 | String key = p.getKey(); |
072 | List<String> vals = p.getValue(); |
073 | for (String val : vals) { |
074 | System.out.println( "PARAM: " + key + " = " + val + "\r\n" ); |
078 | if (request.isChunked()) { |
079 | readingChunks = true ; |
081 | ChannelBuffer content = request.getContent(); |
082 | if (content.readable()) { |
083 | System.out.println(content.toString(CharsetUtil.UTF_8)); |
085 | writeResponse(e, uri); |
088 | HttpChunk chunk = (HttpChunk) e.getMessage(); |
089 | if (chunk.isLast()) { |
090 | readingChunks = false ; |
092 | HttpChunkTrailer trailer = (HttpChunkTrailer) chunk; |
093 | if (!trailer.getHeaderNames().isEmpty()) { |
094 | for (String name : trailer.getHeaderNames()) { |
095 | for (String value : trailer.getHeaders(name)) { |
096 | System.out.println( "TRAILING HEADER: " + name + " = " + value + "\r\n" ); |
100 | writeResponse(e, "/" ); |
102 | System.out.println( "CHUNK: " + chunk.getContent().toString(CharsetUtil.UTF_8) |
108 | private void writeResponse(MessageEvent e, String uri) { |
110 | boolean keepAlive = isKeepAlive(request); |
113 | HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); |
114 | response.setStatus(HttpResponseStatus.OK); |
119 | response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); |
122 | String cookieString = request.getHeader(COOKIE); |
123 | if (cookieString != null ) { |
124 | CookieDecoder cookieDecoder = new CookieDecoder(); |
125 | Set<Cookie> cookies = cookieDecoder.decode(cookieString); |
126 | if (!cookies.isEmpty()) { |
127 | CookieEncoder cookieEncoder = new CookieEncoder( true ); |
128 | for (Cookie cookie : cookies) { |
129 | cookieEncoder.addCookie(cookie); |
131 | response.addHeader(SET_COOKIE, cookieEncoder.encode()); |
134 | final String path = Config.getRealPath(uri); |
135 | File localFile = new File(path); |
137 | if (localFile.isHidden() || !localFile.exists()) { |
142 | if (localFile.isDirectory()) { |
146 | RandomAccessFile raf = null ; |
148 | raf = new RandomAccessFile(localFile, "r" ); |
149 | long fileLength = raf.length(); |
150 | response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(fileLength)); |
151 | Channel ch = e.getChannel(); |
154 | if (!request.getMethod().equals(HttpMethod.HEAD)) { |
155 | ch.write( new ChunkedFile(raf, 0, fileLength, 8192)); |
157 | } catch (Exception e2) { |
158 | e2.printStackTrace(); |
161 | response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); |
164 | e.getFuture().addListener(ChannelFutureListener.CLOSE); |
169 | private void send100Continue(MessageEvent e) { |
170 | HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE); |
171 | e.getChannel().write(response); |
175 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { |
176 | e.getCause().printStackTrace(); |
177 | e.getChannel().close(); |
4.配置类
03 | public static String getRealPath(String uri) { |
04 | StringBuilder sb= new StringBuilder( "/home/guolei/workspace/Test/web" ); |
06 | if (!uri.endsWith( "/" )) { |
5.页面
在项目中新建一个文件夹,名称为web(可以在配置中配置),在文件夹中放入静态页面index.html。
6.启动服务器,测试