//go:build windows package yu_curl import ( yu_http "gogs.qqck.cn/s/tools/http" yu_strings "gogs.qqck.cn/s/tools/strings" yu_sys "gogs.qqck.cn/s/tools/sys" "runtime" "strings" "unsafe" ) // Request // @Description: 请求对象 type Request struct { curl uintptr perform uintptr headers map[string]string headers_setopt bool headers_list uintptr useragent string referer string cookies map[string]string cookies_setopt bool cookies_updata bool connect_to map[string]string connect_to_setopt bool connect_to_list uintptr // -------------------------------------------------------------------resp resp_body []byte resp_headers_raw []byte resp_headers map[string]string resp_cookies map[string]string } // NewRequest // // @Description: 创建请求对象 func NewRequest() (t *Request) { t = &Request{} runtime.SetFinalizer(t, _finalizer) t.curl, _, _ = curl_easy_init.Call() curl_easy_setopt.Call(t.curl, OPT_NOSIGNAL, 1) curl_easy_setopt.Call(t.curl, OPT_SUPPRESS_CONNECT_HEADERS, 1) curl_easy_setopt.Call(t.curl, OPT_PREREQFUNCTION, callback_PREREQFUNCTION_ptr) curl_easy_setopt.Call(t.curl, OPT_PREREQDATA, unsafe.Pointer(t)) curl_easy_setopt.Call(t.curl, OPT_HEADERFUNCTION, callback_HEADERFUNCTION_ptr) curl_easy_setopt.Call(t.curl, OPT_HEADERDATA, unsafe.Pointer(t)) curl_easy_setopt.Call(t.curl, OPT_WRITEFUNCTION, callback_WRITEFUNCTION_ptr) curl_easy_setopt.Call(t.curl, OPT_WRITEDATA, unsafe.Pointer(t)) // ------------------------------------------------init t.SetRdirect(true) t.SetSslVerifyPeer(false) t.SetTimeout(60000) return } func _finalizer(t *Request) { t.Close() } // Close // // @Description: 关闭并销毁请求对象 func (t *Request) Close() { if t.curl != 0 { curl_easy_cleanup.Call(t.curl) t.curl = 0 } t.headers = nil if t.headers_list != 0 { curl_slist_free_all.Call(t.headers_list) t.headers_list = 0 } t.cookies = nil t.connect_to = nil if t.connect_to_list != 0 { curl_slist_free_all.Call(t.connect_to_list) t.connect_to_list = 0 } t.resp_body = nil t.resp_headers_raw = nil t.resp_headers = nil t.resp_cookies = nil } func (t *Request) opt() { if t.headers_setopt { if t.headers_list != 0 { curl_slist_free_all.Call(t.headers_list) t.headers_list = 0 curl_easy_setopt.Call(t.curl, OPT_HTTPHEADER, 0) } for j_key, j_val := range t.headers { t.headers_list, _, _ = curl_slist_append.Call(t.headers_list, yu_sys.S{yu_sys.Gbk, j_key + ":" + j_val}) } curl_easy_setopt.Call(t.curl, OPT_HTTPHEADER, t.headers_list) t.headers_setopt = false } if t.cookies_setopt { curl_easy_setopt.Call(t.curl, OPT_COOKIE, yu_sys.S{yu_sys.Gbk, t.GetCookies()}) t.cookies_setopt = false } if t.connect_to_setopt { if t.connect_to_list != 0 { curl_slist_free_all.Call(t.connect_to_list) t.connect_to_list = 0 } for j_host, j_to := range t.connect_to { t.connect_to_list, _, _ = curl_slist_append.Call(t.connect_to_list, yu_sys.S{yu_sys.Gbk, j_host + ":" + j_to}) } curl_easy_setopt.Call(t.curl, OPT_CONNECT_TO, t.connect_to_list) t.connect_to_setopt = false } } // -----------------------------------------------------------------------------------------------------------------------------------------------Headers // SetHeader // // @Description: 设置单个协议头 func (t *Request) SetHeader(name, value string) *Request { name = yu_http.Normalize(name) if name == "Cookie" { t.SetCookies(value) } else if name == "User-Agent" { t.SetUserAgent(value) } else if name == "Referer" { t.SetReferer(value) } else { if t.headers == nil { t.headers = make(map[string]string) } if value == "" { t.headers[name] = " " // " " 才可以删除 } else { t.headers[name] = value } t.headers_setopt = true } return t } // SetHeaders // // @Description: 清空之前的协议头并设置新的协议头 func (t *Request) SetHeaders(headers string) *Request { if headers == "" { t.headers = nil t.headers_setopt = true } else { t.headers = make(map[string]string) t.AddHeaders(headers) } return t } // AddHeaders // // @Description: 保留之前的协议头并添加新的协议头 func (t *Request) AddHeaders(headers string) *Request { if headers != "" { var j_headers []string if strings.Index(headers, "\r\n") > -1 { j_headers = strings.Split(headers, "\r\n") } else if strings.Index(headers, "\n") > -1 { j_headers = strings.Split(headers, "\n") } for j_i := 0; j_i < len(j_headers); j_i++ { j_key, j_value := yu_strings.Left(j_headers[j_i], ":"), yu_strings.EmptyLeft(yu_strings.Right(j_headers[j_i], ":")) if j_key == "" && j_value == "" { if j_header := yu_strings.EmptyRight(j_headers[j_i]); strings.HasSuffix(j_header, ";") { t.SetHeader(j_header, "") } } else if j_key != "" { t.SetHeader(j_key, j_value) } } } return t } // GetHeader // // @Description: 获取单个协议头 func (t *Request) GetHeader(name string) string { name = yu_http.Normalize(name) if name == "Cookie" { return t.GetCookies() } else if name == "User-Agent" { return t.GetUserAgent() } else if name == "Referer" { return t.GetReferer() } else { if t.headers[name] == " " { return "" } return t.headers[name] } } // GetHeaders // // @Description: 获取全部协议头 func (t *Request) GetHeaders() string { if len(t.headers) == 0 { return "" } j_headers := make([]byte, 0, 4096) for j_name, j_value := range t.headers { j_headers = append(j_headers, j_name...) j_headers = append(j_headers, ':') j_headers = append(j_headers, ' ') j_headers = append(j_headers, j_value...) j_headers = append(j_headers, 13, 10) } return string(j_headers) } // -----------------------------------------------------------------------------------------------------------------------------------------------Content-Type // SetContentType // // @Description: 设置提交的"ContentType"类型 func (t *Request) SetContentType(value string) *Request { t.SetHeader("Content-Type", value) return t } // -----------------------------------------------------------------------------------------------------------------------------------------------Headers-Utils // SetUserAgent // // @Description: 设置请求的 UserAgent func (t *Request) SetUserAgent(value string) *Request { t.useragent = value curl_easy_setopt.Call(t.curl, OPT_USERAGENT, yu_sys.S{yu_sys.Gbk, t.useragent}) return t } // GetUserAgent // // @Description: 获取请求的 UserAgent func (t *Request) GetUserAgent() string { return t.useragent } // SetReferer // // @Description: 设置来路地址 func (t *Request) SetReferer(value string) *Request { t.referer = value curl_easy_setopt.Call(t.curl, OPT_REFERER, yu_sys.S{yu_sys.Gbk, t.referer}) return t } // GetReferer // // @Description: 获取来路地址 func (t *Request) GetReferer() string { return t.referer } // SetAutoReferer // // @Description: 自动更新引用标头,启用后,会自动将 HTTP 请求中的 Referer: 标头字段设置为完整的 URL,它位于 Location: 重定向之后。默认为:false, 禁用 func (t *Request) SetAutoReferer(enable bool) *Request { curl_easy_setopt.Call(t.curl, OPT_AUTOREFERER, enable) return t } // -----------------------------------------------------------------------------------------------------------------------------------------------Cookies // SetCookie // // @Description: 设置单个 Cookie func (t *Request) SetCookie(name, value string) *Request { if value == "" { delete(t.cookies, name) } else { if t.cookies == nil { t.cookies = make(map[string]string) } t.cookies[name] = value } t.cookies_setopt = true return t } // SetCookies // // @Description: 清空之前的 Cookies 并设置新的 Cookies func (t *Request) SetCookies(value string) *Request { if value == "" { t.cookies = nil t.cookies_setopt = true } else { t.cookies = make(map[string]string) t.AddCookies(value) } return t } // AddCookies // // @Description: 保留之前的 Cookies 并增加新的 Cookies func (t *Request) AddCookies(val string) *Request { if val != "" { for _, j_cookie := range strings.Split(val, ";") { if j_key, j_val := yu_strings.Left(j_cookie, "="), yu_strings.Empty(yu_strings.Right(j_cookie, "=")); j_key != "" { t.SetCookie(j_key, j_val) } } } return t } // GetCookie // // @Description: 获取单个 Cookie func (t *Request) GetCookie(name string) string { return t.cookies[name] } // GetCookies // // @Description: 获取全部 Cookie func (t *Request) GetCookies() string { if len(t.cookies) == 0 { return "" } j_cookies := make([]byte, 0, 1024) for j_name, j_value := range t.cookies { j_cookies = append(j_cookies, j_name...) j_cookies = append(j_cookies, '=') j_cookies = append(j_cookies, j_value...) j_cookies = append(j_cookies, ';') j_cookies = append(j_cookies, ' ') } return string(j_cookies) } // SetAutoCookies // // @Description: 设置自动合并更新Cookies,初始值为"false" func (t *Request) SetAutoCookies(enable bool) *Request { t.cookies_updata = enable return t } // -----------------------------------------------------------------------------------------------------------------------------------------------Utils // SetTimeout // // @Description: 设置整个请求的超时,单位:毫秒,默认超时为 60000(60秒),设置为零这意味着它在传输过程中永远不会超时。 func (t *Request) SetTimeout(s int) *Request { if s < 0 { s = 0 } curl_easy_setopt.Call(t.curl, OPT_TIMEOUT_MS, s) return t } // SetRdirect // // @Description: 是否遵循 HTTP 3xx 重定向,默认:true func (t *Request) SetRdirect(enable bool) *Request { curl_easy_setopt.Call(t.curl, OPT_FOLLOWLOCATION, enable) return t } // SetProxy 设置代理 // // @Description: // @param proxy 格式 ProxyType://user:pass@host:port,ProxyType: http, socks4, socks4a, socks5, socks5h,设置为""以取消代理 func (t *Request) SetProxy(proxy string) *Request { curl_easy_setopt.Call(t.curl, OPT_PROXY, yu_sys.S{yu_sys.Gbk, proxy}) return t } // SetMaxConnects // // @Description: 设置的数量将是 libcurl 可以在与此句柄关联的池中缓存的同时打开的最大持久连接数。默认值为 5,更改此值没有多大意义, // 除非您完全了解它的工作原理并更改 libcurl 的行为。这涉及使用任何支持持久连接的协议的连接。 // 当达到最大限制时,curl 会关闭缓存中最早的一个,以防止增加打开的连接数。 // 如果您已经使用此 curl 句柄执行传输,则设置比以前更小的CURLOPT_MAXCONNECTS可能会导致打开的连接不必要地关闭。 // 如果将此简单句柄添加到多句柄,则不会确认此设置,您必须改为使用curl_multi_setopt和CURLMOPT_MAXCONNECTS选项。 // 默认5个 // @param s func (t *Request) SetMaxConnects(s int) *Request { if s < 0 { s = 0 } curl_easy_setopt.Call(t.curl, OPT_MAXCONNECTS, s) return t } // SetSslVerifyPeer // // @Description: 验证 SSL 证书,默认为:false func (t *Request) SetSslVerifyPeer(enable bool) *Request { curl_easy_setopt.Call(t.curl, OPT_SSL_VERIFYPEER, enable) return t } // SetSslVerifyHost // // @Description: 验证 SSL 证书中的主机名 // 指定要验证的内容。 // 此选项确定是否验证服务器证书是否适用于已知的服务器。 // 在协商TLS和SSL连接时,服务器会发送一个表明其身份的证书。 // 当为2时,该证书必须表明服务器是您要连接的服务器,否则连接失败。简单地说,这意味着它必须在证书中与您操作的URL中具有相同的名称。 // 当证书中的“通用名称”字段或“使用者备用名称”字段与您要求Curl连接的URL中的主机名匹配时,Curl认为服务器是预期的服务器。 // 如果验证值设置为1: // 在7.28.0及更早版本中:被视为某种调试选项,由于经常导致程序员错误而不再受支持。 // 从7.28.1到7.65.3:将其设置为1则无效,并保持标志不变。 // 从7.66.0开始:对1和2进行相同处理。 // 当验证值为0时,无论证书中的名称如何,连接都会成功。小心使用这个能力! // 此选项的默认值为 2。 // 此选项控制检查服务器证书声明的身份。服务器可能在撒谎。要控制说谎,请参见 "SetSslVerifyPeer"。 func (t *Request) SetSslVerifyHost(verify uintptr) *Request { curl_easy_setopt.Call(t.curl, OPT_SSL_VERIFYHOST, verify) return t } // SetHttplVersion // // @Description: 传递一个长版本,设置为下面描述的值之一。他们要求 libcurl 使用特定的 HTTP 版本。 // HTTP_VERSION_NONE // 我们不关心库使用什么版本。libcurl 将使用它认为合适的任何内容。 // // HTTP_VERSION_1_0 // 强制执行 HTTP 1.0 请求。 // // HTTP_VERSION_1_1 // 执行 HTTP 1.1 请求。 // // HTTP_VERSION_2_0 // 尝试 HTTP 2 请求。如果无法与服务器协商 HTTP 2,libcurl 将退回到 HTTP 1.1。(在 7.33.0 中添加) // // 当 libcurl 在 HTTPS 上使用 HTTP/2 时,它本身并不坚持使用 TLS 1.2 或更高版本,即使这是规范所要求的。用户可以使用CURLOPT_SSLVERSION添加此版本要求。 // 在 7.43.0 中添加了别名CURL_HTTP_VERSION_2以更好地反映实际的协议名称。 // // HTTP_VERSION_2TLS // 仅尝试通过 TLS (HTTPS) 的 HTTP 2。如果无法与 HTTPS 服务器协商 HTTP 2,libcurl 将回退到 HTTP 1.1。对于明文 HTTP 服务器,libcurl 将使用 1.1。(在 7.47.0 中添加) // // HTTP_VERSION_2_PRIOR_KNOWLEDGE // 在没有 HTTP/1.1 升级的情况下使用 HTTP/2 发出非 TLS HTTP 请求。它需要事先知道服务器直接支持 HTTP/2。HTTPS 请求仍将以标准方式执行 HTTP/2,并在 TLS 握手中协商协议版本。(在 7.49.0 中添加) // // HTTP_VERSION_3 // (在 7.66.0 中添加)此选项使 libcurl 尝试对 URL 中给定的主机使用 HTTP/3,并在需要时回退到早期的 HTTP 版本。 func (t *Request) SetHttplVersion(ver uintptr) *Request { curl_easy_setopt.Call(t.curl, OPT_HTTP_VERSION, ver) return t } // SetConnectTo // // @Description: 设置DNS转向 // @param old 要拦截的 域名:端口,数字IPv6地址必须写在[括号]内,例如:"example.com"、"example.com:443"、"17.59.98.164:"、"17.59.98.164:80"、"[240e:e9:6002:189:0:ff:b06d:69df]"、"[240e:e9:6002:189:0:ff:b06d:69df]:443" // @param new 目标 域名:端口,数字IPv6地址必须写在[括号]内,例如:"example.com"、"example.com:443"、"17.59.98.164:"、"17.59.98.164:80"、"[240e:e9:6002:189:0:ff:b06d:69df]"、"[240e:e9:6002:189:0:ff:b06d:69df]:443" func (t *Request) SetConnectTo(old, new string) *Request { if t.connect_to == nil { t.connect_to = make(map[string]string) } if new == "" { delete(t.connect_to, old) } else { t.connect_to[old] = new } t.connect_to_setopt = true return t } // CleanConnectTo // // @Description: 清空DNS转向 func (t *Request) CleanConnectTo() *Request { t.connect_to = nil t.connect_to_setopt = true return t } // SetSSLVersion // // @Description:传递一个长参数来控制要使用的 SSL/TLS 版本的版本范围。 // SSL 和 TLS 版本通常从最不安全的版本发展到越来越安全的顺序:SSL v2、SSLv3、TLS v1.0、TLS v1.1、TLS v1.2 和最新的 TLS v1 .3. // 为此目的使用可用的定义之一。可用的选项是: // // SSLVERSION_DEFAULT // 默认可接受的版本范围。自 7.39.0 起,最低可接受版本默认为 TLS v1.0(除非 TLS 库有更严格的规则)。 // // SSLVERSION_TLSv1 // # TLS v1.0 或更高版本 // // SSLVERSION_SSLv2 // # SSL v2 - 拒绝 // // SSLVERSION_SSLv3 // # SSL v3 - 拒绝 // // SSLVERSION_TLSv1_0 // TLS v1.0 或更高版本(在 7.34.0 中添加) // // SSLVERSION_TLSv1_1 // TLS v1.1 或更高版本(在 7.34.0 中添加) // // SSLVERSION_TLSv1_2 // TLS v1.2 或更高版本(在 7.34.0 中添加) // // SSLVERSION_TLSv1_3 // TLS v1.3 或更高版本(在 7.52.0 中添加) func (t *Request) SetSSLVersion(ver uintptr) *Request { curl_easy_setopt.Call(t.curl, OPT_SSLVERSION, ver) return t } // -----------------------------------------------------------------------------------------------------------------------------------------------Send // Get // // @Description: 发起Get请求 func (t *Request) Get(url string) *Request { t.SetContentType("") t.SetHeader("Content-Encoding", "") curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url}) curl_easy_setopt.Call(t.curl, OPT_HTTPGET, 1) t.opt() t.perform, _, _ = curl_easy_perform.Call(t.curl) return t } // Post // // @Description: 发起Post请求 func (t *Request) Post(url string, data []byte) *Request { curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url}) curl_easy_setopt.Call(t.curl, OPT_POST, 1) curl_easy_setopt.Call(t.curl, OPT_POSTFIELDSIZE, len(data)) if len(data) > 0 { curl_easy_setopt.Call(t.curl, OPT_COPYPOSTFIELDS, data) } t.opt() t.perform, _, _ = curl_easy_perform.Call(t.curl) return t } // PostS // // @Description: 发起Post请求 func (t *Request) PostS(url string, data string) *Request { curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url}) curl_easy_setopt.Call(t.curl, OPT_POST, 1) curl_easy_setopt.Call(t.curl, OPT_POSTFIELDSIZE, len(data)) if len(data) > 0 { curl_easy_setopt.Call(t.curl, OPT_COPYPOSTFIELDS, data) } t.opt() t.perform, _, _ = curl_easy_perform.Call(t.curl) return t } // // OpenWs // // // // @Description: 发起 Ws 请求 // func (t *Request) OpenWs(url string) *Request { // t.SetContentType("") // t.SetHeader("Content-Encoding", "") // curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url}) // curl_easy_setopt.Call(t.curl, OPT_CONNECT_ONLY, 2) // t.opt() // t.perform, _, _ = curl_easy_perform.Call(t.curl) // return t // }