6 February 2022

Racket HTTP Server

When I start playing with a new language, I like to have a look at how I could build a simple HTTP server in that language.

Racket's racket/tcp library provides us with functions for managing TCP connections. Using this, we can implement a server.

Reading Our First Request

We can use tcp-listen to get a listener object for the given port, and then we can use tcp-accept to await and accept the next connection attempt made to this port.

The in and out ports are used to read the request and write out the response. For now, we will ignore the request, and just always return Hello World! with an HTTP 200 status. We can use the display function to write strings to the response.

Once we are finished handling the request, we close both the ports.

#lang racket

(define port-no 8083)

(define listener (tcp-listen 8083 5 #t))
(define-values (in out) (tcp-accept listener))

(display "HTTP/1.0 200 Okay\r\n\r\n" out)
(display "Hello World!\r\n" out)

(close-input-port in)
(close-output-port out)

We can now run our server by running racket just-one-request.rkt, and we can test it by running curl localhost:8083. We should see the server respond Hello World!, before terminating.

Handling Repeat Requests

If we put the request handling code inside a loop, our server will keep responding to requests made to it, until the process is stopped.

#lang racket

(define (serve port-no)
  (define listener (tcp-listen port-no 5 #t))
  (define (loop)
    (accept-and-handle listener)
    (loop))
  (loop))

(define (accept-and-handle listener)
  (define-values (in out) (tcp-accept listener))
  (handle in out)
  (close-input-port in)
  (close-output-port out))

(define (handle in out)
      (display "HTTP/1.0 200 Okay\r\n\r\n" out)
      (display "Hello World!\r\n" out))

(serve 8083)

Simple Logging

We can also log to stdout using display. Alternatively we can use displayln, which will handle newlines for us. The default output port is stdout, so if we do not specify a port for display or displayln, we will print to stdout.

(define (handle in out)
      (displayln "received a request!")
      (display "HTTP/1.0 200 Okay\r\n\r\n" out)
      (display "Hello World!\r\n" out))

display vs write vs print

display displays the value as raw bytes or characters, for example: (display "Foo") will display Foo.

write writes the value as a racket value, that can usually be read back in, for example: (write "Foo") will write out "Foo" (i.e. with quotes printed).

print is not restricted to either writing raw data, or racket values, and may do either.

For more information, visit docs.racket-lang.org/reference/Writing.html.

For debugging, writeln is great, because it will show if invisible characters, like "\r", are present in text. Additionally, we can copy the output back into the racket shell, to examine and manipulate it.




We now have a functioning simple HTTP server, which we can continue to build upon:

I also recommend looking at the documentation for racket/tcp at docs.racket-lang.org, it is really pretty good.

If this has been useful, you have any comments or questions, or I have made any mistakes: please get in touch!

Tags: Scheme Racket