factor/basis/http/server/server.factor

243 lines
6.4 KiB
Factor
Raw Normal View History

! Copyright (C) 2003, 2010 Slava Pestov.
2007-09-20 18:09:08 -04:00
! See http://factorcode.org/license.txt for BSD license.
USING: accessors arrays assocs combinators
combinators.short-circuit continuations debugger destructors fry
hashtables html html.streams html.templates http
http.server.remapping http.server.requests http.server.responses
io io.crlf io.encodings io.encodings.ascii io.encodings.iana
io.encodings.utf8 io.servers io.sockets io.sockets.secure
io.streams.limited kernel logging logging.insomniac math
mime.types namespaces present sequences splitting tools.time
urls vectors vocabs vocabs.refresh xml.writer ;
2007-09-20 18:09:08 -04:00
IN: http.server
2008-07-02 22:52:28 -04:00
GENERIC: write-response ( response -- )
GENERIC: write-full-response ( request response -- )
: write-response-line ( response -- response )
dup
[ "HTTP/" write version>> write bl ]
[ code>> present write bl ]
[ message>> write crlf ]
tri ;
: unparse-content-type ( request -- content-type )
[ content-type>> ] [ content-charset>> ] bi
over mime-type-encoding encoding>name or
[ "application/octet-stream" or ] dip
[ "; charset=" glue ] when* ;
2008-07-02 22:52:28 -04:00
: ensure-domain ( cookie -- cookie )
[
url get host>> dup "localhost" =
2008-07-02 22:52:28 -04:00
[ drop ] [ or ] if
] change-domain ;
: write-response-header ( response -- response )
2015-09-08 19:15:10 -04:00
! We send one set-cookie header per cookie, because that's
! what Firefox expects.
2008-07-02 22:52:28 -04:00
dup header>> >alist >vector
over unparse-content-type "content-type" pick set-at
over cookies>> [
ensure-domain unparse-set-cookie
2011-10-15 22:19:44 -04:00
"set-cookie" swap 2array suffix!
2008-07-02 22:52:28 -04:00
] each
write-header ;
: write-response-body ( response -- response )
dup body>> call-template ;
M: response write-response ( respose -- )
write-response-line
write-response-header
flush
drop ;
M: response write-full-response ( request response -- )
dup write-response
swap method>> "HEAD" = [
[ content-encoding>> encode-output ]
2008-07-02 22:52:28 -04:00
[ write-response-body ]
bi
2009-03-15 19:28:46 -04:00
] unless drop ;
2008-07-02 22:52:28 -04:00
M: raw-response write-response ( respose -- )
write-response-line
write-response-body
drop ;
2009-03-15 19:28:46 -04:00
M: raw-response write-full-response ( request response -- )
nip write-response ;
2008-07-02 22:52:28 -04:00
: post-request? ( -- ? ) request get method>> "POST" = ;
2008-06-02 16:00:03 -04:00
SYMBOL: responder-nesting
SYMBOL: main-responder
2008-06-15 05:56:15 -04:00
SYMBOL: development?
SYMBOL: benchmark?
2008-06-02 16:00:03 -04:00
2008-04-25 04:23:47 -04:00
! path is a sequence of path component strings
GENERIC: call-responder* ( path responder -- response )
2008-03-11 04:39:09 -04:00
2008-02-29 01:57:38 -05:00
TUPLE: trivial-responder response ;
2008-02-25 15:53:18 -05:00
2008-02-29 01:57:38 -05:00
C: <trivial-responder> trivial-responder
2008-02-25 15:53:18 -05:00
2008-06-02 16:00:03 -04:00
M: trivial-responder call-responder* nip response>> clone ;
2008-02-25 15:53:18 -05:00
main-responder [ <404> <trivial-responder> ] initialize
2008-04-25 04:23:47 -04:00
: invert-slice ( slice -- slice' )
2008-06-02 16:00:03 -04:00
dup slice? [ [ seq>> ] [ from>> ] bi head-slice ] [ drop { } ] if ;
2008-06-02 16:00:03 -04:00
: add-responder-nesting ( path responder -- )
[ invert-slice ] dip 2array responder-nesting get push ;
2008-04-25 04:23:47 -04:00
: call-responder ( path responder -- response )
2008-06-02 16:00:03 -04:00
[ add-responder-nesting ] [ call-responder* ] 2bi ;
2008-03-03 03:19:36 -05:00
2009-01-31 21:54:49 -05:00
: make-http-error ( error -- xml )
[ "Internal server error" f ] dip
[ print-error nl :c ] with-html-writer
simple-page ;
2008-04-22 22:08:27 -04:00
2008-02-29 01:57:38 -05:00
: <500> ( error -- response )
500 "Internal server error" <trivial-response>
2009-01-31 21:54:49 -05:00
swap development? get [ make-http-error >>body ] [ drop ] if ;
2008-02-29 01:57:38 -05:00
2008-03-11 04:39:09 -04:00
: do-response ( response -- )
2009-03-15 19:28:46 -04:00
'[ request get _ write-full-response ]
[
2008-06-16 02:35:06 -04:00
[ \ do-response log-error ]
[
utf8 [
development? get
2009-01-31 21:54:49 -05:00
[ make-http-error ] [ drop "Response error" ] if
write-xml
2008-06-16 02:35:06 -04:00
] with-encoded-output
] bi
] recover ;
2008-02-25 15:53:18 -05:00
LOG: httpd-hit NOTICE
2008-06-14 05:01:25 -04:00
LOG: httpd-header NOTICE
2009-01-23 19:20:47 -05:00
: log-header ( request name -- )
[ nip ] [ header ] 2bi 2array httpd-header ;
2008-06-14 05:01:25 -04:00
2008-02-25 15:53:18 -05:00
: log-request ( request -- )
2008-06-15 05:56:15 -04:00
[ [ method>> ] [ url>> ] bi 2array httpd-hit ]
2008-06-14 05:01:25 -04:00
[ { "user-agent" "x-forwarded-for" } [ log-header ] with each ]
bi ;
2008-03-17 05:31:13 -04:00
2008-04-25 04:23:47 -04:00
: split-path ( string -- path )
2008-05-14 00:36:55 -04:00
"/" split harvest ;
2008-04-25 04:23:47 -04:00
: request-params ( request -- assoc )
dup method>> {
{ "GET" [ url>> query>> ] }
{ "HEAD" [ url>> query>> ] }
{ "POST" [ post-data>> params>> ] }
} case ;
SYMBOL: params
: param ( name -- value )
params get at ;
: set-param ( value name -- )
params get set-at ;
: init-request ( request -- )
[ request set ]
[ url>> url set ]
[ request-params >hashtable params set ] tri
2008-06-02 16:00:03 -04:00
V{ } clone responder-nesting set ;
: dispatch-request ( request -- response )
url>> path>> split-path main-responder get call-responder ;
: prepare-request ( request -- )
2008-06-12 19:53:53 -04:00
[
local-address get
[ secure? "https" "http" ? >>protocol ]
[ port>> remap-port >>port ]
bi
] change-url drop ;
2008-06-12 19:53:53 -04:00
: valid-request? ( request -- ? )
url>> port>> remap-port
local-address get port>> remap-port = ;
2008-06-12 19:53:53 -04:00
2008-03-11 04:39:09 -04:00
: do-request ( request -- response )
2008-06-02 18:51:06 -04:00
'[
2008-09-10 23:11:40 -04:00
_
2008-06-12 19:53:53 -04:00
{
[ prepare-request ]
2008-09-22 17:54:34 -04:00
[ init-request ]
2008-06-12 19:53:53 -04:00
[ log-request ]
[ dup valid-request? [ dispatch-request ] [ drop <400> ] if ]
} cleave
2008-06-02 16:00:03 -04:00
] [ [ \ do-request log-error ] [ <500> ] bi ] recover ;
2008-03-11 04:39:09 -04:00
2008-03-05 22:38:15 -05:00
: ?refresh-all ( -- )
development? get-global [ [ refresh-all ] with-global ] when ;
2008-03-05 22:38:15 -05:00
2008-06-15 05:56:15 -04:00
LOG: httpd-benchmark DEBUG
: ?benchmark ( quot -- )
benchmark? get [
[ benchmark ] [ first ] bi url get rot 3array
2008-06-15 05:56:15 -04:00
httpd-benchmark
] [ call ] if ; inline
2009-03-20 02:53:49 -04:00
TUPLE: http-server < threaded-server ;
SYMBOL: request-limit
request-limit [ 64 1024 * ] initialize
LOG: httpd-bad-request NOTICE
: handle-client-error ( error -- )
dup request-error? [
dup { [ bad-request-line? ] [ parse-error>> got>> empty? ] } 1&&
[ drop ] [ httpd-bad-request <400> write-response ] if
] [ rethrow ] if ;
2008-06-17 01:10:46 -04:00
M: http-server handle-client*
drop [
[
?refresh-all
request-limit get limited-input
[ read-request ] ?benchmark
[ do-request ] ?benchmark
[ do-response ] ?benchmark
] [ handle-client-error ] recover
2008-03-05 22:38:15 -05:00
] with-destructors ;
2008-02-29 01:57:38 -05:00
2008-06-17 01:10:46 -04:00
: <http-server> ( -- server )
ascii http-server new-threaded-server
2008-06-17 01:10:46 -04:00
"http.server" >>name
"http" protocol-port >>insecure
"https" protocol-port >>secure ;
2008-06-14 03:45:26 -04:00
: httpd ( port -- http-server )
2008-06-17 06:25:21 -04:00
<http-server>
swap >>insecure
f >>secure
start-server ;
2008-06-17 01:10:46 -04:00
: http-insomniac ( -- )
"http.server" { "httpd-hit" } schedule-insomniac ;
2008-09-27 13:16:15 -04:00
"http.server.filters" require
"http.server.dispatchers" require
"http.server.redirection" require
"http.server.static" require
"http.server.cgi" require