You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
4.1 KiB

package assets
import (
"XProxy/next/logger"
"bytes"
"errors"
"github.com/andybalholm/brotli"
"github.com/go-http-utils/headers"
"github.com/klauspost/compress/flate"
"github.com/klauspost/compress/gzip"
"io"
"net/http"
"net/url"
"time"
)
// broltiDecode handles brolti encoding in http responses.
func broltiDecode(stream io.Reader) ([]byte, error) {
var buffer bytes.Buffer
_, err := io.Copy(&buffer, brotli.NewReader(stream))
if err != nil {
logger.Errorf("Failed to decode http responses with brolti encoding -> %v", err)
return nil, err
}
return buffer.Bytes(), nil
}
// gzipDecode handles gzip encoding in http responses.
func gzipDecode(stream io.Reader) ([]byte, error) {
reader, err := gzip.NewReader(stream)
if err != nil {
logger.Errorf("Failed to decode http responses with gzip encoding -> %v", err)
return nil, err
}
var buffer bytes.Buffer
_, err = io.Copy(&buffer, reader)
if err != nil {
logger.Errorf("Failed to handle gzip reader -> %v", err)
return nil, err
}
return buffer.Bytes(), nil
}
// deflateDecode handles deflate encoding in http responses.
func deflateDecode(stream io.Reader) ([]byte, error) {
var buffer bytes.Buffer
_, err := io.Copy(&buffer, flate.NewReader(stream))
if err != nil {
logger.Errorf("Failed to decode http responses with deflate encoding -> %v", err)
return nil, err
}
return buffer.Bytes(), nil
}
// nonDecode handles plain encoding in http responses.
func nonDecode(stream io.Reader) ([]byte, error) {
var buffer bytes.Buffer
_, err := io.Copy(&buffer, stream)
if err != nil {
logger.Errorf("Failed to read http responses -> %v", err)
return nil, err
}
return buffer.Bytes(), nil
}
// createClient build http client based on http or socks proxy url.
func createClient(remoteUrl string, proxyUrl string) (http.Client, error) {
if proxyUrl == "" {
logger.Infof("Downloading `%s` without proxy", remoteUrl)
return http.Client{}, nil
}
logger.Infof("Downloading `%s` via `%s`", remoteUrl, proxyUrl)
proxy, err := url.Parse(proxyUrl)
if err != nil {
logger.Errorf("Invalid proxy url `%s` -> %v", proxyUrl, err)
return http.Client{}, err
}
return http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxy),
},
}, nil
}
// assetDate attempts to obtain the last modification time of the remote
// file and returns nil if it does not exist or is invalid.
func assetDate(resp *http.Response) *time.Time {
date, err := http.ParseTime(resp.Header.Get(headers.LastModified))
if err != nil {
logger.Warnf("Unable to get remote data modification time")
return nil
}
logger.Debugf("Remote data modification time -> `%v`", date)
return &date
}
// download obtains resource file from the remote server, gets its
// modification time, and supports proxy acquisition.
func download(url string, proxy string) ([]byte, *time.Time, error) {
client, err := createClient(url, proxy)
if err != nil {
return nil, nil, err
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
logger.Errorf("Failed to create http request -> %v", err)
return nil, nil, err
}
req.Header.Set(headers.AcceptEncoding, "gzip, deflate, br")
resp, err := client.Do(req)
if err != nil {
logger.Errorf("Failed to execute http request -> %v", err)
return nil, nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
logger.Errorf("Download `%s` with http status code -> %d", url, resp.StatusCode)
return nil, nil, errors.New("http status code non-2xx")
}
logger.Debugf("Remote data downloaded successfully")
var content []byte
switch resp.Header.Get(headers.ContentEncoding) {
case "br":
logger.Debugf("Downloaded content using brolti encoding")
content, err = broltiDecode(resp.Body)
case "gzip":
logger.Debugf("Downloaded content using gzip encoding")
content, err = gzipDecode(resp.Body)
case "deflate":
logger.Debugf("Downloaded content using deflate encoding")
content, err = deflateDecode(resp.Body)
default:
content, err = nonDecode(resp.Body)
}
if err != nil {
return nil, nil, err
}
logger.Debugf("Download `%s` successfully -> %d bytes", url, len(content))
return content, assetDate(resp), nil
}