Skip to content

Instantly share code, notes, and snippets.

@GavinRay97
Created January 1, 2020 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GavinRay97/1aeea7630f4ca147e8df23f25546da45 to your computer and use it in GitHub Desktop.
Save GavinRay97/1aeea7630f4ca147e8df23f25546da45 to your computer and use it in GitHub Desktop.
V match-expression web routing
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