November 3, 2016

What's Coming in Go 1.8

With the feature set for Go 1.8 now frozen, I thought it would be fun to highlight a few of the more interesting API changes that we can expect to see in Go 1.8 when it’s released around February 1, 2017.

HTTP server connection draining

At the eleventh hour Brad Fitzpatrick closed a nearly four-year-old issue by implementing connection draining on http.Server. It is now possible to call srv.Close() to halt an http.Server immediately, or srv.Shutdown(ctx) to stop and gracefully drain the server of connections (users of the github.com/tylerb/graceful package will be familiar with this behavior).

As an example, this server shuts down gracefully upon receiving the SIGINT command (hit ^C).

package main

import (
	"context"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
)

func main() {

	// subscribe to SIGINT signals
	stopChan := make(chan os.Signal)
	signal.Notify(stopChan, os.Interrupt)

	mux := http.NewServeMux()

	mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(5 * time.Second)
		io.WriteString(w, "Finished!")
	}))

	srv := &http.Server{Addr: ":8081", Handler: mux}

	go func() {
		// service connections
		if err := srv.ListenAndServe(); err != nil {
			log.Printf("listen: %s\n", err)
		}
	}()

	<-stopChan // wait for SIGINT
	log.Println("Shutting down server...")

	// shut down gracefully, but wait no longer than 5 seconds before halting
	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
	srv.Shutdown(ctx)

	log.Println("Server gracefully stopped")

}

Immediately after receiving SIGINT, this server stops accepting new connections and the srv.ListenAndServe() call returns with http.ErrServerClosed. The srv.Shutdown(...) call remains blocked until all outstanding requests have been handled and their underlying connections closed.

More complex behavior can be created using a context with additional behavior. For example, one may easily enforce a maximum grace period using context.Timeout. Try cloning this example from github.com/tylerchr/examples/draining and implementing it.

Someone asked how one might write a test to verify that a given handler initiates a Server Push, as http.NewTLSServer doesn’t start an HTTP/2 server, and httptest.ResponseRecorder doesn’t implement http.Pusher. My solution is to wrap httptest.ResponseRecorder and implement the Pusher interface manually; there’s an example of this in the repo.

HTTP/2.0 server push via http.Pusher

HTTP/2.0 includes the Server Push feature that enables HTTP/2 servers to proactively send additional HTTP responses to a client for requests that client has not yet made. This aims to reduce the client’s latency by initiating the transfer of resources the server believes a client is likely to need, but for which the client has not yet asked. See the HTTP/2 Server Push Wikipedia page for a concrete example.

If a web server supports HTTP/21, the http.ResponseWriter provided to handlers will also implement the new http.Pusher interface. Handlers can use its functionality to trigger a Server Push for a resource by providing an HTTP method, path, and request headers. This “synthetic request” is serviced by the registered handler of the http.Server that served the original request.

The following Go program handles /index.html by initiating a push for /static/gopher.png:

package main

import "net/http"

func main() {

	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

	http.Handle("/index.html", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		// server push is available if w implements http.Pusher
		if p, ok := w.(http.Pusher); ok {
			p.Push("/static/gopher.png", nil}
		}

		// load the main page
		w.Header().Set("Content-Type", "text/html")
		w.Write([]byte(`<img src="/static/gopher.png" />`))

	}))

	http.ListenAndServeTLS(":4430", "cert.pem", "key.pem", nil)

}

If you have a tip build of Go, you can clone this example from github.com/tylerchr/examples/serverpush and observe the push occuring. Here’s what it looks like in Chrome 54:

Chrome showing a Server Push interaction

Obviously, the “Push” prefix in the Initiator column gives away that gopher.png was received via Server Push. You can also see gopher.png’s light-blue “Receiving Push” bar appear in the timeline before /index.html finishes downloading, showing that the image is transferred before the page knows to ask for it. The “Reading Push” phase is triggered later, when the HTML finishes downloading and the <img> tag is discovered.

database/sql

The database/sql package has several major additions that give more control over database queries and allow users to take advantage of additional database features.

  • Queries take a context.Context that can be used to cancel queries.
  • Native database column types are exposed via sql.ColumnType.
  • Queries can be executed with named parameters if the underlying driver implements support for them.

