375 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
		
			8.1 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
 | |
| }
 | |
| 
 | |
| // IOBody 获取ioBody值
 | |
| func (body *Body) IOBody() 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 {
 | |
| 
 | |
| 	// Query       map[string][]string
 | |
| 	IOBody() interface{}
 | |
| 	// SetIOBody
 | |
| 	SetIOBody(iobody interface{})
 | |
| 	// Files       []UploadFile
 | |
| 	ContentType() string
 | |
| 	// AppendContent
 | |
| 	AddContentType(ct string)
 | |
| 	// AddPrefix 添加 Prefix
 | |
| 	SetPrefix(ct string)
 | |
| }
 | |
| 
 | |
| // BasicAuth 帐号认真结构
 | |
| type BasicAuth struct {
 | |
| 	// User 帐号
 | |
| 	User string
 | |
| 	// Password 密码
 | |
| 	Password string
 | |
| }
 | |
| 
 | |
| // Session 的基本方法
 | |
| type Session struct {
 | |
| 	auth *BasicAuth
 | |
| 
 | |
| 	body IBody
 | |
| 
 | |
| 	client    *http.Client
 | |
| 	cookiejar http.CookieJar
 | |
| 
 | |
| 	transport *http.Transport
 | |
| 
 | |
| 	Header http.Header
 | |
| 	Query  url.Values
 | |
| }
 | |
| 
 | |
| 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(秒为单位)
 | |
| 
 | |
| 	// CProxy 代理链接
 | |
| 	CProxy // http, https, socks5
 | |
| 
 | |
| 	// CInsecure InsecureSkipVerify
 | |
| 	CInsecure // true, false
 | |
| 
 | |
| 	// CBasicAuth 帐号认证
 | |
| 	CBasicAuth // user pwd
 | |
| 
 | |
| 	// CTLS 帐号认证
 | |
| 	CTLS // user pwd
 | |
| 
 | |
| 	// CCookiejar 持久化 CookieJar
 | |
| 	CCookiejar // true or false ; default = true
 | |
| )
 | |
| 
 | |
| // NewSession 创建Session
 | |
| func NewSession() *Session {
 | |
| 	client := &http.Client{}
 | |
| 	transport := &http.Transport{DisableCompression: true}
 | |
| 
 | |
| 	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)}
 | |
| }
 | |
| 
 | |
| // 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:
 | |
| 		// 没时间实现这些小细节
 | |
| 	case CCookiejar:
 | |
| 		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.User = v.Password
 | |
| 		case BasicAuth:
 | |
| 			ses.auth.User = v.User
 | |
| 			ses.auth.User = v.Password
 | |
| 		case []string:
 | |
| 			ses.auth.User = v[0]
 | |
| 			ses.auth.User = 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)
 | |
| }
 | |
| 
 | |
| // Cookies 返回 Cookies
 | |
| func (ses *Session) Cookies(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
 | |
| 		}
 | |
| 	}
 | |
| 	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
 | |
| }
 | |
| 
 | |
| // 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
 | |
| }
 | |
| 
 | |
| // Delete 请求
 | |
| func (ses *Session) Delete(url string) *Workflow {
 | |
| 	wf := NewWorkflow(ses, url)
 | |
| 	wf.Method = "DELETE"
 | |
| 	return wf
 | |
| }
 | |
| 
 | |
| // Head 请求
 | |
| func (ses *Session) Head(url string) *Workflow {
 | |
| 	wf := NewWorkflow(ses, url)
 | |
| 	wf.Method = "HEAD"
 | |
| 	return wf
 | |
| }
 | |
| 
 | |
| // Options 请求
 | |
| func (ses *Session) Options(url string) *Workflow {
 | |
| 	wf := NewWorkflow(ses, url)
 | |
| 	wf.Method = "OPTIONS"
 | |
| 	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()
 | |
| 	})
 | |
| }
 |