Gauche:HTTPD

Gauche:HTTPD

インターネットエイジのこの時代、HTTP サーバの実装の一つや二つないと モダーンなプログラミング言語とはいえないんじゃなかろうか...

(長くなってきたので、トピック毎にまとめてみました。 コメント毎に時間を入れておいて頂くと良いかも。--Shiro (2002/11/22 22:04:59 PST))


参考資料

http://www.jaist.ac.jp/~kiyoshiy/software/kumonoito/spec.txt

大学生のプログラミングの課題。ミニマルな仕様。

http://www.w3.org/Protocols/

HTTP

http://www.w3.org/Protocols/rfc2616/rfc2616.html

HTTP1.1

http://www.pugo.org:8080/

Postscript による実装。 skimu 版の参考にした。


nekoie による実装

phttpd


skimu による実装

(ソース 最新版 v1.12 v1.10)

SIGPIPE問題

    cons:sakae$ /www/bin/ab -c 5 -n 100 http://localhost:8081/
 
        :
    ("GET" "/" "HTTP/1.0")
    ("GET" "/" "HTTP/1.0")
    *** ERROR: unhandled signal 13 (SIGPIPE)
    Stack Trace:
    _______________________________________
      0  (zap (mime-type->Content-type mime-type))
            At line 134 of "./httpd.scm"
      1  (let ((in (socket-input-port s)) (out (socket-output-port s))) (le ...
            At line 146 of "./httpd.scm"
      2  (do-http cnl)
            At line 174 of "./httpd.scm"
 
    環境は
    cons:sakae$ gosh -V
    Gauche scheme interpreter, version 0.6.4 [euc-jp]
    cons:sakae$ uname -a
    FreeBSD 4.7-STABLE FreeBSD 4.7-STABLE #0: Mon Oct 14 20:15:12 JST           
      ("GET" "/" "HTTP/1.0")
      (("user-agent" "ApacheBench/1.3d") ("host" "localhost:8080") ("accept" "*/
      "One reqest processed"
      "Waiting for a client"
      "A Client accepted"
      "My handler: bad type of argument for s: #<eof>"
      "Waiting for a client"

MacOSX10.2.2で落ちる

skimu MacOSX10.2.2 だとなんとバスエラーで落ちました。

 Program received signal EXC_BAD_ACCESS, Could not access memory.
 Scm_ClassOf (obj=0x0) at class.c:398
 398             return SCM_CLASS_OF(obj);
 (gdb) bt
 #0  Scm_ClassOf (obj=0x0) at class.c:398
 #1  0x00020124 in Scm_ComputeApplicableMethods (gf=0x0, args=0x2e41b0, nargs=2)   at class.c:1301
 #2  0x000084b8 in run_loop () at vm.c:585
 #3  0x0000b2bc in user_eval_inner (program=0x4700d0) at vm.c:1707
 #4  0x0000318c in main (argc=3, argv=0xbffffdc0) at main.c:275
 #5  0x000024f4 in _start (argc=3, argv=0xbffffdc0, envp=0xbffffdd0) at  /SourceCache/Csu/Csu-45/crt.c:267
 #6  0x00002374 in start ()
    (let loop ((cs #f))
      (with-error-handler 
          (lambda (e) 
            (print-log LOG_ERR #`"My handler: ,(slot-ref e 'message)")
            (report-error e))

     ...

      (loop #f)
*** ext/net/addr.c      6 Jul 2002 23:01:17 -0000       1.12
--- ext/net/addr.c      30 Nov 2002 09:19:03 -0000
***************
*** 77,83 ****
  /* creates sockaddr from struct sockaddr. */
  ScmObj Scm_MakeSockAddr(ScmClass *klass, struct sockaddr *saddr, int len)
  {
!     ScmSockAddr *addr = SCM_NEW2(ScmSockAddr*, sizeof(ScmObj)+len);
      SCM_SET_CLASS(addr, klass);
      addr->addrlen = len;
      memset(&addr->addr, len, 0);
--- 77,85 ----
  /* creates sockaddr from struct sockaddr. */
  ScmObj Scm_MakeSockAddr(ScmClass *klass, struct sockaddr *saddr, int len)
  {
!     ScmSockAddr *addr;
!     addr = SCM_NEW2(ScmSockAddr*,

!                     sizeof(ScmSockAddr) - sizeof(struct sockaddr) + len);
      SCM_SET_CLASS(addr, klass);
      addr->addrlen = len;
      memset(&addr->addr, len, 0);

もじらで見られない問題

            (begin
              (rfc822-header->list in)
              (receive (text type len) (gen-text path)
                (reply text type len out)))

エラーハンドラ

   (define (httpd-main nport)
     (let ((ss (make-server-socket 'inet nport :reuse-addr? #t))
           (cs #f))
       (set-signal-handler! SIGPIPE (lambda (n) (error #`"Catched signal ,|n|")))
       (do () (#f)
         (with-error-handler (lambda (e) (print-log #`"My handler: ,(slot-ref e 'message)"))
           (lambda ()
             (dynamic-wind 
                 (lambda () #f)
                 (lambda ()
                   (print-log "Waiting for a client")
                   (set! cs (socket-accept ss))
                   (print-log "A Client accepted")
                   (do-http cs)
                   (print-log "One reqest processed")
                   )
                 (lambda ()
                   (socket-close cs))))))))

ログの方法

  (define (main args)
    (case (length args)
      ((2)
        ;; ログファイルを指定しなければ、デバッグモードで stderr に詳しいログ
       (set! log-level 4)
       (httpd-main (string->number (cadr args)) #t))
      ((3)
        ;; 二番目の引数はログファイル。 syslog を指定すれば syslog へログ。
       (close-input-port (current-input-port))
       (close-output-port (current-output-port))
       (close-output-port (current-error-port))
       (if (string=? (caddr args) "syslog")
           (httpd-main (string->number (cadr args)) 'syslog)
           (httpd-main (string->number (cadr args)) (caddr args))))
      (else
       (display "Usage: httpd port [logfile]\n" (current-error-port)))))

kawによる実装

子プロセス起動による並行サーバ(の雛型)を書いてみたのですが、Memory faultで落ちてしまいます。

LinuxとOpenBSDでやってみてどちらでも落ちる。なにが悪いんだろう?

... というわけで、詳細は kaw へ。


Shiroによる実装

https://github.com/shirok/Gauche-makiki

仕事で使ってて、必要に応じて機能を少しづつ足してます。

More ...