73 lines
1.9 KiB
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))
|
|
}
|
|
}
|
|
}
|