[Echo Framework tutorial] Chapter 1. Understanding HTTP, Go, and Echo

Echo is a performance-focused, open source Go web application framework. Go is an increasingly popular language choice for creating web applications due to the flexibility and performance built into the net/http standard library package that comes with the language. The net/http package provides many useful and powerful primitives, such as its web server implementation, request and response types and methods, uniform resource location router, as well as a clean handler interface and function type declaration.

We will begin by taking an inventory of the basic building blocks of a web application from the protocol. Then, we will explore what the net/http package provides, and very briefly touch on how a web application framework could build with these primitives to provide developers with structure for building successful applications. It will be shown how the Echo web application framework fills the voids left by the net/http package, allowing for ease of development. Finally, we will end with best practices for setting up your web application using Echo.

Within this chapter, we will cover:

  • A refresher of the primitives of the Hypertext Transport Protocol ( HTTP )
  • An overview of what the net/http standard library provides
  • A brief explanation of why the standard library is lacking and in need of a framework
  • How to set up your Go environment and install the Echo Framework

Technical requirements

HTTP basics

Two of the most critical structures within web application development are requests and responses. As with most other programs, a web application needs to take inputs, and produce outputs. As HTTP is a stateless protocol, each request made will result in a response message back to the caller. For a web application, the input comes in the form of an HTTP request, and the output as an HTTP response. The net/http package contains data structures that completely model the HTTP protocol message types, which are used by most of the many web frameworks built with Go. In the following diagram, you can see the HTTP request and response protocol data structures:

image

HTTP request

A request, as defined by RFC-7230 Section 3 , is a text-based message, which includes the following required information at a minimum:

  • Request method (RFC-7231 4.3; RFC-5789 2) : The method is the action the client would like to perform on the resource. Enumerated HTTP methods are a finite list of verbs, and are outlined in the preceding two RFCs. The Go net/http package contains a list of constants for referencing the various methods, as can be seen in the documentation:https://golang.org/pkg/net/http/#pkg-constants.
  • Request target (RFC-7230 5.3) : The request target, derived from the Uniform Resource Identifier ( URI ), identifies the resource to which it applies the request on the server. This target, which is based on the specification, can consist of multiple parts, including a host portion, path portion, query portion, and fragment portion. The Go net/url package contains logic for parsing a Unified Resource Locator ( URL ), as can be seen in the documentation:https://golang.org/pkg/net/url/.
  • Request HTTP version (RFC-7230 2.6) : The HTTP version is the textual representation of the highest minor version within the major version of the protocol that the client is able to support. Within the Go net/http web server, this version information is populated in the request type.

The following is an example of the plain text HTTP request message which will GET the resource /file.txt from the web server, from which the client would like to navigate to the HTTP/1.1 protocol:

   GET /file.txt HTTP/1.1

The first line in an HTTP message is known as the start-line of the HTTP message. If the message is a request type message, this line is more clearly known as the request-line of the message. As you can see in the Go documentation for the request type, the first five attributes of the http.Request type encompass the data from the start-line of the request: https://golang.org/pkg/net/http/#Request.

Additionally, and optionally, the request can contain the following data and metadata if applicable within the structure of the HTTP message:

  • Request header fields (RFC-7230 3.2) : Request header fields, known as just headers, are key/value pairs of metadata associated with either of the HTTP message types, request or response. This metadata is used to further describe attributes of the request, such as the length of the content body, or authentication credentials. The key/value pairs are separated by a : character, and are enumerated directly after the start-line of the message, one line per pair. There is a helpful list of registered headers that are commonly in use, which can provide guidance on request and response metadata. These registered headers are defined here:http://www.iana.org/assignments/message-headers. Within the http.Request type, there is a Header attribute which is a key to value mapping.
  • Message body (RFC-7230 3.3) : The message body is the payload of the request or response in the HTTP message protocol. The client and server are signaled the fact that the message body is present by the existence of the Content-Length or Transfer-Encoding headers. The Go web server implementation takes care of these headers automatically when a response is written to the response writer. The message body on a request is accessible from the Body attribute on the Request type in the net/http package. Of particular note here is that the Body is an implementation of the io.ReadCloser interface, which contains two methods: Read and Close .

