v/vlib/vweb/request.v

88 lines
2.0 KiB
V

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
mut h := http.new_header()
line = reader.read_line() ?
for line != '' {
key, value := parse_header(line) ?
h.add_str(key, value) ?
line = reader.read_line() ?
}
// 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
}
// body
mut body := [byte(0)]
if length := h.get(.content_length) {
n := length.int()
body = []byte{len: n, cap: n + 1}
reader.read(mut body) or { }
body << 0
}
return http.Request{
method: method
url: target.str()
headers: headers
lheaders: lheaders
data: string(body)
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
}
fn parse_header(s string) ?(string, string) {
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
return words[0], words[1].trim_left(' \t')
}
// 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
}