O título é explícito.
Etapas reproduzíveis
* Inicie uma instância do Ubuntu na AWS com regra HTTP na porta 80
* Instalar sbcl
sudo apt install sbcl -y
* Instale a biblioteca usocket para root
Veja o próximo passo para entender porque estou fazendo isso como root
curl -O https://beta.quicklisp.org/quicklisp.lisp
sudo sbcl --load quicklisp.lisp
As próximas linhas precisam ser digitadas em sbcl REPL
(quicklisp-quickstart:install)
(ql:add-to-init-file)
(ql:quickload "usocket")
* Use o exemplo Land of Lisp ( http://landoflisp.com/ )
Em um arquivo chamadoserver.lisp
(require 'usocket)
(defun http-char (c1 c2 &optional (default #\Space))
(let ((code (parse-integer
(coerce (list c1 c2) 'string)
:radix 16
:junk-allowed t)))
(if code
(code-char code)
default)))
(defun decode-param (s)
(labels ((f (lst)
(when lst
(case (car lst)
(#\% (cons (http-char (cadr lst) (caddr lst))
(f (cdddr lst))))
(#\+ (cons #\space (f (cdr lst))))
(otherwise (cons (car lst) (f (cdr lst))))))))
(coerce (f (coerce s 'list)) 'string)))
(defun parse-params (s)
(let* ((i1 (position #\= s))
(i2 (position #\& s)))
(cond (i1 (cons (cons (intern (string-upcase (subseq s 0 i1)))
(decode-param (subseq s (1+ i1) i2)))
(and i2 (parse-params (subseq s (1+ i2))))))
((equal s "") nil)
(t s))))
(defun parse-url (s)
(let* ((url (subseq s
(+ 2 (position #\space s))
(position #\space s :from-end t)))
(x (position #\? url)))
(if x
(cons (subseq url 0 x) (parse-params (subseq url (1+ x))))
(cons url '()))))
(defun get-header (stream)
(let* ((s (read-line stream))
(h (let ((i (position #\: s)))
(when i
(cons (intern (string-upcase (subseq s 0 i)))
(subseq s (+ i 2)))))))
(when h
(cons h (get-header stream)))))
(defun get-content-params (stream header)
(let ((length (cdr (assoc 'content-length header))))
(when length
(let ((content (make-string (parse-integer length))))
(read-sequence content stream)
(parse-params content)))))
(defun serve (request-handler)
(let ((socket (usocket:socket-listen #(127 0 0 1) 80)))
(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))))
(defun hello-request-handler (path header params)
(if (equal path "greeting")
(let ((name (assoc 'name params)))
(if (not name)
(princ "<html><form>What is your name?<input name='name'/></form></html>")
(format t "<html>Nice to meet you, ~a!</html>" (cdr name))))
(princ "Sorry... I don't know that page")))
(serve #'hello-request-handler)
Então você inicia o servidor como root:
sudo sbcl --load "server.lisp"
Estou usando root porque não consigo me livrar da seguinte mensagem de erro com um usuário normal
The condition Socket error in "bind": 13 (Permission denied) occurred with errno :0.
Então tudo parece estar certo, mas não consigo acessar o servidor de um navegador padrão usando:
http://IPv4.Public.IP:80
* Diagnósticos complementares:
Grupo de segurança da AWS/regras de entrada
╔══════╦══════════╦════════════╦═══════════╗
║ Type ║ Protocol ║ Port Range ║ Source ║
╠══════╬══════════╬════════════╬═══════════╣
║ HTTP ║ TCP ║ 80 ║ 0.0.0.0/0 ║
║ HTTP ║ TCP ║ 80 ║ ::/0 ║
║ SSH ║ TCP ║ 22 ║ 0.0.0.0/0 ║
╚══════╩══════════╩════════════╩═══════════╝
iptables
sudo iptables -L -v
Chain INPUT (policy ACCEPT 346 packets, 23760 bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 244 packets, 32428 bytes)
pkts bytes target prot opt in out source destination
sudo iptables -t nat -L -v
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
enrolar no servidor
curl 127.0.0.1
Sorry... I don't know that page
como esperado !
ping de máquina distante
É necessário adicionar uma regra ICMP personalizada à política de grupo de segurança de entrada (agora sei que ping
está usando ICMP ...)
ping 35.180.138.87
64 bytes from 35.180.138.87: icmp_seq=1 ttl=49 time=173 ms
64 bytes from 35.180.138.87: icmp_seq=2 ttl=49 time=32.2 ms
^C
--- 35.180.138.87 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1005ms
rtt min/avg/max/mdev = 32.248/102.884/173.520/70.636 ms
enrolar da máquina distante
curl 35.180.138.87
curl: (7) Failed to connect to 35.180.138.87 port 80: Connection refused
netstat
netstat -nlp
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:80 0.0.0.0:* LISTEN -
Essa linha com a porta 80 só aparece quando meu servidor está rodando.
Tente fazer ping no IPV4 público para ter certeza de alcançá-lo.
Se você realmente o alcançar, verifique as regras do iptables/firewalld para a porta 80 (es.
iptables -L
...)Se o firewall estiver OK, verifique se o servidor está vinculado a todas as interfaces de rede e não apenas ao loopback (127.0.0.1) com
netstat -nlp
. Verifique também se outros programas estão vinculados à porta 80.Você também pode testar
curl 127.0.0.1
para verificar se o servidor está realmente servindo a página e você não está acessando, ou se o servidor está quebrado em geral.Certifique-se também de que a regra HTTP na porta 80 definida na AWS esteja em um grupo de segurança associado à sua instância.
O problema estava no endereço IP usado para abrir o soquete, ou seja,
127.0.0.1
Tentei usar o endereço IPv4 fornecido pela AWS, mas essa não foi a solução
Em vez disso, é necessário procurar o endereço IP do servidor host:
A resposta é por exemplo
111.111.111.111
E então use o endereço encontrado dentro do código lisp