SBCL でのソケットとネットワーク接続の処理についてもう少し学習しようとしています。そこで、HTTP 用の簡単なラッパーを作成しました。これまでのところ、ストリームを作成し、最終的に Web サイトのヘッダー データとページ コンテンツを取得するための要求を実行するだけです。
今までは、そこそこまともに動いていました。自慢できることは何もありませんが、少なくともうまくいきました。
ただし、奇妙な問題に遭遇しました。「400 Bad Request」エラーが発生し続けます。
最初は、HTTP リクエストをどのように処理しているか (多かれ少なかれリクエスト文字列を関数の引数として渡す) についてやや不安でしたが、必要なすべての部分を使用してクエリ文字列をフォーマットし、それを返す関数を作成しました。後で...しかし、まだエラーが発生します。
さらに奇妙なのは、エラーが毎回発生するとは限らないことです。Google などのページでスクリプトを試すと、「200 OK」という戻り値が返されますが、他のサイトでは「400 Bad Request」という結果になります。
私のコードに問題があることは確かですが、何が原因であるかを正確に知っていれば、気が滅入るでしょう。
ここに私が取り組んでいるコードがあります:
(use-package :sb-bsd-sockets)
(defun read-buf-nonblock (buffer stream)
(let ((eof (gensym)))
(do ((i 0 (1+ i))
(c (read-char stream nil eof)
(read-char-no-hang stream nil eof)))
((or (>= i (length buffer)) (not c) (eq c eof)) i)
(setf (elt buffer i) c))))
(defun http-connect (host &optional (port 80))
"Create I/O stream to given host on a specified port"
(let ((socket (make-instance 'inet-socket
:type :stream
:protocol :tcp)))
(socket-connect
socket (car (host-ent-addresses (get-host-by-name host))) port)
(let ((stream (socket-make-stream socket
:input t
:output t
:buffering :none)))
stream)))
(defun http-request (stream request &optional (buffer 1024))
"Perform HTTP request on a specified stream"
(format stream "~a~%~%" request )
(let ((data (make-string buffer)))
(setf data (subseq data 0
(read-buf-nonblock data
stream)))
(princ data)
(> (length data) 0)))
(defun request (host request)
"formated HTTP request"
(format nil "~a HTTP/1.0 Host: ~a" request host))
(defun get-page (host &optional (request "GET /"))
"simple demo to get content of a page"
(let ((stream (http-connect host)))
(http-request stream (request host request)))