From cc0dc64d91568bd65da920fc415f2555ba3ca0f5 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 14 Mar 2026 11:36:04 +0500 Subject: [PATCH] Add blocklist package with config and data fetching functions --- blocklist.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 blocklist.go diff --git a/blocklist.go b/blocklist.go new file mode 100644 index 0000000..06a2e7b --- /dev/null +++ b/blocklist.go @@ -0,0 +1,93 @@ +package blocklist + +import ( + "context" + "fmt" + "net/http" + "net/url" + "time" + + "git.kor-elf.net/kor-elf-shield/blocklist/parser" +) + +const ( + + // contextTimeout defines the maximum duration for context operations before timing out. + contextTimeout = 15 * time.Second + // requestTimeout defines the maximum duration for request operations before timing out. + requestTimeout = 20 * time.Second +) + +// Config defines the configuration for the blocklist. +type Config struct { + + // Limit specifies the maximum number of items to process or validate. + Limit uint + + // Validator specifies the IP validator to use. + Validator parser.IPValidator + + // ContextTimeout defines the maximum duration for context operations before timing out. + ContextTimeout time.Duration + + // RequestTimeout defines the maximum duration for request operations before timing out. + RequestTimeout time.Duration +} + +// NewConfig creates a new Config with default values. +func NewConfig(limit uint) Config { + return Config{ + Limit: limit, + Validator: &parser.DefaultIPValidator{}, + ContextTimeout: contextTimeout, + RequestTimeout: requestTimeout, + } +} + +// NewConfigWithValidator creates a new Config with the specified validator. +func NewConfigWithValidator(limit uint, validator parser.IPValidator) Config { + return Config{ + Limit: limit, + Validator: validator, + ContextTimeout: contextTimeout, + RequestTimeout: requestTimeout, + } +} + +// Get fetches data from the given URL, parses the response using the provided parser, and applies the given configuration. +// It returns the parsed IPs and any errors that occurred during the process. +func Get(fileUrl string, parser parser.Parser, c Config) (parser.IPs, error) { + parsedURL, err := url.Parse(fileUrl) + if err != nil { + return nil, fmt.Errorf("invalid url: %w", err) + } + if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { + return nil, fmt.Errorf("invalid url scheme: %s", parsedURL.Scheme) + } + + ctx, cancel := context.WithTimeout(context.Background(), c.ContextTimeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileUrl, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + + client := &http.Client{ + Timeout: c.RequestTimeout, + } + + res, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("request failed: %w", err) + } + defer func() { + _ = res.Body.Close() + }() + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode) + } + + return parser.Parse(res.Body, c.Validator, c.Limit) +}