adguardhome/adguardhome_client.go
Rama main · réplica de lectura del repositorio original.
package adguardhome
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
log "github.com/sirupsen/logrus"
)
// Client is an interface for the AdguardHome API.
// See OpenAPI spec for details: https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/openapi/openapi.yaml
type Client interface {
GetFilteringRules(ctx context.Context) ([]string, error)
SaveFilteringRules(ctx context.Context, rules []string) error
}
type client struct {
hc *http.Client
lifecycleCtx context.Context
endpoint string
user string
pass string
dryRun bool
}
type filteringStatus struct {
UserRules []string `json:"user_rules"`
}
type setRules struct {
Rules []string `json:"rules"`
}
// requestContext preserves operation deadlines and values while linking shutdown
// cancellation. Release it only after the response body has been closed.
func (c *client) requestContext(ctx context.Context) (context.Context, context.CancelFunc) {
requestCtx, cancel := context.WithCancel(ctx)
stop := context.AfterFunc(c.lifecycleCtx, cancel)
if c.lifecycleCtx.Err() != nil {
cancel()
}
return requestCtx, func() {
stop()
cancel()
}
}
func (c *client) doRequest(ctx context.Context, method, path string, body io.Reader) (*http.Response, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
log.Debugf("making %s request to %s", method, path)
req, err := http.NewRequestWithContext(ctx, method, c.endpoint+path, body)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.user, c.pass)
req.Header.Set("Content-Type", "application/json")
resp, err := c.hc.Do(req)
if err != nil {
return nil, err
}
log.Debugf("response status code %d", resp.StatusCode)
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
return resp, nil
}
func (c *client) GetFilteringRules(ctx context.Context) ([]string, error) {
if c.dryRun {
return []string{}, nil
}
ctx, release := c.requestContext(ctx)
defer release()
r, err := c.doRequest(ctx, http.MethodGet, "filtering/status", nil)
if err != nil {
return nil, err
}
defer r.Body.Close()
var resp filteringStatus
err = json.NewDecoder(r.Body).Decode(&resp)
if err != nil {
return nil, err
}
return resp.UserRules, nil
}
func (c *client) SaveFilteringRules(ctx context.Context, rules []string) error {
if c.dryRun {
return nil
}
ctx, release := c.requestContext(ctx)
defer release()
body := setRules{Rules: rules}
b := bytes.NewBuffer(nil)
err := json.NewEncoder(b).Encode(body)
if err != nil {
return err
}
r, err := c.doRequest(ctx, http.MethodPost, "filtering/set_rules", b)
if err != nil {
return err
}
_ = r.Body.Close()
return nil
}
func newAdguardHomeClient(ctx context.Context, endpoint, user, pass string, dryRun bool) *client {
hc := http.Client{}
return &client{
hc: &hc,
lifecycleCtx: ctx,
endpoint: endpoint,
user: user,
pass: pass,
dryRun: dryRun,
}
}