Skip to content

Instantly share code, notes, and snippets.

@fsarradin
Last active December 12, 2015 03:48
Show Gist options
  • Save fsarradin/4709182 to your computer and use it in GitHub Desktop.
Save fsarradin/4709182 to your computer and use it in GitHub Desktop.
Two ways to create a basic Web application from scratch with route management (in Scala and in Java 8)
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
public class WebMain {
public static void main(String[] args) throws IOException {
WebServer.serve(8080, "/").withRoute(
inCaseOf("/poney",
httpExchange -> closeOn(httpExchange, 200, "<html><body><h1>Poney Islandais</h1></body></html>")),
inCaseOf("/hello",
httpExchange -> closeOn(httpExchange, 200, "Hello world")),
otherwise(httpExchange -> closeOn(httpExchange, 404, "Not Found"))
);
}
static interface HttpFunction {
void apply(HttpExchange httpExchange) throws IOException;
}
static interface RouteMatcher {
boolean match(HttpExchange httpExchange) throws IOException;
void apply(HttpExchange httpExchange) throws IOException;
}
static class PathRouteMatcher implements RouteMatcher {
private String path;
private HttpFunction httpFunction;
PathRouteMatcher(String path, HttpFunction httpFunction) {
this.path = path;
this.httpFunction = httpFunction;
}
@Override
public boolean match(HttpExchange httpExchange) throws IOException {
return path.equals(httpExchange.getRequestURI().getPath());
}
@Override
public void apply(HttpExchange httpExchange) throws IOException {
httpFunction.apply(httpExchange);
}
}
static class OtherwiseRouteMatcher implements RouteMatcher {
private HttpFunction httpFunction;
OtherwiseRouteMatcher(HttpFunction httpFunction) {
this.httpFunction = httpFunction;
}
@Override
public boolean match(HttpExchange httpExchange) throws IOException {
return true;
}
@Override
public void apply(HttpExchange httpExchange) throws IOException {
httpFunction.apply(httpExchange);
}
}
static class RoutePatternMatcher {
private final RouteMatcher[] routeMatchers;
public RoutePatternMatcher(RouteMatcher... routeMatchers) {
this.routeMatchers = routeMatchers;
}
public void match(HttpExchange httpExchange) throws IOException {
for (RouteMatcher routeMatcher : routeMatchers) {
if (routeMatcher.match(httpExchange)) {
routeMatcher.apply(httpExchange);
}
}
}
}
static class WebServer {
private final int port;
private final String root;
public WebServer(int port, String root) {
this.port = port;
this.root = root;
}
public static WebServer serve(int port, String root) throws IOException {
return new WebServer(port, root);
}
public void withRoute(RouteMatcher... routeMatchers) throws IOException {
RoutePatternMatcher routePatternMatcher = new RoutePatternMatcher(routeMatchers);
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext(root, httpExchange -> {
routePatternMatcher.match(httpExchange);
});
System.out.println("Server started at port " + port + " (http://localhost:" + port + "/)");
server.start();
}
}
private static void closeOn(HttpExchange httpExchange, int responseCode, String content) throws IOException {
byte[] response = content.getBytes();
httpExchange.sendResponseHeaders(responseCode, response.length);
httpExchange.getResponseBody().write(response);
httpExchange.close();
}
public static RouteMatcher inCaseOf(String path, HttpFunction httpFunction) {
return new PathRouteMatcher(path, httpFunction);
}
public static RouteMatcher otherwise(HttpFunction httpFunction) {
return new OtherwiseRouteMatcher(httpFunction);
}
}
import com.sun.net.httpserver.{HttpExchange, HttpHandler, HttpServer}
import java.net.InetSocketAddress
import concurrent.Future
import concurrent.ExecutionContext.Implicits.global
import util.{Failure, Success}
object WebMain {
type HttpFunction = String => Future[(Int, String)]
def createContextFor(server: HttpServer, root: String)(httpFunction: HttpFunction) {
server.createContext(root, new HttpHandler {
def handle(httpExchange: HttpExchange) {
val path: String = httpExchange.getRequestURI.getPath
.stripPrefix(root)
.stripPrefix("/")
.stripSuffix("/")
println(Thread.currentThread().getName)
val futureResponse: Future[(Int, String)] = httpFunction(path)
futureResponse.onComplete {
case Success((responseCode, content)) =>
send(httpExchange, responseCode, content)
case Failure(e) =>
send(httpExchange, 500, s"Server Error ${e.getMessage}")
}
}
})
}
def send(httpExchange: HttpExchange, responseCode: Int, content: String) {
val response = content.getBytes
httpExchange.sendResponseHeaders(responseCode, response.length)
httpExchange.getResponseBody.write(response)
httpExchange.close()
}
class RichHttpServer(server: HttpServer) {
def withContext(root: String)(httpFunction: HttpFunction) = {
createContextFor(server, root)(httpFunction)
this
}
def start() {
println(s"server started on port ${server.getAddress.getPort}...")
server.start()
}
}
def createServer(port: Int): RichHttpServer =
new RichHttpServer(HttpServer.create(new InetSocketAddress(port), 0))
implicit def intString2Future(httpResponse: => (Int, String)): Future[(Int, String)] =
Future(httpResponse)
def main(args: Array[String]) {
createServer(8080)
.withContext("/") {
case "hello" => {
println(Thread.currentThread().getName)
Thread.sleep(2000)
(200, "hello")
}
case "world" => (200, "world")
case badPath => (404, s"Not Found $badPath")
}
.withContext("/geronimo") {
case "hello" => (200, "(geronimo) hello")
case "world" => (200, "(geronimo) world")
case badPath => (404, s"(geronimo) Not Found $badPath")
}
.start()
}
}
@simcap
Copy link

simcap commented Feb 4, 2013

Here is one way to do it using only Ruby core https://gist.github.com/4709848

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment