|
import "net" for Server, Socket
|
|
import "io" for File, Directory, Stat
|
|
import "scheduler" for Scheduler
|
|
|
|
class HttpServer {
|
|
construct new(port) {
|
|
_port = port
|
|
_server = Server.bind("0.0.0.0", port)
|
|
System.print("HTTP Server listening on http://localhost:%(port)")
|
|
}
|
|
|
|
run() {
|
|
while (true) {
|
|
var socket = _server.accept()
|
|
Fiber.new {
|
|
handleClient_(socket)
|
|
}.call()
|
|
}
|
|
}
|
|
|
|
handleClient_(socket) {
|
|
var requestData = socket.read()
|
|
if (requestData == null || requestData == "") {
|
|
socket.close()
|
|
return
|
|
}
|
|
|
|
var lines = requestData.split("\r\n")
|
|
if (lines.count == 0) {
|
|
socket.close()
|
|
return
|
|
}
|
|
|
|
var requestLine = lines[0].split(" ")
|
|
if (requestLine.count < 2) {
|
|
sendError_(socket, 400, "Bad Request")
|
|
return
|
|
}
|
|
|
|
var method = requestLine[0]
|
|
var path = requestLine[1]
|
|
|
|
System.print("%(method) %(path)")
|
|
|
|
if (method != "GET") {
|
|
sendError_(socket, 405, "Method Not Allowed")
|
|
return
|
|
}
|
|
|
|
path = path.replace("\%20", " ")
|
|
|
|
if (path.contains("..")) {
|
|
sendError_(socket, 403, "Forbidden")
|
|
return
|
|
}
|
|
|
|
var localPath = "." + path
|
|
if (localPath.endsWith("/")) localPath = localPath[0..-2]
|
|
if (localPath == "") localPath = "."
|
|
|
|
if (!exists(localPath)) {
|
|
sendError_(socket, 404, "Not Found")
|
|
return
|
|
}
|
|
|
|
if (isDirectory(localPath)) {
|
|
serveDirectory_(socket, localPath, path)
|
|
} else {
|
|
serveFile_(socket, localPath)
|
|
}
|
|
|
|
socket.close()
|
|
}
|
|
|
|
exists(path) {
|
|
if (File.exists(path)) return true
|
|
if (Directory.exists(path)) return true
|
|
return false
|
|
}
|
|
|
|
isDirectory(path) {
|
|
return Directory.exists(path)
|
|
}
|
|
|
|
serveDirectory_(socket, localPath, requestPath) {
|
|
var files
|
|
var fiber = Fiber.new {
|
|
files = Directory.list(localPath)
|
|
}
|
|
fiber.try()
|
|
|
|
if (fiber.error != null) {
|
|
sendError_(socket, 500, "Internal Server Error: " + fiber.error)
|
|
return
|
|
}
|
|
|
|
files.sort(Fn.new {|a, b|
|
|
var ba = a.bytes
|
|
var bb = b.bytes
|
|
var len = ba.count
|
|
if (bb.count < len) len = bb.count
|
|
|
|
for (i in 0...len) {
|
|
if (ba[i] < bb[i]) return true
|
|
if (ba[i] > bb[i]) return false
|
|
}
|
|
|
|
return ba.count < bb.count
|
|
})
|
|
|
|
var html = "<!DOCTYPE html><html><head><title>Index of %(requestPath)</title></head><body>"
|
|
html = html + "<h1>Index of %(requestPath)</h1><hr><ul>"
|
|
|
|
if (requestPath != "/") {
|
|
var parent = requestPath.split("/")
|
|
if (parent.count > 1) {
|
|
parent.removeAt(-1)
|
|
var parentPath = parent.join("/")
|
|
if (parentPath == "") parentPath = "/"
|
|
html = html + "<li><a href=\"%(parentPath)\">..</a></li>"
|
|
}
|
|
}
|
|
|
|
for (file in files) {
|
|
var href = requestPath
|
|
if (!href.endsWith("/")) href = href + "/"
|
|
href = href + file
|
|
html = html + "<li><a href=\"%(href)\">%(file)</a></li>"
|
|
}
|
|
|
|
html = html + "</ul><hr></body></html>"
|
|
|
|
sendResponse_(socket, 200, "OK", "text/html", html)
|
|
}
|
|
|
|
serveFile_(socket, localPath) {
|
|
var content
|
|
var fiber = Fiber.new {
|
|
content = File.read(localPath)
|
|
}
|
|
fiber.try()
|
|
|
|
if (fiber.error != null) {
|
|
sendError_(socket, 500, "Error reading file: " + fiber.error)
|
|
return
|
|
}
|
|
|
|
var contentType = "application/octet-stream"
|
|
if (localPath.endsWith(".html")) {
|
|
contentType = "text/html"
|
|
} else if (localPath.endsWith(".txt")) {
|
|
contentType = "text/plain"
|
|
} else if (localPath.endsWith(".wren")) {
|
|
contentType = "text/plain"
|
|
} else if (localPath.endsWith(".c")) {
|
|
contentType = "text/plain"
|
|
} else if (localPath.endsWith(".h")) {
|
|
contentType = "text/plain"
|
|
} else if (localPath.endsWith(".md")) {
|
|
contentType = "text/markdown"
|
|
} else if (localPath.endsWith(".json")) {
|
|
contentType = "application/json"
|
|
} else if (localPath.endsWith(".png")) {
|
|
contentType = "image/png"
|
|
} else if (localPath.endsWith(".jpg")) {
|
|
contentType = "image/jpeg"
|
|
}
|
|
|
|
sendResponse_(socket, 200, "OK", contentType, content)
|
|
}
|
|
|
|
sendError_(socket, code, message) {
|
|
var html = "<h1>%(code) %(message)</h1>"
|
|
sendResponse_(socket, code, message, "text/html", html)
|
|
socket.close()
|
|
}
|
|
|
|
sendResponse_(socket, code, status, contentType, body) {
|
|
var response = "HTTP/1.1 %(code) %(status)\r\n" +
|
|
"Content-Type: %(contentType)\r\n" +
|
|
"Content-Length: %(body.count)\r\n" +
|
|
"Connection: close\r\n" +
|
|
"\r\n" +
|
|
body
|
|
socket.write(response)
|
|
}
|
|
}
|
|
|
|
var server = HttpServer.new(8080)
|
|
server.run() |