mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 13:00:52 +08:00
feat(executor): add HttpRequest support across executors for better http request handling
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -32,6 +34,9 @@ type ProviderExecutor interface {
|
||||
Refresh(ctx context.Context, auth *Auth) (*Auth, error)
|
||||
// CountTokens returns the token count for the given request.
|
||||
CountTokens(ctx context.Context, auth *Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error)
|
||||
// HttpRequest injects provider credentials into the supplied HTTP request and executes it.
|
||||
// Callers must close the response body when non-nil.
|
||||
HttpRequest(ctx context.Context, auth *Auth, req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// RefreshEvaluator allows runtime state to override refresh decisions.
|
||||
@@ -1572,6 +1577,23 @@ type RequestPreparer interface {
|
||||
PrepareRequest(req *http.Request, auth *Auth) error
|
||||
}
|
||||
|
||||
func executorKeyFromAuth(auth *Auth) string {
|
||||
if auth == nil {
|
||||
return ""
|
||||
}
|
||||
if auth.Attributes != nil {
|
||||
providerKey := strings.TrimSpace(auth.Attributes["provider_key"])
|
||||
compatName := strings.TrimSpace(auth.Attributes["compat_name"])
|
||||
if compatName != "" {
|
||||
if providerKey == "" {
|
||||
providerKey = compatName
|
||||
}
|
||||
return strings.ToLower(providerKey)
|
||||
}
|
||||
}
|
||||
return strings.ToLower(strings.TrimSpace(auth.Provider))
|
||||
}
|
||||
|
||||
// logEntryWithRequestID returns a logrus entry with request_id field if available in context.
|
||||
func logEntryWithRequestID(ctx context.Context) *log.Entry {
|
||||
if ctx == nil {
|
||||
@@ -1647,7 +1669,7 @@ func (m *Manager) InjectCredentials(req *http.Request, authID string) error {
|
||||
a := m.auths[authID]
|
||||
var exec ProviderExecutor
|
||||
if a != nil {
|
||||
exec = m.executors[a.Provider]
|
||||
exec = m.executors[executorKeyFromAuth(a)]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
if a == nil || exec == nil {
|
||||
@@ -1658,3 +1680,80 @@ func (m *Manager) InjectCredentials(req *http.Request, authID string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrepareHttpRequest injects provider credentials into the supplied HTTP request.
|
||||
func (m *Manager) PrepareHttpRequest(ctx context.Context, auth *Auth, req *http.Request) error {
|
||||
if m == nil {
|
||||
return &Error{Code: "provider_not_found", Message: "manager is nil"}
|
||||
}
|
||||
if auth == nil {
|
||||
return &Error{Code: "auth_not_found", Message: "auth is nil"}
|
||||
}
|
||||
if req == nil {
|
||||
return &Error{Code: "invalid_request", Message: "http request is nil"}
|
||||
}
|
||||
if ctx != nil {
|
||||
*req = *req.WithContext(ctx)
|
||||
}
|
||||
providerKey := executorKeyFromAuth(auth)
|
||||
if providerKey == "" {
|
||||
return &Error{Code: "provider_not_found", Message: "auth provider is empty"}
|
||||
}
|
||||
exec := m.executorFor(providerKey)
|
||||
if exec == nil {
|
||||
return &Error{Code: "provider_not_found", Message: "executor not registered for provider: " + providerKey}
|
||||
}
|
||||
preparer, ok := exec.(RequestPreparer)
|
||||
if !ok || preparer == nil {
|
||||
return &Error{Code: "not_supported", Message: "executor does not support http request preparation"}
|
||||
}
|
||||
return preparer.PrepareRequest(req, auth)
|
||||
}
|
||||
|
||||
// NewHttpRequest constructs a new HTTP request and injects provider credentials into it.
|
||||
func (m *Manager) NewHttpRequest(ctx context.Context, auth *Auth, method, targetURL string, body []byte, headers http.Header) (*http.Request, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
method = strings.TrimSpace(method)
|
||||
if method == "" {
|
||||
method = http.MethodGet
|
||||
}
|
||||
var reader io.Reader
|
||||
if body != nil {
|
||||
reader = bytes.NewReader(body)
|
||||
}
|
||||
httpReq, err := http.NewRequestWithContext(ctx, method, targetURL, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if headers != nil {
|
||||
httpReq.Header = headers.Clone()
|
||||
}
|
||||
if errPrepare := m.PrepareHttpRequest(ctx, auth, httpReq); errPrepare != nil {
|
||||
return nil, errPrepare
|
||||
}
|
||||
return httpReq, nil
|
||||
}
|
||||
|
||||
// HttpRequest injects provider credentials into the supplied HTTP request and executes it.
|
||||
func (m *Manager) HttpRequest(ctx context.Context, auth *Auth, req *http.Request) (*http.Response, error) {
|
||||
if m == nil {
|
||||
return nil, &Error{Code: "provider_not_found", Message: "manager is nil"}
|
||||
}
|
||||
if auth == nil {
|
||||
return nil, &Error{Code: "auth_not_found", Message: "auth is nil"}
|
||||
}
|
||||
if req == nil {
|
||||
return nil, &Error{Code: "invalid_request", Message: "http request is nil"}
|
||||
}
|
||||
providerKey := executorKeyFromAuth(auth)
|
||||
if providerKey == "" {
|
||||
return nil, &Error{Code: "provider_not_found", Message: "auth provider is empty"}
|
||||
}
|
||||
exec := m.executorFor(providerKey)
|
||||
if exec == nil {
|
||||
return nil, &Error{Code: "provider_not_found", Message: "executor not registered for provider: " + providerKey}
|
||||
}
|
||||
return exec.HttpRequest(ctx, auth, req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user