The title is explicit.
Reproductible steps
* Launch an Ubuntu instance on AWS with HTTP rule on port 80
* Install sbcl
sudo apt install sbcl -y
* Install usocket library for root
See next step to understand why I am doing this as root
curl -O https://beta.quicklisp.org/quicklisp.lisp
sudo sbcl --load quicklisp.lisp
The next lines need to be typed into sbcl REPL
(quicklisp-quickstart:install)
(ql:add-to-init-file)
(ql:quickload "usocket")
* Use Land of Lisp example (http://landoflisp.com/)
In a file named server.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)
Then you launch the server as root :
sudo sbcl --load "server.lisp"
I am using root because I cannot get rid of the following error message with a normal user
The condition Socket error in "bind": 13 (Permission denied) occurred with errno :0.
Then everything seems to be right but I cannot access the server from a standard browser using :
http://IPv4.Public.IP:80
* Supplementary diagnostics :
AWS Security group / inboud rules
╔══════╦══════════╦════════════╦═══════════╗
║ 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
curl on server
curl 127.0.0.1
Sorry... I don't know that page
as expected !
ping from distant machine
Need to add Custom ICMP rule to inbound security group policy (now I know that ping
is using 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
curl from distant machine
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 -
This line with port 80 only appears when my server is running.