go-captiveportal/rfc8908/handler.go

73 lines
1.9 KiB
Go

package rfc8908
import (
"encoding/json"
"net/http"
"github.com/pkg/errors"
)
type IsCaptiveFunc func(*http.Request) (bool, error)
// RFC8098
// See https://tools.ietf.org/html/rfc8908 - "5. API State Structure"
type Response struct {
Captive bool `json:"captive"`
UserPortalURL string `json:"user-portal-url,omitempty"`
VenueInfoURL string `json:"venue-info-url,omitempty"`
CanExtendSession *bool `json:"can-extend-session,omitempty"`
SecondsRemaining *int `json:"seconds-remaining,omitempty"`
BytesRemaining *int `json:"bytes-remaining,omitempty"`
}
// Handler returns a basic implementation of a Captive Portal API endpoint
// as described in the IETF 8908 RFC (https://tools.ietf.org/html/rfc8908)
func Handler(isCaptive IsCaptiveFunc, opts ...OptionsFunc) http.HandlerFunc {
options := DefaultOptions()
for _, o := range opts {
o(options)
}
return func(w http.ResponseWriter, r *http.Request) {
isCaptive, err := isCaptive(r)
if err != nil {
panic(errors.Wrap(err, "could not retrieve client id"))
}
ctx := r.Context()
var bytesRemaining *int
if options.BytesRemaining != nil {
bytesRemaining, err = options.BytesRemaining(ctx)
if err != nil {
panic(errors.Wrap(err, "could not retrieve client remaining bytes"))
}
}
var secondsRemaining *int
if options.SecondsRemaining != nil {
secondsRemaining, err = options.SecondsRemaining(ctx)
if err != nil {
panic(errors.Wrap(err, "could not retrieve client remaining seconds"))
}
}
w.Header().Add("Content-Type", "application/captive+json")
encoder := json.NewEncoder(w)
res := &Response{
Captive: isCaptive,
VenueInfoURL: options.VenueInfoURL,
UserPortalURL: options.UserPortalURL,
CanExtendSession: options.CanExtendSession,
SecondsRemaining: secondsRemaining,
BytesRemaining: bytesRemaining,
}
if err := encoder.Encode(res); err != nil {
panic(errors.WithStack(err))
}
}
}