プログラミングを頑張る日記

プログラミングを勉強して、ハッカーになろう

Common Lispを頑張る(39)

やっていきます「Land of Lisp」。
色々試し、参考書通りにもやってみましたが駄目でした。
(間違い無く繋がり、それでも「無効な応答」とブラウザに言われてしまった)
usocketで挑戦し、締めくくろうと思います。

(defun serve (request-handler)
  (let ((socket (socket-server 8080)))
    (unwind-protect
        (loop (with-open-stream (stream (socket-accept socket))
                                (let* ((url (parse-url (read-line stream)))
                                       (path (car url))
                                       (header (get-header stream))
                                       (params (append (cdr url)
                                                       (get-content-params stream header)))
                                       (*standard-output* stream))
                                  (funcall request-handler path header params))))
      (socket-server-close socket))))

あらためて、参考書例です。

さて。まずはusocketにきてもらいます。
The usocket Reference Manual
どうすればいいのかな。

ASDFとはなんぞやとググってみたところ、パッケージ管理ソフトなのかな。
roswellでできれば話が早くて助かるのですが。

ros install usocket

できた気がします。

USOCKET API documentation
さて、ここが参考にできるかな。

なにはともあれ、まずはusocketを使えるようにせねば。

CL-USER> (require :usocket)
; compiling file "/Users/user/.roswell/lisp/quicklisp/dists/quicklisp/software/split-sequence-v1.4.1/split-sequence.lisp" (written 10 JUL 2017 04:56:57 AM):
.
.(省略)
.
; compilation unit finished
;   printed 41 notes
NIL
CL-USER>

うまくいったようなので、ソケットを作ります。

CL-USER> (defun socket-server (port)
           (usocket:socket-listen *localhost-address* port))
WARNING: redefining COMMON-LISP-USER::SOCKET-SERVER in DEFUN
SOCKET-SERVER
CL-USER>

そしてこんな感じかな。

CL-USER> (defun socket-server (port)
           (usocket:socket-listen *localhost-address* port))
WARNING: redefining COMMON-LISP-USER::SOCKET-SERVER in DEFUN
SOCKET-SERVER
CL-USER> (defun serve (request-handler)
  (let ((socket (socket-server 8080)))
    (unwind-protect
        (loop (with-open-stream (stream (usocket:socket-stream (USOCKET:socket-accept socket)))
                                (let* ((url (parse-url (read-line stream)))
                                       (path (car url))
                                       (header (get-header stream))
                                       (params (append (cdr url)
                                                       (get-content-params stream header)))
                                       (*standard-output* stream))
                                  (funcall request-handler path header params))))
      (usocket:socket-close socket))))
WARNING: redefining COMMON-LISP-USER::SERVE in DEFUN
SERVE
CL-USER>

使ってみます。

CL-USER> (serve #'hello-request-handler)
; Evaluation aborted on #<SB-KERNEL:CASE-FAILURE expected-type:
                         (OR STRING (OR (VECTOR T 4) (ARRAY (UNSIGNED-BYTE 8) (4))) ..)

                         datum: (127 0 0 1)>.
CL-USER>

配列かあ。

CL-USER> (defparameter *localhost-address* #(127 0 0 1))
*LOCALHOST-ADDRESS*
CL-USER>

修正。再挑戦。
......。
「無効な応答が送信されました」だそう。
しかし、エラーコードは、「ERR_INVALID_HTTP_RESPONSE」。
悪いのは接続ではなさそうだと思えます。

と、調べるとそのものずばりなものが。
HTTP - localhostへの接続が拒否される|teratail
...そうなのかあ。
とりあえず真似します。

CL-USER> (defun hello-request-handler (path header params)
  (if (equal path "greeting")
      (let ((name (assoc 'name params)))
        (format t "HTTP/1.0 200 OK~C~C" #\return #\linefeed)
        (format t "Content-Type: text/html~C~C" #\return #\linefeed)
        (format t "~C~C" #\return #\linefeed)
        (if (not name)
            (princ "<form>What is your name?<input name='name' /></form>")
            (format t "Nice to meet you, ~a!" (cdr name))))
     (progn
      (format t "HTTP/1.0 404 NG~C~C" #\return #\linefeed)
      (format t "Content-Type: text/html~C~C" #\return #\linefeed)
      (format t "~C~C" #\return #\linefeed)
      (princ "Sorry... I don't know that page."))))
; in: DEFUN HELLO-REQUEST-HANDLER
;     (SB-INT:NAMED-LAMBDA HELLO-REQUEST-HANDLER
;         (PATH HEADER PARAMS)
;       (BLOCK HELLO-REQUEST-HANDLER
;         (IF (EQUAL PATH "greeting")
;             (LET (#)
;               (FORMAT T "HTTP/1.0 200 OK~C~C" #\Return #\Newline)
;               (FORMAT T "Content-Type: text/html~C~C" #\Return #\Newline)
;               (FORMAT T "~C~C" #\Return #\Newline)
;               (IF #
;                   #
;                   #))
;             (PROGN
;              (FORMAT T "HTTP/1.0 404 NG~C~C" #\Return #\Newline)
;              (FORMAT T "Content-Type: text/html~C~C" #\Return #\Newline)
;              (FORMAT T "~C~C" #\Return #\Newline)
;              (PRINC "Sorry... I don't know that page.")))))
; 
; caught STYLE-WARNING:
;   The variable HEADER is defined but never used.
; 
; compilation unit finished
;   caught 1 STYLE-WARNING condition
WARNING: redefining COMMON-LISP-USER::HELLO-REQUEST-HANDLER in DEFUN
HELLO-REQUEST-HANDLER
CL-USER>

こいつで実行したところ…
f:id:programcat:20181022235817p:plain
できました!
名前欄に「猫」と打ち込めば…。
f:id:programcat:20181023000034p:plain

ふう…短いですがここまでにします。感無量…といいたいですが、
書いてあることを真似しただけでしかないので悔しいですね。

先程のページの回答者の方の説明をよく読んでWebのえきすぱーとになります!
それではおやすみなさい。