More httpd work

db4
Slava Pestov 2008-03-03 02:19:36 -06:00
parent d2517908ee
commit c26b1a895f
10 changed files with 148 additions and 95 deletions

View File

@ -18,6 +18,11 @@ IN: http.tests
[ "\u001234hi\u002045" ] [ "\u001234hi\u002045" url-encode url-decode ] unit-test [ "\u001234hi\u002045" ] [ "\u001234hi\u002045" url-encode url-decode ] unit-test
[ "/" ] [ "http://foo.com" url>path ] unit-test
[ "/" ] [ "http://foo.com/" url>path ] unit-test
[ "/bar" ] [ "http://foo.com/bar" url>path ] unit-test
[ "/bar" ] [ "/bar" url>path ] unit-test
STRING: read-request-test-1 STRING: read-request-test-1
GET http://foo/bar HTTP/1.1 GET http://foo/bar HTTP/1.1
Some-Header: 1 Some-Header: 1
@ -31,7 +36,7 @@ blah
TUPLE{ request TUPLE{ request
port: 80 port: 80
method: "GET" method: "GET"
path: "bar" path: "/bar"
query: H{ } query: H{ }
version: "1.1" version: "1.1"
header: H{ { "some-header" "1; 2" } { "content-length" "4" } } header: H{ { "some-header" "1; 2" } { "content-length" "4" } }
@ -45,7 +50,7 @@ blah
] unit-test ] unit-test
STRING: read-request-test-1' STRING: read-request-test-1'
GET bar HTTP/1.1 GET /bar HTTP/1.1
content-length: 4 content-length: 4
some-header: 1; 2 some-header: 1; 2
@ -69,7 +74,7 @@ Host: www.sex.com
TUPLE{ request TUPLE{ request
port: 80 port: 80
method: "HEAD" method: "HEAD"
path: "bar" path: "/bar"
query: H{ } query: H{ }
version: "1.1" version: "1.1"
header: H{ { "host" "www.sex.com" } } header: H{ { "host" "www.sex.com" } }

View File

@ -180,9 +180,15 @@ cookies ;
: set-query-param ( request value key -- request ) : set-query-param ( request value key -- request )
pick query>> set-at ; pick query>> set-at ;
: chop-hostname ( str -- str' )
CHAR: / over index over length or tail
dup empty? [ drop "/" ] when ;
: url>path ( url -- path ) : url>path ( url -- path )
url-decode "http://" ?head #! Technically, only proxies are meant to support hostnames
[ "/" split1 "" or nip ] [ "/" ?head drop ] if ; #! in HTTP requests, but IE sends these sometimes so we
#! just chop the hostname part.
url-decode "http://" ?head [ chop-hostname ] when ;
: read-method ( request -- request ) : read-method ( request -- request )
" " read-until [ "Bad request: method" throw ] unless " " read-until [ "Bad request: method" throw ] unless

View File

@ -0,0 +1,12 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: http.server.actions
TUPLE: action quot params method ;
C: <action> action
: extract-params ( assoc action -- ... )
params>> [ first2 >r swap at r> call ] with each ;
: call-action ;

View File

@ -50,12 +50,12 @@ SYMBOL: exit-continuation
#! When executed inside a 'show' call, this will force a #! When executed inside a 'show' call, this will force a
#! HTTP 302 to occur to instruct the browser to forward to #! HTTP 302 to occur to instruct the browser to forward to
#! the request URL. #! the request URL.
<temporary-redirect> exit-with ; request get swap <temporary-redirect> exit-with ;
: cont-id "factorcontid" ; : cont-id "factorcontid" ;
: id>url ( id -- url ) : id>url ( id -- url )
request get clone request get
swap cont-id associate >>query swap cont-id associate >>query
request-url ; request-url ;
@ -102,9 +102,8 @@ SYMBOL: current-show
[ restore-request store-current-show ] when* ; [ restore-request store-current-show ] when* ;
: show-final ( quot -- * ) : show-final ( quot -- * )
[ >r redirect-to-here store-current-show
>r store-current-show redirect-to-here r> call exit-with r> call exit-with ; inline
] with-scope ; inline
M: callback-responder call-responder M: callback-responder call-responder
[ [
@ -122,49 +121,15 @@ M: callback-responder call-responder
] callcc1 >r 3drop r> ; ] callcc1 >r 3drop r> ;
: show-page ( quot -- ) : show-page ( quot -- )
>r redirect-to-here store-current-show r>
[ [
>r store-current-show redirect-to-here r> [ ] register-callback
[ with-scope
[ ] register-callback exit-with
call ] callcc1 restore-request ; inline
exit-with
] callcc1 restore-request
] with-scope ; inline
: quot-id ( quot -- id ) : quot-id ( quot -- id )
current-show get swap t register-callback ; current-show get swap t register-callback ;
: quot-url ( quot -- url ) : quot-url ( quot -- url )
quot-id id>url ; quot-id id>url ;
! SYMBOL: current-show
!
! : store-current-show ( -- )
! #! Store the current continuation in the variable 'current-show'
! #! so it can be returned to later by href callbacks. Note that it
! #! recalls itself when the continuation is called to ensure that
! #! it resets its value back to the most recent show call.
! [ ( 0 -- )
! [ ( 0 1 -- )
! current-show set ( 0 -- )
! continue
! ] callcc1
! nip
! store-current-show
! ] callcc0 ;
!
!
! : show-final ( quot -- * )
! store-current-show
! redirect-to-here
! call
! exit-with ; inline
!
! : show-page ( quot -- request )
! store-current-show redirect-to-here
! [
! register-continuation
! call
! exit-with
! ] callcc1 restore-request ; inline

13
extra/http/server/db/db.factor Executable file
View File

@ -0,0 +1,13 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: db http.server kernel new-slots accessors ;
IN: http.server.db
TUPLE: db-persistence responder db params ;
C: <db-persistence> db-persistence
M: db-persistence call-responder
dup db>> over params>> [
responder>> call-responder
] with-db ;

View File

@ -51,3 +51,11 @@ M: mock-responder call-responder
header>> "location" swap at "baz/" tail? r> and header>> "location" swap at "baz/" tail? r> and
] unit-test ] unit-test
] with-scope ] with-scope
[
<dispatcher>
"default" <mock-responder> >>default
default-host set
[ "/default" ] [ "/default" default-host get find-responder drop ] unit-test
] with-scope

