11 February 2014

Simple HTTP Server in Java

You may meticulously take care of  correctly setting and configuring sockets' options, binding, accepting and listening to the port, as you have to in C or C++, or you may prefer to benefit of what the Java developers have already done (com.sun.net.httpserver), and focus on your application's logics. With Java you are always on the safe side.

1. Minimal server


This is a simple server listening on port 8080:

1
2
3
4
5
6
7
8
9
10
11
public class httpserver {

  public static void main(String[] args) throws Exception {

    HttpServer server = HttpServer.create(new java.net.InetSocketAddress(8080), 0);
    server.createContext("/", new info());
    server.setExecutor(null);
    server.start();
    System.out.println("> Server running on PORT: 8080...");
  }
}

Actually, you create a class and provide at least one route, in this case, by assigning the handler info() to "/".

2. Routes/Contexts


Note that HttpServer.create takes two parameters, the second being the backlogs. You may set it to a positive number to define the requests' queue size, or you may set it to zero or negative value and let the system use its defaults. Admit that the requests may arrive faster than your server's throughput is capable to handle, i.e. than the queue of backlogs is created. You may address the issue by increasing the hardware power, or, as a software routine solution, by using Threads and multithreading.

You may have as many routes (contexts) as your application needs. Moreover, if the frontal approach to the routes as mere strings doesn't suit you and you need a more sophisticated parsing, then you can consider using RegExp.

For example, assume your request contains parameters in the path (HttpExchange.getRequestURI().getPath()) rather than in the querystring (HttpExchange.getRequestURI().getQuery()), as shown below. Or, put more specifically, you need a handler for all requests on the company's Departments and personnel IDs, like "/dept/HR/id/fin4503/". Then you may check the route with the following regular expression:

1
2
3
4
5
6
7
8
9
String path = "/dept/HR/id/fin4503/";
Matcher match = Pattern.compile("/dept/(.*)/id/(.*)/").matcher(path);
if (match.find()) {
  // match.group(1) contains "HR"
  // match.group(2) contains "fin4503"
}
else {
  // the path doesn't match
}

3. Code sample


The sample below has one single route, which returns an HTML with the basic information on your last request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.io.*;
import java.util.*;
import com.sun.net.httpserver.*;

public class httpserver {

  public static void main(String[] args) throws Exception {

    HttpServer server = HttpServer.create(new java.net.InetSocketAddress(8080), 0);
    server.createContext("/", new info())
    server.setExecutor(null);
    server.start();
    System.out.println("> Server running on PORT: 8080...");
  }

  static class info implements HttpHandler {
    public void handle(HttpExchange t) throws IOException {
      String RES = "<html><body>";
      RES += "<form action='/' method='GET'>";
      RES += "<input type='text' name='issue' value='Check string for tests.'/>";
      RES += "<input type='text' name='answer' value='Another check string.'/>";
      RES += "<input type='submit' value='Submit (get)'/>";
      RES += "</form>";
      RES += "<table style='font:normal 10pt sans-serif;'>";
      RES += "<tr><td bgColor='#ff8'>URI/path:</td><td>" + t.getRequestURI().getPath() + "</td></tr>";
          String q = t.getRequestURI().getQuery();
          if(q != null) {
            String[] arr = q.split("&");
            for(int i=0; i< arr.length; i++) {
              int ix = arr[i].indexOf('=');
              arr[i] = "<b style='color:blue;'>" + arr[i].substring(0, ix) + "</b>: " + java.net.URLDecoder.decode(arr[i].substring(ix+1), "UTF-8");
            }
            RES += "<tr><td bgColor='#ff8' valign='top'>QUERY:</td><td>" + String.join("<br>", arr) + "</td></tr>";
          }
      RES += "<tr><td bgColor='#ff8'>PROTOCOL:</td><td>" + t.getProtocol() + "</td></tr>";
      RES += "<tr><td bgColor='#ff8'>METHOD:</td><td>" + t.getRequestMethod() + "</td></tr>";

      RES += "<tr bgColor='#8f8'><td>HEADERS:</td><td></td></tr>";
      Headers hs = t.getRequestHeaders();
      for(Map.Entry> h : hs.entrySet()) {
        RES += "<tr><td bgColor='#ff8'>"+h.getKey()+"</td><td>" + h.getValue().get(0) + "</td></tr>";;
      }
      RES += "<tr bgColor='#8f8'><td>Content-Type</td><td>"+ hs.get("Content-Type") +"</td></tr>";
      RES += "<tr bgColor='#8f8'><td>Accept</td><td>"+ hs.get("Accept") +"</td></tr>";

      byte[] decoded = Base64.getDecoder().decode(hs.get("Authorization").get(0).substring(6));
      RES += "<tr bgColor='#88f'><td>Authorization</td><td>"+ (new String(decoded)) +"</td></tr>";
      RES += "<tr bgColor='#8f8'><td>Cookie</td><td>"+ hs.get("Cookie") +"</td></tr>";

      RES += "</table></body></html>";
      respond(t, RES, "html");
    }
  }

  static void respond(HttpExchange t, String Res, String type) throws IOException {

    Headers h = t.getResponseHeaders();
    h.set("Content-Type", "text/" + type);
    t.sendResponseHeaders(200, Res.length());
    OutputStream os = t.getResponseBody();
    os.write(Res.getBytes());
    os.close();
  }  
}

The sample above has one single route, which returns an HTML with the basic information on your last request. The repond method write the response back to the client.

4. Authentication


This is pretty much all. Let's see how to perform the basic Authentication in Java.

No comments:

Post a Comment