For more details, see What is new in database/sql? by Daniel Theophanes, who implemented most of the changes.

Dynamic plugins via new package plugin

Preliminary plugin support is implemented via the new plugin stdlib package. This will allow Go programs to load additional code—like plugins—while the program is running.

This feature seems buggy still, as I can’t manage to write a program that can use it without segfaulting, but it’s supposed to look something like this:

// hexify.go
package main

import "encoding/hex"

func Hexify(in string) string {
	return hex.EncodeToString([]byte(in))
}
$ go build -buildmode=shared hexify.go
// produces hexify.so
// main.go
package main

import "plugin"

func main() {
	p, _ = plugin.Open("hexify.so")
	f := p.Lookup("Hexify")
	fmt.Println(f.(func(string) string)("gopher"))
	// 676f70686572
}

In this example, hexify.go implements a function, Hexify, which is dynamically loaded by the second program. This allows code to be used that isn’t part of the program when it was compiled.

You can imagine a database loading user-defined functions this way, or perhaps extending Caddy with plugins withut having to create a custom build.

Identifier aliasing implemented

UPDATE (4 Nov): Aliases have been backed out of Go 1.8 due to newly-discovered technical implications; see this post from Russ Cox for details. They’ll likely appear in Go 1.9.

Identifier aliasing is a syntax addition for defining multiple types as identical. One use case is to ease refactoring in complex codebases by allowing code to be rearranged without introducing subtle type inequalities, as in this good example from Ian Lance Taylor:

For a concrete example, see the move of golang.org/x/net/context into the standard library as context. Because the context package is widely used, and it is difficult to convert all users of the package at once, it is desirable to permit packages using golang.org/x/net/context to interoperate with packages that have converted to context.

This interoperability is permitted by code similar to this:

type Foo => pkg.Bar

This syntax defines that, in all practical regards, the type Foo is identical to the type pkg.Bar. In other words, Foo may be used in place of pkg.Bar anywhere where pkg.Bar is expected. Using Ian’s context example, aliasing allows the external golang.net/x/net/context.Context type and the standard library’s context.Context to work interchangeably rather than being syntactically-identical but semantically-separate types.

Aliases are available for constants, variables, and functions in addition to types, and follow the conventional export rules.

This is a controversial feature; see issue 16339 and this golang-dev post for details. It’s also too early to tell what idiomatic usage of aliasing might look like, so it is recommended that Go developers use this feature conservatively for now.

New slice sorting API

Universal slice sorting is implemented via new sort.Slice2 function. This allows any slice to be sorted by providing a comparator callback rather than creating a specialized sort.Interface implementation. This function has no return value; like the existing sort functions, it sorts the provided slice in place.

This example sorts a list of worldwide mountains by the elevation of their summits:

type Peak struct {
	Name      string
	Elevation int // in feet
}

peaks := []Peak{
	{"Aconcagua", 22838},
	{"Denali", 20322},
	{"Kilimanjaro", 19341},
	{"Mount Elbrus", 18510},
	{"Mount Everest", 29029},
	{"Mount Kosciuszko", 7310},
	{"Mount Vinson", 16050},
	{"Puncak Jaya", 16024},
}

// does an in-place sort on the peaks slice, with tallest peak first
sort.Slice(peaks, func(i, j int) bool {
	return peaks[i].Elevation >= peaks[j].Elevation
})

// peaks is now sorted

This is possible because the Len() and Swap(i, j int) methods of the existing sort.Interface interface are identical for all slice types, and can be abstracted away. The Less(i, j int) method can’t be automatically inferred, and is the comparator function passed into sort.Slice.

Grab Bag

  • 87b1aaa The encoding/base64 encoder now has a strict mode.
  • 6ba5b32 The expvar route can now be obtained and mounted outside the default mux.
  • 003a598 Pseudorandom uint64 values may be generated via rand.Uint64() (previously, only uint32 support existed).
  • 67ea710 A new time.Until function is added to complement the existing time.Since.

  1. The net/http package intentionally implements HTTP/2 only over TLS-secured connections; see issue 14141 or the HTTP/2 spec for details. [return]
  2. There’s also the complementary sort.SliceStable function, which maintains the “original order of equal elements” just like sort.Stable. [return]

© Tyler Christensen 2016