View File

@ -3,7 +3,7 @@
USING: assocs kernel namespaces io io.timeouts strings splitting USING: assocs kernel namespaces io io.timeouts strings splitting
threads http sequences prettyprint io.server logging calendar threads http sequences prettyprint io.server logging calendar
new-slots html.elements accessors math.parser combinators.lib new-slots html.elements accessors math.parser combinators.lib
vocabs.loader debugger html continuations random ; vocabs.loader debugger html continuations random combinators ;
IN: http.server IN: http.server
GENERIC: call-responder ( request path responder -- response ) GENERIC: call-responder ( request path responder -- response )
@ -12,7 +12,7 @@ TUPLE: trivial-responder response ;
C: <trivial-responder> trivial-responder C: <trivial-responder> trivial-responder
M: trivial-responder call-responder 2nip response>> call ; M: trivial-responder call-responder nip response>> call ;
: trivial-response-body ( code message -- ) : trivial-response-body ( code message -- )
<html> <html>
@ -33,18 +33,26 @@ M: trivial-responder call-responder 2nip response>> call ;
SYMBOL: 404-responder SYMBOL: 404-responder
[ <404> ] <trivial-responder> 404-responder set-global [ drop <404> ] <trivial-responder> 404-responder set-global
: <redirect> ( to code message -- response ) : modify-for-redirect ( request to -- url )
{
{ [ dup "http://" head? ] [ nip ] }
{ [ dup "/" head? ] [ >>path request-url ] }
{ [ t ] [ >r dup path>> "/" last-split1 drop "/" r> 3append >>path request-url ] }
} cond ;
: <redirect> ( request to code message -- response )
<trivial-response> <trivial-response>
swap "location" set-header ; -rot modify-for-redirect
"location" set-header ;
\ <redirect> DEBUG add-input-logging \ <redirect> DEBUG add-input-logging
: <permanent-redirect> ( to -- response ) : <permanent-redirect> ( request to -- response )
301 "Moved Permanently" <redirect> ; 301 "Moved Permanently" <redirect> ;
: <temporary-redirect> ( to -- response ) : <temporary-redirect> ( request to -- response )
307 "Temporary Redirect" <redirect> ; 307 "Temporary Redirect" <redirect> ;
: <content> ( content-type -- response ) : <content> ( content-type -- response )
@ -54,31 +62,46 @@ SYMBOL: 404-responder
TUPLE: dispatcher default responders ; TUPLE: dispatcher default responders ;
: get-responder ( name dispatcher -- responder ) : <dispatcher> ( -- dispatcher )
tuck responders>> at [ ] [ default>> ] ?if ; 404-responder H{ } clone dispatcher construct-boa ;
: set-main ( dispatcher name -- dispatcher )
[ <temporary-redirect> ] curry
<trivial-responder> >>default ;
: split-path ( path -- rest first )
[ CHAR: / = ] left-trim "/" split1 swap ;
: find-responder ( path dispatcher -- path responder ) : find-responder ( path dispatcher -- path responder )
>r [ CHAR: / = ] left-trim "/" split1 over split-path pick responders>> at*
swap [ CHAR: / = ] right-trim r> get-responder ; [ >r >r 2drop r> r> ] [ 2drop default>> ] if ;
: redirect-with-/ ( request -- response ) : redirect-with-/ ( request -- response )
dup path>> "/" append >>path dup path>> "/" append <permanent-redirect> ;
request-url <permanent-redirect> ;
M: dispatcher call-responder M: dispatcher call-responder
over [ over [
find-responder call-responder 3dup find-responder call-responder [
>r 3drop r>
] [
default>> [
call-responder
] [
3drop f
] if*
] if*
] [ ] [
2drop redirect-with-/ 2drop redirect-with-/
] if ; ] if ;
: <dispatcher> ( -- dispatcher )
404-responder get-global H{ } clone
dispatcher construct-boa ;
: add-responder ( dispatcher responder path -- dispatcher ) : add-responder ( dispatcher responder path -- dispatcher )
pick responders>> set-at ; pick responders>> set-at ;
: add-main-responder ( dispatcher responder path -- dispatcher )
[ add-responder ] keep set-main ;
: <webapp> ( class -- dispatcher )
<dispatcher> swap construct-delegate ; inline
SYMBOL: virtual-hosts SYMBOL: virtual-hosts
SYMBOL: default-host SYMBOL: default-host
@ -88,23 +111,33 @@ default-host global [ drop 404-responder get-global ] cache drop
: find-virtual-host ( host -- responder ) : find-virtual-host ( host -- responder )
virtual-hosts get at [ default-host get ] unless* ; virtual-hosts get at [ default-host get ] unless* ;
SYMBOL: development-mode
: <500> ( error -- response ) : <500> ( error -- response )
500 "Internal server error" <trivial-response> 500 "Internal server error" <trivial-response>
swap [ swap [
"Internal server error" [ "Internal server error" [
[ print-error nl :c ] with-html-stream development-mode get [
[ print-error nl :c ] with-html-stream
] [
500 "Internal server error"
trivial-response-body
] if
] simple-page ] simple-page
] curry >>body ; ] curry >>body ;
: handle-request ( request -- ) : do-response ( request response -- )
[
dup dup path>> over host>>
find-virtual-host call-responder
] [ <500> ] recover
dup write-response dup write-response
swap method>> "HEAD" = swap method>> "HEAD" =
[ drop ] [ write-response-body ] if ; [ drop ] [ write-response-body ] if ;
: do-request ( request -- request )
[
dup dup path>> over host>>
find-virtual-host call-responder
[ <404> ] unless*
] [ dup \ do-request log-error <500> ] recover ;
: default-timeout 1 minutes stdio get set-timeout ; : default-timeout 1 minutes stdio get set-timeout ;
LOG: httpd-hit NOTICE LOG: httpd-hit NOTICE
@ -112,16 +145,17 @@ LOG: httpd-hit NOTICE
: log-request ( request -- ) : log-request ( request -- )
{ method>> host>> path>> } map-exec-with httpd-hit ; { method>> host>> path>> } map-exec-with httpd-hit ;
SYMBOL: development-mode : handle-client ( -- )
: (httpd) ( -- )
default-timeout default-timeout
development-mode get-global development-mode get-global
[ global [ refresh-all ] bind ] when [ global [ refresh-all ] bind ] when
read-request dup log-request handle-request ; read-request
dup log-request
do-request do-response ;
: httpd ( port -- ) : httpd ( port -- )
internet-server "http.server" [ (httpd) ] with-server ; internet-server "http.server"
[ handle-client ] with-server ;
: httpd-main ( -- ) 8888 httpd ; : httpd-main ( -- ) 8888 httpd ;

