From 3356a3a3bb857a2ad69e6e4e0db81e9c03f2fdc1 Mon Sep 17 00:00:00 2001 From: Brennan Cheung Date: Thu, 29 Jul 2010 17:54:24 -0700 Subject: [PATCH] fastcgi alpha preview --- extra/fastcgi/authors.txt | 3 + extra/fastcgi/fastcgi.factor | 216 +++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 extra/fastcgi/authors.txt create mode 100644 extra/fastcgi/fastcgi.factor diff --git a/extra/fastcgi/authors.txt b/extra/fastcgi/authors.txt new file mode 100644 index 0000000000..8341b66a3d --- /dev/null +++ b/extra/fastcgi/authors.txt @@ -0,0 +1,3 @@ +Brennan Cheung + + diff --git a/extra/fastcgi/fastcgi.factor b/extra/fastcgi/fastcgi.factor new file mode 100644 index 0000000000..6500aabae1 --- /dev/null +++ b/extra/fastcgi/fastcgi.factor @@ -0,0 +1,216 @@ +! Copyright (C) 2010 Brennan Cheung. +! See http://factorcode.org/license.txt for BSD license. + +! This version of the FastCGI library only supports single connections. +! As opposed to multiplexing multiple requests across a single +! connection. +! +! It also expects the following FastCGI parameters to be present: +! * REQUEST_METHOD +! * REQUEST_URI +! +! The following are recommended: +! * HTTP_USER_AGENT +! * REMOTE_ADDR + +USING: accessors alien.enums alien.syntax assocs combinators +combinators.smart formatting http http.server http.server.responses +io io.binary io.directories +io.encodings.binary io.files io.servers.connection io.sockets +io.streams.byte-array kernel locals math namespaces pack prettyprint +sequences sequences.deep strings threads urls.encoding unix.users ; +IN: fastcgi + +SYMBOL: fcgi-server +SYMBOL: fcgi-role +SYMBOL: fcgi-flags +SYMBOL: fcgi-params +SYMBOL: fcgi-request +SYMBOL: stdin-data + +CONSTANT: fcgi-version 1 +CONSTANT: socket-path "/chroot/web/var/run/factor.sock" + +TUPLE: fcgi-header version type request-id content-length + padding-length reserved ; + + +ENUM: fcgi-header-types + { FCGI_BEGIN_REQUEST 1 } + FCGI_ABORT_REQUEST + FCGI_END_REQUEST + FCGI_PARAMS + FCGI_STDIN + FCGI_STDOUT + FCGI_STDERR + FCGI_DATA + FCGI_GET_VALUES + FCGI_GET_VALUES_RESULT + FCGI_UNKNOWN_TYPE + { FCGI_MAXTYPE 11 } ; + +ENUM: fcgi-roles + { FCGI_RESPONDER 1 } + FCGI_AUTHORIZER + FCGI_FILTER ; + +ENUM: fcgi-protocol-status + { FCGI_REQUEST_COMPLETE 0 } + FCGI_CANT_MAX_CONN + FCGI_OVERLOADED + FCGI_UNKNOWN_ROLE ; + +:: debug-print ( print-quot -- ) + global [ print-quot call flush ] bind ; inline + +! read either a 1 byte or 4 byte big endian integer +: read-var-int ( -- n/f ) + read1 [ + dup 7 bit? + [ 127 bitand 3 read swap suffix be> ] when + ] [ f ] if* ; + +:: store-key-value-param ( key value -- ) + request tget value key set-header drop ; + +: read-params ( -- ) + [ + read-var-int read-var-int 2dup and + [ + [ read >string ] bi@ + store-key-value-param + t + ] [ 2drop f ] if + ] loop ; + +: delete-if-exists ( file -- ) + dup exists? [ delete-file ] [ drop ] if ; + +: make-local-socket ( socket-path -- socket ) + [ delete-if-exists ] keep + ; + +: get-header ( -- header ) + "CCSSCC" read-packed-be + [ fcgi-header boa ] input> fcgi-header-types number>enum >>type ; + +: get-content-data ( header -- content ) + dup + [ content-length>> ] + [ padding-length>> ] bi or 0 > ! because 0 read blocks + [ + [ content-length>> read ] + [ padding-length>> read drop ] bi + ] [ drop f ] if ; + +: begin-request-body ( seq -- ) + binary [ "SCCCCCC" read-packed-be ] with-byte-reader + first2 fcgi-flags tset fcgi-roles + number>enum fcgi-role tset ; + +: process-begin-request ( header -- ) + get-content-data begin-request-body ; + +: process-params ( header -- ) + get-content-data binary [ read-params ] with-byte-reader ; + +:: make-response-packet ( content -- seq ) + [ + fcgi-version ! version + FCGI_STDOUT enum>number ! type + 1 ! request id + content length ! content length + 0 ! padding length + 0 ! reserved + ] output>array + "CCSSCC" pack-be content append ; + +:: make-end-request-body ( app-status protocol-status -- seq ) + [ app-status protocol-status 0 0 0 ] output>array + "ICCCC" pack-be ; + +: make-end-request ( -- seq ) + [ + fcgi-version ! version + FCGI_END_REQUEST enum>number ! type + 1 ! request id + 8 ! content length (always 8 for end-request-body) + 0 ! padding length + 0 ! reserved + 0 0 make-end-request-body + ] output>array flatten ; + +: write-response ( content -- ) + make-response-packet write make-end-request write ; + +:: append-stdin-data ( str -- ) + stdin-data [ str append ] tchange ; + +! process a header and determine whether we are +! expecting more input +: dispatch-by-header ( header -- ? ) + dup type>> + { + { FCGI_BEGIN_REQUEST [ process-begin-request t ] } + { FCGI_PARAMS [ process-params t ] } + { FCGI_STDIN [ get-content-data dup append-stdin-data length 0 > ] } ! keep going until STDIN empty + { FCGI_DATA [ [ "FCGI_DATA ------------------\n" print ] debug-print get-content-data [ >string . ] debug-print f ] } + [ [ "unkown packet type" print ] debug-print drop [ . ] debug-print f ] + } case ; + +: make-new-request ( -- ) + request tset ; + +: parse-packets ( -- ) + [ get-header dispatch-by-header ] loop ; + +: post? ( -- ? ) request tget method>> "POST" = ; + +:: handle-post-data* ( post-data data params -- ) + post-data data >>data params >>params + request tget swap >>post-data drop ; + +: handle-post-data ( -- ) + post? [ + request tget dup "CONTENT_TYPE" header + [ >>post-data ] keep nip + stdin-data tget >string dup query>assoc + handle-post-data* + ] when ; + +: prepare-request ( -- ) + request tget + dup "REQUEST_METHOD" header >>method + dup "REQUEST_URI" header >>url + handle-post-data + [ . ] debug-print ; + +: fcgi-handler ( -- ) + make-new-request parse-packets + prepare-request + "/path" main-responder get call-responder* + [ content-type>> "\n\n" append ] [ body>> ] bi append write-response ; + +: ( addr -- server ) + binary + + swap >>insecure + "fastcgi-server" >>name + [ fcgi-handler ] >>handler ; + +: test-output ( -- str ) + "
" 
+    request tget header>> [ "%s => %s\n" sprintf ] { }
+    assoc>map concat append
+    "
" append ; + +TUPLE: test-responder ; +C: test-responder +M: test-responder call-responder* 2drop test-output "text/html" ; + +: do-it ( -- ) + main-responder set + socket-path [ delete-if-exists ] keep + make-local-socket dup fcgi-server set + start-server ;