Java NIO-based implementation of a simple HTTP server

Source: Internet
Author: User
Tags creative commons attribution readfile

1. Introduction

This article is a practical article in the previous article, in the previous article, I analyzed the principle of selector Selector. In this article, let's say Selector's application, as the title shows, here I implemented a simple HTTP server based on Java NIO. In the following chapters, I will explain in detail the HTTP server implementation process. In addition, the code corresponding to this article has been uploaded to GitHub, the need for pickup, warehouse address is toyhttpd. Well, don't say much, get to the point.

2. Implement

The HTTP server described in this section is a simple implementation that only supports the very few features of the HTTP protocol. This includes identifying the file suffix and returning the corresponding content-type. Support 200, 400, 403, 404, 500 and other error codes. Because of the small number of supported features, the code logic is relatively simple, here is a list:

    1. Processing requests, parsing request headers
    2. Responds to requests, gets the resource path from the request header, detects whether the requested resource path is legitimate
    3. Match Content-type according to file suffix
    4. Reads the file data and sets the Content-length, or returns 404 if the file does not exist
    5. Sets the response header and returns the response header and data to the browser.

Next we follow the process request and response request two steps, say code implementation. Let's take a look at the core code structure, as follows:

/*** TINYHTTPD * * @authorCODE4WT* @date 2018-03-26 22:28:44 */ Public classTINYHTTPD {Private Static Final intDefault_port =8080;Private Static Final intDefault_buffer_size =4096;Private Static FinalString Index_page ="Index.html";Private Static FinalString Static_resource_dir ="Static";Private Static FinalString Meta_resource_dir_prefix ="/meta/";Private Static FinalString Key_value_separator =":";Private Static FinalString CRLF ="\ r \ n";Private intPort Public tinyhttpd() { This(Default_port); } Public tinyhttpd(intPort) { This.Port= port; } Public void Start()throwsIOException {//Initialize ServersocketchannelServersocketchannel SSC = Serversocketchannel.Open(); SSc.Socket().Bind(NewInetsocketaddress ("localhost", port)); SSc.configureblocking(false);//Create SelectorSelector Selector = Selector.Open();//Register eventSSc.Register(Selector, Selectionkey.op_accept); while(true) {intReadynum = Selector.Select();if(Readynum = =0) {Continue; } set<selectionkey> Selectedkeys = selector.Selectedkeys(); iterator<selectionkey> it = Selectedkeys.iterator(); while(it.Hasnext()) {Selectionkey Selectionkey = it.Next(); It.Remove();if(Selectionkey.isacceptable()) {Socketchannel Socketchannel = SSC.Accept(); Socketchannel.configureblocking(false); Socketchannel.Register(Selector, Selectionkey.Op_read); }Else if(Selectionkey.isreadable()) {//Processing requests                    Request(Selectionkey); Selectionkey.Interestops(Selectionkey.Op_write); }Else if(Selectionkey.iswritable()) {//Response Request                    Response(Selectionkey); }            }        }    }Private void Request(Selectionkey Selectionkey)throwsIOException {...}PrivateHeadersParseheader(String headerstr) {...}Private void Response(Selectionkey Selectionkey)throwsIOException {...}Private void Handleok(Socketchannel channel, String Path)throwsIOException {...}Private void Handlenotfound(Socketchannel Channel) {...}Private void handlebadrequest(Socketchannel Channel) {...}Private void Handleforbidden(Socketchannel Channel) {...}Private void Handleinternalservererror(Socketchannel Channel) {...}Private void HandleError(Socketchannel Channel,intStatusCode)throwsIOException {...}PrivateBytebufferReadFile(String Path)throwsIOException {...}PrivateStringgetextension(String Path) {...}Private void Log(String IP, Headers Headers,intCode) {}}

The code above is the code structure of the core class of the HTTP server. Where request is responsible for processing requests, response is responsible for responding to requests. The Handleok method is used to respond to a normal request, handlenotfound, and so on in response to a request that was faulted. The ReadFile method is used to read the resource file, and getextension gets the file suffix.

2.1 Processing Requests

The logic of processing requests is simple, the main task is to parse the message header. The relevant code is as follows:

Private void Request(Selectionkey Selectionkey)throwsIOException {//Read the request header data from the channelSocketchannel channel = (socketchannel) selectionkey.Channel(); Bytebuffer buffer = Bytebuffer.Allocate(default_buffer_size); Channel.Read(buffer); Buffer.Flip();byte[] bytes =New byte[Buffer.Limit()]; Buffer.Get(bytes); String Headerstr =NewString (bytes);Try{//Parse request headerHeaders Headers =Parseheader(HEADERSTR);//Put the request header object into the SelectionkeySelectionkey.Attach(Optional. of(headers)); }Catch(Invalidheaderexception e) {Selectionkey.Attach(Optional.Empty()); }}PrivateHeadersParseheader(String headerstr) {if(Objects.IsNull(HEADERSTR) | | Headerstr.IsEmpty()) {Throw New invalidheaderexception(); }//Parse the first line of the request header    intindex = Headerstr.indexOf(CRLF);if(Index = =-1) {Throw New invalidheaderexception(); } Headers Headers =New Headers(); String firstline = headerstr.substring(0, index); string[] Parts = firstline.Split(" ");/** The first line of the request header must consist of three parts, the METHOD PATH VERSION, respectively* For example:* get/index.html http/1.1     */    if(Parts.length<3) {Throw New invalidheaderexception(); } headers.Setmethod(parts[0]); Headers.SetPath(parts[1]); Headers.setversion(parts[2]);//Parse request header belongs to partParts = headerstr.Split(CRLF); for(String part:parts) {index = part.indexOf(Key_value_separator);if(Index = =-1) {Continue; } String key = part.substring(0, index);if(Index = =-1|| Index +1>= part.length()) {headers.Set(Key,"");Continue; } String value = part.substring(Index +1); Headers.Set(key, value); }returnHeaders;}

Briefly summarize the above code logic, the first is to read the request header from the channel, and then parse the read to the request header, and finally the parsed header object into the Selectionkey. The logic to process the request is simple, not much to say.

2.2 Responding to requests

After reading the logic of processing the request, take a look at the logic of the response request. The code is as follows:

Private void Response(Selectionkey Selectionkey)throwsIOException {Socketchannel channel = (socketchannel) selectionkey.Channel();//Remove the request header object from the SelectionkeyOptionalAttachment();//Handle Invalid request, return 400 error    if(!op.isPresent()) {handlebadrequest(channel); Channel.Close();return; The String IP = channel.getremoteaddress().toString().Replace("/",""); Headers Headers = Op.Get();//If a resource under the/meta/path is requested, it is considered an illegal request and returns a 403 error    if(Headers.GetPath().StartsWith(Meta_resource_dir_prefix)) {Handleforbidden(channel); Channel.Close();Log(IP, headers, FORBIDDEN.GetCode());return; }Try{Handleok(channel, headers.GetPath());Log(IP, headers, OK.)GetCode()); }Catch(FileNotFoundException e) {//File not found, return 404 error        Handlenotfound(channel);Log(IP, headers, not_found.GetCode()); }Catch(Exception e) {//Other exception, return 500 error        Handleinternalservererror(channel);Log(IP, headers, internal_server_error.GetCode()); }finally{Channel.Close(); }}//Handle a normal requestPrivate void Handleok(Socketchannel channel, String Path)throwsIOException {responseheaders headers =New responseheaders(OK.)GetCode());//Read fileBytebuffer Bodybuffer =ReadFile(path);//Set the response headerHeaders.Setcontentlength(Bodybuffer.capacity()); Headers.setContentType(Contenttypeutils.getContentType(getextension(path))); Bytebuffer Headerbuffer = Bytebuffer.Wrap(Headers.toString().getBytes());//Return the response header and resource data togetherChannel.Write(NewBytebuffer[]{headerbuffer, Bodybuffer});}//Processing errors not found by the request resourcePrivate void Handlenotfound(Socketchannel Channel) {Try{HandleError(channel, Not_found.GetCode()); }Catch(Exception e) {Handleinternalservererror(channel); }}Private void HandleError(Socketchannel Channel,intStatusCode)throwsIOException {responseheaders headers =New responseheaders(StatusCode);//Read fileBytebuffer Bodybuffer =ReadFile(String. Format("/%d. html ", StatusCode));//Set the response headerHeaders.Setcontentlength(Bodybuffer.capacity()); Headers.setContentType(Contenttypeutils.getContentType("HTML")); Bytebuffer Headerbuffer = Bytebuffer.Wrap(Headers.toString().getBytes());//Return the response header and resource data togetherChannel.Write(NewBytebuffer[]{headerbuffer, Bodybuffer});}

The code above is slightly longer, but the logic is still relatively simple. First, determine if the request header exists, and whether the resource path is legitimate. If it is legal, then read the resource file and return a 404 error code if the file does not exist. If another exception occurs, a 500 error is returned. If no errors occur, the response header and resource data are returned normally. Here only the core code, the other code is not posted, we go to see it.

2.3 Effects Demo

After analyzing the code, it's easy to watch it next. Here is a code to run, as follows:

3. Summary

The code I posted in this article is written in the process of learning Selector, and the core code is less than 300 lines. By writing code, I also deepened my understanding of Selector. In the process of learning the JDK, we strongly recommend that you write code more. By writing code, and stepping on some pits, to be more proficient in the use of relevant technology. This is one of the feelings I wrote about the NIO series.

Well, here's the end of the article. Thank you for reading!

This article is published under the Knowledge Sharing License Agreement 4.0, the reprint must indicate the source in the obvious position
Coolblog
This document is posted in my personal blog: HTTP://WWW.COOLBLOG.XYZ/?R=CB


This work is licensed under the Creative Commons Attribution-NonCommercial use-no derivative of the 4.0 International License Agreement.

Java NIO-based implementation of a simple HTTP server

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.