Files
goget/internal/protocol/http/handler.go
T

180 lines
4.0 KiB
Go
Raw Normal View History

//go:build linux || freebsd
// +build linux freebsd
package http
import (
"fmt"
"mime"
"net/http"
"net/url"
"path/filepath"
"strings"
)
// URLInfo contains extracted information from an HTTP URL
type URLInfo struct {
Scheme string
Host string
Port string
Path string
Query string
Fragment string
User *url.Userinfo
}
// Handler contains HTTP-specific logic
type Handler struct {
client *Client
}
// NewHandler creates new handler
func NewHandler(client *Client) *Handler {
return &Handler{client: client}
}
// ParseHTTPURL parses an HTTP URL and extracts information
func ParseHTTPURL(u *url.URL) (*URLInfo, error) {
if u.Scheme != "http" && u.Scheme != "https" {
return nil, fmt.Errorf("invalid http url scheme: %s", u.Scheme)
}
host := u.Hostname()
port := u.Port()
if port == "" {
if u.Scheme == "https" {
port = "443"
} else {
port = "80"
}
}
return &URLInfo{
Scheme: u.Scheme,
Host: host,
Port: port,
Path: u.Path,
Query: u.RawQuery,
Fragment: u.Fragment,
User: u.User,
}, nil
}
// GetContentType extracts Content-Type from the response
func GetContentType(resp *http.Response) string {
return resp.Header.Get("Content-Type")
}
// GetContentLength extracts Content-Length from the response
func GetContentLength(resp *http.Response) int64 {
return resp.ContentLength
}
// GetContentEncoding extracts Content-Encoding from the response
func GetContentEncoding(resp *http.Response) string {
return resp.Header.Get("Content-Encoding")
}
// GetETag extracts ETag from the response
func GetETag(resp *http.Response) string {
return resp.Header.Get("ETag")
}
// GetLastModified extracts Last-Modified from the response
func GetLastModified(resp *http.Response) string {
return resp.Header.Get("Last-Modified")
}
// SupportsRangeCheck checks whether the server supports range requests
func SupportsRangeCheck(resp *http.Response) bool {
acceptRanges := resp.Header.Get("Accept-Ranges")
return strings.ToLower(acceptRanges) == "bytes"
}
// IsRedirect checks whether the response is a redirect
func IsRedirect(statusCode int) bool {
return statusCode >= 300 && statusCode < 400
}
// GetRedirectLocation gets the Location header from a redirect response
func GetRedirectLocation(resp *http.Response) string {
return resp.Header.Get("Location")
}
// BuildURLWithQuery creates a URL with query parameters
func BuildURLWithQuery(base *url.URL, params map[string]string) (*url.URL, error) {
if base == nil {
return nil, fmt.Errorf("base url cannot be nil")
}
result := *base
query := result.Query()
for key, value := range params {
query.Set(key, value)
}
result.RawQuery = query.Encode()
return &result, nil
}
// GetUserinfo extracts authentication credentials from the URL
func GetUserinfo(u *url.URL) (username, password string, ok bool) {
if u.User == nil {
return "", "", false
}
username = u.User.Username()
password, ok = u.User.Password()
return username, password, true
}
// IsSecure checks whether the URL uses HTTPS
func IsSecure(u *url.URL) bool {
return u.Scheme == "https"
}
// GetDefaultPortForScheme returns the default port for the scheme
func GetDefaultPortForScheme(scheme string) string {
switch scheme {
case "https":
return "443"
case "http":
return "80"
default:
return ""
}
}
// ParseContentDisposition extracts filename from Content-Disposition header
func ParseContentDisposition(headerValue string) string {
if headerValue == "" {
return ""
}
_, params, err := mime.ParseMediaType(headerValue)
if err != nil {
return ""
}
// Try filename* first (RFC 5987, extended filename)
if filename, ok := params["filename*"]; ok {
return filename
}
// Fall back to filename
if filename, ok := params["filename"]; ok {
return filename
}
return ""
}
// SanitizeFilename removes path components from a filename for security
func SanitizeFilename(name string) string {
name = filepath.Base(name)
if name == "" || name == "." || name == ".." {
return ""
}
return name
}