Coffee Space


Listen:

Server One Liner

Preview Image

Background

I happened upon a GitHub gist containing a community created list of server one liners. The idea is that the server should fulfil the following:

After reviewing the list at the time, I noticed there were no Java entries that didn’t have some kind of dependency. I wrote a basic Java web server quite some years ago and decided it would be a good exercise in minimalism.

Existing Solutions

Curl

renatoathaydes commented on 10 Mar 2019:

Hi, your list is missing an entry for Java.

I am the author of a HTTP server that fits your requirements. You can install it as a single jar with:

0001 curl https://jcenter.bintray.com/com/athaydes/rawhttp/rawhttp-cli/1.0/rawhttp-cli-1.0-all.jar -o rawhttp.jar

Then, run with:

0002 java -jar ./rawhttp.jar serve . -p 8000

Works with Java 8+ including latest Java 11 versions.

Documentation

Sources

Firstly, random binaries from some unknown place are disgusting - who knows what this code runs without decompiling it.

Secondly it feels as if the use of curl is quite ridiculous, especially as it already has so much web capability within it’s own right.

Sun HTTP Server

informvisu commented on 5 Oct 2019:

All you need is JAVA 1.6+

./httpServer.sh

0003 /opt/java_1.6/bin/javac HTTPServer.java
0004 /opt/java_1.6/bin/java HTTPServer $1

HTTPServer.java

0005 import java.io.IOException;
0006 import java.io.OutputStream;
0007 import java.net.InetAddress;
0008 import java.net.InetSocketAddress;
0009 import java.util.Date;
0010 import com.sun.net.httpserver.HttpExchange;
0011 import com.sun.net.httpserver.HttpHandler;
0012 import com.sun.net.httpserver.HttpServer;
0013 
0014 public class HTTPServer {
0015     static int port = 8000;
0016     public static void main(String[] args) throws Exception {
0017         if(args.length > 0) try {
0018             port = Integer.parseInt(args[0]);
0019         } catch (Exception e) {System.out.println("Invalid port number "+args[0]);}
0020         HttpServer server = HttpServer.create(new InetSocketAddress(InetAddress.getLocalHost(), port), 5);
0021         System.out.println("Server is listening at "+server.getAddress());
0022         server.createContext("/", new MyHandler());
0023         server.setExecutor(null); // creates a default executor
0024         server.start();
0025     }
0026     static class MyHandler implements HttpHandler {
0027         @Override
0028         public void handle(HttpExchange t) throws IOException {
0029             String response = "This is the response"; //TODO construct your own response
0030             t.sendResponseHeaders(200, response.length());
0031             OutputStream os = t.getResponseBody();
0032             os.write(response.getBytes());
0033             os.close();
0034         }
0035     }
0036 }

Okay, we’re getting there, but I still have a few complaints:

  • The imports can be simplified, for example using import java.io.* to reduce to imports down to one.
  • The use of a Sun/Oracle library basically removes the possibility of use with OpenJDK (boo).
  • IS NOT ONE LINE.
  • Doesn’t have to do any hard work - it’s basically all implemented in the libraries.

Solution

The solution I posted is here:

0037 echo -e 'import java.net.*;import java.nio.file.*;public class M{public static void main(String[] args)throws Exception{byte[]b=new byte[16777215];ServerSocket s=new ServerSocket(3333);for(;;)try{Socket c=s.accept();c.getInputStream().read(b);c.getOutputStream().write(("HTTP/1.1 200 OK\\r\\n\\r\\n"+new String(Files.readAllBytes(Paths.get(new String(b).split(" ")[1].substring(1))))).getBytes());c.getOutputStream().flush();c.close();}catch(Exception e){}}}'>M.java;javac M.java;java M

Okay, it’s quite compact and if we un-pack the Java source, we get something like this (with added comments for readability):

0038 /* Imports used by the program */
0039 import java.net.*;
0040 import java.nio.file.*;
0041 
0042 public class M{
0043   /**
0044    * main()
0045    *
0046    * The main entry method into the program. Note that we ignore all command
0047    * line arguments, but Java insists we pretend to need them.
0048    *
0049    * @param args The command line arguments.
0050    **/
0051   public static void main(String[] args) throws Exception{
0052     /* A buffer for reading input files into */
0053     byte[] b = new byte[16777215];
0054     /* Start the server on the given port */
0055     ServerSocket s = new ServerSocket(3333);
0056     /* Infinte loop the server */
0057     for(;;)
0058       /* Catch any thrown exception */
0059       try{
0060         /* Block until there is a new connection */
0061         Socket c = s.accept();
0062         /* Read the input from the client (we could infinitely hang here!) */
0063         c.getInputStream().read(b);
0064         /* Form a response */
0065         c.getOutputStream().write(
0066           (
0067             /* Basic HTTP response header */
0068             "HTTP/1.1 200 OK\\r\\n\\r\\n" +
0069             /* Form the file content */
0070             new String(
0071               Files.readAllBytes(
0072                 Paths.get(
0073                   /* Assume the second "part" is the fiel to be loaded */
0074                   /* Note that we reuse our input buffer as an output buffer */
0075                   new String(b).split(" ")[1].substring(1)
0076                   /* The substring removes the forward-slash */
0077                 )
0078               )
0079             )
0080           /* Convert String to byte array to be written */
0081           ).getBytes()
0082         );
0083         /* Make sure all of the bytes are written before closing */
0084         c.getOutputStream().flush();
0085         /* Be kind and close the stream off */
0086         c.close();
0087       }catch(Exception e){
0088         /* We don't care if this loop dies, just don't crash the server */
0089       }
0090   }
0091 }

I think it almost fulfils the task, but there are some very glaring issues (i.e. never use this code in production):

And that’s just to name a few that instantly stick out at me. For a small test server (on a private network), or teaching students the basics of web servers, it will suffice.

It would actually make a very nice project for cyber security students to explore and report on the very large attack surface, possibly firstly without seeing the code and then after getting to see the code.