Lisp奇怪的HTTP问题/错误

时间:2009-01-15 04:01:13

标签: http lisp sockets sbcl

我正在尝试学习更多关于在SBCL中处理套接字和网络连接的知识;所以我为HTTP写了一个简单的包装器。到目前为止,它只是制作流并执行请求以最终获取网站的标题数据和页面内容。

到目前为止,它已经有点体面了。没什么好吹嘘的,但它至少有效。

然而,我遇到了一个奇怪的问题;我一直收到“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)))

2 个答案:

答案 0 :(得分:4)

一些事情。首先,如果您对正在回归的400个错误感到担忧,可以想到一些可能性:

  • “主机:”实际上并不是HTTP / 1.0中的有效标头字段,并且根据您所联系的Web服务器的法西斯主义是如何与标准相关的,它会根据您声称的协议将其拒绝为错误请求说话。
  • 您的请求行和每个标题行之间需要一个CRLF。
  • 您的(请求)函数可能会为Request-URI字段返回一些内容 - 您将request的值替换为Request-line的这一部分的内容 - 这在某种程度上是伪造的,或者另一个(严重逃脱的角色等)。看看输出的内容可能有所帮助。

其他一些更通用的指针可以帮助您:

  • (read-buf-nonblock)非常混乱。符号'c'在哪里定义?为什么'eof'(gensym)ed,然后没有分配任何值?它看起来非常像一个逐字节的副本,直接从命令式程序中获取,并且插入到Lisp中。看起来你在这里重新实现的是(读序列)。在Common Lisp Hyperspec中查看here,看看这是否是您需要的。另一半是将您创建的套接字设置为非阻塞。这很简单,即使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应该仍然​​返回正确的值。

  • In(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的方式。它在函数式编程范例中非常有用。但是,您应该更改(请求)函数的名称或变量请求。两者都存在混淆。

哎呀,双手受伤。但希望这会有所帮助。完成打字。 : - )

答案 1 :(得分:3)

这是一种可能性:

HTTP / 1.0将序列CR LF定义为行尾标记。

~%格式指令正在生成#\Newline(大多数平台上的LF,但请参见CLHS)。

有些网站可能会容忍丢失的CR,而其他网站则没有那么多。