http.server.requests: if the content-length header is missing or invalid, a (controlled) error is thrown
parent
d30beb13ed
commit
eac41a588a
|
@ -1,73 +1,37 @@
|
||||||
USING: accessors assocs continuations http http.server http.server.requests
|
USING: accessors assocs continuations http http.client http.client.private
|
||||||
io.streams.limited io.streams.string kernel multiline namespaces peg sequences
|
http.server http.server.requests io.streams.limited io.streams.string kernel
|
||||||
splitting tools.test urls ;
|
multiline namespaces peg sequences splitting tools.test urls ;
|
||||||
IN: http.server.requests.tests
|
IN: http.server.requests.tests
|
||||||
|
|
||||||
: normalize-nl ( str -- str' )
|
: normalize-nl ( str -- str' )
|
||||||
"\n" "\r\n" replace ;
|
"\n" "\r\n" replace ;
|
||||||
|
|
||||||
|
: request>string ( request -- string )
|
||||||
|
[ write-request ] with-string-writer ;
|
||||||
|
|
||||||
: string>request ( str -- request )
|
: string>request ( str -- request )
|
||||||
normalize-nl
|
|
||||||
[ request-limit get limited-input read-request ] with-string-reader ;
|
[ request-limit get limited-input read-request ] with-string-reader ;
|
||||||
|
|
||||||
! POST requests
|
! POST requests
|
||||||
STRING: test-post-no-content-type
|
|
||||||
POST / HTTP/1.1
|
|
||||||
connection: close
|
|
||||||
host: 127.0.0.1:55532
|
|
||||||
user-agent: Factor http.client
|
|
||||||
content-length: 7
|
|
||||||
|
|
||||||
foo=bar
|
|
||||||
;
|
|
||||||
{ "foo=bar" "7" } [
|
{ "foo=bar" "7" } [
|
||||||
test-post-no-content-type string>request
|
"foo=bar" "localhost" <post-request> request>string string>request
|
||||||
[ post-data>> data>> ] [ header>> "content-length" of ] bi
|
[ post-data>> data>> ] [ header>> "content-length" of ] bi
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
STRING: test-post-0-content-length
|
|
||||||
POST / HTTP/1.1
|
|
||||||
connection: close
|
|
||||||
host: 127.0.0.1:55532
|
|
||||||
user-agent: Factor http.client
|
|
||||||
content-length: 0
|
|
||||||
|
|
||||||
|
|
||||||
;
|
|
||||||
{ f "0" } [
|
{ f "0" } [
|
||||||
test-post-0-content-length string>request
|
"" "localhost" <post-request> request>string string>request
|
||||||
[ post-data>> data>> ] [ header>> "content-length" of ] bi
|
[ post-data>> data>> ] [ header>> "content-length" of ] bi
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
! Should work no problem.
|
! Incorrect content-length works fine
|
||||||
STRING: test-post-wrong-content-length
|
|
||||||
POST / HTTP/1.1
|
|
||||||
connection: close
|
|
||||||
host: 127.0.0.1:55532
|
|
||||||
user-agent: Factor http.client
|
|
||||||
Content-Type: application/x-www-form-urlencoded; charset=utf-8
|
|
||||||
content-length: 190
|
|
||||||
|
|
||||||
foo=bar
|
|
||||||
;
|
|
||||||
{ H{ { "foo" "bar" } } } [
|
{ H{ { "foo" "bar" } } } [
|
||||||
test-post-wrong-content-length string>request post-data>> params>>
|
{ { "foo" "bar" } } "localhost" <post-request> request>string
|
||||||
|
"7" "190" replace string>request post-data>> params>>
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
STRING: test-post-urlencoded
|
|
||||||
POST / HTTP/1.1
|
|
||||||
Accept: */*
|
|
||||||
Accept-Encoding: gzip, deflate
|
|
||||||
Connection: keep-alive
|
|
||||||
Content-Length: 15
|
|
||||||
Content-Type: application/x-www-form-urlencoded; charset=utf-8
|
|
||||||
Host: news.ycombinator.com
|
|
||||||
User-Agent: HTTPie/0.9.0-dev
|
|
||||||
|
|
||||||
name=John+Smith
|
|
||||||
;
|
|
||||||
{ H{ { "name" "John Smith" } } } [
|
{ H{ { "name" "John Smith" } } } [
|
||||||
test-post-urlencoded string>request post-data>> params>>
|
{ { "name" "John Smith" } } "localhost" <post-request> request>string
|
||||||
|
string>request post-data>> params>>
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
! multipart/form-data
|
! multipart/form-data
|
||||||
|
@ -95,8 +59,8 @@ hello
|
||||||
"form-data; name=\"text\"; filename=\"upload.txt\"" }
|
"form-data; name=\"text\"; filename=\"upload.txt\"" }
|
||||||
}
|
}
|
||||||
} [
|
} [
|
||||||
test-multipart/form-data string>request post-data>> params>> "text" of
|
test-multipart/form-data normalize-nl string>request
|
||||||
[ filename>> ] [ headers>> ] bi
|
post-data>> params>> "text" of [ filename>> ] [ headers>> ] bi
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
! Error handling
|
! Error handling
|
||||||
|
@ -119,21 +83,39 @@ hello
|
||||||
--768de80194d942619886d23f1337aa15--
|
--768de80194d942619886d23f1337aa15--
|
||||||
|
|
||||||
;
|
;
|
||||||
{ t } [
|
[ test-multipart/form-data-missing-boundary string>request ]
|
||||||
[
|
[ no-boundary? ] must-fail-with
|
||||||
test-multipart/form-data-missing-boundary string>request
|
|
||||||
] [ no-boundary? ] recover
|
|
||||||
] unit-test
|
|
||||||
|
|
||||||
! Relative urls are invalid.
|
! Relative urls are invalid.
|
||||||
{ "foo" } [
|
[ "GET foo HTTP/1.1" string>request ] [ path>> "foo" = ] must-fail-with
|
||||||
[ "GET foo HTTP/1.1" string>request ] [ path>> ] recover
|
|
||||||
] unit-test
|
|
||||||
|
|
||||||
! Empty request lines
|
! Empty request lines
|
||||||
{ t } [
|
[ "" string>request ] [ parse-error>> parse-error? ] must-fail-with
|
||||||
[ "" string>request ] [ parse-error>> parse-error? ] recover
|
|
||||||
] unit-test
|
! Missing content-length is probably not ok. It's plausible
|
||||||
|
! transfer-length could replace it, but we don't handle it atm anyway.
|
||||||
|
[
|
||||||
|
{ { "foo" "bar" } } "localhost" <post-request> request>string
|
||||||
|
"content-length" "foo" replace string>request
|
||||||
|
] [ content-length-missing? ] must-fail-with
|
||||||
|
|
||||||
|
! Non-numeric content-length is ofc crap.
|
||||||
|
[
|
||||||
|
{ { "foo" "bar" } } "localhost" <post-request> request>string
|
||||||
|
"7" "i am not a number!" replace string>request
|
||||||
|
] [
|
||||||
|
[ invalid-content-length? ]
|
||||||
|
[ content-length>> "i am not a number!" = ] bi and
|
||||||
|
] must-fail-with
|
||||||
|
|
||||||
|
! Negative is it too.
|
||||||
|
[
|
||||||
|
{ { "foo" "bar" } } "localhost" <post-request> request>string
|
||||||
|
"7" "-1234" replace string>request
|
||||||
|
] [
|
||||||
|
[ invalid-content-length? ]
|
||||||
|
[ content-length>> -1234 = ] bi and
|
||||||
|
] must-fail-with
|
||||||
|
|
||||||
! RFC 2616: Section 4.1
|
! RFC 2616: Section 4.1
|
||||||
! In the interest of robustness, servers SHOULD ignore any empty
|
! In the interest of robustness, servers SHOULD ignore any empty
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
USING: accessors combinators continuations http http.parsers io io.crlf
|
USING: accessors combinators continuations http http.parsers io io.crlf
|
||||||
io.encodings io.encodings.binary io.streams.limited kernel math.order
|
io.encodings io.encodings.binary io.streams.limited kernel math math.order
|
||||||
math.parser namespaces sequences splitting urls urls.encoding ;
|
math.parser namespaces sequences splitting urls urls.encoding ;
|
||||||
FROM: mime.multipart => parse-multipart ;
|
FROM: mime.multipart => parse-multipart ;
|
||||||
IN: http.server.requests
|
IN: http.server.requests
|
||||||
|
@ -10,6 +10,10 @@ ERROR: no-boundary < request-error ;
|
||||||
|
|
||||||
ERROR: invalid-path < request-error path ;
|
ERROR: invalid-path < request-error path ;
|
||||||
|
|
||||||
|
ERROR: invalid-content-length < request-error content-length ;
|
||||||
|
|
||||||
|
ERROR: content-length-missing < request-error ;
|
||||||
|
|
||||||
ERROR: bad-request-line < request-error parse-error ;
|
ERROR: bad-request-line < request-error parse-error ;
|
||||||
|
|
||||||
: check-absolute ( url -- )
|
: check-absolute ( url -- )
|
||||||
|
@ -34,22 +38,28 @@ upload-limit [ 200,000,000 ] initialize
|
||||||
";" split1 nip
|
";" split1 nip
|
||||||
"=" split1 nip [ no-boundary ] unless* ;
|
"=" split1 nip [ no-boundary ] unless* ;
|
||||||
|
|
||||||
: read-multipart-data ( request -- mime-parts )
|
: maybe-limit-input ( content-length -- )
|
||||||
[ "content-type" header ]
|
unlimited-input upload-limit get [ min ] when* limited-input ;
|
||||||
[ "content-length" header string>number ] bi
|
|
||||||
unlimited-input
|
|
||||||
upload-limit get [ min ] when* limited-input
|
|
||||||
binary decode-input
|
|
||||||
parse-multipart-form-data parse-multipart ;
|
|
||||||
|
|
||||||
: read-content ( request -- bytes )
|
: read-multipart-data ( request content-length -- mime-parts )
|
||||||
"content-length" header string>number read ;
|
maybe-limit-input binary decode-input
|
||||||
|
"content-type" header parse-multipart-form-data parse-multipart ;
|
||||||
|
|
||||||
|
: parse-content-length-safe ( request -- content-length )
|
||||||
|
"content-length" header [
|
||||||
|
dup string>number [
|
||||||
|
nip dup 0 >= [ invalid-content-length ] unless
|
||||||
|
] [ invalid-content-length ] if*
|
||||||
|
] [ content-length-missing ] if* ;
|
||||||
|
|
||||||
: parse-content ( request content-type -- post-data )
|
: parse-content ( request content-type -- post-data )
|
||||||
[ <post-data> swap ] keep {
|
dup <post-data> -rot over parse-content-length-safe swap
|
||||||
|
{
|
||||||
{ "multipart/form-data" [ read-multipart-data >>params ] }
|
{ "multipart/form-data" [ read-multipart-data >>params ] }
|
||||||
{ "application/x-www-form-urlencoded" [ read-content query>assoc >>params ] }
|
{ "application/x-www-form-urlencoded" [
|
||||||
[ drop read-content >>data ]
|
nip read query>assoc >>params
|
||||||
|
] }
|
||||||
|
[ drop nip read >>data ]
|
||||||
} case ;
|
} case ;
|
||||||
|
|
||||||
: read-post-data ( request -- request )
|
: read-post-data ( request -- request )
|
||||||
|
|
Loading…
Reference in New Issue