11// vtest build: !windows
2- module http
3-
2+ import net.http
43import io
54
65struct StringReader {
@@ -30,40 +29,40 @@ fn reader(s string) &io.BufferedReader {
3029
3130fn test_parse_request_not_http () {
3231 mut reader__ := reader ('hello' )
33- parse_request (mut reader__) or { return }
32+ http. parse_request (mut reader__) or { return }
3433 panic ('should not have parsed' )
3534}
3635
3736fn test_parse_request_no_headers () {
3837 mut reader_ := reader ('GET / HTTP/1.1\r\n\r\n ' )
39- req := parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
38+ req := http. parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
4039 assert req.method == .get
4140 assert req.url == '/'
4241 assert req.version == .v1_1
4342}
4443
4544fn test_parse_request_two_headers () {
4645 mut reader_ := reader ('GET / HTTP/1.1\r\n Test1: a\r\n Test2: B\r\n\r\n ' )
47- req := parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
46+ req := http. parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
4847 assert req.header.custom_values ('Test1' ) == ['a' ]
4948 assert req.header.custom_values ('Test2' ) == ['B' ]
5049}
5150
5251fn test_parse_request_two_header_values () {
5352 mut reader_ := reader ('GET / HTTP/1.1\r\n Test1: a; b\r\n Test2: c\r\n Test2: d\r\n\r\n ' )
54- req := parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
53+ req := http. parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
5554 assert req.header.custom_values ('Test1' ) == ['a; b' ]
5655 assert req.header.custom_values ('Test2' ) == ['c' , 'd' ]
5756}
5857
5958fn test_parse_request_body () {
6059 mut reader_ := reader ('GET / HTTP/1.1\r\n Test1: a\r\n Test2: b\r\n Content-Length: 4\r\n\r\n bodyabc' )
61- req := parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
60+ req := http. parse_request (mut reader_) or { panic ('did not parse: ${err }' ) }
6261 assert req.data == 'body'
6362}
6463
6564fn test_parse_request_line () {
66- method , target , version := parse_request_line ('GET /target HTTP/1.1' ) or {
65+ method , target , version := http. parse_request_line ('GET /target HTTP/1.1' ) or {
6766 panic ('did not parse: ${err }' )
6867 }
6968 assert method == .get
@@ -72,34 +71,34 @@ fn test_parse_request_line() {
7271}
7372
7473fn test_parse_form () {
75- assert parse_form ('foo=bar&bar=baz' ) == {
74+ assert http. parse_form ('foo=bar&bar=baz' ) == {
7675 'foo' : 'bar'
7776 'bar' : 'baz'
7877 }
79- assert parse_form ('foo=bar=&bar=baz' ) == {
78+ assert http. parse_form ('foo=bar=&bar=baz' ) == {
8079 'foo' : 'bar='
8180 'bar' : 'baz'
8281 }
83- assert parse_form ('foo=bar%3D&bar=baz' ) == {
82+ assert http. parse_form ('foo=bar%3D&bar=baz' ) == {
8483 'foo' : 'bar='
8584 'bar' : 'baz'
8685 }
87- assert parse_form ('foo=b%26ar&bar=baz' ) == {
86+ assert http. parse_form ('foo=b%26ar&bar=baz' ) == {
8887 'foo' : 'b&ar'
8988 'bar' : 'baz'
9089 }
91- assert parse_form ('a=b& c=d' ) == {
90+ assert http. parse_form ('a=b& c=d' ) == {
9291 'a' : 'b'
9392 ' c' : 'd'
9493 }
95- assert parse_form ('a=b&c= d ' ) == {
94+ assert http. parse_form ('a=b&c= d ' ) == {
9695 'a' : 'b'
9796 'c' : ' d '
9897 }
99- assert parse_form ('{json}' ) == {
98+ assert http. parse_form ('{json}' ) == {
10099 'json' : '{json}'
101100 }
102- assert parse_form ('{
101+ assert http. parse_form ('{
103102 "_id": "76c",
104103 "friends": [
105104 {
@@ -139,10 +138,10 @@ Content-Disposition: form-data; name=\"${names[1]}\"\r
139138${contents [1 ]}\r
140139--${boundary }--\r
141140"
142- form , files := parse_multipart_form (data, boundary)
141+ form , files := http. parse_multipart_form (data, boundary)
143142 assert files == {
144143 names[0 ]: [
145- FileData{
144+ http. FileData{
146145 filename: file
147146 content_type: ct
148147 data: contents[0 ]
@@ -167,7 +166,7 @@ Content-Disposition: form-data; name="password"\r
167166admin123\r
168167--${boundary }--\r
169168'
170- form , files := parse_multipart_form (data, boundary)
169+ form , files := http. parse_multipart_form (data, boundary)
171170 for k, v in form {
172171 eprintln ('> k: ${k } | v: ${v }' )
173172 eprintln ('>> k.bytes(): ${k .bytes ()}' )
@@ -180,7 +179,7 @@ admin123\r
180179fn test_multipart_form_body () {
181180 files := {
182181 'foo' : [
183- FileData{
182+ http. FileData{
184183 filename: 'bar.v'
185184 content_type: 'application/octet-stream'
186185 data: 'baz'
@@ -191,8 +190,8 @@ fn test_multipart_form_body() {
191190 'fooz' : 'buzz'
192191 }
193192
194- body , boundary := multipart_form_body (form, files)
195- parsed_form , parsed_files := parse_multipart_form (body, boundary)
193+ body , boundary := http. multipart_form_body (form, files)
194+ parsed_form , parsed_files := http. parse_multipart_form (body, boundary)
196195 assert parsed_files == files
197196 assert parsed_form == form
198197}
@@ -201,17 +200,33 @@ fn test_parse_large_body() {
201200 body := 'A' .repeat (10_001 ) // greater than max_bytes
202201 req := 'GET / HTTP/1.1\r\n Content-Length: ${body .len }\r\n\r\n ${body }'
203202 mut reader_ := reader (req)
204- result := parse_request (mut reader_)!
203+ result := http. parse_request (mut reader_)!
205204 assert result.data.len == body.len
206205 assert result.data == body
207206}
208207
209- fn test_build_request_headers_with_empty_body_adds_content_length_zero () {
210- // Create a request with no data.
211- mut req := Request{}
212- // Build the headers for it. Ensure that Content-Length: 0 is added
213- // for requests without a body, which is required by some servers.
214- // We use a POST request, as it is most likely to be affected by this.
215- headers := req.build_request_headers (.post, 'localhost' , 80 , '/' )
216- assert headers.contains ('Content-Length: 0\r\n ' )
208+ fn test_parse_multipart_form_empty_body () {
209+ body := ''
210+ boundary := '----WebKitFormBoundaryQcBIkwnOACVsvR8b'
211+ form , files := http.parse_multipart_form (body, boundary)
212+ assert form.len == 0
213+ assert files.len == 0
214+ }
215+
216+ fn test_parse_multipart_form_issue_24974_raw () {
217+ 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'
218+ boundary := r '----WebKitFormBoundaryQcBIkwnOACVsvR8b'
219+ form , files := http.parse_multipart_form (body, boundary)
220+ assert form.len == 0
221+ assert files.len == 0
222+ }
223+
224+ fn test_parse_multipart_form_issue_24974_cooked () {
225+ body := '------WebKiormBoundaryQcBIkwnOACVsvR8b\r\n Content-Disposition: form-data; name="files"; filename="michael-sum-LEpfefQf4rU-unsplash.jpg"\r\n Content-Type: image/jpeg\r\n\r\n\r\n ------WebKitFormBoundaryQcBIkwnOACVsvR8b\r\n Content-Disposition: form-data; name="files"; filename="mikhail-vasilyev-IFxjDdqK_0U-unsplash.jpg"\r\n Content-Type: image/jpeg\r\n\r\n\r\n ------WebKitFormBoundaryQcBIkwnOACVsvR8b--\r\n '
226+ boundary := '----WebKitFormBoundaryQcBIkwnOACVsvR8b'
227+ form , files := http.parse_multipart_form (body, boundary)
228+ assert form.len == 0
229+ assert files.len == 1
230+ assert files['files' ][0 ].filename == 'mikhail-vasilyev-IFxjDdqK_0U-unsplash.jpg'
231+ assert files['files' ][0 ].content_type == 'image/jpeg'
217232}
0 commit comments