Host/Guest Module

You can make your own module polymorphic by supporting the architecture of host/guest module. You have already seen a form of host/guest module in the HTTP handlers part of Caddy. The handlers are guest modules of routes. The way to implement that is to define an interface, a contract, of what the guest modules should implement. Add a field into your struct as json.RawMessage having the struct tag in the form of caddy:"namespace=foo.bar inline_key=baz" (note that inline_key=baz is optional and has certain uses) for the configuration body of the guest module. Add another field for the guest module typed as the defined interface. Then use the caddy.LoadModule() function to load and transform the configured module into a Go type and as a Caddy module. Finally type-assert the interface{} value received from caddy.LoadModule() into the interface defined earlier.

The official docs provide a decent illustrator into how to define a host/guest module and how to load the guest module:


type Gadgeter interface {
	Beep()
}

type Gizmo struct {
	GadgetRaw json.RawMessage `json:"gadget,omitempty" caddy:"namespace=foo.gizmo.gadgets inline_key=gadgeter"`

	gadget Gadgeter `json:"-"`
}

// Provision sets up g and loads its gadget.
func (g *Gizmo) Provision(ctx caddy.Context) error {
	if g.GadgetRaw != nil {
		val, err := ctx.LoadModule(g, "GadgetRaw")
		if err != nil {
			return fmt.Errorf("loading gadget module: %v", err)
		}
		g.Gadget = val.(Gadgeter)
	}
	return nil
}

Exercise

Include additinal functionality to the dump_headers module that enables pluggable test criteria of whether a header is allowed to be printed or not. Here’s an example of possible config:

{
	"apps": {
		"http": {
			"servers": {
				"srv0": {
					"listen": [
						":443"
					],
					"routes": [
						{
							"match": [
								{
									"host": [
										"localhost"
									]
								}
							],
							"handle": [
								{
									"filter": {
										"test": "no_filter"
									},
									"handler": "dump_headers"
								}
							]
						}
					]
				}
			}
		}
	}
}

Bonus

The implementation of the exercise of this section invalidates the Caddyfile unmarshaller. In other words, the unmarshaller does not support the filtering functionality yet. As bonus exercise, edit the implementation of the UnmarshalCaddyfile method to account for the guest module.