1 Star 1 Fork 1

xiaoyutab / xgotool

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
quest.go 7.99 KB
一键复制 编辑 原始数据 按行查看 历史
xiaoyutab 提交于 2024-04-09 17:53 . 1
package https
import (
"bytes"
"context"
"crypto/md5"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strconv"
"strings"
"time"
)
// 网络请求操作
// 使用GET请求
// 此处仅能使用 `Param` 进行设置请求参数
func (c *CURL) Get() *CURL {
c.hookBefore() // 请求前操作
if c.Error != nil {
return c
}
if len(c.files) > 0 {
c.Error = fmt.Errorf("文件上传暂只支持PostForm形式进行上传")
return c
}
if c.option.cache_hit {
c.EndTime = time.Now()
return c
}
// 进行GET请求
req, err := http.NewRequest("GET", c.getParamUrl("-"), nil)
if err != nil {
c.Error = err
return c
}
return c.queryBody(req)
}
// 使用POST请求
// 此处仅能使用 `Param` 进行设置请求参数
func (c *CURL) Post() *CURL {
c.
header_default("Content-Type", "application/x-www-form-urlencoded").
header_default("accept", "application/x-www-form-urlencoded").
hookBefore()
if c.Error != nil {
return c
}
if c.option.cache_hit {
c.EndTime = time.Now()
return c
}
var req *http.Request
var err error
if len(c.files) > 0 {
// 传输文件的话
bodyBuffer := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuffer)
// 读取文件
for k, v := range c.files {
_, err := os.Stat(v)
if err != nil {
c.Error = fmt.Errorf("文件 %s 未找到:%w", v, err)
return c
}
fs, err := os.Open(v)
if err != nil {
c.Error = fmt.Errorf("文件 %s 打开失败:%w", v, err)
return c
}
defer fs.Close()
fileWriter1, _ := bodyWriter.CreateFormFile(k, v)
io.Copy(fileWriter1, fs)
}
for k, v := range c.Param_quest {
err := bodyWriter.WriteField(k, v)
if err != nil {
c.Error = fmt.Errorf("参数 %s 追加失败:%w", v, err)
return c
}
}
c.HeaderKV("Content-Type", bodyWriter.FormDataContentType())
bodyWriter.Close()
req, err = http.NewRequest("POST", c.Uri, bodyBuffer)
} else {
req, err = http.NewRequest("POST", c.Uri, strings.NewReader(c.getParamUrl(".")))
}
if err != nil {
c.Error = err
return c
}
return c.queryBody(req)
}
// 使用POST请求
// 此处使用 `ParamJson` 进行设置请求参数,支持使用 `Param` 设置的参数列表
func (c *CURL) PostJson() *CURL {
c.hookBefore()
if c.Error != nil {
return c
}
if len(c.files) > 0 {
c.Error = fmt.Errorf("文件上传暂只支持PostForm形式进行上传")
return c
}
if c.option.cache_hit {
c.EndTime = time.Now()
return c
}
j, _ := json.Marshal(c.ParamJson_quest)
if len(c.ParamJson_quest) > 0 {
if v, ok := c.ParamJson_quest["_"]; ok && len(c.ParamJson_quest) == 1 {
j, _ = json.Marshal(v)
}
} else {
j, _ = json.Marshal(c.Param_quest)
}
req, err := http.NewRequest("POST", c.Uri, strings.NewReader(string(j)))
if err != nil {
c.Error = err
return c
}
return c.queryBody(req)
}
// 下载文件,将请求的内容保存到本地
//
// file 保存文件名
func (c *CURL) Download(file string) *CURL {
if c.Error != nil {
return c
}
if c.HttpCode == 0 && len(c.Body) == 0 {
// 该请求还未进行最终的GET/POST请求,所以此处直接默认为GET请求
c.Get()
}
// 写入文件
f, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
c.Error = err
return c
}
defer f.Close()
f.WriteString(c.Body)
return c
}
// 下载文件到对外IO中进行重定向
//
// f 写入对象的IO方法
func (c *CURL) DownloadIO(f io.Writer) *CURL {
if c.Error != nil {
return c
}
if c.HttpCode == 0 && len(c.Body) == 0 {
// 该请求还未进行最终的GET/POST请求,所以此处直接默认为GET请求
c.Get()
}
f.Write([]byte(c.Body))
return c
}
// 获取请求客户端标识
func (c *CURL) getClient() *http.Client {
c.StartTime = time.Now()
// 如果传入了Client选项,则直接使用传入的client项
cl := &http.Client{}
if c.option.jar != nil {
cl.Jar = c.option.jar
}
tra := http.Transport{}
// 跳过HTTPS证书
if c.option.https_continue {
tra.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
// 进行本地host域名解析
if len(c.option.hosts) > 0 {
tra.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
ls := strings.Split(addr, ":")
if addrs, ok := c.option.hosts[ls[0]]; ok {
addr = addrs + ":" + ls[1]
} else if addrs, ok := c.option.hosts[addr]; ok {
addr = addrs
}
dialer := net.Dialer{}
return dialer.DialContext(ctx, network, addr)
}
}
cl.Transport = &tra
// 设置超时时间
if c.option.time_out > 0 {
cl.Timeout = c.option.time_out
}
return cl
}
// 获取缓存下标
func (c *CURL) getKey() string {
return fmt.Sprintf("https_cache_%0x", md5.Sum([]byte(fmt.Sprintf("%T: %v, param: %T: %v, param_json: %T: %v, header: %T: %v",
c.Uri, c.Uri,
c.Param_quest, c.Param_quest,
c.ParamJson_quest, c.ParamJson_quest,
c.Header_quest, c.Header_quest,
))))
}
// 获取CURL的GET请求网址
//
// uri 要拼接的URI前缀,传入-表示使用CURL中的URI网址 ,传入.表示只返回拼接的URL参数
func (c *CURL) getParamUrl(uri string) string {
if c.Error != nil {
return ""
}
dat := url.Values{}
for i, v := range c.Param_quest {
dat.Set(i, v)
}
if c.Uri == "" {
c.Error = errors.New("网址不能为空")
return dat.Encode()
}
if uri == "-" {
uri = c.Uri
} else if uri == "." {
return dat.Encode()
}
if strings.Contains(uri, "?") {
uri += "&" + dat.Encode()
} else {
uri += "?" + dat.Encode()
}
return uri
}
// 从quest结构体中获取到数据并回写到CURL结构体中
//
// req 请求的待处理操作
func (curl *CURL) queryBody(req *http.Request) *CURL {
if curl.Error != nil {
return curl
}
req = req.WithContext(curl.option.context)
defer curl.hookEnd()
// 设置Header请求头
for i, v := range curl.Header_quest {
req.Header.Set(i, v)
}
// 执行HTTP请求
resp, err := curl.getClient().Do(req)
if err != nil {
curl.Error = err
return curl
}
defer resp.Body.Close()
// 获取相应结果
bodys, err := io.ReadAll(resp.Body)
if err != nil {
curl.Error = err
return curl
}
curl.Body = string(bodys)
curl.HttpCode = resp.StatusCode
curl.Cookie_quest = resp.Header.Values("set-cookie")
curl.Version = 1.1
if f, err := strconv.ParseFloat(fmt.Sprintf("%d.%d", resp.ProtoMajor, resp.ProtoMinor), 64); err == nil {
curl.Version = f
}
// HTTP-Code错误
if curl.HttpCode > 300 || curl.HttpCode < 200 {
curl.Error = fmt.Errorf("网络请求Code返回错误:%d", curl.HttpCode)
}
return curl
}
// 获取域名下的cookie信息
// 如果使用了jar进行cookie管理,会直接返回jar中该域名下的cookie
// 如果未使用jar进行管理,则会获取Cookie_quest的值,然后使用;=进行分割,以提取其中的key/value值
// 响应的,如果未使用jar进行管理,此处会手动循环cookie中的值进行strings切分,所以此处速度会稍慢一些
func (c *CURL) Cookie() []*http.Cookie {
u, err := url.Parse(c.Uri)
if err != nil {
return nil
}
if c.option == nil || c.option.jar == nil {
if len(c.Cookie_quest) == 0 {
return nil
}
cookie, err := cookiejar.New(nil)
if err != nil {
return nil
}
ck := []*http.Cookie{}
for i := 0; i < len(c.Cookie_quest); i++ {
// 使用;切分数组
temps := strings.Split(c.Cookie_quest[i], ";")
// 使用= 切分数组
tmp := strings.Split(temps[0], "=")
ck_one := http.Cookie{
Name: tmp[0],
Value: strings.Join(tmp[1:], "="),
}
for i := 0; i < len(temps); i++ {
if i == 0 {
continue
}
tmp := strings.Split(temps[i], "=")
switch tmp[0] {
case "expires":
t, err := time.Parse(time.RFC850, tmp[1])
if err != nil {
continue
}
ck_one.Expires = t
case "max-age":
if s, err := strconv.Atoi(tmp[1]); err == nil {
ck_one.MaxAge = int(s)
}
case "path":
ck_one.Path = tmp[1]
case "domain":
ck_one.Domain = tmp[1]
}
}
ck = append(ck, &ck_one)
}
cookie.SetCookies(u, ck)
return cookie.Cookies(u)
}
return c.option.jar.Cookies(u)
}
Go
1
https://gitee.com/xiaoyutab/xgotool.git
git@gitee.com:xiaoyutab/xgotool.git
xiaoyutab
xgotool
xgotool
v0.3.11

搜索帮助

53164aa7 5694891 3bd8fe86 5694891