plugins/http/http.go

368 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package http
import (
"apigo.cloud/git/apigo/plugin"
"github.com/gorilla/websocket"
"github.com/ssgo/httpclient"
"github.com/ssgo/log"
"github.com/ssgo/u"
"net/http"
"strings"
"time"
)
type Client struct {
pool *httpclient.ClientPool
baseURL string
globalHeaders map[string]string
}
var defaultClient = Client{
pool: httpclient.GetClient(time.Duration(0) * time.Second),
globalHeaders: map[string]string{},
}
func init() {
defaultClient.pool.EnableRedirect()
plugin.Register(plugin.Plugin{
Id: "http",
Name: "HTTP客户端",
Objects: map[string]interface{}{
"new": NewHTTP,
"newH2C": NewH2CHTTP,
"newWithoutRedirect": NewHTTPWithoutRedirect,
"newH2CWithoutRedirect": NewH2CHTTPWithoutRedirect,
"setBaseURL": defaultClient.SetBaseURL,
"setGlobalHeaders": defaultClient.SetGlobalHeaders,
"get": defaultClient.Get,
"post": defaultClient.Post,
"put": defaultClient.Put,
"delete": defaultClient.Delete,
"head": defaultClient.Head,
"do": defaultClient.Do,
"manualDo": defaultClient.ManualDo,
"open": defaultClient.Open,
},
})
}
// SetBaseURL 设置一个URL前缀后续请求中可以只提供path部分
// SetBaseURL url 以http://或https://开头的URL地址
func (c *Client) SetBaseURL(url string) {
c.baseURL = url
}
// SetGlobalHeaders 设置固定的HTTP头部信息在每个请求中都加入这些HTTP头
// SetGlobalHeaders headers 传入一个Key-Value对象的HTTP头信息
func (c *Client) SetGlobalHeaders(headers map[string]string) {
c.globalHeaders = headers
}
// Get 发送GET请求
// * url 以http://或https://开头的URL地址如果设置了baseURL可以只提供path部分
// * headers 传入一个Key-Value对象的HTTP头信息如果不指定头信息这个参数可以省略不传
// * return 返回结果对象如果返回值是JSON格式将自动转化为对象否则将字符串放在.result中如发生错误将抛出异常返回的对象中还包括headers、statusCode、statusMessage
func (c *Client) Get(logger *log.Logger, url string, headers *map[string]string) (map[string]interface{}, error) {
return makeResult(logger, c.pool.Get(c.makeURL(url), c.makeHeaderArray(headers)...))
}
// Post 发送POST请求
// * body 可以传入任意类型如果不是字符串或二进制数组时会自动添加application/json头数据将以json格式发送
func (c *Client) Post(logger *log.Logger, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
return makeResult(logger, c.pool.Post(c.makeURL(url), body, c.makeHeaderArray(headers)...))
}
// Put 发送PUT请求
func (c *Client) Put(logger *log.Logger, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
return makeResult(logger, c.pool.Put(c.makeURL(url), body, c.makeHeaderArray(headers)...))
}
// Delete 发送DELETE请求
func (c *Client) Delete(logger *log.Logger, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
return makeResult(logger, c.pool.Delete(c.makeURL(url), body, c.makeHeaderArray(headers)...))
}
// Head 发送HEAD请求
func (c *Client) Head(logger *log.Logger, url string, headers *map[string]string) (map[string]interface{}, error) {
return makeResult(logger, c.pool.Head(c.makeURL(url), c.makeHeaderArray(headers)...))
}
// Do 发送请求
// * method 请求方法GET、POST等
func (c *Client) Do(logger *log.Logger, method string, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
return makeResult(logger, c.pool.Do(method, c.makeURL(url), body, c.makeHeaderArray(headers)...))
}
// ManualDo 手动处理请求需要自行从返回结果中读取数据可实现SSE客户端
// ManualDo return 应答的对象(需手动读取数据并关闭请求)
func (c *Client) ManualDo(logger *log.Logger, method string, url string, body interface{}, headers *map[string]string) (*Reader, error) {
result := c.pool.ManualDo(method, url, body, c.makeHeaderArray(headers)...)
err, outHeaders, statusCode, _ := _makeResult(logger, result)
return &Reader{
Error: err,
Headers: outHeaders,
StatusCode: statusCode,
response: result.Response,
logger: logger,
}, result.Error
}
// Open 打开一个Websocket连接
// Open return Websocket对象使用完毕请关闭连接
func (c *Client) Open(logger *log.Logger, url string, headers *map[string]string) (*WS, error) {
reqHeader := http.Header{}
if headers != nil {
for k, v := range *headers {
reqHeader.Set(k, v)
}
}
if conn, _, err := websocket.DefaultDialer.Dial(url, reqHeader); err == nil {
logger.Error(err.Error())
return &WS{conn: conn, logger: logger}, err
} else {
return nil, err
}
}
func makeResult(logger *log.Logger, result *httpclient.Result) (map[string]interface{}, error) {
err, headers, statusCode, output := _makeResult(logger, result)
if v, ok := output.(map[string]interface{}); ok {
if err != "" {
v["error"] = err
}
v["headers"] = headers
v["statusCode"] = statusCode
return v, result.Error
} else {
return map[string]interface{}{
"error": err,
"headers": headers,
"statusCode": statusCode,
"result": output,
}, result.Error
}
}
func _makeResult(logger *log.Logger, result *httpclient.Result) (err string, headers map[string]string, statusCode int, output interface{}) {
if result.Error != nil {
err = result.Error.Error()
logger.Error(result.Error.Error())
}
if result.Response != nil {
headers = map[string]string{}
for k, v := range result.Response.Header {
if len(v) == 1 {
headers[k] = v[0]
} else {
headers[k] = strings.Join(v, " ")
}
}
statusCode = result.Response.StatusCode
if strings.Contains(result.Response.Header.Get("Content-Type"), "application/json") {
output = map[string]interface{}{}
u.UnJson(result.String(), &output)
} else {
output = result.String()
}
}
return
}
//func makeResult(logger *log.Logger, result *httpclient.Result) (map[string]interface{}, error) {
// r := map[string]interface{}{}
// if result.Error != nil {
// logger.Error(result.Error.Error())
// return nil, result.Error
// }
//
// if result.Response != nil {
// headers := map[string]string{}
// for k, v := range result.Response.Header {
// if len(v) == 1 {
// headers[k] = v[0]
// } else {
// headers[k] = strings.Join(v, " ")
// }
// }
// r["headers"] = headers
// r["statusCode"] = result.Response.StatusCode
// r["statusMessage"] = result.Response.Status
//
// if strings.Contains(result.Response.Header.Get("Content-Type"), "application/json") {
// u.UnJson(result.String(), &r)
// } else {
// r["result"] = result.String()
// }
// }
// return r, nil
//}
func (c *Client) makeURL(url string) string {
if !strings.Contains(url, "://") && c.baseURL != "" {
if strings.HasSuffix(c.baseURL, "/") && strings.HasPrefix(url, "/") {
return c.baseURL + url[1:]
} else if !strings.HasSuffix(c.baseURL, "/") && !strings.HasPrefix(url, "/") {
return c.baseURL + "/" + url
}
return c.baseURL + url
}
return url
}
func (c *Client) makeHeaderArray(in *map[string]string) []string {
out := make([]string, 0)
if c.globalHeaders != nil {
for k, v := range c.globalHeaders {
out = append(out, k, v)
}
}
if in != nil {
for k, v := range *in {
out = append(out, k, v)
}
}
return out
}
// NewHTTP 创建新的HTTP客户端
// * timeout 请求的超时时间,单位(毫秒)
// NewHTTP return HTTP客户端对象
func NewHTTP(timeout int) *Client {
pool := httpclient.GetClient(time.Duration(timeout) * time.Second)
pool.EnableRedirect()
return &Client{
pool: pool,
globalHeaders: map[string]string{},
}
}
// NewHTTPWithoutRedirect 创建新的HTTP客户端不自动跟踪301和302跳转
// * timeout 请求的超时时间,单位(毫秒)
// NewHTTPWithoutRedirect return HTTP客户端对象
func NewHTTPWithoutRedirect(timeout int) *Client {
pool := httpclient.GetClient(time.Duration(timeout) * time.Second)
return &Client{
pool: pool,
globalHeaders: map[string]string{},
}
}
// NewH2CHTTP 创建新的H2C客户端
// NewH2CHTTP return H2C客户端对象
func NewH2CHTTP(timeout int) *Client {
pool := httpclient.GetClientH2C(time.Duration(timeout) * time.Second)
pool.EnableRedirect()
return &Client{
pool: pool,
globalHeaders: map[string]string{},
}
}
// NewH2CHTTPWithoutRedirect 创建新的H2C客户端不自动跟踪301和302跳转
// NewH2CHTTPWithoutRedirect return H2C客户端对象
func NewH2CHTTPWithoutRedirect(timeout int) *Client {
pool := httpclient.GetClientH2C(time.Duration(timeout) * time.Second)
return &Client{
pool: pool,
globalHeaders: map[string]string{},
}
}
type WS struct {
conn *websocket.Conn
closed bool
logger *log.Logger
}
// Read 读取文本数据
// Read return 读取到的字符串
func (ws *WS) Read() (string, error) {
_, buf, err := ws.conn.ReadMessage()
return string(buf), err
}
// ReadBytes 读取二进制数据
// ReadBytes return 读取到的二进制数据
func (ws *WS) ReadBytes() ([]byte, error) {
_, buf, err := ws.conn.ReadMessage()
return buf, err
}
// ReadJSON 读取JSON对象
// ReadJSON return 读取到的对象
func (ws *WS) ReadJSON() (interface{}, error) {
var obj interface{}
err := ws.conn.ReadJSON(&obj)
return obj, err
}
// Write 写入文本数据
// Write content 文本数据
func (ws *WS) Write(content string) error {
return ws.conn.WriteMessage(websocket.TextMessage, []byte(content))
}
// WriteBytes 写入二进制数据
// WriteBytes content 二进制数据
func (ws *WS) WriteBytes(content []byte) error {
return ws.conn.WriteMessage(websocket.BinaryMessage, content)
}
// WriteJSON 写入对象
// WriteJSON content 对象
func (ws *WS) WriteJSON(content interface{}) error {
return ws.conn.WriteJSON(content)
}
//// OnClose 关闭事件
//// OnClose callback 对方关闭时调用
//func (ws *WS) OnClose(callback func()) {
// ws.conn.SetCloseHandler(func(code int, text string) error {
// callback()
// return nil
// })
//}
// Close 关闭连接
func (ws *WS) Close() error {
if ws.closed {
return nil
}
ws.closed = true
return ws.conn.Close()
}
// EnableCompression 启用压缩
func (ws *WS) EnableCompression() {
ws.conn.EnableWriteCompression(true)
}
type Reader struct {
Error string
Headers map[string]string
StatusCode int
response *http.Response
closed bool
logger *log.Logger
}
func (hr *Reader) Read(n int) (string, error) {
buf := make([]byte, n)
n1, err := hr.response.Body.Read(buf)
return string(buf[0:n1]), err
}
func (hr *Reader) ReadBytes(n int) ([]byte, error) {
buf := make([]byte, n)
n1, err := hr.response.Body.Read(buf)
return buf[0:n1], err
}
func (hr *Reader) Close() error {
if hr.closed {
return nil
}
hr.closed = true
return hr.response.Body.Close()
}