summaryrefslogtreecommitdiffstatshomepage
path: root/tekuti/mod-lisp.scm
diff options
context:
space:
mode:
Diffstat (limited to 'tekuti/mod-lisp.scm')
-rw-r--r--tekuti/mod-lisp.scm256
1 files changed, 175 insertions, 81 deletions
diff --git a/tekuti/mod-lisp.scm b/tekuti/mod-lisp.scm
index f209756..b6c8faf 100644
--- a/tekuti/mod-lisp.scm
+++ b/tekuti/mod-lisp.scm
@@ -1,5 +1,5 @@
;; Tekuti
-;; Copyright (C) 2008 Andy Wingo <wingo at pobox dot com>
+;; Copyright (C) 2008, 2010 Andy Wingo <wingo at pobox dot com>
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
@@ -20,102 +20,196 @@
;;; Commentary:
;;
-;; This is the main script that will launch tekuti.
+;; Web server implementation for mod-lisp.
;;
;;; Code:
(define-module (tekuti mod-lisp)
#:use-module (ice-9 rdelim)
- #:use-module (tekuti config)
- #:use-module (tekuti util)
- #:use-module (tekuti index)
- #:use-module (tekuti request)
- #:use-module (tekuti web)
- #:export (event-loop))
+ #:use-module (system repl error-handling)
+ #:use-module (rnrs bytevectors)
+ #:use-module (web http)
+ #:use-module (web request)
+ #:use-module (web response)
+ #:use-module (web server))
+
;;; FIXME: ignore SIGPIPE, otherwise apache dying will kill us
-(define (read-headers socket)
+(define *mod-lisp-headers* (make-hash-table))
+
+(define (define-mod-lisp-header! sym name parser)
+ (hash-set! *mod-lisp-headers* name (cons sym parser)))
+
+(define (mod-lisp-sym-and-parser name)
+ (hash-ref *mod-lisp-headers* name))
+
+(define-mod-lisp-header! 'server-protocol
+ "server-protocol"
+ parse-http-version)
+
+(define-mod-lisp-header! 'method
+ "method"
+ parse-http-method)
+
+(define-mod-lisp-header! 'url
+ "URL"
+ parse-request-uri)
+
+(define-mod-lisp-header! 'server-ip-addr
+ "server-ip-addr"
+ identity)
+
+(define-mod-lisp-header! 'server-ip-port
+ "server-ip-port"
+ string->number)
+
+(define-mod-lisp-header! 'remote-ip-addr
+ "remote-ip-addr"
+ identity)
+
+(define-mod-lisp-header! 'remote-ip-port
+ "remote-ip-port"
+ string->number)
+
+(define-mod-lisp-header! 'server-id
+ "server-id"
+ identity)
+
+(define-mod-lisp-header! 'server-ip-addr
+ "server-ip-addr"
+ identity)
+
+(define-mod-lisp-header! 'server-baseversion
+ "server-baseversion"
+ identity)
+
+(define-mod-lisp-header! 'modlisp-version
+ "modlisp-version"
+ identity)
+
+(define-mod-lisp-header! 'modlisp-major-version
+ "modlisp-major-version"
+ string->number)
+
+(define (read-headers/mod-lisp socket)
(define (read-line*)
(let ((line (read-line socket)))
(if (eof-object? line)
(error "unexpected eof")
line)))
- (let lp ((keys '()) (values '()))
+ (let lp ((headers '()) (meta '()))
(let ((k (read-line*)))
(if (string=? k "end")
- (reverse (map cons keys values))
- (lp (cons (string-downcase k) keys)
- (cons (read-line*) values))))))
+ (values (reverse! headers) (reverse! meta))
+ (let ((sym-and-parser (mod-lisp-sym-and-parser k))
+ (v (read-line*)))
+ (if sym-and-parser
+ (lp headers
+ (acons (car sym-and-parser)
+ ((cdr sym-and-parser) v)
+ meta))
+ (call-with-values (lambda () (parse-header k v))
+ (lambda (k v)
+ (lp (acons k v headers) meta)))))))))
-(define (read-chars nchars port)
- (let ((buf (make-string nchars)))
- (read-delimited! "" buf port)
- buf))
+(define (read-request/mod-lisp port)
+ (call-with-values (lambda () (read-headers/mod-lisp port))
+ (lambda (headers meta)
+ (build-request
+ #:method (assq-ref meta 'method)
+ #:uri (assq-ref meta 'url)
+ #:version (assq-ref meta 'url)
+ #:headers headers
+ #:meta meta
+ #:port port))))
-(define (write-headers headers port)
+(define (write-header/mod-lisp name val port)
+ (if (string? name)
+ ;; assume that it's a header we don't know about...
+ (begin
+ (display name port) (newline port)
+ (display val port) (newline port))
+ (let ((decl (lookup-header-decl name)))
+ (if (not decl)
+ (error "Unknown header" name)
+ (begin
+ (display (header-decl-name decl) port) (newline port)
+ ((header-decl-writer decl) val port) (newline port))))))
+
+(define (write-response-line/mod-lisp code phrase port)
+ (write-header/mod-lisp "Status"
+ (string-append (number->string code) " " phrase)
+ port))
+
+(define (write-headers/mod-lisp headers port)
(for-each
- (lambda (pair)
- (format port "~a\n~a\n" (car pair) (cdr pair)))
- headers)
- (display "end\n" port))
-
-(define (write-body request socket)
- ((rref request 'output error) socket))
-
-(define (request-output-headers request)
- (rref request 'output-headers '()))
-
-(define (connection-received socket sockaddr index)
- (let* ((headers (read-headers socket))
- (post-data (read-chars (string->number
- (or (assoc-ref headers "content-length")
- "0"))
- socket)))
- (catch
- #t
- (lambda ()
- (let ((res (handle-request (make-request 'headers headers
- 'post-data post-data)
- index)))
- (write-headers (request-output-headers res) socket)
- (write-body res socket)))
- (lambda args
- (write-headers '(("Status" . "500 Internal Server Error")
- ("Content-Type" . "text/plain"))
- socket)
- (display "Sorry bub, had an error. Please let the admin know. Thanks!\n"
- socket)
- (write headers) (newline)
- (write args) (newline)
- (backtrace))
- (lambda args
- (fluid-set! the-last-stack (make-stack #t 2 0))
- (apply throw args)))
-
- (close-port socket)))
-
-(define (with-socket proc)
- (pk 'listening)
- (let ((socket (socket PF_INET SOCK_STREAM 0)))
- (setsockopt socket SOL_SOCKET SO_REUSEADDR 1)
- (bind socket AF_INET (inet-aton *host*) *port*)
- (listen socket *backlog*)
- (unwind-protect
- (proc socket)
- (shutdown socket 2))))
-
-(define (inner-loop socket index)
- (let* ((pair (accept socket))
- (fd (car pair))
- (sockaddr (cdr pair))
- (new-index (maybe-reindex index)))
- (connection-received fd sockaddr (cdr new-index))
- (inner-loop socket new-index)))
-
-(define (event-loop)
- (with-socket
- (lambda (socket)
- (format #t "entering inner loop\n")
- (inner-loop socket (read-index)))))
+ (lambda (pair)
+ (write-header (car pair) (cdr pair) port))
+ headers))
+
+(define (write-response/mod-lisp r port)
+ (write-response-line/mod-lisp (response-code r)
+ (response-reason-phrase r) port)
+ (write-headers (response-headers r) port)
+ (display "end" port) (newline port)
+ (if (eq? port (response-port r))
+ r
+ (build-response #:version (response-version r)
+ #:code (response-code r)
+ #:reason-phrase (response-reason-phrase r)
+ #:headers (response-headers r)
+ #:port port)))
+
+(define (make-default-socket family addr port)
+ (let ((sock (socket PF_INET SOCK_STREAM 0)))
+ (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
+ (bind sock family addr port)
+ sock))
+
+;; -> server
+(define* (mod-lisp-open #:key
+ (host #f)
+ (family AF_INET)
+ (addr (if host
+ (inet-pton family host)
+ INADDR_LOOPBACK))
+ (port 8080)
+ (socket (make-default-socket family addr port)))
+ (listen socket 5)
+ socket)
+
+;; For mod-lisp, we don't do keep-alive.
+(define (keep-alive? response)
+ #f)
+
+;; -> (keep-alive client request body | keep-alive #f #f #f)
+(define (mod-lisp-read server keep-alive)
+ (let* ((client (accept server))
+ (req (read-request/mod-lisp (car client)))
+ (body-str (read-request-body/latin-1 req)))
+ (values keep-alive (car client) req body-str)))
+
+;; -> (#f | client)
+(define (mod-lisp-write server client response body)
+ (let ((response (write-response/mod-lisp response client)))
+ (cond
+ ((not body)) ; pass
+ ((string? body)
+ (write-response-body/latin-1 response body))
+ ((bytevector? body)
+ (write-response-body/bytevector response body))
+ (else
+ (error "Expected a string or bytevector for body" body)))
+ (close-port (response-port response))
+ #f))
+
+;; -> unspecified values
+(define (mod-lisp-close server)
+ (close-port server))
+(define-server-impl mod-lisp
+ mod-lisp-open
+ mod-lisp-read
+ mod-lisp-write
+ mod-lisp-close)