Last active
December 12, 2015 03:48
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is one way to do it using only Ruby core https://gist.github.com/4709848