Documentation
¶
Overview ¶
Package httputil은 공용 HTTP client, JSON request, response 검증 helper를 제공합니다.
패키지 개요 ¶
이 패키지는 서비스 간 HTTP 호출과 외부 API 호출에서 반복되는 client timeout, connection pool, HTTP/2 정책을 TransportProfile로 맞춥니다. 기본 transport의 proxy, keep-alive, TLS 기본값은 유지하고 호출 목적에 맞는 profile만 주입합니다.
JSON request 생성, API key header 적용, response body decode/discard, non-2xx response를 APIError로 변환하는 흐름도 이 패키지에서 제공합니다. 호출부는 error helper로 HTTP status와 API error code를 분기할 수 있습니다.
관리 HTTP surface에서 반복되는 API key 인증, fixed-window rate limiting, trusted proxy client IP 식별도 프레임워크 독립 helper로 제공합니다. gin 전용 adapter는 pkg/httputil/ginauth에 분리되어 있습니다.
주요 사용 패턴 ¶
client := httputil.NewExternalAPIClient(30 * time.Second)
resp, err := client.Get(url)
if err != nil {
return err
}
// non-2xx 시 CheckStatus가 body를 drain+close하므로 아래 defer는 no-op이 됩니다.
// 2xx success 경로에서는 caller가 body를 읽고 닫습니다.
defer resp.Body.Close()
if err := httputil.CheckStatus(resp); err != nil {
return err
}
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
return err
}
api := httputil.NewJSONClient(baseURL, apiKey, 10*time.Second)
req, err := api.NewJSONRequest(ctx, http.MethodPost, "/v1/jobs", payload)
if err != nil {
return err
}
resp, err := api.Do(req)
if err != nil {
return err
}
// non-2xx 시 CheckStatus가 body를 drain+close하므로 아래 defer는 no-op이 됩니다.
defer resp.Body.Close()
if err := api.CheckStatus(resp); err != nil {
return err
}
var out jobResponse
if err := api.DecodeJSON(resp, &out); err != nil {
return err
}
Index ¶
- Constants
- Variables
- func APIKeyFromRequest(r *http.Request) string
- func AdminAuthMiddleware(cfg AdminAuthConfig) func(http.Handler) http.Handler
- func CheckStatus(resp *http.Response) error
- func ClientIP(r *http.Request, opts ClientIPOptions) string
- func ConstantTimeStringEqual(left, right string) bool
- func DecodeJSON(resp *http.Response, v any) error
- func DecodeJSONLimited(resp *http.Response, v any, maxBytes int64) error
- func DecodeJSONRequest(w http.ResponseWriter, r *http.Request, v any, opts DecodeJSONRequestOptions) error
- func DecodeJSONRequestStatus(err error) int
- func FixedWindowRateLimitMiddleware(cfg RateLimitMiddlewareConfig) func(http.Handler) http.Handler
- func IsStatus(err error, statusCode int) bool
- func NewClient(timeout time.Duration) *http.Client
- func NewExternalAPIClient(timeout time.Duration) *http.Client
- func NewInternalServiceClient(timeout time.Duration) *http.Client
- func NewProfiledClient(profile TransportProfile) *http.Client
- func ParseTrustedProxies(values []string) ([]netip.Prefix, error)
- func ParseTrustedProxyCSV(raw string) ([]netip.Prefix, error)
- func RateLimitIdentity(r *http.Request, apiKey string, opts ClientIPOptions) string
- func RateLimitKeyHash(value string) string
- func WriteErrorJSON(w http.ResponseWriter, status int, code, message string) error
- func WriteRateLimitExceededJSON(w http.ResponseWriter, _ *http.Request, _ string)
- type APIError
- type AdminAuthConfig
- type ClientIPOptions
- type DecodeJSONRequestOptions
- type ErrorResponse
- type FixedWindowOptions
- type FixedWindowRateLimiter
- type ForwardedHeaderMode
- type IdentityFunc
- type JSONClient
- func (c *JSONClient) CheckStatus(resp *http.Response) error
- func (c *JSONClient) DecodeJSON(resp *http.Response, out any) error
- func (c *JSONClient) DiscardBody(resp *http.Response) error
- func (c *JSONClient) Do(req *http.Request) (*http.Response, error)
- func (c *JSONClient) NewJSONRequest(ctx context.Context, method, path string, payload any) (*http.Request, error)
- func (c *JSONClient) NewRequest(ctx context.Context, method, path string) (*http.Request, error)
- type JSONRequestError
- type LoginFailureRateLimiter
- type LoginFailureRateLimiterOptions
- type RateLimitMiddlewareConfig
- type RateLimitRejectFunc
- type TransportProfile
Constants ¶
const ( // ContentTypeJSON은 JSON 응답 Content-Type 값이다. ContentTypeJSON = "application/json" // HeaderContentType은 HTTP Content-Type 헤더 이름이다. HeaderContentType = "Content-Type" // HeaderAPIKey는 관리 API key 인증 헤더 이름이다. HeaderAPIKey = "X-API-Key" //nolint:gosec // 헤더 이름 상수이며 credential 값이 아니다. )
const DefaultMaxBodyBytes int64 = 16 << 20
const DefaultMaxRequestBodyBytes int64 = 64 << 10
DefaultMaxRequestBodyBytes는 JSON 요청 본문의 기본 최대 크기다.
Variables ¶
var ( // ErrRequestBodyRequired는 필수 JSON 요청 본문이 없을 때의 오류다. ErrRequestBodyRequired = errors.New("httputil: request body is required") // ErrUnsupportedJSONContentType는 JSON Content-Type이 아닐 때의 오류다. ErrUnsupportedJSONContentType = errors.New("httputil: content type must be application/json") // ErrRequestBodyTooLarge는 JSON 요청 본문이 허용 크기를 넘을 때의 오류다. ErrRequestBodyTooLarge = errors.New("httputil: request body exceeds limit") // ErrMultipleJSONValues는 JSON 요청 본문에 값이 둘 이상 있을 때의 오류다. ErrMultipleJSONValues = errors.New("httputil: request body must contain a single JSON value") )
var ErrResponseBodyTooLarge = errors.New("httputil: response body exceeds limit")
Functions ¶
func APIKeyFromRequest ¶
APIKeyFromRequest는 X-API-Key 또는 Bearer Authorization 값을 관리 API key로 추출한다.
func AdminAuthMiddleware ¶
func AdminAuthMiddleware(cfg AdminAuthConfig) func(http.Handler) http.Handler
AdminAuthMiddleware는 twentyq형 관리 API key 인증 middleware를 만든다.
func CheckStatus ¶
func ClientIP ¶
func ClientIP(r *http.Request, opts ClientIPOptions) string
ClientIP는 trusted proxy 경계 안에서만 forwarded header를 반영한 client IP를 반환한다.
func ConstantTimeStringEqual ¶
ConstantTimeStringEqual은 두 문자열을 길이 차이까지 포함해 일정 시간 비교한다.
func DecodeJSONRequest ¶ added in v1.28.0
func DecodeJSONRequest(w http.ResponseWriter, r *http.Request, v any, opts DecodeJSONRequestOptions) error
DecodeJSONRequest는 HTTP 요청 본문에서 단일 JSON 값을 decode한다.
func DecodeJSONRequestStatus ¶ added in v1.28.0
DecodeJSONRequestStatus는 JSON 요청 오류에 대응하는 HTTP status를 반환한다.
func FixedWindowRateLimitMiddleware ¶
func FixedWindowRateLimitMiddleware(cfg RateLimitMiddlewareConfig) func(http.Handler) http.Handler
FixedWindowRateLimitMiddleware는 fixed-window limiter를 net/http middleware로 감싼다.
func NewProfiledClient ¶
func NewProfiledClient(profile TransportProfile) *http.Client
기본 keep-alive, proxy, TLS 기본 동작은 유지하고 timeout/pool/HTTP2 정책만 profile로 주입합니다.
func ParseTrustedProxies ¶
ParseTrustedProxies는 IP/CIDR 문자열을 trusted proxy prefix 목록으로 변환한다.
func ParseTrustedProxyCSV ¶
ParseTrustedProxyCSV는 comma-separated trusted proxy CIDR/IP 목록을 변환한다.
func RateLimitIdentity ¶
func RateLimitIdentity(r *http.Request, apiKey string, opts ClientIPOptions) string
RateLimitIdentity는 API key가 있으면 key hash, 아니면 client IP 기반 identity를 반환한다.
func RateLimitKeyHash ¶
RateLimitKeyHash는 rate-limit key/log용 짧은 SHA-256 해시를 반환한다.
func WriteErrorJSON ¶
func WriteErrorJSON(w http.ResponseWriter, status int, code, message string) error
WriteErrorJSON은 표준 관리 HTTP JSON 에러 응답을 쓴다.
func WriteRateLimitExceededJSON ¶
func WriteRateLimitExceededJSON(w http.ResponseWriter, _ *http.Request, _ string)
WriteRateLimitExceededJSON은 twentyq형 429 JSON 응답을 쓴다.
Types ¶
type APIError ¶
type AdminAuthConfig ¶
AdminAuthConfig는 관리 API key 인증 middleware 설정이다. Disabled가 false인 zero value는 인증을 강제한다.
type ClientIPOptions ¶
type ClientIPOptions struct {
TrustForwarded bool
TrustedProxies []netip.Prefix
ForwardedMode ForwardedHeaderMode
}
ClientIPOptions는 forwarded header 신뢰 경계를 지정한다.
type DecodeJSONRequestOptions ¶ added in v1.28.0
type DecodeJSONRequestOptions struct {
// MaxBodyBytes는 요청 본문 최대 크기다.
MaxBodyBytes int64
// Strict는 알 수 없는 JSON field를 거부한다.
Strict bool
// RequireContentType은 application/json Content-Type을 필수로 한다.
RequireContentType bool
}
DecodeJSONRequestOptions는 JSON 요청 decode 동작을 조정한다.
type ErrorResponse ¶
ErrorResponse는 관리 HTTP JSON 에러 응답 본문이다.
type FixedWindowOptions ¶
FixedWindowOptions는 fixed-window limiter 선택 동작을 지정한다. MaxIdentities와 EntryTTL의 zero value는 안전 기본값을 사용한다.
type FixedWindowRateLimiter ¶
type FixedWindowRateLimiter struct {
// contains filtered or unexported fields
}
FixedWindowRateLimiter는 identity별 fixed-window 요청 수를 제한한다.
func NewFixedWindowRateLimiter ¶
func NewFixedWindowRateLimiter(limit int, window time.Duration, opts FixedWindowOptions) *FixedWindowRateLimiter
NewFixedWindowRateLimiter는 fixed-window limiter를 생성한다.
func (*FixedWindowRateLimiter) Allow ¶
func (l *FixedWindowRateLimiter) Allow(identity string) bool
Allow는 identity가 현재 window에서 허용되는지 반환한다.
type ForwardedHeaderMode ¶
type ForwardedHeaderMode int
ForwardedHeaderMode는 trusted proxy에서 전달된 client IP 선택 방식을 지정한다.
const ( // ForwardedHeaderLeftmost는 X-Forwarded-For의 첫 유효 IP를 선택한다. ForwardedHeaderLeftmost ForwardedHeaderMode = iota // ForwardedHeaderRightmostNonTrusted는 오른쪽에서 첫 비신뢰 hop을 선택한다. ForwardedHeaderRightmostNonTrusted )
type IdentityFunc ¶
IdentityFunc는 요청에서 rate-limit identity를 만든다.
type JSONClient ¶
type JSONClient struct {
// contains filtered or unexported fields
}
JSONClient는 내부 서비스 간 JSON API 호출용 공통 HTTP 클라이언트입니다.
func NewJSONClient ¶
func NewJSONClient(baseURL, apiKey string, timeout time.Duration) *JSONClient
NewJSONClient는 공통 internal service client를 생성합니다.
func NewJSONClientWithHTTPClient ¶
func NewJSONClientWithHTTPClient(baseURL, apiKey string, httpClient *http.Client) *JSONClient
NewJSONClientWithHTTPClient는 caller가 준비한 http.Client로 JSONClient를 생성합니다.
func (*JSONClient) CheckStatus ¶
func (c *JSONClient) CheckStatus(resp *http.Response) error
func (*JSONClient) DecodeJSON ¶
func (c *JSONClient) DecodeJSON(resp *http.Response, out any) error
func (*JSONClient) DiscardBody ¶
func (c *JSONClient) DiscardBody(resp *http.Response) error
func (*JSONClient) NewJSONRequest ¶
func (c *JSONClient) NewJSONRequest(ctx context.Context, method, path string, payload any) (*http.Request, error)
NewJSONRequest는 JSON body 요청을 생성합니다.
func (*JSONClient) NewRequest ¶
NewRequest는 body 없는 요청을 생성합니다.
type JSONRequestError ¶ added in v1.28.0
type JSONRequestError struct {
// StatusCode는 응답해야 할 HTTP status code다.
StatusCode int
// Code는 machine-readable 오류 code다.
Code string
// Message는 응답에 사용할 오류 message다.
Message string
// Err는 원래 decode 또는 validation 오류다.
Err error
}
JSONRequestError는 JSON 요청 decode 실패의 HTTP taxonomy다.
func (*JSONRequestError) Error ¶ added in v1.28.0
func (e *JSONRequestError) Error() string
Error는 JSON 요청 오류를 문자열로 반환한다.
func (*JSONRequestError) Unwrap ¶ added in v1.28.0
func (e *JSONRequestError) Unwrap() error
Unwrap은 원래 JSON 요청 decode 오류를 반환한다.
type LoginFailureRateLimiter ¶
type LoginFailureRateLimiter struct {
// contains filtered or unexported fields
}
LoginFailureRateLimiter는 로그인 실패 횟수 기반 lockout limiter다.
func NewDefaultLoginFailureRateLimiter ¶
func NewDefaultLoginFailureRateLimiter() *LoginFailureRateLimiter
NewDefaultLoginFailureRateLimiter는 admin-dashboard와 같은 기본 lockout 값을 사용한다.
func NewLoginFailureRateLimiter ¶
func NewLoginFailureRateLimiter(opts LoginFailureRateLimiterOptions) *LoginFailureRateLimiter
NewLoginFailureRateLimiter는 로그인 실패 기반 lockout limiter를 생성한다.
func (*LoginFailureRateLimiter) IsAllowed ¶
func (l *LoginFailureRateLimiter) IsAllowed(identity string) (bool, time.Duration)
IsAllowed는 identity가 현재 로그인 시도를 할 수 있는지와 retry-after를 반환한다.
func (*LoginFailureRateLimiter) RecordFailure ¶
func (l *LoginFailureRateLimiter) RecordFailure(identity string) int
RecordFailure는 identity의 실패 횟수를 기록하고 현재 window count를 반환한다.
func (*LoginFailureRateLimiter) RecordSuccess ¶
func (l *LoginFailureRateLimiter) RecordSuccess(identity string)
RecordSuccess는 identity의 실패 기록을 삭제한다.
func (*LoginFailureRateLimiter) Start ¶
func (l *LoginFailureRateLimiter) Start()
Start는 stale attempt cleanup loop를 시작한다.
func (*LoginFailureRateLimiter) Stop ¶
func (l *LoginFailureRateLimiter) Stop()
Stop은 cleanup loop를 정지한다.
type LoginFailureRateLimiterOptions ¶
type LoginFailureRateLimiterOptions struct {
MaxAttempts int
Window time.Duration
Lockout time.Duration
CleanupInterval time.Duration
Now func() time.Time
}
LoginFailureRateLimiterOptions는 로그인 실패 lockout limiter 설정이다.
type RateLimitMiddlewareConfig ¶
type RateLimitMiddlewareConfig struct {
Limiter *FixedWindowRateLimiter
Identity IdentityFunc
Reject RateLimitRejectFunc
Skip func(*http.Request) bool
}
RateLimitMiddlewareConfig는 fixed-window HTTP middleware 설정이다.
type RateLimitRejectFunc ¶
type RateLimitRejectFunc func(http.ResponseWriter, *http.Request, string)
RateLimitRejectFunc는 rate-limit 초과 응답을 쓴다.