var socket = use('socket') var c_http = use('net/http') def CRLF = "\r\n" def status_texts = { "200": "OK", "201": "Created", "204": "No Content", "400": "Bad Request", "401": "Unauthorized", "403": "Forbidden", "404": "Not Found", "405": "Method Not Allowed", "500": "Internal Server Error" } function serve(port) { var fd = socket.socket("AF_INET", "SOCK_STREAM") socket.setsockopt(fd, "SOL_SOCKET", "SO_REUSEADDR", true) socket.bind(fd, {address: "127.0.0.1", port: port}) socket.listen(fd, 16) return fd } function parse_request(conn_fd) { var data = socket.recv(conn_fd, 65536) var raw = text(data) var hdr_end = search(raw, CRLF + CRLF) if (hdr_end == null) disrupt var header_text = text(raw, 0, hdr_end) var body_text = text(raw, hdr_end + 4) var lines = array(header_text, CRLF) var parts = array(lines[0], " ") var method = parts[0] var url = parts[1] var qpos = search(url, "?") var path = qpos != null ? text(url, 0, qpos) : url var headers = {} var i = 1 var colon = null var key = null var val = null while (i < length(lines)) { colon = search(lines[i], ": ") if (colon != null) { key = lower(text(lines[i], 0, colon)) val = text(lines[i], colon + 2) headers[key] = val } i = i + 1 } var cl = headers["content-length"] var content_length = null var remaining = null var more = null if (cl != null) content_length = number(cl) if (content_length != null && length(body_text) < content_length) { remaining = content_length - length(body_text) more = socket.recv(conn_fd, remaining) body_text = body_text + text(more) } if (content_length == null || content_length == 0) body_text = null return { method: method, path: path, url: url, headers: headers, body: body_text, _conn: conn_fd } } function accept(server_fd) { var conn = socket.accept(server_fd) return parse_request(conn.socket) } function on_request(server_fd, handler) { var _accept = function() { var conn = socket.accept(server_fd) var req = null var _parse = function() { req = parse_request(conn.socket) } disruption { req = null } _parse() if (req != null) handler(req) socket.on_readable(server_fd, _accept) } socket.on_readable(server_fd, _accept) } function respond(conn, status, headers, body) { var st = status_texts[text(status)] if (st == null) st = "Unknown" var out = "HTTP/1.1 " + text(status) + " " + st + CRLF out = out + "Connection: close" + CRLF var body_str = "" var keys = null var i = 0 if (body != null) { if (is_text(body)) body_str = body else body_str = text(body) } if (headers != null) { keys = array(headers) i = 0 while (i < length(keys)) { out = out + keys[i] + ": " + headers[keys[i]] + CRLF i = i + 1 } } out = out + "Content-Length: " + text(length(body_str)) + CRLF out = out + CRLF + body_str socket.send(conn, out) socket.close(conn) } function sse_open(conn, headers) { var out = "HTTP/1.1 200 OK" + CRLF out = out + "Content-Type: text/event-stream" + CRLF out = out + "Cache-Control: no-cache" + CRLF out = out + "Connection: keep-alive" + CRLF var keys = null var i = 0 if (headers != null) { keys = array(headers) i = 0 while (i < length(keys)) { out = out + keys[i] + ": " + headers[keys[i]] + CRLF i = i + 1 } } out = out + CRLF socket.send(conn, out) } function sse_event(conn, event, data) { var frame = "event: " + event + "\ndata: " + data + "\n\n" var ok = true var _send = function() { socket.send(conn, frame) } disruption { ok = false } _send() return ok } function sse_close(conn) { socket.close(conn) } function request(method, url, headers, body) { var parts = array(url, "/") var host_port = parts[2] var path = "/" + text(array(parts, 3, length(parts)), "/") var hp = array(host_port, ":") var host = hp[0] var port = length(hp) > 1 ? number(hp[1]) : 80 var fd = socket.socket("AF_INET", "SOCK_STREAM") var raw = null var hdr_end = null var _do = function() { socket.connect(fd, {address: host, port: port}) var body_str = "" if (body != null) { if (is_text(body)) body_str = body else body_str = text(body) } var keys = null var i = 0 var req = method + " " + path + " HTTP/1.1" + CRLF req = req + "Host: " + host_port + CRLF req = req + "Connection: close" + CRLF if (headers != null) { keys = array(headers) i = 0 while (i < length(keys)) { req = req + keys[i] + ": " + headers[keys[i]] + CRLF i = i + 1 } } if (length(body_str) > 0) { req = req + "Content-Length: " + text(length(body_str)) + CRLF } req = req + CRLF + body_str socket.send(fd, req) raw = text(socket.recv(fd, 65536)) } disruption { raw = null } _do() socket.close(fd) if (raw == null) return null hdr_end = search(raw, CRLF + CRLF) if (hdr_end == null) return null var header_text = text(raw, 0, hdr_end) var lines = array(header_text, CRLF) var status_parts = array(lines[0], " ") var status = number(status_parts[1]) var resp_headers = {} var hi = 1 var colon = null while (hi < length(lines)) { colon = search(lines[hi], ": ") if (colon != null) { resp_headers[lower(text(lines[hi], 0, colon))] = text(lines[hi], colon + 2) } hi = hi + 1 } return { status: status, headers: resp_headers, body: text(raw, hdr_end + 4) } } function close(fd) { socket.close(fd) } return { serve: serve, accept: accept, on_request: on_request, respond: respond, request: request, sse_open: sse_open, sse_event: sse_event, sse_close: sse_close, close: close, fetch: c_http.fetch }