The following is a more complete HTTP request message:

   POST /file.txt HTTP/1.1
        Host: localhost
        Accept: */*
        Content-Length: 8
        Content-Type: application/x-www-form-urlencoded

        hi=there

In the preceding example, the request dictates that we want to use the POST method on the /file.txt target resource. Our request headers specify that we are attempting to contact a particular host which is localhost, and are willing to accept any type of response content. Since we have a request body that is URL encoded, we need a Content-Type header specifying the content type as well as the length of the request body in bytes. Finally, our request body, hi=there , is submitted as the message body of the HTTP request message.

HTTP response

Very similar to the request structure, responses also contain a start-line that is designated as the response status-line. The response may include a message body and header fields, just as the request message is capable of doing. The following data is included within the response line:

  • HTTP-Version (RFC-7230 2.6) : Similar to the request HTTP-Version field in the request-line, the HTTP-Version is the protocol major and minor version in which the server is responding.
  • Status-Code (RFC-7231 6) : The response status-code is a three-digit integer code that explains the response of the server. There are five different classes of status codes, namely informational (1xx), successful (2xx), redirection (3xx), client Error (4xx) and server error (5xx).
  • Reason-Phrase : The reason-phrase of the response status-line should be ignored by the client, as it is a textual explanation concerning the status of the request.

Within Go, there is an http.Response type which contains all of the previously enumerated elements within the status-line as well as a response header structure and a message body. Typically, if you are developing a web application in Go, you will generally only interface with the response type through the http.ResponseWriter type defined (https://golang.org/pkg/net/http/#ResponseWriter), which is an interface. This interface allows the developer to populate the message header fields with the Header method, and write the status-line in the message with the WriteHeader method. If the response requires a message body in the response message, the http.ResponseWriter interface has a Write method which will send the raw data as the response message body.

When looking at web application frameworks, it is important to keep in mind the bare essentials of the protocol. The HTTP protocol is a very simple text-based protocol, consisting of request and response messages that were designed to be stateless. However, some very interesting and complex systems and services have been built on top of this simple protocol. When we talk about web frameworks and Echo, we need to keep in mind how the features will affect our application, which in the end is a simple text-based protocol.

Go HTTP handlers

At some point in your web application, when you are given a request, you will need to perform actions and provide a response to the request. Within the net/http package, there is an http.Handler interface which is implementable, allowing a common entry point scheme for the web server to run your handler code. The Go web server implementation effectively takes a handler you specify, and for each request it runs your handler’s ServeHTTP method, which is defined in the interface. As seen in the documentation (https://golang.org/pkg/net/http/#Handler) the handler’s ServeHTTP method signature includes a parameter for a http.ResponseWriter interface as well as a pointer to the http.Request structure. The following is an example handler implementation:

package main

import "net/http"

func main() {
 http.Handle("/", new(myHandler))
 http.ListenAndServe(":8080", nil)
}

type myHandler struct{}

func (mh *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 w.WriteHeader(200)
 w.Write([]byte("hello!"))
}

Go HTTP web server

A web server’s primary purpose is to accept HTTP request messages, process the requests, and respond with HTTP response messages. The web server that is built into the Go net/http package is a wonderful example of a bare bones web server. The web server listens for and accepts new TCP requests, launches a new Go routine for each request, and then runs the developer-specified handler for the request in the new Go routine. The web server implementation watches for any application-level panics that occur within the code and then sends the response generated by the handler code back to the client that initiated the request.

This works due to the lightweight thread scheduling that is done within the Go runtime environment. Go routines are much cheaper to initialize and run than traditional threads and subprocesses, and are designed so that thousands of Go routines can be running at the same time. The following is a high-level diagram of how a request is processed within the Go web server:

As a recap, the Go web server will spawn and execute each handler function within a Go routine for every single request that the web server receives. The Go web server will handle the network’s listen and accept life cycles, and has a Transport Layer Security ( TLS ) built-in if requested by the developer.

Reasons for a framework

Due to the minimalist implementations within the net/http package, classic web application paradigms that developers have been employing for years in the industry are noticeably absent. This is not an oversight by the creators of the net/http package, but rather a feature. By keeping the functionality of the package stripped of higher level functionality, the developer building a web application has the flexibility to build solutions that fit their particular use cases.

This minimalism-based mindset does cause work for developers, in that higher level functionality must be built into the web application itself. An example would be middleware and request pipeline processing. This is common in many web application frameworks, and request pipelines have been implemented in a plethora of ways by various projects, all motivated by different priorities. Another example would be URL routing to handlers. Though an implementation exists in the standard library, it has been shown in the industry that the current embodiment within the net/http package is lacking in functionality for the majority of common use cases. Moreover, there are no helpers for common problems such as encoding and rendering responses, or binding request inputs to variables within a handler. Lastly, up until very recently, the net/http package did not have a mechanism by which request context could be kept throughout the request’s lifetime.

Setting up the environment

In order to get started with Echo, you will need a working Go environment. To that end, installing Go in your environment is fairly trivial. Firstly, we need to download the Go source code and tool chain and install them.

Setting up Echo framework

Using go get

$ cd <PROJECT IN $GOPATH>
$ go get -u github.com/labstack/echo/...

Using dep

$ cd <PROJECT IN $GOPATH>
$ dep ensure -add github.com/labstack/[email protected]^3.1

Using glide

$ cd <PROJECT IN $GOPATH>
$ glide get github.com/labstack/echo#~3.1

Using govendor

$ cd <PROJECT IN $GOPATH>
$ govendor fetch github.com/labstack/[email protected]

Echo is developed using Go 1.10.x and tested with Go 1.9.x and 1.10.x . Echo follows semantic versioning managed through GitHub releases, specific version of Echo can be installed using a package manager.

Echo Quick Start

Installation

$ go get -u github.com/labstack/echo/...

Hello, World!

Create server.go

package main

import (
	"net/http"
	
	"github.com/labstack/echo"
)

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":1323"))
}

Start server

$ go run server.go

Browse to http://localhost:1323 and you should see Hello, World! on the page.

Next Chapter:

Refer: Echo quick start guide

1 Like