Book Image

.Go Programming Blueprints - Second Edition

By : Mat Ryer
Book Image

.Go Programming Blueprints - Second Edition

By: Mat Ryer

Overview of this book

Go is the language of the Internet age, and the latest version of Go comes with major architectural changes. Implementation of the language, runtime, and libraries has changed significantly. The compiler and runtime are now written entirely in Go. The garbage collector is now concurrent and provides dramatically lower pause times by running in parallel with other Go routines when possible. This book will show you how to leverage all the latest features and much more. This book shows you how to build powerful systems and drops you into real-world situations. You will learn to develop high quality command-line tools that utilize the powerful shell capabilities and perform well using Go's in-built concurrency mechanisms. Scale, performance, and high availability lie at the heart of our projects, and the lessons learned throughout this book will arm you with everything you need to build world-class solutions. You will get a feel for app deployment using Docker and Google App Engine. Each project could form the basis of a start-up, which means they are directly applicable to modern software markets.
Table of Contents (19 chapters)
Go Programming Blueprints Second Edition
Credits
About the Author
Acknowledgments
About the Reviewer
www.PacktPub.com
Preface

A simple web server


The first thing our chat application needs is a web server that has two main responsibilities:

  • Serving the HTML and JavaScript chat clients that run in the user's browser

  • Accepting web socket connections to allow the clients to communicate

Note

The GOPATH environment variable is covered in detail in Appendix, Good Practices for a Stable Go environment. Be sure to read that first if you need help getting set up.

Create a main.go file inside a new folder called chat in your GOPATH and add the following code:

