// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package secwebsocketkey

import (
	"crypto/rand"
	"encoding/base64"
	"net/http"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)

const (
	secWebsocketKeyHeader = "Sec-WebSocket-Key"
	secWebsocketKeyModule = "gitpod.sec_websocket_key"
)

func init() {
	caddy.RegisterModule(SecWebsocketKey{})
	httpcaddyfile.RegisterHandlerDirective(secWebsocketKeyModule, parseWebsocketCaddyfile)
}

// SecWebsocketKey implements an HTTP handler that adds a random sec-websocket-key if the header is missing
type SecWebsocketKey struct {
	BaseDomain string `json:"base_domain,omitempty"`
	Debug      bool   `json:"debug,omitempty"`
}

// CaddyModule returns the Caddy module information.
func (SecWebsocketKey) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "http.handlers.gitpod_sec_websocket_key",
		New: func() caddy.Module { return new(SecWebsocketKey) },
	}
}

// ServeHTTP implements caddyhttp.MiddlewareHandler.
func (m SecWebsocketKey) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
	key := r.Header.Get(secWebsocketKeyHeader)
	if key == "" {
		buf := make([]byte, 20)
		_, err := rand.Read(buf)
		if err != nil {
			return err
		}
		randomKey := base64.StdEncoding.EncodeToString(buf)
		r.Header.Set(secWebsocketKeyHeader, randomKey)
	}

	return next.ServeHTTP(w, r)
}

// UnmarshalCaddyfile implements Caddyfile.Unmarshaler.
func (m *SecWebsocketKey) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	return nil
}

func parseWebsocketCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
	m := new(SecWebsocketKey)
	err := m.UnmarshalCaddyfile(h.Dispenser)
	if err != nil {
		return nil, err
	}

	return m, nil
}

// Interface guards
var (
	_ caddyhttp.MiddlewareHandler = (*SecWebsocketKey)(nil)
	_ caddyfile.Unmarshaler       = (*SecWebsocketKey)(nil)
)
