net: vfmt everything

pull/10452/head
Delyan Angelov 2021-06-14 10:08:41 +03:00
parent 535dcac8fa
commit d7d9305d96
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
13 changed files with 387 additions and 215 deletions

View File

@ -44,7 +44,6 @@ const (
]
vfmt_known_failing_exceptions = arrays.merge(verify_known_failing_exceptions, [
'vlib/strconv/' /* prevent conflicts, till the new pure V string interpolation is merged */,
'vlib/net/http/' /* prevent conflicts, till ipv6 support is merged */,
'vlib/term/ui/input.v' /* comment after a struct embed is removed */,
'vlib/regex/regex_test.v' /* contains meaningfull formatting of the test case data */,
'vlib/readline/readline_test.v' /* vfmt eats `{ Readline }` from `import readline { Readline }` */,

View File

@ -100,13 +100,13 @@ const aoffset = __offsetof(Addr, addr)
fn (a Addr) len() u32 {
match a.family() {
.ip {
return sizeof(Ip) + aoffset
return sizeof(Ip) + net.aoffset
}
.ip6 {
return sizeof(Ip6) + aoffset
return sizeof(Ip6) + net.aoffset
}
.unix {
return sizeof(Unix) + aoffset
return sizeof(Unix) + net.aoffset
}
else {
panic('Unknown address family')

View File

@ -14,7 +14,7 @@ $if windows {
}
fn test_diagnostics() {
dump(net.aoffset)
dump(aoffset)
eprintln('--------')
in6 := C.sockaddr_in6{}
our_ip6 := Ip6{}
@ -72,17 +72,17 @@ fn test_sizes_unix_sun_path() {
}
fn test_offsets_ipv6() {
assert __offsetof(C.sockaddr_in6, sin6_addr) == __offsetof(Ip6, addr) + net.aoffset
assert __offsetof(C.sockaddr_in6, sin6_port) == __offsetof(Ip6, port) + net.aoffset
assert __offsetof(C.sockaddr_in6, sin6_addr) == __offsetof(Ip6, addr) + aoffset
assert __offsetof(C.sockaddr_in6, sin6_port) == __offsetof(Ip6, port) + aoffset
}
fn test_offsets_ipv4() {
assert __offsetof(C.sockaddr_in, sin_addr) == __offsetof(Ip, addr) + net.aoffset
assert __offsetof(C.sockaddr_in, sin_port) == __offsetof(Ip, port) + net.aoffset
assert __offsetof(C.sockaddr_in, sin_addr) == __offsetof(Ip, addr) + aoffset
assert __offsetof(C.sockaddr_in, sin_port) == __offsetof(Ip, port) + aoffset
}
fn test_offsets_unix() {
assert __offsetof(C.sockaddr_un, sun_path) == __offsetof(Unix, path) + net.aoffset
assert __offsetof(C.sockaddr_un, sun_path) == __offsetof(Unix, path) + aoffset
}
fn test_sizes_ipv6() {

View File

@ -7,6 +7,7 @@ import strings
// followed by \r\n as a line separator,
// followed by a chunk of data of the given size.
// The end is marked with a chunk with size 0.
struct ChunkScanner {
mut:
pos int
@ -23,7 +24,7 @@ fn (mut s ChunkScanner) read_chunk_size() int {
if !c.is_hex_digit() {
break
}
n = n<<4
n = n << 4
n += int(unhex(c))
s.pos++
}
@ -33,11 +34,9 @@ fn (mut s ChunkScanner) read_chunk_size() int {
fn unhex(c byte) byte {
if `0` <= c && c <= `9` {
return c - `0`
}
else if `a` <= c && c <= `f` {
} else if `a` <= c && c <= `f` {
return c - `a` + 10
}
else if `A` <= c && c <= `F` {
} else if `A` <= c && c <= `F` {
return c - `A` + 10
}
return 0

View File

@ -10,20 +10,21 @@ pub struct Cookie {
pub mut:
name string
value string
path string // optional
domain string // optional
path string // optional
domain string // optional
expires time.Time // optional
raw_expires string // for reading cookies only. optional.
raw_expires string // for reading cookies only. optional.
// max_age=0 means no 'Max-Age' attribute specified.
// max_age<0 means delete cookie now, equivalently 'Max-Age: 0'
// max_age>0 means Max-Age attribute present and given in seconds
max_age int
secure bool
http_only bool
same_site SameSite
raw string
unparsed []string // Raw text of unparsed attribute-value pairs
max_age int
secure bool
http_only bool
same_site SameSite
raw string
unparsed []string // Raw text of unparsed attribute-value pairs
}
// SameSite allows a server to define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests. The main
// goal is to mitigate the risk of cross-origin information leakage, and provide
@ -61,12 +62,10 @@ pub fn read_set_cookies(h map[string][]string) []&Cookie {
if !is_cookie_name_valid(name) {
continue
}
value := parse_cookie_value(raw_value, true) or {
continue
}
mut c := &Cookie{
name: name,
value: value,
value := parse_cookie_value(raw_value, true) or { continue }
mut c := &Cookie{
name: name
value: value
raw: line
}
for i, _ in parts {
@ -182,10 +181,11 @@ pub fn read_cookies(h map[string][]string, filter string) []&Cookie {
if filter != '' && filter != name {
continue
}
val = parse_cookie_value(val, true) or {
continue
val = parse_cookie_value(val, true) or { continue }
cookies << &Cookie{
name: name
value: val
}
cookies << &Cookie{name: name, value: val}
}
}
return cookies
@ -203,7 +203,8 @@ pub fn (c &Cookie) str() string {
// extra_cookie_length derived from typical length of cookie attributes
// see RFC 6265 Sec 4.1.
extra_cookie_length := 110
mut b := strings.new_builder(c.name.len + c.value.len + c.domain.len + c.path.len + extra_cookie_length)
mut b := strings.new_builder(c.name.len + c.value.len + c.domain.len + c.path.len +
extra_cookie_length)
b.write_string(c.name)
b.write_string('=')
b.write_string(sanitize_cookie_value(c.value))
@ -229,7 +230,7 @@ pub fn (c &Cookie) str() string {
}
if c.expires.year > 1600 {
e := c.expires
time_str := '${e.weekday_str()}, ${e.day.str()} ${e.smonth()} ${e.year} ${e.hhmmss()} GMT'
time_str := '$e.weekday_str(), $e.day.str() $e.smonth() $e.year $e.hhmmss() GMT'
b.write_string('; expires=')
b.write_string(time_str)
}
@ -264,9 +265,9 @@ pub fn (c &Cookie) str() string {
return b.str()
}
fn sanitize(valid fn(byte) bool, v string) string {
fn sanitize(valid fn (byte) bool, v string) string {
mut ok := true
for i in 0..v.len {
for i in 0 .. v.len {
if valid(v[i]) {
continue
}
@ -370,7 +371,7 @@ pub fn is_cookie_domain_name(_s string) bool {
}
part_len = 0
} else {
return false
return false
}
last = c
}
@ -386,7 +387,7 @@ fn parse_cookie_value(_raw string, allow_double_quote bool) ?string {
if allow_double_quote && raw.len > 1 && raw[0] == `"` && raw[raw.len - 1] == `"` {
raw = raw.substr(1, raw.len - 1)
}
for i in 0..raw.len {
for i in 0 .. raw.len {
if !valid_cookie_value_byte(raw[i]) {
return error('http.cookie: invalid cookie value')
}

View File

@ -2,43 +2,66 @@ import net.http
struct SetCookieTestCase {
cookie &http.Cookie
raw string
raw string
}
struct ReadSetCookiesTestCase {
header map[string][]string
header map[string][]string
cookies []&http.Cookie
}
struct AddCookieTestCase {
cookie []&http.Cookie
raw string
raw string
}
const (
write_set_cookie_tests = [
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-1', value: 'v1'},
cookie: &http.Cookie{
name: 'cookie-1'
value: 'v1'
}
raw: 'cookie-1=v1'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-2', value: 'two', max_age: 3600},
cookie: &http.Cookie{
name: 'cookie-2'
value: 'two'
max_age: 3600
}
raw: 'cookie-2=two; Max-Age=3600'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-3', value: 'three', domain: '.example.com'},
cookie: &http.Cookie{
name: 'cookie-3'
value: 'three'
domain: '.example.com'
}
raw: 'cookie-3=three; domain=example.com'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-4', value: 'four', path: '/restricted/'},
cookie: &http.Cookie{
name: 'cookie-4'
value: 'four'
path: '/restricted/'
}
raw: 'cookie-4=four; path=/restricted/'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-5', value: 'five', domain: 'wrong;bad.abc'},
cookie: &http.Cookie{
name: 'cookie-5'
value: 'five'
domain: 'wrong;bad.abc'
}
raw: 'cookie-5=five'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-6', value: 'six', domain: 'bad-.abc'},
cookie: &http.Cookie{
name: 'cookie-6'
value: 'six'
domain: 'bad-.abc'
}
raw: 'cookie-6=six'
},
// SetCookieTestCase{
@ -46,7 +69,11 @@ const (
// raw: 'cookie-7=seven; domain=127.0.0.1'
// },
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-8', value: 'eight', domain: '::1'},
cookie: &http.Cookie{
name: 'cookie-8'
value: 'eight'
domain: '::1'
}
raw: 'cookie-8=eight'
},
// {
@ -63,106 +90,181 @@ const (
// raw: 'cookie-11=invalid-expiry'
// },
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-12', value: 'samesite-default', same_site: .same_site_default_mode},
cookie: &http.Cookie{
name: 'cookie-12'
value: 'samesite-default'
same_site: .same_site_default_mode
}
raw: 'cookie-12=samesite-default; SameSite'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-13', value: 'samesite-lax', same_site: .same_site_lax_mode},
cookie: &http.Cookie{
name: 'cookie-13'
value: 'samesite-lax'
same_site: .same_site_lax_mode
}
raw: 'cookie-13=samesite-lax; SameSite=Lax'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-14', value: 'samesite-strict', same_site: .same_site_strict_mode},
cookie: &http.Cookie{
name: 'cookie-14'
value: 'samesite-strict'
same_site: .same_site_strict_mode
}
raw: 'cookie-14=samesite-strict; SameSite=Strict'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'cookie-15', value: 'samesite-none', same_site: .same_site_none_mode},
cookie: &http.Cookie{
name: 'cookie-15'
value: 'samesite-none'
same_site: .same_site_none_mode
}
raw: 'cookie-15=samesite-none; SameSite=None'
},
// The 'special' cookies have values containing commas or spaces which
// are disallowed by RFC 6265 but are common in the wild.
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-1', value: 'a z'},
cookie: &http.Cookie{
name: 'special-1'
value: 'a z'
}
raw: 'special-1=a z'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-2', value: ' z'},
cookie: &http.Cookie{
name: 'special-2'
value: ' z'
}
raw: 'special-2=" z"'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-3', value: 'a '},
cookie: &http.Cookie{
name: 'special-3'
value: 'a '
}
raw: 'special-3="a "'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-4', value: ' '},
cookie: &http.Cookie{
name: 'special-4'
value: ' '
}
raw: 'special-4=" "'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-5', value: 'a,z'},
cookie: &http.Cookie{
name: 'special-5'
value: 'a,z'
}
raw: 'special-5=a,z'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-6', value: ',z'},
cookie: &http.Cookie{
name: 'special-6'
value: ',z'
}
raw: 'special-6=",z"'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-7', value: 'a,'},
cookie: &http.Cookie{
name: 'special-7'
value: 'a,'
}
raw: 'special-7="a,"'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'special-8', value: ','},
cookie: &http.Cookie{
name: 'special-8'
value: ','
}
raw: 'special-8=","'
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'empty-value', value: ''},
cookie: &http.Cookie{
name: 'empty-value'
value: ''
}
raw: 'empty-value='
},
SetCookieTestCase{
cookie: &http.Cookie{name: ''},
cookie: &http.Cookie{
name: ''
}
raw: ''
},
SetCookieTestCase{
cookie: &http.Cookie{name: '\t'},
cookie: &http.Cookie{
name: '\t'
}
raw: ''
},
SetCookieTestCase{
cookie: &http.Cookie{name: '\r'},
cookie: &http.Cookie{
name: '\r'
}
raw: ''
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'a\nb', value: 'v'},
cookie: &http.Cookie{
name: 'a\nb'
value: 'v'
}
raw: ''
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'a\nb', value: 'v'},
cookie: &http.Cookie{
name: 'a\nb'
value: 'v'
}
raw: ''
},
SetCookieTestCase{
cookie: &http.Cookie{name: 'a\rb', value: 'v'},
cookie: &http.Cookie{
name: 'a\rb'
value: 'v'
}
raw: ''
},
]
add_cookies_tests = [
AddCookieTestCase{
cookie: [],
raw: ""
cookie: []
raw: ''
},
AddCookieTestCase{
cookie: [&http.Cookie{name: "cookie-1", value: "v1"}],
raw: "cookie-1=v1"
cookie: [&http.Cookie{
name: 'cookie-1'
value: 'v1'
}]
raw: 'cookie-1=v1'
},
AddCookieTestCase{
cookie: [
&http.Cookie{name: "cookie-1", value: "v1"},
&http.Cookie{name: "cookie-2", value: "v2"},
&http.Cookie{name: "cookie-3", value: "v3"}
],
raw: "cookie-1=v1; cookie-2=v2; cookie-3=v3"
}
cookie: [&http.Cookie{
name: 'cookie-1'
value: 'v1'
},
&http.Cookie{
name: 'cookie-2'
value: 'v2'
},
&http.Cookie{
name: 'cookie-3'
value: 'v3'
},
]
raw: 'cookie-1=v1; cookie-2=v2; cookie-3=v3'
},
]
read_set_cookies_tests = [
ReadSetCookiesTestCase{
header: {"Set-Cookie": ["Cookie-1=v1"]},
cookies: [&http.Cookie{name: "Cookie-1", value: "v1", raw: "Cookie-1=v1"}]
header: map{
'Set-Cookie': ['Cookie-1=v1']
}
cookies: [&http.Cookie{
name: 'Cookie-1'
value: 'v1'
raw: 'Cookie-1=v1'
}]
},
// ReadSetCookiesTestCase{
// header: {"Set-Cookie": ["NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"]},
@ -190,83 +292,158 @@ const (
// }]
// },
ReadSetCookiesTestCase{
header: {"Set-Cookie": ["ASP.NET_SessionId=foo; path=/; HttpOnly"]},
cookies: [&http.Cookie{
name: "ASP.NET_SessionId",
value: "foo",
path: "/",
http_only: true,
raw: "ASP.NET_SessionId=foo; path=/; HttpOnly"
}]
header: map{
'Set-Cookie': ['ASP.NET_SessionId=foo; path=/; HttpOnly']
}
cookies: [
&http.Cookie{
name: 'ASP.NET_SessionId'
value: 'foo'
path: '/'
http_only: true
raw: 'ASP.NET_SessionId=foo; path=/; HttpOnly'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ["samesitedefault=foo; SameSite"]},
cookies: [&http.Cookie{
name: "samesitedefault",
value: "foo",
same_site: .same_site_default_mode,
raw: "samesitedefault=foo; SameSite"
}]
header: map{
'Set-Cookie': ['samesitedefault=foo; SameSite']
}
cookies: [
&http.Cookie{
name: 'samesitedefault'
value: 'foo'
same_site: .same_site_default_mode
raw: 'samesitedefault=foo; SameSite'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ["samesitelax=foo; SameSite=Lax"]},
cookies: [&http.Cookie{
name: "samesitelax",
value: "foo",
same_site: .same_site_lax_mode,
raw: "samesitelax=foo; SameSite=Lax"
}]
header: map{
'Set-Cookie': ['samesitelax=foo; SameSite=Lax']
}
cookies: [
&http.Cookie{
name: 'samesitelax'
value: 'foo'
same_site: .same_site_lax_mode
raw: 'samesitelax=foo; SameSite=Lax'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ["samesitestrict=foo; SameSite=Strict"]},
cookies: [&http.Cookie{
name: "samesitestrict",
value: "foo",
same_site: .same_site_strict_mode,
raw: "samesitestrict=foo; SameSite=Strict"
}]
header: map{
'Set-Cookie': ['samesitestrict=foo; SameSite=Strict']
}
cookies: [
&http.Cookie{
name: 'samesitestrict'
value: 'foo'
same_site: .same_site_strict_mode
raw: 'samesitestrict=foo; SameSite=Strict'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ["samesitenone=foo; SameSite=None"]},
cookies: [&http.Cookie{
name: "samesitenone",
value: "foo",
same_site: .same_site_none_mode,
raw: "samesitenone=foo; SameSite=None"
}]
header: map{
'Set-Cookie': ['samesitenone=foo; SameSite=None']
}
cookies: [
&http.Cookie{
name: 'samesitenone'
value: 'foo'
same_site: .same_site_none_mode
raw: 'samesitenone=foo; SameSite=None'
},
]
},
// Make sure we can properly read back the Set-Cookie headers we create
// for values containing spaces or commas:
ReadSetCookiesTestCase{
header: {"Set-Cookie": ['special-1=a z']},
cookies: [&http.Cookie{name: "special-1", value: "a z", raw: 'special-1=a z'}]
header: map{
'Set-Cookie': ['special-1=a z']
}
cookies: [
&http.Cookie{
name: 'special-1'
value: 'a z'
raw: 'special-1=a z'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ['special-2=" z"']},
cookies: [&http.Cookie{name: "special-2", value: " z", raw: 'special-2=" z"'}]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ['special-3="a "']},
cookies: [&http.Cookie{name: "special-3", value: "a ", raw: 'special-3="a "'}]
header: map{
'Set-Cookie': ['special-2=" z"']
}
cookies: [
&http.Cookie{
name: 'special-2'
value: ' z'
raw: 'special-2=" z"'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ['special-4=" "']},
cookies: [&http.Cookie{name: "special-4", value: " ", raw: 'special-4=" "'}]
header: map{
'Set-Cookie': ['special-3="a "']
}
cookies: [
&http.Cookie{
name: 'special-3'
value: 'a '
raw: 'special-3="a "'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ['special-5=a,z']},
cookies: [&http.Cookie{name: "special-5", value: "a,z", raw: 'special-5=a,z'}]
header: map{
'Set-Cookie': ['special-4=" "']
}
cookies: [
&http.Cookie{
name: 'special-4'
value: ' '
raw: 'special-4=" "'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ['special-6=",z"']},
cookies: [&http.Cookie{name: "special-6", value: ",z", raw: 'special-6=",z"'}]
header: map{
'Set-Cookie': ['special-5=a,z']
}
cookies: [
&http.Cookie{
name: 'special-5'
value: 'a,z'
raw: 'special-5=a,z'
},
]
},
ReadSetCookiesTestCase{
header: {"Set-Cookie": ['special-7=","']},
cookies: [&http.Cookie{name: "special-7", value: ",", raw: 'special-8=","'}]
header: map{
'Set-Cookie': ['special-6=",z"']
}
cookies: [
&http.Cookie{
name: 'special-6'
value: ',z'
raw: 'special-6=",z"'
},
]
},
ReadSetCookiesTestCase{
header: map{
'Set-Cookie': ['special-7=","']
}
cookies: [
&http.Cookie{
name: 'special-7'
value: ','
raw: 'special-8=","'
},
]
}
// TODO(bradfitz): users have reported seeing this in the
// TODO(bradfitz): users have reported seeing this in the,,
// wild, but do browsers handle it? RFC 6265 just says "don't
// do that" (section 3) and then never mentions header folding
// again.

View File

@ -21,10 +21,10 @@ fn download_cb(ptr voidptr, size size_t, nmemb size_t, userp voidptr) {
data.cb(data.written)
//#data->cb(data->written); // TODO
return written
*/
*/
}
pub fn download_file_with_progress(url string, out string, cb DownloadFn, cb_finished fn()) {
pub fn download_file_with_progress(url string, out string, cb DownloadFn, cb_finished fn ()) {
/*
curl := C.curl_easy_init()
if isnil(curl) {
@ -45,7 +45,7 @@ pub fn download_file_with_progress(url string, out string, cb DownloadFn, cb_fin
C.curl_easy_cleanup(curl)
C.fclose(fp)
cb_finished()
*/
*/
}
fn empty() {

View File

@ -578,9 +578,9 @@ fn (mut h Header) add_key(key string) {
// Custom error struct for invalid header tokens
struct HeaderKeyError {
msg string
code int
header string
msg string
code int
header string
invalid_char byte
}

View File

@ -1,9 +1,9 @@
module http
fn test_header_new() {
h := http.new_header(
{key: .accept, value: 'nothing'},
{key: .expires, value: 'yesterday'}
h := new_header({ key: .accept, value: 'nothing' },
key: .expires
value: 'yesterday'
)
assert h.contains(.accept)
assert h.contains(.expires)
@ -14,21 +14,21 @@ fn test_header_new() {
}
fn test_header_invalid_key() {
mut h := http.new_header()
mut h := new_header()
h.add_custom('space is invalid', ':(') or { return }
panic('should have returned')
}
fn test_header_adds_multiple() {
mut h := http.new_header()
mut h := new_header()
h.add(.accept, 'one')
h.add(.accept, 'two')
assert h.values(.accept) == ['one' 'two']
assert h.values(.accept) == ['one', 'two']
}
fn test_header_get() ? {
mut h := http.new_header(key: .dnt, value: 'one')
mut h := new_header(key: .dnt, value: 'one')
h.add_custom('dnt', 'two') ?
dnt := h.get_custom('dnt') or { '' }
exact := h.get_custom('dnt', exact: true) or { '' }
@ -37,27 +37,27 @@ fn test_header_get() ? {
}
fn test_header_set() ? {
mut h := http.new_header(
{key: .dnt, value: 'one'},
{key: .dnt, value: 'two'}
mut h := new_header({ key: .dnt, value: 'one' },
key: .dnt
value: 'two'
)
assert h.values(.dnt) == ['one' 'two']
assert h.values(.dnt) == ['one', 'two']
h.set_custom('DNT', 'three') ?
assert h.values(.dnt) == ['three']
}
fn test_header_delete() {
mut h := http.new_header(
{key: .dnt, value: 'one'},
{key: .dnt, value: 'two'}
mut h := new_header({ key: .dnt, value: 'one' },
key: .dnt
value: 'two'
)
assert h.values(.dnt) == ['one' 'two']
assert h.values(.dnt) == ['one', 'two']
h.delete(.dnt)
assert h.values(.dnt) == []
}
fn test_header_delete_not_existing() {
mut h := http.new_header()
mut h := new_header()
assert h.data.len == 0
assert h.keys.len == 0
h.delete(.dnt)
@ -66,7 +66,7 @@ fn test_header_delete_not_existing() {
}
fn test_custom_header() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('AbC', 'dEf') ?
h.add_custom('aBc', 'GhI') ?
assert h.custom_values('AbC', exact: true) == ['dEf']
@ -90,7 +90,7 @@ fn test_custom_header() ? {
}
fn test_contains_custom() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('Hello', 'world') ?
assert h.contains_custom('hello')
assert h.contains_custom('HELLO')
@ -100,7 +100,7 @@ fn test_contains_custom() ? {
}
fn test_get_custom() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('Hello', 'world') ?
assert h.get_custom('hello') ? == 'world'
assert h.get_custom('HELLO') ? == 'world'
@ -116,7 +116,7 @@ fn test_get_custom() ? {
}
fn test_starting_with() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('Hello-1', 'world') ?
h.add_custom('Hello-21', 'world') ?
assert h.starting_with('Hello-') ? == 'Hello-1'
@ -124,7 +124,7 @@ fn test_starting_with() ? {
}
fn test_custom_values() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('Hello', 'world') ?
assert h.custom_values('hello') == ['world']
assert h.custom_values('HELLO') == ['world']
@ -134,7 +134,7 @@ fn test_custom_values() ? {
}
fn test_coerce() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('accept', 'foo') ?
h.add(.accept, 'bar')
assert h.values(.accept) == ['foo', 'bar']
@ -146,7 +146,7 @@ fn test_coerce() ? {
}
fn test_coerce_canonicalize() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('accept', 'foo') ?
h.add(.accept, 'bar')
assert h.values(.accept) == ['foo', 'bar']
@ -158,7 +158,7 @@ fn test_coerce_canonicalize() ? {
}
fn test_coerce_custom() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('Hello', 'foo') ?
h.add_custom('hello', 'bar') ?
h.add_custom('HELLO', 'baz') ?
@ -171,7 +171,7 @@ fn test_coerce_custom() ? {
}
fn test_coerce_canonicalize_custom() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('foo-BAR', 'foo') ?
h.add_custom('FOO-bar', 'bar') ?
assert h.custom_values('foo-bar') == ['foo', 'bar']
@ -183,7 +183,7 @@ fn test_coerce_canonicalize_custom() ? {
}
fn test_render_version() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('accept', 'foo') ?
h.add_custom('Accept', 'bar') ?
h.add(.accept, 'baz')
@ -202,7 +202,7 @@ fn test_render_version() ? {
}
fn test_render_coerce() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('accept', 'foo') ?
h.add_custom('Accept', 'bar') ?
h.add(.accept, 'baz')
@ -222,7 +222,7 @@ fn test_render_coerce() ? {
}
fn test_render_canonicalize() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('accept', 'foo') ?
h.add_custom('Accept', 'bar') ?
h.add(.accept, 'baz')
@ -245,7 +245,7 @@ fn test_render_canonicalize() ? {
}
fn test_render_coerce_canonicalize() ? {
mut h := http.new_header()
mut h := new_header()
h.add_custom('accept', 'foo') ?
h.add_custom('Accept', 'bar') ?
h.add(.accept, 'baz')
@ -265,7 +265,7 @@ fn test_render_coerce_canonicalize() ? {
}
fn test_str() ? {
mut h := http.new_header()
mut h := new_header()
h.add(.accept, 'text/html')
h.add_custom('Accept', 'image/jpeg') ?
h.add_custom('X-custom', 'Hello') ?

View File

@ -1,5 +1,6 @@
module http //internal tests have access to *everything in the module*
module http
// internal tests have access to *everything in the module*
import json
struct HttpbinResponseBody {
@ -13,7 +14,6 @@ struct HttpbinResponseBody {
url string
}
fn http_fetch_mock(_methods []string, _config FetchConfig) ?[]Response {
url := 'https://httpbin.org/'
methods := if _methods.len == 0 { ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] } else { _methods }
@ -23,7 +23,7 @@ fn http_fetch_mock(_methods []string, _config FetchConfig) ?[]Response {
for method in methods {
lmethod := method.to_lower()
config.method = method_from_str(method)
res := fetch(url + lmethod, config)?
res := fetch(url + lmethod, config) ?
// TODO
// body := json.decode(HttpbinResponseBody,res.text)?
result << res
@ -32,40 +32,38 @@ fn http_fetch_mock(_methods []string, _config FetchConfig) ?[]Response {
}
fn test_http_fetch_bare() {
$if !network ? { return }
responses := http_fetch_mock([], FetchConfig{}) or {
panic(err)
$if !network ? {
return
}
responses := http_fetch_mock([], FetchConfig{}) or { panic(err) }
for response in responses {
assert response.status_code == 200
}
}
fn test_http_fetch_with_data() {
$if !network ? { return }
responses := http_fetch_mock(['POST', 'PUT', 'PATCH', 'DELETE'], {
data: 'hello world'
}) or {
panic(err)
$if !network ? {
return
}
responses := http_fetch_mock(['POST', 'PUT', 'PATCH', 'DELETE'],
data: 'hello world'
) or { panic(err) }
for response in responses {
payload := json.decode(HttpbinResponseBody,response.text) or {
panic(err)
}
payload := json.decode(HttpbinResponseBody, response.text) or { panic(err) }
assert payload.data == 'hello world'
}
}
fn test_http_fetch_with_params() {
$if !network ? { return }
responses := http_fetch_mock([], {
params: {
'a': 'b',
$if !network ? {
return
}
responses := http_fetch_mock([],
params: map{
'a': 'b'
'c': 'd'
}
}) or {
panic(err)
}
) or { panic(err) }
for response in responses {
// payload := json.decode(HttpbinResponseBody,response.text) or {
// panic(err)
@ -78,14 +76,14 @@ fn test_http_fetch_with_params() {
}
fn test_http_fetch_with_headers() ? {
$if !network ? { return }
$if !network ? {
return
}
mut header := new_header()
header.add_custom('Test-Header', 'hello world') ?
responses := http_fetch_mock([], {
responses := http_fetch_mock([],
header: header
}) or {
panic(err)
}
) or { panic(err) }
for response in responses {
// payload := json.decode(HttpbinResponseBody,response.text) or {
// panic(err)

View File

@ -15,9 +15,7 @@ fn test_http_get_from_vlang_utc_now() {
urls := ['http://vlang.io/utc_now', 'https://vlang.io/utc_now']
for url in urls {
println('Test getting current time from $url by http.get')
res := http.get(url) or {
panic(err)
}
res := http.get(url) or { panic(err) }
assert 200 == res.status_code
assert res.text.len > 0
assert res.text.int() > 1566403696
@ -39,9 +37,7 @@ fn test_public_servers() {
]
for url in urls {
println('Testing http.get on public url: $url ')
res := http.get(url) or {
panic(err)
}
res := http.get(url) or { panic(err) }
assert 200 == res.status_code
assert res.text.len > 0
}
@ -53,9 +49,7 @@ fn test_relative_redirects() {
} $else {
return
} // tempfix periodic: httpbin relative redirects are broken
res := http.get('https://httpbin.org/relative-redirect/3?abc=xyz') or {
panic(err)
}
res := http.get('https://httpbin.org/relative-redirect/3?abc=xyz') or { panic(err) }
assert 200 == res.status_code
assert res.text.len > 0
assert res.text.contains('"abc": "xyz"')

View File

@ -3,7 +3,7 @@
// that can be found in the LICENSE file.
module http
import chunked
import net.http.chunked
// Response represents the result of the request
pub struct Response {
@ -23,9 +23,9 @@ fn (mut resp Response) free() {
pub fn (resp Response) bytes() []byte {
// TODO: build []byte directly; this uses two allocations
// TODO: cookies
return ('$resp.version $resp.status_code ${status_from_int(resp.status_code).str()}\r\n' +
'${resp.header.render(version: resp.version)}\r\n' +
'$resp.text').bytes()
return ('$resp.version $resp.status_code ${status_from_int(resp.status_code).str()}\r\n' + '${resp.header.render(
version: resp.version
)}\r\n' + '$resp.text').bytes()
}
// Parse a raw HTTP response into a Response object
@ -66,7 +66,9 @@ pub fn parse_response(resp string) Response {
parts := cookie.split_nth('=', 2)
cookies[parts[0]] = parts[1]
}
if header.get(.transfer_encoding) or { '' } == 'chunked' || header.get(.content_length) or { '' } == '' {
if header.get(.transfer_encoding) or { '' } == 'chunked' || header.get(.content_length) or {
''
} == '' {
text = chunked.decode(text)
}
return Response{

View File

@ -3,7 +3,7 @@
// that can be found in the LICENSE file.
module http
// The status codes listed here are based on the comprehensive list,
// The status codes listed here are based on the comprehensive list,
// available at:
// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
pub enum Status {
@ -228,7 +228,9 @@ pub fn (code Status) str() string {
// int converts an assigned and known Status to its integral equivalent.
// if a Status is unknown or unassigned, this method will return zero
pub fn (code Status) int() int {
if code in [.unknown, .unassigned] { return 0 }
if code in [.unknown, .unassigned] {
return 0
}
return int(code)
}
@ -238,14 +240,14 @@ pub fn (code Status) is_valid() bool {
return number >= 100 && number < 600
}
// is_error will return true if the status code represents either a client or
// is_error will return true if the status code represents either a client or
// a server error; otherwise will return false
pub fn (code Status) is_error() bool {
number := code.int()
return number >= 400 && number < 600
}
// is_success will return true if the status code represents either an
// is_success will return true if the status code represents either an
// informational, success, or redirection response; otherwise will return false
pub fn (code Status) is_success() bool {
number := code.int()