factor/extra/http/server/server.factor

297 lines
7.2 KiB
Factor
Raw Normal View History

2008-02-07 02:05:10 -05:00
! Copyright (C) 2003, 2008 Slava Pestov.
2007-09-20 18:09:08 -04:00
! See http://factorcode.org/license.txt for BSD license.
2008-02-09 22:34:42 -05:00
USING: assocs kernel namespaces io io.timeouts strings splitting
2008-05-23 18:45:33 -04:00
threads sequences prettyprint io.server logging calendar http
2008-05-26 01:47:27 -04:00
html.streams html.elements accessors math.parser
combinators.lib tools.vocabs debugger continuations random
combinators destructors io.encodings.8-bit fry classes words
math rss json.writer ;
2007-09-20 18:09:08 -04:00
IN: http.server
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
: request-params ( request -- assoc )
dup method>> {
{ "GET" [ query>> ] }
{ "HEAD" [ query>> ] }
{ "POST" [ post-data>> ] }
} case ;
2008-05-26 01:47:27 -04:00
: <content> ( body content-type -- response )
2008-03-11 04:39:09 -04:00
<response>
200 >>code
2008-03-13 04:48:39 -04:00
"Document follows" >>message
2008-05-26 01:47:27 -04:00
swap >>content-type
swap >>body ;
2008-05-01 17:24:50 -04:00
2008-05-26 01:47:27 -04:00
: <text-content> ( body -- response )
"text/plain" <content> ;
: <html-content> ( body -- response )
"text/html" <content> ;
: <xml-content> ( body -- response )
"text/xml" <content> ;
: <feed-content> ( feed -- response )
'[ , feed>xml ] "text/xml" <content> ;
: <json-content> ( obj -- response )
'[ , >json ] "application/json" <content> ;
2008-02-25 15:53:18 -05: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
M: trivial-responder call-responder* nip response>> call ;
2008-02-25 15:53:18 -05:00
: trivial-response-body ( code message -- )
<html>
<body>
2008-03-11 04:39:09 -04:00
<h1> [ number>string write bl ] [ write ] bi* </h1>
2008-02-25 15:53:18 -05:00
</body>
</html> ;
2008-02-29 01:57:38 -05:00
: <trivial-response> ( code message -- response )
2008-05-01 17:24:50 -04:00
2dup '[ , , trivial-response-body ] <html-content>
2008-03-11 04:39:09 -04:00
swap >>message
swap >>code ;
2008-02-25 15:53:18 -05:00
: <400> ( -- response )
400 "Bad request" <trivial-response> ;
2008-02-29 01:57:38 -05:00
: <404> ( -- response )
2008-02-25 15:53:18 -05:00
404 "Not Found" <trivial-response> ;
2008-02-29 01:57:38 -05:00
SYMBOL: 404-responder
2008-03-11 04:39:09 -04:00
[ <404> ] <trivial-responder> 404-responder set-global
2008-04-25 04:23:47 -04:00
SYMBOL: base-paths
: invert-slice ( slice -- slice' )
dup slice? [
[ seq>> ] [ from>> ] bi head-slice
] [
drop { }
] if ;
: add-base-path ( path dispatcher -- )
[ invert-slice ] [ class word-name ] bi*
base-paths get set-at ;
: call-responder ( path responder -- response )
[ add-base-path ] [ call-responder* ] 2bi ;
SYMBOL: link-hook
2008-04-26 06:49:41 -04:00
: add-link-hook ( quot -- )
link-hook [ compose ] change ; inline
: modify-query ( query -- query )
2008-04-26 06:49:41 -04:00
link-hook get call ;
2008-04-25 04:23:47 -04:00
: base-path ( string -- path )
dup base-paths get at
[ ] [ "No such responder: " swap append throw ] ?if ;
: resolve-base-path ( string -- string' )
"$" ?head [
[
2008-05-26 01:47:27 -04:00
"/" split1 [ base-path [ "/" % % ] each "/" % ] dip %
2008-04-25 04:23:47 -04:00
] "" make
] when ;
: link>string ( url query -- url' )
2008-04-25 04:23:47 -04:00
[ resolve-base-path ] [ modify-query ] bi* (link>string) ;
: write-link ( url query -- )
link>string write ;
SYMBOL: form-hook
2008-04-26 06:49:41 -04:00
: add-form-hook ( quot -- )
form-hook [ compose ] change ;
: hidden-form-field ( -- )
2008-04-26 06:49:41 -04:00
form-hook get call ;
2008-03-11 04:39:09 -04:00
: absolute-redirect ( to query -- url )
#! Same host.
request get clone
2008-04-25 04:23:47 -04:00
swap [ >>query ] when*
swap url-encode >>path
[ modify-query ] change-query
2008-03-11 04:39:09 -04:00
request-url ;
: replace-last-component ( path with -- path' )
2008-05-26 01:47:27 -04:00
[ "/" last-split1 drop "/" ] dip 3append ;
2008-02-29 01:57:38 -05:00
2008-03-11 04:39:09 -04:00
: relative-redirect ( to query -- url )
request get clone
swap [ >>query ] when*
swap [ '[ , replace-last-component ] change-path ] when*
2008-04-25 04:23:47 -04:00
[ modify-query ] change-query
2008-03-11 04:39:09 -04:00
request-url ;
: derive-url ( to query -- url )
2008-03-03 03:19:36 -05:00
{
{ [ over "http://" head? ] [ link>string ] }
2008-03-11 04:39:09 -04:00
{ [ over "/" head? ] [ absolute-redirect ] }
2008-05-26 01:47:27 -04:00
{ [ over "$" head? ] [ [ resolve-base-path ] dip derive-url ] }
2008-04-11 13:55:57 -04:00
[ relative-redirect ]
2008-03-03 03:19:36 -05:00
} cond ;
2008-03-11 04:39:09 -04:00
: <redirect> ( to query code message -- response )
<trivial-response> -rot derive-url "location" set-header ;
2008-02-25 15:53:18 -05:00
2008-02-29 01:57:38 -05:00
\ <redirect> DEBUG add-input-logging
2008-03-11 04:39:09 -04:00
: <permanent-redirect> ( to query -- response )
2008-02-25 15:53:18 -05:00
301 "Moved Permanently" <redirect> ;
2008-03-11 04:39:09 -04:00
: <temporary-redirect> ( to query -- response )
2008-02-25 15:53:18 -05:00
307 "Temporary Redirect" <redirect> ;
: <standard-redirect> ( to query -- response )
request get method>> "POST" =
[ <permanent-redirect> ] [ <temporary-redirect> ] if ;
2008-02-29 01:57:38 -05:00
TUPLE: dispatcher default responders ;
2008-02-25 15:53:18 -05:00
2008-04-14 05:34:26 -04:00
: new-dispatcher ( class -- dispatcher )
2008-04-14 05:42:43 -04:00
new
2008-04-14 05:34:26 -04:00
404-responder get >>default
H{ } clone >>responders ; inline
2008-03-03 03:19:36 -05:00
: <dispatcher> ( -- dispatcher )
2008-04-14 05:34:26 -04:00
dispatcher new-dispatcher ;
2008-03-03 03:19:36 -05:00
2008-02-29 01:57:38 -05:00
: find-responder ( path dispatcher -- path responder )
2008-04-25 04:23:47 -04:00
over empty? [
"" over responders>> at*
[ nip ] [ drop default>> ] if
2008-02-29 01:57:38 -05:00
] [
2008-04-25 04:23:47 -04:00
over first over responders>> at*
2008-05-26 01:47:27 -04:00
[ [ drop rest-slice ] dip ] [ drop default>> ] if
2008-02-29 01:57:38 -05:00
] if ;
2008-02-25 15:53:18 -05:00
M: dispatcher call-responder* ( path dispatcher -- response )
find-responder call-responder ;
2008-04-25 04:23:47 -04:00
2008-03-15 07:22:47 -04:00
TUPLE: vhost-dispatcher default responders ;
: <vhost-dispatcher> ( -- dispatcher )
404-responder get H{ } clone vhost-dispatcher boa ;
2008-03-15 07:22:47 -04:00
: find-vhost ( dispatcher -- responder )
request get host>> over responders>> at*
[ nip ] [ drop default>> ] if ;
M: vhost-dispatcher call-responder* ( path dispatcher -- response )
2008-03-15 07:22:47 -04:00
find-vhost call-responder ;
2008-02-29 01:57:38 -05:00
: add-responder ( dispatcher responder path -- dispatcher )
pick responders>> set-at ;
2008-02-25 15:53:18 -05:00
2008-03-03 03:19:36 -05:00
: add-main-responder ( dispatcher responder path -- dispatcher )
2008-04-25 04:23:47 -04:00
[ add-responder drop ]
[ drop "" add-responder drop ]
[ 2drop ] 3tri ;
2008-03-03 03:19:36 -05:00
TUPLE: filter-responder responder ;
M: filter-responder call-responder*
responder>> call-responder ;
2008-03-11 04:39:09 -04:00
SYMBOL: main-responder
2008-02-25 15:53:18 -05:00
2008-03-11 04:39:09 -04:00
main-responder global
[ drop 404-responder get-global ] cache
drop
2008-02-25 15:53:18 -05:00
2008-03-03 03:19:36 -05:00
SYMBOL: development-mode
2008-04-22 22:08:27 -04:00
: http-error. ( error -- )
"Internal server error" [
development-mode get [
[ print-error nl :c ] with-html-stream
] [
500 "Internal server error"
trivial-response-body
] if
] simple-page ;
2008-02-29 01:57:38 -05:00
: <500> ( error -- response )
500 "Internal server error" <trivial-response>
2008-04-22 22:08:27 -04:00
swap '[ , http-error. ] >>body ;
2008-02-29 01:57:38 -05:00
2008-03-11 04:39:09 -04:00
: do-response ( response -- )
2008-02-29 01:57:38 -05:00
dup write-response
2008-03-11 04:39:09 -04:00
request get method>> "HEAD" =
2008-04-22 22:08:27 -04:00
[ drop ] [
'[
, write-response-body
] [
http-error.
] recover
] if ;
2008-02-25 15:53:18 -05:00
LOG: httpd-hit NOTICE
: log-request ( request -- )
{ method>> host>> path>> } map-exec-with httpd-hit ;
2008-02-07 18:07:43 -05:00
2008-03-11 04:39:09 -04:00
SYMBOL: exit-continuation
: exit-with exit-continuation get continue-with ;
2008-03-17 05:31:13 -04:00
: with-exit-continuation ( quot -- )
'[ exit-continuation set @ ] callcc1 exit-continuation off ;
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
: init-request ( -- )
H{ } clone base-paths set
[ ] link-hook set
[ ] form-hook set ;
2008-03-11 04:39:09 -04:00
: do-request ( request -- response )
2008-03-17 05:31:13 -04:00
[
init-request
2008-03-17 05:31:13 -04:00
[ request set ]
[ log-request ]
2008-04-25 04:23:47 -04:00
[ path>> split-path main-responder get call-responder ] tri
2008-03-17 05:31:13 -04:00
[ <404> ] unless*
] [
[ \ 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 ( -- )
2008-02-29 01:57:38 -05:00
development-mode get-global
2008-03-05 22:38:15 -05:00
[ global [ refresh-all ] bind ] when ;
: handle-client ( -- )
[
1 minutes timeouts
2008-03-05 22:38:15 -05:00
?refresh-all
read-request
2008-03-11 04:39:09 -04:00
do-request
do-response
2008-03-05 22:38:15 -05:00
] with-destructors ;
2008-02-29 01:57:38 -05:00
2007-09-20 18:09:08 -04:00
: httpd ( port -- )
2008-05-23 18:45:33 -04:00
dup integer? [ internet-server ] when
"http.server" latin1
[ handle-client ] with-server ;
2007-09-20 18:09:08 -04:00
2008-05-23 18:45:33 -04:00
: httpd-main ( -- )
8888 httpd ;
2007-09-20 18:09:08 -04:00
MAIN: httpd-main