mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 04:50:14 +09:00
301 lines
7.5 KiB
Go
301 lines
7.5 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"chat/globals"
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"runtime/debug"
|
|
"strings"
|
|
|
|
"github.com/goccy/go-json"
|
|
"golang.org/x/net/proxy"
|
|
)
|
|
|
|
func newClient(c []globals.ProxyConfig) *http.Client {
|
|
client := &http.Client{
|
|
Timeout: globals.HttpMaxTimeout,
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
},
|
|
}
|
|
|
|
if len(c) == 0 {
|
|
return client
|
|
}
|
|
|
|
config := c[0]
|
|
if config.ProxyType == globals.NoneProxyType {
|
|
return client
|
|
}
|
|
|
|
if config.ProxyType == globals.HttpProxyType || config.ProxyType == globals.HttpsProxyType {
|
|
proxyUrl, err := url.Parse(config.Proxy)
|
|
if len(config.Username) > 0 || len(config.Password) > 0 {
|
|
proxyUrl.User = url.UserPassword(config.Username, config.Password)
|
|
}
|
|
|
|
if err != nil {
|
|
globals.Warn(fmt.Sprintf("failed to parse proxy url: %s", err))
|
|
return client
|
|
}
|
|
client.Transport = &http.Transport{
|
|
Proxy: http.ProxyURL(proxyUrl),
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
}
|
|
} else if config.ProxyType == globals.Socks5ProxyType {
|
|
var auth *proxy.Auth
|
|
if len(config.Username) > 0 || len(config.Password) > 0 {
|
|
auth = &proxy.Auth{
|
|
User: config.Username,
|
|
Password: config.Password,
|
|
}
|
|
}
|
|
|
|
dialer, err := proxy.SOCKS5("tcp", config.Proxy, auth, proxy.Direct)
|
|
if err != nil {
|
|
globals.Warn(fmt.Sprintf("failed to create socks5 proxy: %s", err))
|
|
return client
|
|
}
|
|
|
|
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
return dialer.Dial(network, addr)
|
|
}
|
|
|
|
client.Transport = &http.Transport{
|
|
DialContext: dialContext,
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
}
|
|
}
|
|
|
|
globals.Debug(fmt.Sprintf("[proxy] configured proxy: %s", config.Proxy))
|
|
return client
|
|
}
|
|
|
|
func fillHeaders(req *http.Request, headers map[string]string) {
|
|
for key, value := range headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
}
|
|
|
|
func Http(uri string, method string, ptr interface{}, headers map[string]string, body io.Reader, config []globals.ProxyConfig) (err error) {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] %s %s\nheaders: \n%s\nbody: \n%s", method, uri, Marshal(headers), Marshal(body)))
|
|
}
|
|
|
|
req, err := http.NewRequest(method, uri, body)
|
|
if err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] failed to create request: %s", err))
|
|
}
|
|
|
|
return err
|
|
}
|
|
fillHeaders(req, headers)
|
|
|
|
client := newClient(config)
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] failed to send request: %s", err))
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if err = json.NewDecoder(resp.Body).Decode(ptr); err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] failed to decode response: %s\nresponse: %s", err, resp.Body))
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] response: %s", Marshal(ptr)))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func HttpRaw(uri string, method string, headers map[string]string, body io.Reader, config []globals.ProxyConfig) (data []byte, err error) {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] %s %s\nheaders: \n%s\nbody: \n%s", method, uri, Marshal(headers), Marshal(body)))
|
|
}
|
|
|
|
req, err := http.NewRequest(method, uri, body)
|
|
if err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] failed to create request: %s", err))
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
fillHeaders(req, headers)
|
|
|
|
client := newClient(config)
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] failed to send request: %s", err))
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if data, err = io.ReadAll(resp.Body); err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] failed to read response: %s", err))
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http] response: %s", string(data)))
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func Get(uri string, headers map[string]string, config ...globals.ProxyConfig) (data interface{}, err error) {
|
|
err = Http(uri, http.MethodGet, &data, headers, nil, config)
|
|
return data, err
|
|
}
|
|
|
|
func GetRaw(uri string, headers map[string]string, config ...globals.ProxyConfig) (data string, err error) {
|
|
buffer, err := HttpRaw(uri, http.MethodGet, headers, nil, config)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(buffer), nil
|
|
}
|
|
|
|
func Post(uri string, headers map[string]string, body interface{}, config ...globals.ProxyConfig) (data interface{}, err error) {
|
|
err = Http(uri, http.MethodPost, &data, headers, ConvertBody(body), config)
|
|
return data, err
|
|
}
|
|
|
|
func ToString(data interface{}) string {
|
|
switch v := data.(type) {
|
|
case string:
|
|
return v
|
|
case int, int8, int16, int32, int64:
|
|
return fmt.Sprintf("%d", v)
|
|
case uint, uint8, uint16, uint32, uint64:
|
|
return fmt.Sprintf("%d", v)
|
|
case float32, float64:
|
|
return fmt.Sprintf("%f", v)
|
|
case bool:
|
|
return fmt.Sprintf("%t", v)
|
|
default:
|
|
data := Marshal(data)
|
|
if len(data) > 0 {
|
|
return data
|
|
}
|
|
|
|
return fmt.Sprintf("%v", data)
|
|
}
|
|
}
|
|
|
|
func PostRaw(uri string, headers map[string]string, body interface{}, config ...globals.ProxyConfig) (data string, err error) {
|
|
buffer, err := HttpRaw(uri, http.MethodPost, headers, ConvertBody(body), config)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(buffer), nil
|
|
}
|
|
|
|
func ConvertBody(body interface{}) (form io.Reader) {
|
|
if buffer, err := json.Marshal(body); err == nil {
|
|
form = bytes.NewBuffer(buffer)
|
|
}
|
|
return form
|
|
}
|
|
|
|
func EventSource(method string, uri string, headers map[string]string, body interface{}, callback func(string) error, config ...globals.ProxyConfig) error {
|
|
// panic recovery
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
stack := debug.Stack()
|
|
globals.Warn(fmt.Sprintf("event source panic: %s (uri: %s, method: %s)\n%s", err, uri, method, stack))
|
|
}
|
|
}()
|
|
|
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http-stream] %s %s\nheaders: \n%s\nbody: \n%s", method, uri, Marshal(headers), Marshal(body)))
|
|
}
|
|
|
|
client := newClient(config)
|
|
req, err := http.NewRequest(method, uri, ConvertBody(body))
|
|
if err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http-stream] failed to create request: %s", err))
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
fillHeaders(req, headers)
|
|
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http-stream] failed to send request: %s", err))
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode >= 400 {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http-stream] request failed with status: %s\nresponse: %s", res.Status, res.Body))
|
|
}
|
|
|
|
if content, err := io.ReadAll(res.Body); err == nil {
|
|
if form, err := Unmarshal[map[string]interface{}](content); err == nil {
|
|
data := MarshalWithIndent(form, 2)
|
|
return fmt.Errorf("request failed with status: %s\n```json\n%s\n```", res.Status, data)
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("request failed with status: %s", res.Status)
|
|
}
|
|
|
|
for {
|
|
buf := make([]byte, 20480)
|
|
n, err := res.Body.Read(buf)
|
|
|
|
if err == io.EOF {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
data := string(buf[:n])
|
|
for _, item := range strings.Split(data, "\n") {
|
|
if globals.DebugMode {
|
|
globals.Debug(fmt.Sprintf("[http-stream] response: %s", item))
|
|
}
|
|
|
|
segment := strings.TrimSpace(item)
|
|
if len(segment) > 0 {
|
|
if err := callback(segment); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|