factor/basis/http/server/server.factor

317 lines
8.0 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.
2008-06-02 16:00:03 -04:00
USING: kernel accessors sequences arrays namespaces splitting
vocabs.loader destructors assocs debugger continuations
combinators vocabs.refresh tools.time math math.parser present
vectors hashtables
io
2008-06-12 19:53:53 -04:00
io.sockets
io.sockets.secure
io.encodings
2008-07-02 22:52:28 -04:00
io.encodings.iana
io.encodings.utf8
io.encodings.ascii
io.encodings.binary
io.streams.limited
2009-01-29 14:33:04 -05:00
io.streams.string
io.streams.throwing
io.servers
io.timeouts
2009-01-28 16:46:34 -05:00
io.crlf
2008-09-29 20:49:17 -04:00
fry logging logging.insomniac calendar urls urls.encoding
2009-01-20 17:35:52 -05:00
unicode.categories
http
2008-07-02 22:52:28 -04:00
http.parsers
http.server.responses
2008-09-22 17:49:50 -04:00
http.server.remapping
2008-07-02 22:52:28 -04:00
html.templates
2009-01-31 21:54:49 -05:00
html.streams
2009-01-30 20:28:16 -05:00
html
mime.types
math.order
xml.writer
vocabs ;
FROM: mime.multipart => parse-multipart ;
2007-09-20 18:09:08 -04:00
IN: http.server
2008-07-02 22:52:28 -04:00
: check-absolute ( url -- url )
dup path>> "/" head? [ "Bad request: URL" throw ] unless ; inline
: read-request-line ( request -- request )
read-?crlf [ dup "" = ] [ drop read-?crlf ] while
parse-request-line first3
2008-07-02 22:52:28 -04:00
[ >>method ] [ >url check-absolute >>url ] [ >>version ] tri* ;
: read-request-header ( request -- request )
read-header >>header ;
2009-01-20 17:35:52 -05:00
ERROR: no-boundary ;
: parse-multipart-form-data ( string -- separator )
";" split1 nip
"=" split1 nip [ no-boundary ] unless* ;
SYMBOL: request-limit
request-limit [ 64 1024 * ] initialize
SYMBOL: upload-limit
upload-limit [ 200,000,000 ] initialize
: read-multipart-data ( request -- mime-parts )
[ "content-type" header ]
[ "content-length" header string>number ] bi
unlimited-input
upload-limit get [ min ] when* limited-input
binary decode-input
parse-multipart-form-data parse-multipart ;
2009-01-20 17:35:52 -05:00
: read-content ( request -- bytes )
"content-length" header string>number read ;
: parse-content ( request content-type -- post-data )
[ <post-data> swap ] keep {
{ "multipart/form-data" [ read-multipart-data >>params ] }
{ "application/x-www-form-urlencoded" [ read-content query>assoc >>params ] }
[ drop read-content >>data ]
2009-01-20 17:35:52 -05:00
} case ;
2008-07-02 22:52:28 -04:00
: read-post-data ( request -- request )
dup method>> "POST" = [
2009-01-20 17:35:52 -05:00
dup dup "content-type" header
";" split1 drop parse-content >>post-data
2008-07-02 22:52:28 -04:00
] when ;
: extract-host ( request -- request )
[ ] [ url>> ] [ "host" header parse-host ] tri
[ >>host ] [ >>port ] bi*
drop ;
2008-07-02 22:52:28 -04:00
: extract-cookies ( request -- request )
dup "cookie" header [ parse-cookie >>cookies ] when* ;
: read-request ( -- request )
<request>
read-request-line
read-request-header
read-post-data
extract-host
extract-cookies ;
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 )
#! We send one set-cookie header per cookie, because that's
#! what Firefox expects.
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 '[ _ or ] change-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 ;
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
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