package httprange
import (
)
const (
httpHeaderETag = "Etag"
httpHeaderLastModified = "Last-Modified"
httpHeaderDate = "Date"
weakETagPrefix = "W/"
)
type HTTPMetadataProvider interface {
Provide() *HTTPMetadata
}
type HTTPMetadata struct {
ContentLength int64
ETag string
LastModified time.Time
Date time.Time
AcceptRanges []string
}
func ( *HTTPMetadata) () *HTTPMetadata {
return
}
type HTTPMetadataExtractor interface {
Extract(context.Context) (HTTPMetadataProvider, error)
}
type HTTPResponseMetadataExtractor struct {
Request HTTPRequestBuilder
Client httpclient.Client
}
func ( *HTTPResponseMetadataExtractor) ( context.Context) (HTTPMetadataProvider, error) {
, := .Request.Build()
if != nil {
return nil,
}
, := .Client.Do()
if != nil {
return nil,
}
:= func() { _ = .Body.Close() }
:= context.AfterFunc(, )
defer func() {
if () {
()
}
}()
, := extractHTTPResponseMetadata()
if != nil {
return nil, &HTTPResponseError{
Response: ,
cause: ,
}
}
return , nil
}
func ( *http.Response) (*HTTPMetadata, error) {
if .StatusCode/100 != 2 {
return nil, &UnexpectedStatusCodeError{
Status: .Status,
StatusCode: .StatusCode,
}
}
, := parseETagOrZero(.Header)
if != nil {
return nil,
}
, := parseTimeHeaderOrZero(.Header, httpHeaderLastModified)
if != nil {
return nil,
}
, := parseTimeHeaderOrZero(.Header, httpHeaderDate)
if != nil {
return nil,
}
return &HTTPMetadata{
ContentLength: .ContentLength,
ETag: ,
LastModified: ,
Date: ,
AcceptRanges: .Header.Values(httpHeaderAcceptRanges),
}, nil
}
func ( http.Header) (string, error) {
:= .Get(httpHeaderETag)
if == "" {
return "", nil
}
if !isValidETag() {
return "", fmt.Errorf(
"httprange: invalid ETag header value %q",
,
)
}
return , nil
}
func ( string) bool {
= strings.TrimPrefix(, weakETagPrefix)
if len() < 2 || [0] != '"' || [len()-1] != '"' {
return false
}
for := 1; < len()-1; ++ {
if := []; <= 0x20 || == '"' || == 0x7F {
return false
}
}
return true
}
func ( http.Header, string) (time.Time, error) {
:= .Get()
if == "" {
return time.Time{}, nil
}
, := http.ParseTime()
if != nil {
return time.Time{}, fmt.Errorf("httprange: parse %q header: %w", , )
}
return , nil
}