You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
3.7 KiB
Go

package fuckgoogle
import (
"math/rand"
"net/http"
"strings"
"time"
"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"
)
func init() {
caddy.RegisterModule(FckGgl{})
httpcaddyfile.RegisterHandlerDirective("fuck-google", parseFuckuppery)
}
type FckGgl struct {
Level FuckupLevel `json:"level,omitempty"`
clientMatcher GoogleMatcher
serverMatcher GoogleMatcher
}
type FuckupLevel string
const (
FuckupLevel1 FuckupLevel = "a teeny tiny bit"
FuckupLevel2 FuckupLevel = "fuck them"
FuckupLevel3 FuckupLevel = "for real"
FuckupLevel4 FuckupLevel = "all the way"
)
func (l FuckupLevel) isValid() bool {
return l == FuckupLevel1 || l == FuckupLevel2 || l == FuckupLevel3 || l == FuckupLevel4
}
// CaddyModule returns the Caddy module information.
func (FckGgl) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.fuck_google",
New: func() caddy.Module { return new(FckGgl) },
}
}
// Provision sets up the module defaults.
func (m *FckGgl) Provision(ctx caddy.Context) error {
if m.Level == "" {
m.Level = FuckupLevel4
}
m.clientMatcher = GoogleMatcher{
UAChrome: true,
UAChromeish: m.Level == FuckupLevel4,
Referer: true,
RefererExt: m.Level == FuckupLevel3 || m.Level == FuckupLevel4,
}
m.serverMatcher = GoogleMatcher{
Bot: m.Level != FuckupLevel1,
IPranges: m.Level == FuckupLevel3 || m.Level == FuckupLevel4,
}
if err := m.clientMatcher.Provision(ctx); err != nil {
return err
}
if err := m.serverMatcher.Provision(ctx); err != nil {
return err
}
return nil
}
func (m FckGgl) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
isGoogleSrv := m.serverMatcher.Match(r)
isGoogleClient := m.clientMatcher.Match(r)
if isGoogleClient {
w.Header().Add("Permissions-Policy", "interest-cohort=()")
}
if isGoogleClient || isGoogleSrv {
var competitiveAdvantage float32
switch m.Level {
case FuckupLevel1:
competitiveAdvantage = 50
case FuckupLevel2:
competitiveAdvantage = 150
case FuckupLevel3:
competitiveAdvantage = 700
case FuckupLevel4:
competitiveAdvantage = 1400
}
// ±10% random jitter, cuz why not.
competitiveAdvantage *= 1 + float32(rand.Intn(20)-10)/100.0
time.Sleep(time.Millisecond * time.Duration(competitiveAdvantage))
}
if isGoogleSrv {
w.WriteHeader(http.StatusUnavailableForLegalReasons)
return nil
}
return next.ServeHTTP(w, r)
}
func parseFuckuppery(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
m := new(FckGgl)
err := m.UnmarshalCaddyfile(h.Dispenser)
if err != nil {
return nil, err
}
return m, nil
}
// UnmarshalCaddyfile sets up the handler from Caddyfile tokens.
//
// fuck-google [<matcher>] [<level>] [{
// level <level>
// }]
//
func (m *FckGgl) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
args := d.RemainingArgs()
if len(args) > 0 {
m.Level = FuckupLevel(strings.Join(args, " "))
if !m.Level.isValid() {
return d.Err("invalid value for level")
}
}
for d.NextBlock(0) {
switch d.Val() {
case "level":
if m.Level != "" {
return d.Err("level already specified")
}
if !d.NextArg() {
return d.ArgErr()
}
m.Level = FuckupLevel(d.Val())
if !m.Level.isValid() {
return d.Err("invalid value for level")
}
default:
return d.Errf("unrecognized subdirective: %s", d.Val())
}
}
}
return nil
}
// Interface guards
var (
_ caddy.Module = (*FckGgl)(nil)
_ caddyhttp.MiddlewareHandler = (*FckGgl)(nil)
_ caddyfile.Unmarshaler = (*FckGgl)(nil)
)