102 lines
2.3 KiB
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)
|
||
|
|
}
|
||
|
|
}
|