Created
January 1, 2020 20:29
-
-
Save GavinRay97/1aeea7630f4ca147e8df23f25546da45 to your computer and use it in GitHub Desktop.
V match-expression web routing
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
module main | |
import ( | |
os | |
net | |
net.http | |
net.urllib | |
strings | |
) | |
pub const ( | |
methods_with_form = ['POST', 'PUT', 'PATCH'] | |
method_all = ['GET','POST','PUT','PATCH','DELETE'] | |
HEADER_SERVER = 'Server: VWeb\r\n' | |
HEADER_CONNECTION_CLOSE = 'Connection: close\r\n' | |
HEADERS_CLOSE = '${HEADER_SERVER}${HEADER_CONNECTION_CLOSE}\r\n' | |
HTTP_404 = 'HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\n${HEADERS_CLOSE}404 Not Found' | |
HTTP_500 = 'HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain\r\n${HEADERS_CLOSE}500 Internal Server Error' | |
MAX_HTTP_POST_SIZE = 1024 * 1024 | |
Default_Port = 8080 | |
) | |
pub struct Context { | |
pub: | |
req http.Request | |
conn net.Socket | |
// TODO Response | |
mut: | |
headers string // response headers | |
done bool | |
} | |
fn (ctx mut Context) send_response_to_client(mimetype string, res string) bool { | |
if ctx.done { return false } | |
ctx.done = true | |
mut sb := strings.new_builder(1024) | |
sb.write('HTTP/1.1 200 OK\r\nContent-Type: ') sb.write(mimetype) | |
sb.write('\r\nContent-Length: ') sb.write(res.len.str()) | |
sb.write(ctx.headers) | |
sb.write('\r\n') | |
sb.write(HEADERS_CLOSE) | |
sb.write(res) | |
ctx.conn.send_string(sb.str()) or { return false } | |
sb.free() | |
return true | |
} | |
pub fn (ctx mut Context) html(s string) { | |
ctx.send_response_to_client('text/html', s) | |
} | |
pub fn (ctx mut Context) text(s string) { | |
ctx.send_response_to_client('text/plain', s) | |
} | |
pub fn (ctx mut Context) json(s string) { | |
ctx.send_response_to_client('application/json', s) | |
} | |
pub fn (ctx mut Context) redirect(url string) { | |
if ctx.done { return } | |
ctx.done = true | |
ctx.conn.send_string('HTTP/1.1 302 Found\r\nLocation: ${url}${ctx.headers}\r\n${HEADERS_CLOSE}') or { return } | |
} | |
pub fn (ctx mut Context) not_found(s string) { | |
if ctx.done { return } | |
ctx.done = true | |
ctx.conn.send_string(HTTP_404) or { return } | |
} | |
pub fn (ctx mut Context) add_header(key, val string) { | |
//println('add_header($key, $val)') | |
ctx.headers = ctx.headers + '\r\n$key: $val' | |
//println(ctx.headers) | |
} | |
pub fn (ctx &Context) get_header(key string) string { | |
return ctx.req.headers[key] | |
} | |
struct App { | |
pub mut: | |
vweb Context // TODO embed | |
cnt int | |
} | |
pub fn run(port int) { | |
println('Running a Vweb app on http://localhost:$port ...') | |
l := net.listen(port) or { panic('failed to listen') } | |
mut app := App{} | |
app.vweb = Context{} | |
app.init() | |
for { | |
conn := l.accept() or { panic('accept() failed') } | |
handle_conn(conn, mut app) | |
} | |
} | |
fn handle_conn<T>(conn net.Socket, app mut T) { | |
first_line := conn.read_line() | |
println('firstline="$first_line"') | |
$if debug { println(first_line) } | |
vals := first_line.split(' ') | |
if vals.len < 2 { | |
println('no vals for http') | |
conn.send_string(HTTP_500) or {} | |
conn.close() or {} | |
return | |
} | |
mut headers := []string | |
mut body := '' | |
mut in_headers := true | |
mut len := 0 | |
mut body_len := 0 | |
for j := 0; j < 100; j++ { | |
line := conn.read_line() | |
sline := strip(line) | |
if sline == '' { | |
if len == 0 { | |
break | |
} | |
in_headers = false | |
} | |
if in_headers { | |
headers << sline | |
if sline.starts_with('Content-Length') { | |
len = sline.all_after(': ').int() | |
} | |
} else { | |
body += sline + '\r\n' | |
body_len += body.len | |
if body_len >= len { | |
break | |
} | |
} | |
} | |
req := http.Request{ | |
headers: http.parse_headers(headers) //s.split_into_lines()) | |
ws_func: 0 | |
user_ptr: 0 | |
method: vals[0] | |
url: vals[1] | |
} | |
if true { | |
println('req.headers = ') | |
println(req.headers) | |
} | |
app.vweb = Context{ | |
req: req | |
conn: conn | |
} | |
if vals.len < 2 { | |
$if debug { | |
println('no vals for http') | |
} | |
conn.close() or {} | |
return | |
} | |
handle_route(req.url, req.method) | |
conn.close() or {} | |
app.reset() | |
} | |
fn strip(s string) string { | |
// strip('\nabc\r\n') => 'abc' | |
return s.trim('\r\n') | |
} | |
fn main() { | |
println('noice') | |
run(8082) | |
} | |
pub fn (app mut App) init() { | |
println('init') | |
} | |
pub fn (app mut App) reset() { | |
} | |
pub fn handle_route(route string, method string) { | |
match route { | |
'/example' { | |
match method { | |
'GET' { println('example GET') } | |
'POST' { println('example POST') } | |
'PUT' { println('example PUT') } | |
'PATCH' { println('example PATCH') } | |
else { println('example no match') } | |
} | |
} | |
'/another' { | |
match method { | |
'GET' { println('another GET') } | |
'POST' { println('another POST') } | |
'PUT' { println('another PUT') } | |
'PATCH' { println('another PATCH') } | |
else { println('another no match') } | |
} | |
} | |
else { println('no match') } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment