Skip to content

Instantly share code, notes, and snippets.

@fsarradin
Created September 5, 2013 12:38
Show Gist options
  • Save fsarradin/6449549 to your computer and use it in GitHub Desktop.
Save fsarradin/6449549 to your computer and use it in GitHub Desktop.
A Web Framework in Scala From Scratch
package scalawebapp
import com.sun.net.httpserver.{HttpExchange, HttpHandler, HttpServer}
import java.net.InetSocketAddress
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
class MyHttpServer(port: Int = 8080) {
type HttpFunction = String => Future[(Int, String)]
val httpServer = HttpServer.create(new InetSocketAddress(port), 0)
def withContext(root: String)(httpFunction: HttpFunction) = {
def send(httpExchange: HttpExchange, responseCode: Int, content: String) {
val response = content.getBytes
httpExchange.sendResponseHeaders(responseCode, response.length)
httpExchange.getResponseBody.write(response)
httpExchange.close()
}
httpServer.createContext("/", new HttpHandler {
def handle(httpExchange: HttpExchange) {
println(Thread.currentThread())
val path: String = httpExchange.getRequestURI.getPath
val futureResponse = httpFunction(path)
futureResponse onComplete {
case Success((responseCode, content)) => send(httpExchange, responseCode, content)
case Failure(e) => send(httpExchange, 500, s"Internal Server Error ${e.getMessage}")
}
}
})
this
}
def start() {
httpServer.start()
}
def stop() {
httpServer.stop(0)
}
}
object MyHttpServer {
implicit def intString2Future(response: (Int, String)) = Future(response)
def main(args: Array[String]) {
new MyHttpServer()
.withContext("/") {
case "/hello" => Future {
println(Thread.currentThread())
(200, "hello")
}
case "/world" => (200, "world")
case badPath => (404, s"Not Found $badPath")
}
.start()
}
}
package scalawebapp
import org.scalatest.{BeforeAndAfterAll, FunSuite}
import scala.io.Source
import org.scalatest.matchers.MustMatchers
import java.io.FileNotFoundException
class MyHttpServerTest extends FunSuite with MustMatchers with BeforeAndAfterAll {
import MyHttpServer.intString2Future
val server = new MyHttpServer(9242)
.withContext("/") {
case "/hello" => (200, "hello")
case "/world" => (200, "world")
case badPath => (404, s"Not Found $badPath")
}
override def beforeAll() {
server.start()
}
override def afterAll() {
server.stop()
}
test("it should say hello when /hello") {
val result = Source.fromURL("http://localhost:9242/hello").mkString
result must be("hello")
}
test("it should say world when /world") {
val result = Source.fromURL("http://localhost:9242/world").mkString
result must be("world")
}
test("it should complain when /whatever") {
evaluating {
Source.fromURL("http://localhost:9242/whatever")
} must produce[FileNotFoundException]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment