2021-02-27 23:18:25 +01:00
|
|
|
module vweb
|
|
|
|
|
|
|
|
import io
|
|
|
|
import net.http
|
|
|
|
import net.urllib
|
|
|
|
|
|
|
|
pub fn parse_request(mut reader io.BufferedReader) ?http.Request {
|
|
|
|
// request line
|
|
|
|
mut line := reader.read_line() ?
|
|
|
|
method, target, version := parse_request_line(line) ?
|
|
|
|
|
|
|
|
// headers
|
2021-03-01 11:50:52 +01:00
|
|
|
mut h := http.new_header()
|
2021-02-27 23:18:25 +01:00
|
|
|
line = reader.read_line() ?
|
|
|
|
for line != '' {
|
2021-03-01 11:50:52 +01:00
|
|
|
key, value := parse_header(line) ?
|
|
|
|
h.add_str(key, value) ?
|
2021-02-27 23:18:25 +01:00
|
|
|
line = reader.read_line() ?
|
|
|
|
}
|
|
|
|
|
2021-03-01 11:50:52 +01:00
|
|
|
// create map[string]string from headers
|
|
|
|
// TODO: replace headers and lheaders with http.Header type
|
|
|
|
mut headers := map[string]string{}
|
|
|
|
mut lheaders := map[string]string{}
|
|
|
|
for key in h.keys() {
|
|
|
|
values := h.values_str(key).join('; ')
|
|
|
|
headers[key] = values
|
|
|
|
lheaders[key.to_lower()] = values
|
2021-02-27 23:18:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// body
|
2021-03-02 20:02:17 +01:00
|
|
|
mut body := []byte{}
|
2021-03-01 11:50:52 +01:00
|
|
|
if length := h.get(.content_length) {
|
|
|
|
n := length.int()
|
2021-03-02 20:02:17 +01:00
|
|
|
if n > 0 {
|
|
|
|
body = []byte{len: n}
|
|
|
|
reader.read(mut body) or { }
|
|
|
|
}
|
2021-02-27 23:18:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return http.Request{
|
|
|
|
method: method
|
|
|
|
url: target.str()
|
2021-03-01 11:50:52 +01:00
|
|
|
headers: headers
|
|
|
|
lheaders: lheaders
|
2021-03-02 20:02:17 +01:00
|
|
|
data: body.bytestr()
|
2021-02-27 23:18:25 +01:00
|
|
|
version: version
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_request_line(s string) ?(http.Method, urllib.URL, http.Version) {
|
|
|
|
words := s.split(' ')
|
|
|
|
if words.len != 3 {
|
|
|
|
return error('malformed request line')
|
|
|
|
}
|
|
|
|
method := http.method_from_str(words[0])
|
|
|
|
target := urllib.parse(words[1]) ?
|
|
|
|
version := http.version_from_str(words[2])
|
|
|
|
if version == .unknown {
|
|
|
|
return error('unsupported version')
|
|
|
|
}
|
|
|
|
|
|
|
|
return method, target, version
|
|
|
|
}
|
|
|
|
|
2021-03-01 11:50:52 +01:00
|
|
|
fn parse_header(s string) ?(string, string) {
|
2021-02-27 23:18:25 +01:00
|
|
|
if ':' !in s {
|
|
|
|
return error('missing colon in header')
|
|
|
|
}
|
|
|
|
words := s.split_nth(':', 2)
|
|
|
|
if !is_token(words[0]) {
|
|
|
|
return error('invalid character in header name')
|
|
|
|
}
|
|
|
|
// TODO: parse quoted text according to the RFC
|
2021-03-01 11:50:52 +01:00
|
|
|
return words[0], words[1].trim_left(' \t')
|
2021-02-27 23:18:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: use map for faster lookup (untested)
|
|
|
|
const token_chars = r"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~".bytes()
|
|
|
|
|
|
|
|
fn is_token(s string) bool {
|
|
|
|
for c in s {
|
|
|
|
if c !in vweb.token_chars {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|