requests/session.go
2019-09-05 00:50:44 +08:00

395 lines
8.9 KiB
Go

package requests
import (
"crypto/tls"
"errors"
"net/http"
"net/http/cookiejar"
"net/url"
"reflect"
"runtime"
"strings"
"time"
"golang.org/x/net/publicsuffix"
)
// Body 相关参数结构
type Body struct {
// Query map[string][]string
ioBody interface{}
// prefix ContentType 前缀
prefix string
// Files []UploadFile
contentTypes map[string]int
}
// NewBody new body pointer
func NewBody() *Body {
b := &Body{}
b.contentTypes = make(map[string]int)
return b
}
// SetIOBody 设置IOBody的值
func (body *Body) SetIOBody(iobody interface{}) {
body.ioBody = iobody
}
// GetIOBody 获取ioBody值
func (body *Body) GetIOBody() interface{} {
return body.ioBody
}
// ContentType 获取ContentType
func (body *Body) ContentType() string {
content := body.prefix
for kvalue := range body.contentTypes {
content += kvalue + ";"
}
return strings.TrimRight(content, ";")
}
// SetPrefix SetPrefix 和 AddContentType的顺序会影响到ContentType()的返回结果
func (body *Body) SetPrefix(ct string) {
body.prefix = strings.TrimRight(ct, ";") + ";"
}
// AddContentType 添加 Add Type类型
func (body *Body) AddContentType(ct string) {
for _, v := range strings.Split(ct, ";") {
v = strings.Trim(v, " ")
if v != "" {
if body.prefix != v {
body.contentTypes[v] = 1
}
}
}
}
// IBody 相关参数结构
type IBody interface {
// GetIOBody 获取iobody data
GetIOBody() interface{}
// SetIOBody 设置iobody data
SetIOBody(iobody interface{})
// ContentType 返回包括 Prefix 所有的ContentType
ContentType() string
// AppendContent
AddContentType(ct string)
// SetPrefix 设置 Prefix; 唯一前缀
SetPrefix(ct string)
}
// BasicAuth 帐号认真结构
type BasicAuth struct {
// User 帐号
User string
// Password 密码
Password string
}
// IsSetting 是否设置的一些情景
type IsSetting struct {
isDecompressNoAccept bool
}
// Session 的基本方法
type Session struct {
auth *BasicAuth
body IBody
client *http.Client
cookiejar http.CookieJar
transport *http.Transport
Header http.Header
Query url.Values
Is IsSetting
}
const (
// TypeJSON 类型
TypeJSON = "application/json"
// TypeXML 类型
TypeXML = "text/xml"
// TypePlain 类型
TypePlain = "text/plain"
// TypeHTML 类型
TypeHTML = "text/html"
// TypeURLENCODED 类型
TypeURLENCODED = "application/x-www-form-urlencoded"
// TypeForm PostForm类型
TypeForm = TypeURLENCODED
// TypeStream application/octet-stream 只能提交一个二进制流, 很少用
TypeStream = "application/octet-stream"
// TypeFormData 类型
TypeFormData = "multipart/form-data"
// TypeMixed Mixed类型
TypeMixed = "multipart/mixed"
// HeaderKeyHost Host
HeaderKeyHost = "Host"
// HeaderKeyUA User-Agent
HeaderKeyUA = "User-Agent"
// HeaderKeyContentType Content-Type
HeaderKeyContentType = "Content-Type"
)
// TypeConfig 配置类型
type TypeConfig int
const (
_ TypeConfig = iota
// CRequestTimeout request 包括 dial request redirect 总时间超时
CRequestTimeout // 支持time.Duration 和 int(秒为单位)
// CDialTimeout 一个Connect过程的Timeout
CDialTimeout // 支持time.Duration 和 int(秒为单位)
// CKeepAlives 默认KeepAlives false, 如果默认为true容易被一直KeepAlives, 没关闭链接
CKeepAlives
// CProxy 代理链接
CProxy // http, https, socks5
// CInsecure InsecureSkipVerify
CInsecure // true, false
// CBasicAuth 帐号认证
CBasicAuth // user pwd
// CTLS 帐号认证
CTLS // user pwd
// CIsWithCookiejar 持久化 CookieJar true or false ; default = true
CIsWithCookiejar
// CIsDecompressNoAccept 解压 当response header 不存在 Accept-Encoding
// 很多特殊情景会不返回Accept-Encoding: Gzip. 如 不按照标准的网站
CIsDecompressNoAccept
)
// NewSession 创建Session
func NewSession() *Session {
client := &http.Client{}
transport := &http.Transport{DisableCompression: true, DisableKeepAlives: true}
EnsureTransporterFinalized(transport)
client.Transport = transport
cjar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
panic(err)
}
client.Jar = cjar
return &Session{client: client, body: NewBody(), transport: transport, auth: nil, cookiejar: client.Jar, Header: make(http.Header), Is: IsSetting{false}}
}
// SetConfig 设置配置
func (ses *Session) SetConfig(typeConfig TypeConfig, values interface{}) {
switch typeConfig {
case CRequestTimeout:
switch v := values.(type) {
case time.Duration:
ses.client.Timeout = v
case int:
ses.client.Timeout = time.Duration(v * int(time.Second))
case int64:
ses.client.Timeout = time.Duration(v * int64(time.Second))
case float32:
ses.client.Timeout = time.Duration(v * float32(time.Second))
case float64:
ses.client.Timeout = time.Duration(v * float64(time.Second))
default:
panic(errors.New("error type " + reflect.TypeOf(v).String()))
}
case CDialTimeout:
// TODO: CDialTimeout CRequestTimeout 与 细节
case CIsDecompressNoAccept:
ses.Is.isDecompressNoAccept = values.(bool)
case CKeepAlives:
// println(ses.transport.DisableKeepAlives)
ses.transport.DisableKeepAlives = !values.(bool)
case CIsWithCookiejar:
v := values.(bool)
if v {
if ses.client.Jar == nil {
ses.client.Jar = ses.cookiejar
}
} else {
ses.client.Jar = nil
}
case CProxy:
switch v := values.(type) {
case string:
purl, err := (url.Parse(v))
if err != nil {
panic(err)
}
ses.transport.Proxy = http.ProxyURL(purl)
case *url.URL:
ses.transport.Proxy = http.ProxyURL(v)
case nil:
ses.transport.Proxy = nil
}
case CInsecure:
ses.transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: values.(bool)}
case CTLS:
ses.transport.TLSClientConfig = values.(*tls.Config)
case CBasicAuth:
if ses.auth == nil {
ses.auth = &BasicAuth{}
}
switch v := values.(type) {
case *BasicAuth:
ses.auth.User = v.User
ses.auth.Password = v.Password
case BasicAuth:
ses.auth.User = v.User
ses.auth.Password = v.Password
case []string:
ses.auth.User = v[0]
ses.auth.Password = v[1]
case nil:
ses.auth = nil
}
default:
panic(errors.New("unknown typeConfig " + reflect.TypeOf(typeConfig).String()))
}
return
}
// SetQuery 设置url query的持久参数的值
func (ses *Session) SetQuery(values url.Values) {
ses.Query = values
}
// GetQuery 获取get query的值
func (ses *Session) GetQuery() url.Values {
return ses.Query
}
// SetHeader 设置set Header的值
func (ses *Session) SetHeader(header http.Header) {
ses.Header = header
}
// GetHeader 获取get Header的值
func (ses *Session) GetHeader() http.Header {
return ses.Header
}
// SetCookies 设置Cookies 或者添加Cookies Del
func (ses *Session) SetCookies(u *url.URL, cookies []*http.Cookie) {
ses.cookiejar.SetCookies(u, cookies)
}
// GetCookies 返回 Cookies
func (ses *Session) GetCookies(u *url.URL) []*http.Cookie {
return ses.cookiejar.Cookies(u)
}
// DelCookies 删除 Cookies
func (ses *Session) DelCookies(u *url.URL, name string) {
cookies := ses.cookiejar.Cookies(u)
for _, c := range cookies {
if c.Name == name {
c.MaxAge = -1
break
}
}
ses.SetCookies(u, cookies)
}
// ClearCookies 清楚所有cookiejar上的cookies
func (ses *Session) ClearCookies() {
cjar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
panic(err)
}
ses.cookiejar = cjar
ses.client.Jar = ses.cookiejar
}
// Head 请求
func (ses *Session) Head(url string) *Workflow {
wf := NewWorkflow(ses, url)
wf.Method = "HEAD"
return wf
}
// Get 请求
func (ses *Session) Get(url string) *Workflow {
wf := NewWorkflow(ses, url)
wf.Method = "GET"
return wf
}
// Post 请求
func (ses *Session) Post(url string) *Workflow {
wf := NewWorkflow(ses, url)
wf.Method = "POST"
return wf
}
// Put 请求
func (ses *Session) Put(url string) *Workflow {
wf := NewWorkflow(ses, url)
wf.Method = "PUT"
return wf
}
// Patch 请求
func (ses *Session) Patch(url string) *Workflow {
wf := NewWorkflow(ses, url)
wf.Method = "PATCH"
return wf
}
// Options 请求
func (ses *Session) Options(url string) *Workflow {
wf := NewWorkflow(ses, url)
wf.Method = "OPTIONS"
return wf
}
// Delete 请求
func (ses *Session) Delete(url string) *Workflow {
wf := NewWorkflow(ses, url)
wf.Method = "DELETE"
return wf
}
// // CloseIdleConnections closes the idle connections that a session client may make use of
// // 从levigross/grequests 借鉴
// func (ses *Session) CloseIdleConnections() {
// ses.client.Transport.(*http.Transport).CloseIdleConnections()
// }
// EnsureTransporterFinalized will ensure that when the HTTP client is GCed
// the runtime will close the idle connections (so that they won't leak)
// this function was adopted from Hashicorp's go-cleanhttp package
func EnsureTransporterFinalized(httpTransport *http.Transport) {
runtime.SetFinalizer(&httpTransport, func(transportInt **http.Transport) {
(*transportInt).CloseIdleConnections()
})
}