Deno World
Published in

Deno World

Deno by example: Proxy file server

In Deno by example series, small but commonly used applications are built step by step in Deno. There is no ordering of articles present in Deno by example series.

In this article, we’ll go through the steps to build a simple proxy file server.

Design

A proxy file server is usually a front facing content server that streams the content from other services. In typical deployments, the front facing server would be publicly accessible while the actual content services would be on a private network. Here is a very simplified architecture:

We’ll build a small application that acts as a proxy file server. On receiving a request, the proxy file server will serve it via a file service.

Steps to build a proxy file server

Let’s go step by step and build a simple proxy file server. We’ll start with a simple HTTP server and slowly add client, streaming, headers, etc.

Step 1

As a proxy file server is an HTTP server, the first step is to write a simple HTTP server that listens on say 8100 port. We need to import serve API from standard library.

The articles to learn about how to write HTTP servers in Deno are here & here.

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

The above code simply sends a 200 OK back without a response body. Let’s do a quick test:

$ curl http://localhost:8100 -v
> GET / HTTP/1.1
> Host: localhost:8100
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 0
< date: Fri, 28 Jan 2022 21:33:14 GMT

Step 2

As the proxy file server is also an HTTP client, we need to make a fetch API call to the file service which usually is located in the private network behind load balancers, etc. The file path would be coming in the request URL. The file path is extracted (using URL API) and forwarded to the file service after appending it to the request URL.

import { serve } from "https://deno.land/std/http/mod.ts";const fileService = "http://localhost:9000/";async function reqHandler(req: Request) {
const path = new URL(req.url).pathname;
const proxyRes = await fetch(fileService + path);
return new Response(null, { status: proxyRes.status });
}
serve(reqHandler, { port: 8100 });

The above code receives an incoming HTTP request, makes an HTTP request to the file service, proxies the response code as is. Let’s do a couple of quick tests:

$ curl http://localhost:8100 -v
> GET / HTTP/1.1
> Host: localhost:8100
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< content-length: 0
< date: Fri, 28 Jan 2022 22:00:24 GMT
$ curl http://localhost:8100/sample.txt -v
> GET /sample.txt HTTP/1.1
> Host: localhost:8100
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 0
< date: Fri, 28 Jan 2022 22:00:59 GMT

As we can see, the file service sends 404 error code for in existent files or directories. For valid files, we receive 200 OK, but there is no content in it. Let’s learn that in the next step.

Step 3

The final step is to forward the response body that was received from the file service. Also, the relevant response headers such as content-length, content-type and content-disposition needs to be forwarded too. The response body can be used as is, i.e. without any processing. Null check is not required on response body, as that’s taken care by Deno.

import { serve } from "https://deno.land/std/http/mod.ts";const fileService = "http://localhost:9000/";function copyHeader (headerName: string, to: Headers, from: Headers) {
const hdrVal = from.get(headerName);
if (hdrVal) {
to.set(headerName, hdrVal);
}
};
async function reqHandler(req: Request) {
const path = new URL(req.url).pathname;
const proxyRes = await fetch(fileService + path);
const headers = new Headers();
copyHeader("content-length", headers, proxyRes.headers);
copyHeader("content-type", headers, proxyRes.headers);
copyHeader("content-disposition", headers, proxyRes.headers);
return new Response(proxyRes.body, {
status: proxyRes.status,
headers,
});
}
serve(reqHandler, { port: 8100 });

Let’s do some quick tests:

$ curl http://localhost:8100/someRandomFile.pdf -v
> GET /someRandomFile.pdf HTTP/1.1
> Host: localhost:8100
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< content-length: 0
< date: Sat, 29 Jan 2022 00:00:02 GMT
$ curl http://localhost:8100/sample.txt -v
> GET /sample.txt HTTP/1.1
> Host: localhost:8100
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 22
< content-type: text/plain
< date: Sat, 29 Jan 2022 00:00:42 GMT
<
Learning Deno Is Fun!
$ curl http://localhost:8100/sample.pdf -v --output /dev/null -s
> GET /sample.pdf HTTP/1.1
> Host: localhost:8100
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 69273
< content-type: application/pdf
< date: Sat, 29 Jan 2022 00:01:17 GMT
<
<< OUTPUT REDIRECTED TO /dev/null >>
$ curl http://localhost:8100/sample.mp3 -v --output /dev/null -s
> GET /sample.mp3 HTTP/1.1
> Host: localhost:8100
> User-Agent: curl/7.77.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 2169782
< content-type: audio/mpeg
< date: Sat, 29 Jan 2022 00:02:02 GMT
<
<< OUTPUT REDIRECTED TO /dev/null >>

The simple proxy file server works fine!

This story is a part of the exclusive medium publication on Deno: Deno World.

--

--

--

The one and only exclusive medium magazine with 200+ articles on Deno

Recommended from Medium

Best of Modern JavaScript — Arrow Functions and New OOP Features

How to Create a JavaScript Array with the Same Element Repeated Multiple Times?

Rails app with React.js using react-rails gem and webpacker the TDD way

Check if Scrollbar is Visible with JavaScript

Getting Started with Vue Router with Vue 3

Top Ten Reasons for Using ReactJS in Enterprise App Development

UI Development with Chakra UI Vue — Number Input Styles and Popovers

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Mayank Choubey

Mayank Choubey

Deno, Node.js, etc.

More from Medium

JSON modules in Deno

Deno by example: Content server — Part 6 Deployment on Deno Deploy

HTTP echo server in Deno

Deno by example: Content Server — Part 1 Introduction