HTTP echo server in Deno

Mayank Choubey
Tech Tonic
Published in
4 min readJan 8, 2022

--

Introduction

An HTTP echo server is great for debugging requests going to some end point. The problematic requests can be temporarily redirected (manually) to an echo server, and the response body & headers can be analyzed. This is useful in confirming what exactly is being sent in the HTTP request.

In this article, we’ll see how to write a simple echo server in Deno. A simple echo server in Deno can be written in about 3 lines.

Problem

Before we jump to the code, let’s look at some echo examples:

  • Echoing basic request
$ curl http://localhost:8000 -v
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 200 OK
< accept: */*
< host: localhost:8000
< user-agent: curl/7.77.0
< content-length: 0
< date: Sat, 08 Jan 2022 19:11:23 GMT
  • Echoing URL encoded body
$ curl http://localhost:8000 -v -d 'a=b' -d 'c=2'
> POST / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
> Content-Length: 7
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< accept: */*
< content-length: 7
< content-type: application/x-www-form-urlencoded
< host: localhost:8000
< user-agent: curl/7.77.0
< date: Sat, 08 Jan 2022 19:12:12 GMT
<
a=b&c=2
  • Echoing JSON encoded body with a custom header
$ curl http://localhost:8000 -v -d '{"a": "b", "c": "d"}' -H 'content-type: application/json' -H 'some-other-hdr: some-other-hdr-value'
> POST / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
> content-type: application/json
> some-other-hdr: some-other-hdr-value
> Content-Length: 20
>
< HTTP/1.1 200 OK
< accept: */*
< content-length: 20
< content-type: application/json
< host: localhost:8000
< some-other-hdr: some-other-hdr-value
< user-agent: curl/7.77.0
< date: Sat, 08 Jan 2022 19:16:33 GMT
<
{"a": "b", "c": "d"}
  • Echoing a multipart form data encoded body
$ curl http://localhost:8000 -v -F 'a=b' -F 'file=@/var/tmp/testdata/sample.txt'
> POST / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
> Content-Length: 301
> Content-Type: multipart/form-data; boundary=------------------------2639aa4a6f4e6555
>
< HTTP/1.1 200 OK
< accept: */*
< content-length: 301
< content-type: multipart/form-data; boundary=------------------------2639aa4a6f4e6555
< host: localhost:8000
< user-agent: curl/7.77.0
< date: Sat, 08 Jan 2022 19:18:00 GMT
<
--------------------------2639aa4a6f4e6555
Content-Disposition: form-data; name="a"
b
--------------------------2639aa4a6f4e6555
Content-Disposition: form-data; name="file"; filename="sample.txt"
Content-Type: text/plain
Learning Deno Is Fun!--------------------------2639aa4a6f4e6555--

As we can see, an echo server could be very useful in debugging purposes.

Echo server

Imports

To create a basic HTTP server, we need to import serve API from Deno’s standard library’s http module:

import { serve } from "https://deno.land/std/http/mod.ts";

There is no special import required for this specific use-case.

The echo server

To write an echo server, we can simply pass the request body and request headers into the response object. There is no need to do any kind of processing on incoming body or headers. There is also no need to do a null check on the body, as that’s taken care by Deno.

In short, the code simply copies data from request to response. That’s all!

Here is the code of a simple echo server:

//app.tsimport { serve } from "https://deno.land/std/http/mod.ts";async function reqHandler(req: Request) {
return new Response(req.body, {
headers: req.headers,
});
}
serve(reqHandler, { port: 8000 });

The above code streams the request body (if any) back into response. It also uses the received headers to set response headers.

There will be additional headers in response as Deno sets some of them internally

Tests

Let’s do a couple of tests. Other examples have been covered in the introduction.

First, let’s check echoing of a textual body:

$ deno run --allow-net=:8000 app.ts$ curl http://localhost:8000 -v -d 'Hello world' -H 'content-type: text/plain'
> POST / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
> content-type: text/plain
> Content-Length: 11
>
< HTTP/1.1 200 OK
< accept: */*
< content-length: 11
< content-type: text/plain
< host: localhost:8000
< user-agent: curl/7.77.0
< date: Sat, 08 Jan 2022 19:23:10 GMT
<
Hello world

Second, let’s echo a PDF file upload:

$ ls -l /var/tmp/testdata/sample.pdf 
-rw-r--r-- 1 mayankc staff 69273 Apr 3 2021 /var/tmp/testdata/sample.pdf
$ curl http://localhost:8000 -v --data-binary "@/var/tmp/testdata/sample.pdf" -H 'content-type: application/pdf' -o /dev/null -sL
> POST / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
> content-type: application/pdf
> Content-Length: 69273
>
< HTTP/1.1 200 OK
< accept: */*
< content-length: 69273
< content-type: application/pdf
< host: localhost:8000
< user-agent: curl/7.77.0
< date: Sat, 08 Jan 2022 19:38:43 GMT
<
/* OUTPUT REDIRECTED TO /DEV/NULL */

Echo service

To make it publicly available, the simple echo server is also deployed on Deno Deploy. This makes the echo service publicly available.

The echo service is deployed here:

https://echo-server.deno.dev

The service runs the exact same code we’ve written in the previous section. The code is available here.

Deno Deploy uses HTTP2

Let’s run a simple test using curl.

$ curl https://echo-server.deno.dev -v --data-binary '{"name":"John", "age":30, "car":null}' -H 'content-type: application/json' -sL
> POST / HTTP/2
> Host: echo-server.deno.dev
> user-agent: curl/7.77.0
> accept: */*
> content-type: application/json
> content-length: 37
>
< HTTP/2 200
< accept: */*
< content-length: 37
< content-type: application/json
< host: echo-server.deno.dev
< user-agent: curl/7.77.0
< x-forwarded-for: 2601:646:8200:6180:6136:1672:1409:6172
< date: Sat, 08 Jan 2022 19:43:11 GMT
< server: deploy/us-west2-a
<
{"name":"John", "age":30, "car":null}

--

--