Pergunta

Eu estou tentando aprender um pouco mais sobre como lidar com soquetes e conexões de rede em SBCL; então eu escrevi um invólucro simples para HTTP. Até agora, ele apenas faz um riacho e executa um pedido para, finalmente, obter os dados de cabeçalho e página de conteúdo de um site.

Até agora, ele tem trabalhado em pouco decentemente. Nada para se gabar sobre a casa, mas pelo menos trabalhou.

Eu vim através de um problema estranho, no entanto; Eu continuo recebendo erros "400 Bad Request".

No início, eu estava um pouco desconfiado sobre como eu estava processando as solicitações HTTP (mais ou menos passar uma string pedido como um argumento de função), então eu fiz uma função que formatos de uma cadeia de consulta com todas as peças que eu preciso e retornos -lo para uso posterior ... mas eu ainda obter erros.

O que é ainda mais estranho é que os erros não acontecem o tempo todo. Se eu tentar o script em uma página como o Google, eu recebo um valor de retorno "200 Ok" ... mas em outras vezes em outros sites, eu vou pegar "400 Bad Request".

Estou certo de seu problema com meu código, mas eu vou ser amaldiçoado se eu sei exatamente o que está causando isso.

Aqui está o código que eu estou trabalhando com:

(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)))
Foi útil?

Solução

Algumas coisas. Em primeiro lugar, a sua preocupação com os 400 erros que você está recebendo de volta, algumas possibilidades vêm à mente:

  • "Host:" não é realmente um campo de cabeçalho válido em HTTP / 1.0, e dependendo de como fascista o servidor web que você está contatando é sobre padrões, seria rejeitar isso como um pedido ruim baseado no protocolo você diz estar falando.
  • Você precisa de um CRLF entre o Request-line e cada uma das linhas de cabeçalho.
  • É possível que a sua função (pedido) está retornando algo para o campo Request-URI - você substituir no valor do pedido, conforme o conteúdo desta parte do Request-line - que é falso, de uma forma ou outra (caracteres mal escaparam, etc.). Vendo o que é saída poderia ajudar alguns.

Algum outro ponteiro mais geral para ajudá-lo junto à sua maneira:

  • (leia-buf-nonblock) é muito confusa. Onde está o símbolo 'c' definido? Por que é 'EOF' (Gensym) ed e depois não atribuído qualquer valor? Ele se parece muito com um byte a byte cópia tomado em linha reta fora de um programa imperativo, e se estatelou em Lisp. Parece que o que você reimplemented aqui é (read-seqüência). Vai olhar aqui no Lisp HyperSpec comum, e ver se isso é o que você precisa. A outra metade é para definir o seu soquete criado para ser non-blocking. Isso é muito fácil, embora a documentação SBCL é quase em silêncio sobre o assunto. Utilize este:

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

  • A última forma (vamos) de (http-connect) não é necessário. Apenas avaliar

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

sem a let, e http-connect ainda deve retornar o valor correto.

  • Em (http-request) ...

Substituir:

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

com

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

e make (leia-buf-nonblock) retorna a string de dados, em vez que tê-lo atribuir dentro da função. Então, onde você tem buffer sendo atribuído, criar um buffer variável dentro e depois devolvê-lo. O que você está fazendo é chamado com base em "efeitos colaterais", e tende a produzir mais erros e mais difícil de encontrar erros. Use-o apenas quando você tem que, especialmente em uma linguagem que torna mais fácil para não depender deles.

  • Eu, principalmente, como o da maneira get-página é definida. É uma sensação muito no paradigma de programação funcional. No entanto, você deverá alterar o nome da função (pedido), ou o pedido variável. Tendo tanto lá dentro é confuso.

Caramba, mãos doem. Mas espero que isso ajude. digitação feito. : -)

Outras dicas

Aqui está uma possibilidade:

HTTP / 1.0 define a sequência CR LF como o marcador de fim-de-linha.

A directiva formato ~% está gerando uma #\Newline (LF na maioria das plataformas, embora Veja CLHS ).

Alguns sites podem ser tolerante do CR faltando, outros nem tanto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top