View File

@ -1,7 +1,9 @@
IN: temporary IN: http.server.sessions.tests
USING: tools.test http.server.sessions math namespaces USING: tools.test http.server.sessions math namespaces
kernel accessors ; kernel accessors ;
: with-session \ session swap with-variable ; inline
"1234" f <session> [ "1234" f <session> [
[ ] [ 3 "x" sset ] unit-test [ ] [ 3 "x" sset ] unit-test

View File

@ -9,10 +9,12 @@ IN: http.server.sessions
! WARNING: this session manager is vulnerable to XSRF attacks ! WARNING: this session manager is vulnerable to XSRF attacks
! ! ! ! ! ! ! ! ! ! ! !
TUPLE: session-manager responder init sessions ; GENERIC: init-session ( responder -- )
TUPLE: session-manager responder sessions ;
: <session-manager> ( responder class -- responder' ) : <session-manager> ( responder class -- responder' )
>r [ ] H{ } clone session-manager construct-boa r> >r H{ } clone session-manager construct-boa r>
construct-delegate ; inline construct-delegate ; inline
TUPLE: session id manager namespace alarm ; TUPLE: session id manager namespace alarm ;
@ -42,13 +44,10 @@ TUPLE: session id manager namespace alarm ;
: schange ( key quot -- ) session swap change-at ; inline : schange ( key quot -- ) session swap change-at ; inline
: with-session ( session quot -- )
>r \ session r> with-variable ; inline
: new-session ( responder -- id ) : new-session ( responder -- id )
[ sessions>> generate-key dup ] keep [ sessions>> generate-key dup ] keep
[ <session> dup touch-session ] keep [ <session> dup touch-session ] keep
[ init>> with-session ] 2keep [ swap \ session [ responder>> init-session ] with-variable ] 2keep
>r over r> sessions>> set-at ; >r over r> sessions>> set-at ;
: get-session ( id responder -- session ) : get-session ( id responder -- session )
@ -59,7 +58,7 @@ TUPLE: session id manager namespace alarm ;
] if ; ] if ;
: call-responder/session ( request path responder session -- response ) : call-responder/session ( request path responder session -- response )
[ responder>> call-responder ] with-session ; \ session set responder>> call-responder ;
: sessions ( -- manager/f ) : sessions ( -- manager/f )
\ session get dup [ manager>> ] when ; \ session get dup [ manager>> ] when ;
@ -82,7 +81,7 @@ M: url-sessions call-responder ( request path responder -- response )
call-responder/session call-responder/session
] [ ] [
new-session nip sess-id set-query-param new-session nip sess-id set-query-param
request-url <temporary-redirect> dup request-url <temporary-redirect>
] if* ; ] if* ;
M: url-sessions session-link* M: url-sessions session-link*
@ -96,14 +95,15 @@ TUPLE: cookie-sessions ;
: <cookie-sessions> ( responder -- responder' ) : <cookie-sessions> ( responder -- responder' )
cookie-sessions <session-manager> ; cookie-sessions <session-manager> ;
: get-session-cookie ( request -- cookie ) : get-session-cookie ( request responder -- cookie )
sess-id get-cookie ; >r sess-id get-cookie dup
[ value>> r> get-session ] [ r> 2drop f ] if ;
: <session-cookie> ( id -- cookie ) : <session-cookie> ( id -- cookie )
sess-id <cookie> ; sess-id <cookie> ;
M: cookie-sessions call-responder ( request path responder -- response ) M: cookie-sessions call-responder ( request path responder -- response )
pick get-session-cookie value>> over get-session [ 3dup nip get-session-cookie [
call-responder/session call-responder/session
] [ ] [
dup new-session dup new-session

View File

@ -87,9 +87,17 @@ TUPLE: file-responder root hook special ;
drop <404> drop <404>
] if ; ] if ;
: <400> 400 "Bad request" <trivial-response> ;
M: file-responder call-responder ( request path responder -- response ) M: file-responder call-responder ( request path responder -- response )
[ over [
responder set ".." pick subseq? [
swap request set 3drop <400>
serve-object ] [
] with-scope ; responder set
swap request set
serve-object
] if
] [
2drop redirect-with-/
] if ;