My goal is something akin to Chapter 12 of Land Of Lisp: using the usocket package in SBCL I want to write a local server that I can connect to with my browser. I started from a helpful example:
#!/usr/bin/sbcl --script
(load "~/quicklisp/setup.lisp")
(require :usocket)
(defparameter *sock* nil)
(defparameter *sock-listen* nil)
(defparameter *my-stream* nil)
(defun communi ()
;; bind socket
(setf *sock* (usocket:socket-listen "127.0.0.1" 4123))
;; listen to incoming connections
(setf *sock-listen* (usocket:socket-accept *sock* :element-type 'character))
; open stream for communication
(setf *my-stream* (usocket:socket-stream *sock-listen*))
;; print message from client
(format t "~a~%" (read *my-stream*))
(force-output)
;; send answer to client
(format *my-stream* "<html><body>Server will write something exciting here.</body></html>")
(force-output *my-stream*))
;; call communication and close socket, no matter what
(unwind-protect (communi)
(format t "Closing socket connection...~%")
(usocket:socket-close *sock-listen*)
(usocket:socket-close *sock*))
When I run this script from the command line (Ubuntu 22.04 LTS), I am able to connect to http://127.0.0.1:4123/
with Firefox. However, instead of rendering the HTML, Firefox only displays its source code:
<html><body>Server will write something exciting here.</body></html>
Question: How can I persuade Firefox to render the page instead of displaying the HTML source?
From the answers here and here I assume that I need to set text/html
in the HTTP headers. This is corroborated by the curl
output
dominik@computer:~$ curl 127.0.0.1:4123 --http0.9 --verbose
* Trying 127.0.0.1:4123...
* Connected to 127.0.0.1 (127.0.0.1) port 4123 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:4123
> User-Agent: curl/8.1.2
> Accept: */*
>
* Closing connection 0
<html><body>Server will write something exciting here.</body></html>
which is markedly different from
dominik@computer:~$ curl example.com -I
HTTP/1.1 200 OK
Accept-Ranges: bytes
Age: 256982
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 26 May 2024 18:05:14 GMT
Etag: "3147526947"
Expires: Sun, 02 Jun 2024 18:05:14 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECAcc (dcd/7D7F)
X-Cache: HIT
Content-Length: 1256
How can I set the content-type
in the usocket package?
Since the server you are implementing is supposed to be a http server, you should supply the response headers then an empty line before the payload. Eg.
(let ((content "<html><body>Server will write something exciting here.</body></html>"))
(format *my-stream*
"HTTP/1.1 200 OK~%~&~
content-type: text/html~%~&~
content-length: ~D~%~&~
~%~&~
~a"
(length content)
content))
None of the response headers are required, but as you have experienced they are needed for the browser to handle it as html.