gemma:継続CGI

gemma:継続CGI

ソース

インストール方法

ヒント


(define-module www.contcgi
  (use srfi-13)
  (use gauche.parameter)
  (use text.tree)
  (use text.html-lite)
  (use www.cgi)
  (use www.fastcgi)
  (export show cont-count contcgi-main))
(select-module www.contcgi)

;;Kimura Fuyuki氏の、Gauche-fastcgi<http://d.tir.jp/pw?Gauche-fastcgi>のコードが参考になりました。ありがとうございます。

;;www.fastcgiモジュールでexportされていない関数で、必要なものがいくつかあるから、取り出す。
(define fcgx-is-cgi (with-module www.fastcgi fcgx-is-cgi))
(define fcgx-accept (with-module www.fastcgi fcgx-accept))
(define fcgx-stream->port (with-module www.fastcgi fcgx-stream->port))
(define fcgx-finish (with-module www.fastcgi fcgx-finish))

;;継続サーバの肝である継続を保存するハッシュテーブル。
(define cont-table (make-hash-table 'eq?))
;;継続を保存するたびに増やすカウンタ
(define cont-count 0)
;;大域脱出用
(define return #f)
;;デバッグフラグ。showの動作が変わる。
(define debug-flag #f)

(define (error? obj) (is-a? obj <error>))

(define (error->string obj)
  (and (is-a? obj <error>) (ref obj 'message)))

(define (call-with-contcgi proc)
  (cond ((fcgx-is-cgi)
         ;;端末からの実行の場合、debugフラグをセットする。
         (set! debug-flag #t)
         (proc '()))
        (else
         ;; rush into the infinity..
         (let loop ()
           ;; leave these signals to libfcgi
           (set-signal-handler! SIGTERM #t)
           (set-signal-handler! SIGHUP #t)
           (set-signal-handler! SIGUSR1 #t)
           ;; mod_fastcgi requires this!
           (set-signal-handler! SIGPIPE (lambda (k) (loop)))
           (receive (in out err env) (fcgx-accept)
             (let ((iport (fcgx-stream->port in))
                   (oport (fcgx-stream->port out))
                   (eport (fcgx-stream->port err))
                   (mvs (map (lambda (s)
                               (receive r (string-scan s #\= 'both) r))
                             env)))
               (with-error-handler
                   (lambda (e)
                     (display (if (error? e) (error->string e) e) eport)
                     (newline eport)
                     (close-input-port iport)
                     (close-output-port oport)
                     (close-output-port eport)
                     (fcgx-finish)
                     (raise e))
                 (lambda ()
                   (parameterize ((cgi-metavariables mvs))
                     (write-tree (proc (cgi-parse-parameters)) oport))
                   (close-input-port iport)
                   (close-output-port oport)
                   (close-output-port eport)))))
           (loop)))))

;;cont-tableハッシュテーブルに見つからないkeyが、CGIパラメータcont-countで指定された場合の、エラーページ
(define (error-page params)
  `(,(cgi-header)
    ,(html-doctype)
    ,(html:html
      (html:head (html:title "error"))
      (html:body
       (html:p "Error. A invalid value on the parameter 'cont-count'")
       (html:p (html:b (format "pid = ~a" (sys-getpid))))
       ;;CGIにあたえられたパラメータをhtml:tableの一覧にして表示。
       (html:table
        :border 1
        (html:tr (html:th "Name") (html:th "Value"))
        (map (lambda (p)
               (html:tr
                (html:td (html-escape-string (car p)))
                (html:td (html-escape-string (x->string (cdr p))))))
             params))))))

;;show は、debug時には標準出力へのwrite-treeと標準入力からのread-lineになる。
(define (show page)
  (if debug-flag
      (begin
        (write-tree page)
        (let1 query (begin
                      (display "Enter parameters (name=value).  ^D to stop.\n")
                      (flush)
                      (let loop ((line (read-line))
                                 (params '()))
                        (if (eof-object? line)
                            (string-join (reverse params) "&")
                            (loop (read-line)
                                  (if (string-null? line)
                                      params
                                      (cons line params))))))
          (cgi-parse-parameters :query-string query)))
      (call/cc
       (lambda (cc)
         ;;継続サーバの肝である継続をcont-tableに保存する
         (hash-table-put! cont-table cont-count cc)
         ;;保存している継続の番号をカウントする。
         (inc! cont-count)
         ;;ここで大域脱出returnで返したものが、CGIの出力結果になる。
         (return page)))))

(define (contcgi-main proc)
  (call-with-contcgi
   (lambda (params)
     (call/cc
      (lambda (cc)
        ;;大域脱出用にreturnをセット。
        (set! return cc)
        (let ((cont-count (cgi-get-parameter "cont-count" params :default #f :convert x->number)))
          (if (boolean cont-count)
              ;;foo.fcgi?cont-count=1などなら、継続を呼び出す。
              ((hash-table-get cont-table cont-count error-page) params)
              ;;foo.fcgiなら、メインロジックであるprocを呼び出す。
              (proc params))))))))

(provide "www/contcgi")

#! /usr/local/bin/gosh

(use www.cgi)
(use www.contcgi)
(use text.html-lite)

(define (main args)
  (contcgi-main
   (lambda (params)
     (let* ((a (cgi-get-parameter "value"
                                  (show `(,(cgi-header)
                                          ,(html-doctype)
                                          ,(html:html
                                            (html:body
                                             (html:p (html:b (format "pid = ~a" (sys-getpid))))
                                             (html:p "? + ? = ?")
                                             (html:p "input number")
                                             (html:form
                                              :method "GET" :action "./sum.fcgi"
                                              (html:input :type "hidden" :name "cont-count" :value cont-count)
                                              (html:input :type "text" :name "value")
                                              (html:input :type "submit"))))))
                                  :convert x->number))
            (b  (cgi-get-parameter "value"
                                   (show `(,(cgi-header)
                                           ,(html-doctype)
                                           ,(html:html
                                             (html:body
                                              (html:p (html:b (format "pid = ~a" (sys-getpid))))
                                              (html:p (format #f "~a + ? = ?" a))
                                              (html:p "input number")
                                              (html:form
                                               :method "GET" :action "./sum.fcgi"
                                               (html:input :type "hidden" :name "cont-count" :value cont-count)
                                               (html:input :type "text" :name "value")
                                               (html:input :type "submit"))))))
                                   :convert x->number)))
       (show `(,(cgi-header)
               ,(html-doctype)
               ,(html:html
                 (html:body
                  (html:p (html:b (format "pid = ~a" (sys-getpid))))
                  (html:p (format #f "~a + ~a = ~a" a b (+ a b)))))))))))

;; Local variables:
;; mode: scheme
;; end:
#! /usr/local/bin/gosh

(use text.html-lite)
(use www.cgi)
(use www.contcgi)

(define (main args)
  (contcgi-main
   (lambda (params)
     (let loop ((counter 0))
       (let1 action (cgi-get-parameter "action"
                                       (show `(,(cgi-header)
                                               ,(html-doctype)
                                               ,(html:html
                                                 (html:body
                                                  (html:p (html:b (format "pid = ~a" (sys-getpid))))
                                                  (html:h1 counter)
                                                  (html:br)
                                                  (html:a
                                                   :href (format #f "./counter.fcgi?cont-count=~a&action=inc" cont-count)
                                                   "++")
                                                  "&nbsp"
                                                  (html:a
                                                   :href (format #f "./counter.fcgi?cont-count=~a&action=dec" cont-count)
                                                  "--"))))))
         (cond
          ((equal? action "inc") (loop (+ counter 1)))
          ((equal? action "dec") (loop (- counter 1)))
          (else (loop counter))))))))

;; Local variables:
;; mode: scheme
;; end:
#! /usr/local/bin/gosh

(use text.html-lite)
(use www.cgi)
(use www.contcgi)

(define (main args)
  (contcgi-main
   (lambda (params)
     (let loop ((counter 0))
       (let1 action (cgi-get-parameter "action"
                                       (show `(,(cgi-header)
                                               ,(html-doctype)
                                               ,(html:html
                                                 (html:body
                                                  (html:p (html:b (format "pid = ~a" (sys-getpid))))
                                                  (html:h1 counter)
                                                  (html:br)
                                                  (html:a
                                                   :href (format #f "./counter.fcgi?cont-count=~a&action=inc" cont-count)
                                                   "++")
                                                  "&nbsp"
                                                  (html:a
                                                   :href (format #f "./counter.fcgi?cont-count=~a&action=dec" cont-count)
                                                  "--"))))))
         (cond
          ((equal? action "inc") (loop (+ counter 1)))
          ((equal? action "dec") (loop (- counter 1)))
          (else (loop counter))))))))

;; Local variables:
;; mode: scheme
;; end:
 rio rio orange

Last modified : 2012/01/23 14:39:23 UTC