spl/http.spl
2023-03-06 14:56:49 +01:00

127 lines
3.3 KiB
Text

"stream.spl" import
"net.spl" import
"http" net:register
construct net:http namespace {
Request
Response
help
}
construct net:http:Request {
host port
method path
headers
body
;
construct { this | with host port method path this ;
host this:=host
port this:=port
method this:=method
path this:=path
List:new this:=headers
"" this:=body
this
}
add-header { this | with header this ;
header this:headers:push
this
}
set-body { this | with body this ;
body this:=body
this
}
send { net:http:Response | with this ;
def stream this:host this:port StreamTypes:tcp:create =stream
def response net:http:Response:new =response
this:method:to-bytes stream:write-exact;
" " _:to-bytes stream:write-exact;
this:path:to-bytes stream:write-exact;
" HTTP/1.0\r\n" _:to-bytes stream:write-exact;
"Host: " _:to-bytes stream:write-exact;
this:host:to-bytes stream:write-exact;
"\r\nConnection: Close\r\nUser-Agent: http.spl v0.1 2023-03 (spl@mail.tudbut.de)\r\n"
_:to-bytes stream:write-exact;
{ | with header ;
header:to-bytes stream:write-exact;
"\r\n" stream:write-exact;
} this:headers:foreach
"Content-Length: " _:to-bytes stream:write-exact;
def body this:body:to-bytes =body
body:len _str:to-bytes stream:write-exact;
"\r\n\r\n" _:to-bytes stream:write-exact;
body stream:write-exact;
stream:flush;
def response 1024 stream:read-to-end =response
response net:http:Response:new:read-from-bytes
stream:close;
}
}
construct net:http:help namespace {
;
assert-str { | with expected iter _ ;
[ { | pop iter:next } (expected _array):len:foreach ] _str
expected _str
eq not if {
"Expected " expected concat throw
}
}
until-str { str | with expected iter _ ;
def match 0 =match
def bytes expected:to-bytes =bytes
[
while { match bytes:len eq not } {
iter:next dup (match bytes:get) eq dup if {
match ++ =match
} not if {
0 =match
}
}
{ | pop pop } match:foreach
] _str
}
}
construct net:http:Response {
version
state-num state-msg
headers
body
;
construct { this | with this ;
MicroMap:new this:=headers
"" this:=body
this
}
read-from-bytes { this | with bytes this ;
use net:http:help
bytes:iter =bytes
"HTTP/" bytes help:assert-str
" " bytes help:until-str this:=version
" " bytes help:until-str _mega this:=state-num
"\r\n" bytes help:until-str this:=state-msg
while { "\r\n" bytes help:until-str dup "" eq not } {
def iter ": " swap:split:iter =iter
(
iter:next ": "
iter:join
) this:headers:set;
} pop
0 ("Content-Length" this:headers:get _mega) bytes:collect:sub this:=body
this
}
content-type { str | with this ;
"Content-Type" this:headers:get
}
}