This article is a translation of qiita.com.com --Self-made HTTP router made with net / http
This is a summary of the article I wrote the other day.
I wrote this article because I wish I could get more people's eyes and feedback.
I'm waiting for a star to bmf-san/goblin. :D
If you just want to see the code, see below.
bmf-san/introduction-to-golang-http-router-made-with-net-http/blob/main/trie.go
I would be grateful if you could also see the HTTP router I made.
bmf-san/goblin
What are you talking about?
It's a story about how to think and how to make your own HTTP router using only standard packages in Go.
Create your own HTTP router like the one below.
bmf-san/introduction-to-golang-http-router-made-with-net-http/blob/main/trie.go
What's interesting?
- It will be an opportunity to deepen your understanding of net/http
- It will be an opportunity to learn algorithms and data structures
- You can know the contents of the HTTP router
- You can make your own HTTP router and play with it.
Prerequisite knowledge
- Understanding the basic grammar of Go
- It would be nice if you had touched some kind of HTTP router
What is an HTTP router?
It is an application that connects the requested URL and the processing of the response.
Mapped data is required to connect the URL and the process.
Request URL | Handler |
---|---|
GET / | IndexHandler |
GET /foo | FooHandler |
POST /foo | FooHandler |
GET /foo/:id | FooHandler |
POST /foo/:id | FooHandler |
GET /foo/:id/:name | FooHandler |
POST /foo/:id/:name | FooHandler |
GET /foo/bar | FooBarHandler |
GET /foo/bar/:id | FooBarHandler |
GET /foo/bar/:id/:name | FooBarHandler |
GET /foo/bar/baz | FooBarBazHandler |
GET /bar | BarHandler |
GET /baz | BazHandler |
Inside the HTTP router, such data is converted into a manageable data structure and held.
data structure
Adopt a tree structure that is just right for expressing the hierarchical structure of URLs.
The tree is made up of Nodes. The Node at the top is called Root, and the Node at the bottom is called Leaf. The "branch" that connects Nodes is called Edge.
The operation to add a Node to a tree is called Insert, and the operation to find a Node from a tree is called Search.
When making your own HTTP router, I think you should know at least this much.
It is even better to understand one of the basic trees in the world of algorithms and data structures, the binary search tree.
bmf-tech.com - アルゴリズムとデータ構造 - 二分探索木
In talking about my own work this time, I will adopt a tree structure based on a data structure called a trie tree.
I think that many libraries adopt some kind of tree structure in the observation range, but it seems that famous places often adopt a data structure called radix tree.
For a description of tri-trees, see bmf-tech.com - Golangでトライ木を実装する
, so I will omit it.
For an understanding of the algorithm, we also recommend Algorithm Visualizations - Trie (Prefix Tree).
Since we will implement a tree structure based on the tri-tree, if you understand the tri-tree, it will be easier to understand the implementation of the HTTP router you will make this time.
The data structure of the HTTP router that I make this time is the following tree structure.
This tree structure represents the following mapping of URL and response processing.
Request URL | Handler |
---|---|
GET / | IndexHandler |
GET /foo | FooHandler |
POST /foo | FooHandler |
GET /foo/bar | FooBarHandler |
GET /foo/bar/baz | FooBarBazHandler |
GET /bar | BarHandler |
GET /baz | BazHandler |
Since it is an introduction, it is assumed that the functions will be reduced as much as possible and simple things will be implemented.
net/http Basic knowledge
We need to know how to use net/http to extend the functionality of HTTP routers, so let's sort out the points.
net/http has features called multiplexers and handlers.
The multiplexer has the role of comparing the URL of the request with the registered pattern and calling the handler that matches the best.
The handler is responsible for returning the processing in response to the request.
Go allows you to implement an HTTP server with just a few lines of code.
package main
import (
"fmt"
"net / http"
)
func main () {
mux: = http.NewServeMux ()
mux.HandleFunc ("/", handler)
http.ListenAndServe (": 8080", mux)
}
func handler (w http.ResponseWriter, r * http.Request) {
fmt.Fprintf (w, "Hello World")
}
From the above code, if you read the net / http code with the following points in mind, you can see what to implement.
- Where and how the multiplexer registers handlers, and what are the specifications for matching URLs and patterns?
- How to create a handler (whether there is a variation of handler creation, what interface should be satisfied)
Details bmf-tech.com - 第3章HTTPサーバーのコードリーディング, so please refer to it if you have time.
Implementation
As I introduced at the beginning, the HTTP router implemented this time is in the following repository.
bmf-san/introduction-to-golang-http-router-made-with-net-http
This time we will implement a simple HTTP router that "supports method-based routing".
- Since it is a summary version, detailed explanation of the code is omitted.
Method-based routing means that you can register URLs by HTTP method.
In the function of Go standard package only, if you want to route by method, you need to implement conditional branching in the handler.
// ex.
func indexHandler (w http.ResponseWriter, r * http.Request) {
switch r.Method {
case http.MethodGet:
// do something ...
case http.MethodPost:
// do something ...
...
default: default:
This time, we will implement it only with the goal of eliminating this trouble.
In order to achieve method-based routing, we will adopt a tri-tree-based data structure.
Since it is a simple function, a simpler data structure (such as making it possible to use the standard multiplexer function for each method) is fine, but we will adopt it in anticipation of various implementations later. (To be honest, I just simplified the implementation of bmf-san/goblin.)
The general things to do in implementing an HTTP router are as follows.
- Process to add URL and handler mapping to data structure
- Process to search for matching data from the mapped data structure (≈ tri-tree-based data structure)
- Implementation of DSL for registering mapping
- Implementation of function as a multiplexer (≈ implementation of ServeHTTP)
The first thing you want to do to implement is to implement the data structure.
It is easier to work if you use the debug function of the editor (using delve in vscode) or write test code to check if it is implemented well.
For implementation details, see bmf-tech.com - 第4章HTTPルーターの実装.
Summary
HTTP routers are almost indispensable for creating web applications, and aren't they very much taken care of?
My motivation was that I wanted to make something that I used as a matter of course and use it myself.
There are already a lot of great HTTP routers out there, so I think one of the real thrills is that it's worth researching.
This time I implemented it in Go, but if I understand the mechanism, I think that it can be implemented in languages other than Go.
In Go, the interface is provided in a form that makes it easy to extend the function of the standard package, so I felt that it was relatively easy to make by myself.
If you are interested in this article, I would be grateful if you could take a look at bmf-san/goblin.
Feel free to send an Issue or Pull Request. :D
Top comments (0)