5

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)))
4

2 に答える 2

4

いくつかのこと。まず、戻ってきた400のエラーについて懸念している場合、いくつかの可能性が思い浮かびます。

  • 「ホスト:」は実際にはHTTP / 1.0の有効なヘッダーフィールドではなく、接続しているWebサーバーが標準についてどれほどファシストであるかに応じて、話していると主張するプロトコルに基づいて、これを不正な要求として拒否します。
  • リクエスト行と各ヘッダー行の間にCRLFが必要です。
  • (request)関数がRequest-URIフィールドに何かを返している可能性があります-Request-lineのこの部分の内容としてrequestの値に置き換えます-何らかの形で偽物です(ひどくエスケープ文字など)。それが何を出力しているのかを見ることは、いくらか助けになるかもしれません。

あなたの道に沿ってあなたを助けるためのいくつかの他のより一般的なポインタ:

  • (read-buf-nonblock)は非常に紛らわしいです。記号「c」はどこで定義されていますか?'eof'(gensym)が実行された後、値が割り当てられないのはなぜですか?これは、命令型プログラムから直接取り出され、Lispに挿入されたバイト単位のコピーに非常によく似ています。ここで再実装したのは(読み取りシーケンス)のようです。Common Lisp Hyperspecを見て、これが必要かどうかを確認してください。これの残りの半分は、作成したソケットを非ブロッキングに設定することです。SBCLのドキュメントではこのトピックについてほとんど言及されていませんが、これは非常に簡単です。これを使って:

    (socket-make-stream socket :input t :output t :buffering :none :timeout 0)

  • (http-connect)の最後の(let)形式は必要ありません。評価するだけ

    (socket-make-stream socket :input t :output t :buffering :none)

letがなくても、http-connectは正しい値を返すはずです。

  • (http-request)で..。

交換:

 (format stream "~a~%~%" request )
 (let ((data (make-string buffer)))
 (setf data (subseq data 0
            (read-buf-nonblock data
                               stream)))
 (princ data)
 (> (length data) 0)))

(format stream "~a~%~%" request )
(let ((data (read-buf-nonblock stream)))
    (princ data)
    (> (length data) 0)))

そして、make(read-buf-nonblock)は、関数内でデータを割り当てるのではなく、データの文字列を返します。したがって、buffer割り当てられている場所で、変数を作成してbufferから返します。あなたがしていることは「副作用」に依存することと呼ばれ、より多くのエラーを生成し、エラーを見つけるのが難しくなる傾向があります。必要な場合にのみ使用してください。特に、それらに依存しないようにする言語で使用してください。

  • get-pageの定義方法が一番好きです。それは関数型プログラミングのパラダイムに非常に感じます。ただし、(request)関数の名前または変数requestのいずれかを変更する必要があります。そこに両方があると混乱します。

うん、手が痛い。しかし、うまくいけば、これが役立つでしょう。タイピングが完了しました。:-)

于 2009-01-15T05:54:52.177 に答える
3

可能性は次のとおりです。

HTTP/1.0 では、シーケンス CR LF を行末マーカーとして定義しています。

~%format ディレクティブは (ほとんどのプラットフォームで LF を生成します#\Newlineが、CLHSを参照してください)。

CR の欠落を許容するサイトもあれば、それほど許容しないサイトもあります。

于 2009-01-15T05:02:51.190 に答える