package main 
import ( 
  "log" 
  "net/http" 
) 
func main() { 
  http.HandleFunc("/", func(w http.ResponseWriter, r  *http.Request) { 
    w.Write([]byte(` 
      <html> 
        <head> 
          <title>Chat</title> 
        </head> 
        <body> 
          Let's chat! 
        </body> 
      </html> 
    )) 
  }) 
  // start the web server 
  if err := http.ListenAndServe(":8080", nil); err != nil { 
    log.Fatal("ListenAndServe:", err) 
  } 
} 

This is a complete, albeit simple, Go program that will:

  • Listen to the root path using the net/http package

  • Write out the hardcoded HTML when a request is made

  • Start a web server on port :8080 using the ListenAndServe method

The http.HandleFunc function maps the path pattern / to the function we pass as the second argument, so when the user hits http://localhost:8080/, the function will be executed. The function signature of func(w http.ResponseWriter, r *http.Request) is a common way of handling HTTP requests throughout the Go standard library.

Tip

We are using package main because we want to build and run our program from the command line. However, if we were building a reusable chatting package, we might choose to use something different, such as package chat.

In a terminal, run the program by navigating to the main.go file you just created and execute the following command:

go run main.go

Tip

The go run command is a helpful shortcut for running simple Go programs. It builds and executes a binary in one go. In the real world, you usually use go build yourself to create and distribute binaries. We will explore this later.

Open the browser and type http://localhost:8080 to see the Let's chat! message.

Having the HTML code embedded within our Go code like this works, but it is pretty ugly and will only get worse as our projects grow. Next, we will see how templates can help us clean this up.

Separating views from logic using templates

Templates allow us to blend generic text with specific text, for instance, injecting a user's name into a welcome message. For example, consider the following template:

Hello {{name}}, how are you? 

We are able to replace the {{name}} text in the preceding template with the real name of a person. So if Bruce signs in, he might see:

Hello Bruce, how are you? 

The Go standard library has two main template packages: one called text/template for text and one called html/template for HTML. The html/template package does the same as the text version except that it understands the context in which data will be injected into the template. This is useful because it avoids script injection attacks and resolves common issues such as having to encode special characters for URLs.

Initially, we just want to move the HTML code from inside our Go code to its own file, but won't blend any text just yet. The template packages make loading external files very easy, so it's a good choice for us.

Create a new folder under our chat folder called templates and create a chat.html file inside it. We will move the HTML from main.go to this file, but we will make a minor change to ensure our changes have taken effect:

<html> 
  <head> 
    <title>Chat</title> 
  </head> 
  <body> 
    Let's chat (from template) 
  </body> 
</html> 

Now, we have our external HTML file ready to go, but we need a way to compile the template and serve it to the user's browser.

Tip

Compiling a template is a process by which the source template is interpreted and prepared for blending with various data, which must happen before a template can be used but only needs to happen once.

We are going to write our own struct type that is responsible for loading, compiling, and delivering our template. We will define a new type that will take a filename string, compile the template once (using the sync.Once type), keep the reference to the compiled template, and then respond to HTTP requests. You will need to import the text/templatepath/filepath, and sync packages in order to build your code.

In main.go, insert the following code above the func main() line:

// templ represents a single template 
type templateHandler struct { 
  once     sync.Once 
  filename string 
  templ    *template.Template 
} 
// ServeHTTP handles the HTTP request. 
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r  *http.Request) { 
  t.once.Do(func() { 
    t.templ =  template.Must(template.ParseFiles(filepath.Join("templates",
      t.filename))) 
  }) 
  t.templ.Execute(w, nil) 
} 

Tip

Did you know that you could automate the adding and removing of imported packages? See Appendix, Good Practices for a Stable Go Environment, on how to do this.

The templateHandler type has a single method called ServeHTTP whose signature looks suspiciously like the method we passed to http.HandleFunc earlier. This method will load the source file, compile the template and execute it, and write the output to the specified http.ResponseWriter method. Because the ServeHTTP method satisfies the http.Handler interface, we can actually pass it directly to http.Handle.

Tip

A quick look at the Go standard library source code, which is located at http://golang.org/pkg/net/http/#Handler, will reveal that the interface definition for http.Handler specifies that only the ServeHTTP method need be present in order for a type to be used to serve HTTP requests by the net/http package.

Doing things once

We only need to compile the template once, and there are a few different ways to approach this in Go. The most obvious is to have a NewTemplateHandler function that creates the type and calls some initialization code to compile the template. If we were sure the function would be called by only one goroutine (probably the main one during the setup in the main function), this would be a perfectly acceptable approach. An alternative, which we have employed in the preceding section, is to compile the template once inside the ServeHTTP method. The sync.Once type guarantees that the function we pass as an argument will only be executed once, regardless of how many goroutines are calling ServeHTTP. This is helpful because web servers in Go are automatically concurrent and once our chat application takes the world by storm, we could very well expect to have many concurrent calls to the ServeHTTP method.

Compiling the template inside the ServeHTTP method also ensures that our code does not waste time doing work before it is definitely needed. This lazy initialization approach doesn't save us much in our present case, but in cases where the setup tasks are time- and resource-intensive and where the functionality is used less frequently, it's easy to see how this approach would come in handy.

Using your own handlers

To implement our templateHandler type, we need to update the main body function so that it looks like this:

func main() { 
  // root 
  http.Handle("/", &templateHandler{filename: "chat.html"}) 
  // start the web server 
  if err := http.ListenAndServe(":8080", nil); err != nil { 
    log.Fatal("ListenAndServe:", err) 
  } 
} 

The templateHandler structure is a valid http.Handler type so we can pass it directly to the http.Handle function and ask it to handle requests that match the specified pattern. In the preceding code, we created a new object of the type templateHandler,  specifying the filename as chat.html that we then take the address of (using the & address of the operator) and pass it to the http.Handle function. We do not store a reference to our newly created templateHandler type, but that's OK because we don't need to refer to it again.

In your terminal, exit the program by pressing Ctrl + C and re-run it, then refresh your browser and notice the addition of the (from template) text. Now our code is much simpler than an HTML code and free from its ugly blocks.

Properly building and executing Go programs

Running Go programs using a go run command is great when our code is made up of a single main.go file. However, often we might quickly need to add other files. This requires us to properly build the whole package into an executable binary before running it. This is simple enough, and from now on, this is how you will build and run your programs in a terminal:

go build -o {name}
./{name}

The go build command creates the output binary using all the .go files in the specified folder, and the -o flag indicates the name of the generated binary. You can then just run the program directly by calling it by name.

For example, in the case of our chat application, we could run:

go build -o chat
./chat

Since we are compiling templates the first time the page is served, we will need to restart your web server program every time anything changes in order to see the changes take effect.