m3u8d/proxy.go

102 lines
2.3 KiB
Go

package m3u8d
import (
"bufio"
"bytes"
"context"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"golang.org/x/net/proxy"
"net"
"net/http"
"net/url"
"strings"
)
func newDialContext(setProxy string) func(ctx context.Context, network, addr string) (net.Conn, error) {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
if setProxy == "" {
return (&net.Dialer{}).DialContext(ctx, network, addr)
} else if strings.HasPrefix(setProxy, "http") {
return proxyByHttp(setProxy, ctx, addr)
} else { // socks5
urlObj, err := url.Parse(setProxy)
if err != nil {
return nil, err
}
dialer, err := proxy.FromURL(urlObj, nil)
if err != nil {
return nil, err
}
return dialer.Dial(network, addr)
}
}
}
func proxyByHttp(setProxy string, ctx context.Context, to string) (net.Conn, error) {
// https://github.com/aidenesco/connect/blob/master/proxy.go
urlObj, err := url.Parse(setProxy)
if err != nil {
return nil, err
}
var tConn net.Conn
if strings.HasPrefix(setProxy, "https://") {
host := urlObj.Host
_, _, err = net.SplitHostPort(host)
if err != nil {
host = host + ":443"
}
tConn, err = (&tls.Dialer{}).DialContext(ctx, "tcp", host)
} else {
host := urlObj.Host
_, _, err = net.SplitHostPort(host)
if err != nil {
host = host + ":80"
}
tConn, err = (&net.Dialer{}).DialContext(ctx, "tcp", host)
}
if err != nil {
return nil, err
}
defer func() {
if err != nil {
tConn.Close()
}
}()
buf0 := bytes.NewBuffer(nil)
buf0.WriteString(`CONNECT ` + to + ` HTTP/1.1` + "\r\n")
if u := urlObj.User; u != nil {
username := u.Username()
password, _ := u.Password()
buf0.WriteString("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) + "\r\n")
}
buf0.WriteString("\r\n")
_, err = tConn.Write(buf0.Bytes())
if err != nil {
return nil, err
}
bufr := bufio.NewReader(tConn)
var response *http.Response
response, err = http.ReadResponse(bufr, nil)
if err != nil {
return nil, err
}
switch response.StatusCode {
case http.StatusOK:
return tConn, nil
case http.StatusProxyAuthRequired:
return nil, errors.New("connect: invalid or missing \"Proxy-Authorization\" header")
default:
return nil, fmt.Errorf("connect: unexpected CONNECT response status \"%s\" (expect 200 OK)", response.Status)
}
}