mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
This commit is contained in:
parent
34cfe19ae3
commit
ec5eeb57b2
11
vlib/net/http/build_request_headers_test.v
Normal file
11
vlib/net/http/build_request_headers_test.v
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module http
|
||||||
|
|
||||||
|
fn test_build_request_headers_with_empty_body_adds_content_length_zero() {
|
||||||
|
// Create a request with no data.
|
||||||
|
mut req := Request{}
|
||||||
|
// Build the headers for it. Ensure that Content-Length: 0 is added
|
||||||
|
// for requests without a body, which is required by some servers.
|
||||||
|
// We use a POST request, as it is most likely to be affected by this.
|
||||||
|
headers := req.build_request_headers(.post, 'localhost', 80, '/')
|
||||||
|
assert headers.contains('Content-Length: 0\r\n')
|
||||||
|
}
|
@ -566,9 +566,10 @@ pub fn parse_multipart_form(body string, boundary string) (map[string]string, ma
|
|||||||
mut files := map[string][]FileData{}
|
mut files := map[string][]FileData{}
|
||||||
// TODO: do not use split, but only indexes, to reduce copying of potentially large data
|
// TODO: do not use split, but only indexes, to reduce copying of potentially large data
|
||||||
sections := body.split(boundary)
|
sections := body.split(boundary)
|
||||||
fields := sections[1..sections.len - 1]
|
fields := sections#[1..sections.len - 1]
|
||||||
|
mut line_segments := []LineSegmentIndexes{cap: 100}
|
||||||
for field in fields {
|
for field in fields {
|
||||||
mut line_segments := []LineSegmentIndexes{cap: 100}
|
line_segments.clear()
|
||||||
mut line_idx, mut line_start := 0, 0
|
mut line_idx, mut line_start := 0, 0
|
||||||
for cidx, c in field {
|
for cidx, c in field {
|
||||||
if line_idx >= 6 {
|
if line_idx >= 6 {
|
||||||
@ -582,6 +583,9 @@ pub fn parse_multipart_form(body string, boundary string) (map[string]string, ma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
line_segments << LineSegmentIndexes{line_start, field.len}
|
line_segments << LineSegmentIndexes{line_start, field.len}
|
||||||
|
if line_segments.len < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
line1 := field[line_segments[1].start..line_segments[1].end]
|
line1 := field[line_segments[1].start..line_segments[1].end]
|
||||||
line2 := field[line_segments[2].start..line_segments[2].end]
|
line2 := field[line_segments[2].start..line_segments[2].end]
|
||||||
disposition := parse_disposition(line1.trim_space())
|
disposition := parse_disposition(line1.trim_space())
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// vtest build: !windows
|
// vtest build: !windows
|
||||||
module http
|
import net.http
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
struct StringReader {
|
struct StringReader {
|
||||||
@ -30,13 +29,13 @@ fn reader(s string) &io.BufferedReader {
|
|||||||
|
|
||||||
fn test_parse_request_not_http() {
|
fn test_parse_request_not_http() {
|
||||||
mut reader__ := reader('hello')
|
mut reader__ := reader('hello')
|
||||||
parse_request(mut reader__) or { return }
|
http.parse_request(mut reader__) or { return }
|
||||||
panic('should not have parsed')
|
panic('should not have parsed')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_parse_request_no_headers() {
|
fn test_parse_request_no_headers() {
|
||||||
mut reader_ := reader('GET / HTTP/1.1\r\n\r\n')
|
mut reader_ := reader('GET / HTTP/1.1\r\n\r\n')
|
||||||
req := parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
req := http.parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
||||||
assert req.method == .get
|
assert req.method == .get
|
||||||
assert req.url == '/'
|
assert req.url == '/'
|
||||||
assert req.version == .v1_1
|
assert req.version == .v1_1
|
||||||
@ -44,26 +43,26 @@ fn test_parse_request_no_headers() {
|
|||||||
|
|
||||||
fn test_parse_request_two_headers() {
|
fn test_parse_request_two_headers() {
|
||||||
mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n')
|
mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n')
|
||||||
req := parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
req := http.parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
||||||
assert req.header.custom_values('Test1') == ['a']
|
assert req.header.custom_values('Test1') == ['a']
|
||||||
assert req.header.custom_values('Test2') == ['B']
|
assert req.header.custom_values('Test2') == ['B']
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_parse_request_two_header_values() {
|
fn test_parse_request_two_header_values() {
|
||||||
mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n')
|
mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n')
|
||||||
req := parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
req := http.parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
||||||
assert req.header.custom_values('Test1') == ['a; b']
|
assert req.header.custom_values('Test1') == ['a; b']
|
||||||
assert req.header.custom_values('Test2') == ['c', 'd']
|
assert req.header.custom_values('Test2') == ['c', 'd']
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_parse_request_body() {
|
fn test_parse_request_body() {
|
||||||
mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc')
|
mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc')
|
||||||
req := parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
req := http.parse_request(mut reader_) or { panic('did not parse: ${err}') }
|
||||||
assert req.data == 'body'
|
assert req.data == 'body'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_parse_request_line() {
|
fn test_parse_request_line() {
|
||||||
method, target, version := parse_request_line('GET /target HTTP/1.1') or {
|
method, target, version := http.parse_request_line('GET /target HTTP/1.1') or {
|
||||||
panic('did not parse: ${err}')
|
panic('did not parse: ${err}')
|
||||||
}
|
}
|
||||||
assert method == .get
|
assert method == .get
|
||||||
@ -72,34 +71,34 @@ fn test_parse_request_line() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_parse_form() {
|
fn test_parse_form() {
|
||||||
assert parse_form('foo=bar&bar=baz') == {
|
assert http.parse_form('foo=bar&bar=baz') == {
|
||||||
'foo': 'bar'
|
'foo': 'bar'
|
||||||
'bar': 'baz'
|
'bar': 'baz'
|
||||||
}
|
}
|
||||||
assert parse_form('foo=bar=&bar=baz') == {
|
assert http.parse_form('foo=bar=&bar=baz') == {
|
||||||
'foo': 'bar='
|
'foo': 'bar='
|
||||||
'bar': 'baz'
|
'bar': 'baz'
|
||||||
}
|
}
|
||||||
assert parse_form('foo=bar%3D&bar=baz') == {
|
assert http.parse_form('foo=bar%3D&bar=baz') == {
|
||||||
'foo': 'bar='
|
'foo': 'bar='
|
||||||
'bar': 'baz'
|
'bar': 'baz'
|
||||||
}
|
}
|
||||||
assert parse_form('foo=b%26ar&bar=baz') == {
|
assert http.parse_form('foo=b%26ar&bar=baz') == {
|
||||||
'foo': 'b&ar'
|
'foo': 'b&ar'
|
||||||
'bar': 'baz'
|
'bar': 'baz'
|
||||||
}
|
}
|
||||||
assert parse_form('a=b& c=d') == {
|
assert http.parse_form('a=b& c=d') == {
|
||||||
'a': 'b'
|
'a': 'b'
|
||||||
' c': 'd'
|
' c': 'd'
|
||||||
}
|
}
|
||||||
assert parse_form('a=b&c= d ') == {
|
assert http.parse_form('a=b&c= d ') == {
|
||||||
'a': 'b'
|
'a': 'b'
|
||||||
'c': ' d '
|
'c': ' d '
|
||||||
}
|
}
|
||||||
assert parse_form('{json}') == {
|
assert http.parse_form('{json}') == {
|
||||||
'json': '{json}'
|
'json': '{json}'
|
||||||
}
|
}
|
||||||
assert parse_form('{
|
assert http.parse_form('{
|
||||||
"_id": "76c",
|
"_id": "76c",
|
||||||
"friends": [
|
"friends": [
|
||||||
{
|
{
|
||||||
@ -139,10 +138,10 @@ Content-Disposition: form-data; name=\"${names[1]}\"\r
|
|||||||
${contents[1]}\r
|
${contents[1]}\r
|
||||||
--${boundary}--\r
|
--${boundary}--\r
|
||||||
"
|
"
|
||||||
form, files := parse_multipart_form(data, boundary)
|
form, files := http.parse_multipart_form(data, boundary)
|
||||||
assert files == {
|
assert files == {
|
||||||
names[0]: [
|
names[0]: [
|
||||||
FileData{
|
http.FileData{
|
||||||
filename: file
|
filename: file
|
||||||
content_type: ct
|
content_type: ct
|
||||||
data: contents[0]
|
data: contents[0]
|
||||||
@ -167,7 +166,7 @@ Content-Disposition: form-data; name="password"\r
|
|||||||
admin123\r
|
admin123\r
|
||||||
--${boundary}--\r
|
--${boundary}--\r
|
||||||
'
|
'
|
||||||
form, files := parse_multipart_form(data, boundary)
|
form, files := http.parse_multipart_form(data, boundary)
|
||||||
for k, v in form {
|
for k, v in form {
|
||||||
eprintln('> k: ${k} | v: ${v}')
|
eprintln('> k: ${k} | v: ${v}')
|
||||||
eprintln('>> k.bytes(): ${k.bytes()}')
|
eprintln('>> k.bytes(): ${k.bytes()}')
|
||||||
@ -180,7 +179,7 @@ admin123\r
|
|||||||
fn test_multipart_form_body() {
|
fn test_multipart_form_body() {
|
||||||
files := {
|
files := {
|
||||||
'foo': [
|
'foo': [
|
||||||
FileData{
|
http.FileData{
|
||||||
filename: 'bar.v'
|
filename: 'bar.v'
|
||||||
content_type: 'application/octet-stream'
|
content_type: 'application/octet-stream'
|
||||||
data: 'baz'
|
data: 'baz'
|
||||||
@ -191,8 +190,8 @@ fn test_multipart_form_body() {
|
|||||||
'fooz': 'buzz'
|
'fooz': 'buzz'
|
||||||
}
|
}
|
||||||
|
|
||||||
body, boundary := multipart_form_body(form, files)
|
body, boundary := http.multipart_form_body(form, files)
|
||||||
parsed_form, parsed_files := parse_multipart_form(body, boundary)
|
parsed_form, parsed_files := http.parse_multipart_form(body, boundary)
|
||||||
assert parsed_files == files
|
assert parsed_files == files
|
||||||
assert parsed_form == form
|
assert parsed_form == form
|
||||||
}
|
}
|
||||||
@ -201,17 +200,33 @@ fn test_parse_large_body() {
|
|||||||
body := 'A'.repeat(10_001) // greater than max_bytes
|
body := 'A'.repeat(10_001) // greater than max_bytes
|
||||||
req := 'GET / HTTP/1.1\r\nContent-Length: ${body.len}\r\n\r\n${body}'
|
req := 'GET / HTTP/1.1\r\nContent-Length: ${body.len}\r\n\r\n${body}'
|
||||||
mut reader_ := reader(req)
|
mut reader_ := reader(req)
|
||||||
result := parse_request(mut reader_)!
|
result := http.parse_request(mut reader_)!
|
||||||
assert result.data.len == body.len
|
assert result.data.len == body.len
|
||||||
assert result.data == body
|
assert result.data == body
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_build_request_headers_with_empty_body_adds_content_length_zero() {
|
fn test_parse_multipart_form_empty_body() {
|
||||||
// Create a request with no data.
|
body := ''
|
||||||
mut req := Request{}
|
boundary := '----WebKitFormBoundaryQcBIkwnOACVsvR8b'
|
||||||
// Build the headers for it. Ensure that Content-Length: 0 is added
|
form, files := http.parse_multipart_form(body, boundary)
|
||||||
// for requests without a body, which is required by some servers.
|
assert form.len == 0
|
||||||
// We use a POST request, as it is most likely to be affected by this.
|
assert files.len == 0
|
||||||
headers := req.build_request_headers(.post, 'localhost', 80, '/')
|
}
|
||||||
assert headers.contains('Content-Length: 0\r\n')
|
|
||||||
|
fn test_parse_multipart_form_issue_24974_raw() {
|
||||||
|
body := r'------WebKiormBoundaryQcBIkwnOACVsvR8b\r\nContent-Disposition: form-data; name="files"; filename="michael-sum-LEpfefQf4rU-unsplash.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundaryQcBIkwnOACVsvR8b\r\nContent-Disposition: form-data; name="files"; filename="mikhail-vasilyev-IFxjDdqK_0U-unsplash.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundaryQcBIkwnOACVsvR8b--\r\n'
|
||||||
|
boundary := r'----WebKitFormBoundaryQcBIkwnOACVsvR8b'
|
||||||
|
form, files := http.parse_multipart_form(body, boundary)
|
||||||
|
assert form.len == 0
|
||||||
|
assert files.len == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_parse_multipart_form_issue_24974_cooked() {
|
||||||
|
body := '------WebKiormBoundaryQcBIkwnOACVsvR8b\r\nContent-Disposition: form-data; name="files"; filename="michael-sum-LEpfefQf4rU-unsplash.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundaryQcBIkwnOACVsvR8b\r\nContent-Disposition: form-data; name="files"; filename="mikhail-vasilyev-IFxjDdqK_0U-unsplash.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundaryQcBIkwnOACVsvR8b--\r\n'
|
||||||
|
boundary := '----WebKitFormBoundaryQcBIkwnOACVsvR8b'
|
||||||
|
form, files := http.parse_multipart_form(body, boundary)
|
||||||
|
assert form.len == 0
|
||||||
|
assert files.len == 1
|
||||||
|
assert files['files'][0].filename == 'mikhail-vasilyev-IFxjDdqK_0U-unsplash.jpg'
|
||||||
|
assert files['files'][0].content_type == 'image/jpeg'
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user