Revision: 6129
Author: iratqq
Date: Thu Dec 31 15:44:40 2009
Log: * scm/http-server.scm:
- New file.
* scm/Makefile.am (SCM_FILES):
- Add http-server.scm.
http://code.google.com/p/uim/source/detail?r=6129
Added:
/trunk/scm/http-server.scm
Modified:
/trunk/scm/Makefile.am
=======================================
--- /dev/null
+++ /trunk/scm/http-server.scm Thu Dec 31 15:44:40 2009
@@ -0,0 +1,252 @@
+;;; http-server.scm: http server library for uim.
+;;;
+;;; Copyright (c) 2010 uim Project http://code.google.com/p/uim/
+;;;
+;;; All rights reserved.
+;;;
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+;;; 1. Redistributions of source code must retain the above copyright
+;;; notice, this list of conditions and the following disclaimer.
+;;; 2. Redistributions in binary form must reproduce the above copyright
+;;; notice, this list of conditions and the following disclaimer in the
+;;; documentation and/or other materials provided with the distribution.
+;;; 3. Neither the name of authors nor the names of its contributors
+;;; may be used to endorse or promote products derived from this
software
+;;; without specific prior written permission.
+;;;
+;;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND
+;;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+;;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
+;;; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
CONTRIBUTORS BE LIABLE
+;;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+;;; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+;;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT
+;;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY
+;;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+;;; SUCH DAMAGE.
+;;;;
+
+(require-extension (srfi 1 2 9 48))
+
+(require "socket.scm")
+(require "input-parse.scm")
+(require "i18n.scm")
+(require "util.scm")
+(require "wlos.scm")
+
+(set! parser-error
+ (lambda (port message . specialising-msg)
+ (print
+ (format "~a: ~a"
+ message
+ (apply string-append
+ (map (lambda (s)
+ (write-to-string s display))
+ specialising-msg))))))
+
+(define alist-cdr
+ (lambda (key alist . args)
+ (let-optionals* args ((comp eq?))
+ (and-let* ((ret
+ (cond ((eq? comp eq?)
+ (assq key alist))
+ ((eq? comp eqv?)
+ (assv key alist))
+ ((eq? comp assoc)
+ (assq key alist))
+ (else
+ (find (lambda (kons)
+ (comp (car kons) key))
+ alist)))))
+ (cdr ret)))))
+
+(define (http-server:decode-query-uri s)
+ (define hex-alist '((#\0 . #x0) (#\1 . #x1) (#\2 . #x2) (#\3 . #x3)
+ (#\4 . #x4) (#\5 . #x5) (#\6 . #x6) (#\7 . #x7)
+ (#\8 . #x8) (#\9 . #x9)
+ (#\a . #xa) (#\b . #xb) (#\c . #xc) (#\d . #xd)
+ (#\e . #xe) (#\f . #xf)
+ (#\A . #xa) (#\B . #xb) (#\C . #xc) (#\D . #xd)
+ (#\E . #xe) (#\F . #xf)))
+ (let loop ((l (string->list s))
+ (rest '()))
+ (cond ((null? l)
+ (list->string (reverse rest)))
+ ((eq? (car l) #\+)
+ (loop (cdr l) (cons #\space rest)))
+ ((and (<= 3 (length l))
+ (eq? (car l) #\%)
+ (assq (cadr l) hex-alist)
+ (assq (caddr l) hex-alist))
+ (loop (drop l 3)
+ (cons (integer->char (+ (* 16 (cdr (assq (cadr l)
hex-alist)))
+ (cdr (assq (caddr l) hex-alist))))
+ rest)))
+ (else
+ (loop (cdr l) (cons (car l) rest))))))
+
+(define (http-server:parse-post s)
+ (if s
+ (let* ((qs (string-split s "&"))
+ (pqs (map (lambda (x)
+ (let ((s (string-split x "=")))
+ (cond ((= 1 (length s))
+ (cons (car s) ""))
+ ((= 2 (length s))
+ (cons (car s) (cadr s)))
+ (else
+ '()))))
+ qs)))
+ (map (lambda (x)
+ (if (pair? x)
+ (cons (http-server:decode-query-uri (car x))
+ (http-server:decode-query-uri (cdr x)))
+ '()))
+ pqs))
+ '()))
+
+(define (http-server:make-server thunk)
+ (lambda (sockets)
+ (let loop ()
+ (let ((fds (file-ready? sockets -1)))
+ (for-each (lambda (pfd)
+ (call-with-sockaddr-storage
+ (lambda (ss)
+ (let ((socket (accept (car pfd) ss)))
+ (thunk socket)))))
+ fds)
+ (loop)))))
+
+(define (http-server:header-field-search l h)
+ (alist-cdr h l (lambda (x y)
+ (and (string? x)
+ (string? y)
+ (string-ci=? x y)))))
+
+(define (http-server:read-header port)
+ (let loop ((str (file-read-line port))
+ (rest '()))
+ (if (or (eof-object? str)
+ (not str)
+ (not (string? str))
+ (string=? "\r" str))
+ (reverse rest)
+ (loop (file-read-line port) (cons str rest)))))
+
+(define (http-server:parse-header lines)
+ (let loop ((lines lines)
+ (state '(status header))
+ (rest '()))
+ (if (null? lines)
+ (reverse rest)
+ (call-with-input-string
+ (car lines)
+ (lambda (port)
+ (cond ((eq? 'status (car state))
+ (let ((method
+ (next-token '() '(#\space)
+ (format (N_ "Invalid header: ~a")
(car lines))
+ port))
+ (resource
+ (next-token '(#\space) '(#\space)
+ (format (N_ "Invalid header: ~a")
(car lines))
+ port))
+ (http
+ (find-string-from-port?
+ "HTTP/"
+ port))
+ (version-number
+ (next-token '(#\space #\.) '(#\return)
+ (format (N_ "Invalid header: ~a")
(car lines))
+ port)))
+ (loop (cdr lines)
+ (cdr state)
+ (append rest (list
+ (cons 'method method)
+ (cons 'resource resource)
+ (cons 'version-number
version-number))))))
+ ((eq? 'header (car state))
+ (let ((field-name
+ (next-token '(#\space #\tab) '(#\:)
+ (format (N_ "Invalid header: ~a")
(car lines))
+ port))
+ (field-value
+ (next-token '(#\: #\space #\tab) '(#\return *eof*)
+ (format (N_ "Invalid header: ~a")
(car lines))
+ port)))
+ (loop (cdr lines)
+ state
+ (cons (cons field-name field-value)
rest))))))))))
+
+
+(define http-server-not-found-response
+ (string-append "HTTP/1.0 404 Not Found\r\n"
+ "Content-Type: text/plain\r\n"
+ "\n"
+ "File not Found\n"))
+(define http-server-internal-error
+ (string-append "HTTP/1.0 501 Internal Error\r\n"
+ "Content-Type: text/plain\r\n"
+ "\n"
+ "File not Found\n"))
+
+(define-class http-server object
+ '((sockets #f)
+ (resource ())
+ (server #f))
+ '(start
+ stop
+ regist-resource!
+ ))
+
+(class-set-method! http-server start
+ (lambda (self hostname servname)
+ (http-server-set-sockets!
+ self
+ (tcp-listen hostname servname))
+ (http-server-set-server!
+ self
+ (http-server:make-server
+ (lambda (s)
+ (call-with-open-file-port
+ s
+ (lambda (port)
+ (or
+ (and-let* ((header (http-server:read-header port))
+ (parsed-header (http-server:parse-header header))
+ (resource (assq-cdr 'resource parsed-header)))
+ (let* ((service (alist-cdr resource
(http-server-resource self) string=?))
+ (content-length
(http-server:header-field-search parsed-header "Content-Length")))
+ (if (not service)
+ (file-display http-server-not-found-response
port)
+ (let* ((body (if content-length
+ (file-read-buffer port
(string->number content-length))
+ #f))
+ (message (service resource
parsed-header (http-server:parse-post body))))
+ (if message
+ (file-display
+ (string-append "HTTP/1.0 302 Found\r\n"
+ "Content-Type:
text/html\r\n"
+ "Content-Length: "
(number->string (string-length message)) "\r\n"
+ "\n"
+ message)
+ port)
+ (file-display http-server-internal-error
port))))))
+ ;; unknown request
+ (file-display http-server-internal-error port)))))))
+ ((http-server-server self)
+ (http-server-sockets self))))
+
+(class-set-method! http-server stop
+ (lambda (self)
+ #t))
+
+(class-set-method! http-server regist-resource!
+ (lambda (self resource thunk)
+ (http-server-set-resource!
+ self
+ (alist-replace (cons resource thunk)
+ (http-server-resource self)))))
=======================================
--- /trunk/scm/Makefile.am Mon Dec 7 02:48:20 2009
+++ /trunk/scm/Makefile.am Thu Dec 31 15:44:40 2009
@@ -46,11 +46,12 @@
ajax-ime.scm ajax-ime-custom.scm ajax-ime-key-custom.scm \
yahoo-jp.scm yahoo-jp-custom.scm yahoo-jp-key-custom.scm \
uim-module-manager.scm \
- fileio.scm socket.scm http-client.scm process.scm \
+ fileio.scm socket.scm process.scm \
openssl.scm \
sqlite3.scm \
lolevel.scm \
- input-parse.scm match.scm
+ input-parse.scm match.scm \
+ http-client.scm http-server.scm
ETAGS_ARGS=$(SCM_FILES) $(GENERATED_SCM_FILES)