package ibd import ( "context" "errors" "log/slog" "net/http" "slices" "github.com/ansg191/ibd-trader-backend/internal/database" "github.com/ansg191/ibd-trader-backend/internal/ibd/transport" "github.com/ansg191/ibd-trader-backend/internal/keys" ) var ErrNoAvailableCookies = errors.New("no available cookies") var ErrNoAvailableTransports = errors.New("no available transports") type Client struct { transports []transport.Transport db database.Executor kms keys.KeyManagementService } func NewClient( db database.Executor, kms keys.KeyManagementService, transports ...transport.Transport, ) *Client { return &Client{transports, db, kms} } func (c *Client) getCookie(ctx context.Context, subject *string) (uint, *http.Cookie, error) { if subject == nil { // No subject requirement, get any cookie cookie, err := database.GetAnyCookie(ctx, c.db, c.kms) if err != nil { return 0, nil, err } if cookie == nil { return 0, nil, ErrNoAvailableCookies } return cookie.ID, cookie.ToHTTPCookie(), nil } // Get cookie by subject cookies, err := database.GetCookies(ctx, c.db, c.kms, *subject, false) if err != nil { return 0, nil, err } if len(cookies) == 0 { return 0, nil, ErrNoAvailableCookies } cookie := cookies[0] return cookie.ID, cookie.ToHTTPCookie(), nil } func (c *Client) Do(req *http.Request, opts ...optionFunc) (*http.Response, error) { o := defaultOptions for _, opt := range opts { opt(&o) } // Sort and filter transports by properties transports := transport.FilterTransports(c.transports, o.requiredProps) transport.SortTransports(transports) for _, tp := range transports { resp, err := tp.Do(req) if errors.Is(err, transport.ErrUnsupportedRequest) { // Skip unsupported transport continue } if err != nil { slog.ErrorContext(req.Context(), "transport error", "transport", tp.String(), "error", err, ) continue } if slices.Contains(o.expectedStatuses, resp.StatusCode) { return resp, nil } else { slog.ErrorContext(req.Context(), "unexpected status code", "transport", tp.String(), "expected", o.expectedStatuses, "actual", resp.StatusCode, ) continue } } return nil, ErrNoAvailableTransports }