requests/session.go

398 lines
9.3 KiB
Go
Raw 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 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; 唯一前缀; 就是ContentType的第一个, ContentType(Prefix);ContentType;ContentType
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 *cookiejar.Jar
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 类型 Upload File 支持path(string) 自动转换成UploadFile
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
// CIsKeepCookiejar 发生http请求时 保持当前Cookiejar, 不被Set-Cookie更新; true of false; default false
CIsKeepCookiejar
)
// 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: cjar, Header: make(http.Header), Is: IsSetting{false}}
}
// SetConfig 设置配置, 如果使用高级方法需要了解这个可以配置的属性, 需要参考 TypeConfig
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 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
}
case CIsKeepCookiejar:
v := values.(bool)
if v {
ses.client.Jar = &notUpdateJar{ProxyJar: ses.cookiejar}
} else {
ses.client.Jar = ses.cookiejar
}
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的值, 必须符合规范 HaHa -> Haha 如果真要HaHa,只能这样 Ha-Ha
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()
})
}