Skip to content

Writing a Plugin: HTTP Middleware

Jason Chu edited this page Oct 25, 2020 · 1 revision

This page is obsolete.

It is about Casket v1.


Join the Casket Community Forum to chat with other Casket developers!

This page describes how to write a plugin that adds middleware to the HTTP server.

IMPORTANT: You should first read Writing a Plugin: Directives. This tutorial builds on that one.

In this tutorial, you will learn:

  • How HTTP Middleware Works in Casket
  • How to Write a Handler
  • How to Add the Handler to Casket

How HTTP Middleware Works in Casket

Check out the godoc for the httpserver package. The two most important types are httpserver.Handler and httpserver.Middleware.

Handler is a function that actually handles an HTTP request.

Middleware is a way to chain one handler to another.

Casket will do all the bookkeeping of setting up an HTTP server for you, but you need to implement these two types.

Writing a Handler

httpserver.Handler is an interface exactly like http.Handler except that the ServeHTTP method returns (int, error). This method signature follows a recommendation from the Go Blog for error handling related to middleware. The int is the HTTP status code, and the error is one that should be handled and/or logged. Read the godoc for more details about these return values.

Handlers are usually a struct with at least one field, the next Handler in the chain:

type MyHandler struct {
	Next httpserver.Handler
}

To implement the httpserver.Handler interface, we write a method called ServeHTTP. This method is the actual handler function, and, unless it fully handles the request by itself, it should call the next handler in the chain:

func (h MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	return h.Next.ServeHTTP(w, r)
}

That's all there is to it.

Be careful of race conditions -- avoid state whenever possible and use the Go race detector (-race)!

How to Add the Handler to Casket

So, back in your setup function. You've just parsed the tokens and set up your middleware handler with all the proper configuration:

func setup(c *casket.Controller) error {
	for c.Next() {
		// get configuration
	}
	// now what?
}

To chain in your new handler, get the config for the current site from the httpserver package. Then wrap your handler in a middleware function:

cfg := httpserver.GetConfig(c)
mid := func(next httpserver.Handler) httpserver.Handler {
	return MyHandler{Next: next}
}
cfg.AddMiddleware(mid)

And you're done! Of course, in this example, we simply allocated a MyHandler with no special configuration. In your case, you may want to store the configuration in structs called "rules" so that you can handle multiple uses of the directive and add rule upon rule. Or not. It depends on what you want to do. It doesn't really matter as long as you chain in the next handler properly!