net/http
客户端
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH" // RFC 5789
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
)
GET
- 普通方式
func httpGet() {
resp, err := http.Get("http://www.01happy.com/demo/accept.php?id=1&page=2")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
- ?id-=1&page=2
func main() {
addr := "http://httpbin.org/get"
resp, err := http.NewRequest(http.MethodGet, addr, nil)
if err != nil {
panic(err)
}
/*
net/url
type URL struct {
Scheme string
Opaque string // 编码后的不透明数据
User *Userinfo // 用户名和密码信息
Host string // host或host:port
Path string
RawQuery string // 编码后的查询字符串,没有'?'
Fragment string // 引用的片段(文档位置),没有'#'
}
type Values map[string][]string
*/
params := make(url.Values)
params.Add("id", "1")
params.Add("page", "2")
resp.URL.RawQuery = params.Encode()
res, err := http.DefaultClient.Do(resp)
if err != nil {
panic(err)
}
defer res.Body.Close()
result, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Printf("res:%s", result)
}
- 设置请求头
func get() {
client := &http.Client{}
url := "https://book.douban.com/"
reqest, err := http.NewRequest("GET", url, nil)
reqest.Header.Add("Referer", "https://book.douban.com/")
reqest.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1;WOW64) AppleWebKit/537.36 (KHTML,like GeCKO) Chrome/45.0.2454.85 Safari/537.36 115Broswer/6.0.3")
reqest.Header.Add("Connection", "keep-alive")
if err != nil {
panic(err)
}
//处理返回结果
resp, err := client.Do(reqest)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Println("Error status: ", resp.StatusCode)
}
result, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(result))
}
POST
form表单
- http.Post
func httpPost() {
data := make(url.Values)
data.Add("name", "wang")
data.Add("age", "18")
payload := data.Encode()
//payload:=strings.NewReader("name=cjb&age=18")
resp, err := http.Post("http://httpbin.org/post",
"application/x-www-form-urlencoded",
strings.NewReader(payload))
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
panic(err)
}
fmt.Println(string(body))
}
使用这个方法的话,第二个参数要设置成”application/x-www-form-urlencoded”,否则post参数无法传递。
- http.PostForm
func httpPostForm() {
resp, err := http.PostForm("http://httpbin.org/post",
url.Values{"key": {"Value"}, "id": {"123"}})
if err != nil {
// handle error
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
panic(err)
}
fmt.Println(string(body))
}
json
func httpJson() {
u := struct {
Name string `json:"name"`
Age int `json:"age"`
}{
Name: "wang",
Age: 18,
}
payload, _ := json.Marshal(u)
resp, err := http.Post("http://httpbin.org/post",
"application/json",
bytes.NewBuffer(payload))
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
body二进制数据流
//body提交二进制数据流
func DoBytesPost(url string, data []byte) ([]byte, error) {
body := bytes.NewReader(data)
request, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
log.Printf("http.NewRequest,[err=%s][url=%s]", err, url)
return []byte(""), err
}
request.Header.Set("Connection", "Keep-Alive")
var resp *http.Response
resp, err = http.DefaultClient.Do(request)
if err != nil {
log.Printf("http.Do failed,[err=%s][url=%s]", err, url)
return []byte(""), err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("http.Do failed,[err=%s][url=%s]", err, url)
}
return b, err
}
上传文件
- 上传协议格式
/*
边界信息,区分每个form字段和上传文件
multipart/form-data; boundary=c5b68f675c4b0c714afed89857bd5f5d4f68e717e620cbc434abcbab8f30
*/
--c5b68f675c4b0c714afed89857bd5f5d4f68e717e620cbc434abcbab8f30 //边界信息,每次随机生成
Content-Disposition: form-data; name="words"
123 //form字段和数据
--c5b68f675c4b0c714afed89857bd5f5d4f68e717e620cbc434abcbab8f30
Content-Disposition: form-data; name="uploadfield1"; filename="filename1"
Content-Type: application/octet-stream
123456789 //上传的文件内容
--c5b68f675c4b0c714afed89857bd5f5d4f68e717e620cbc434abcbab8f30
Content-Disposition: form-data; name="uploadfield2"; filename="filename2"
Content-Type: application/octet-stream
abcdefghigklmn //上传的文件内容
--c5b68f675c4b0c714afed89857bd5f5d4f68e717e620cbc434abcbab8f30-- //boundary后面跟--表示上传协议结束
func PostFile() {
body := &bytes.Buffer{}
//form
wirter := multipart.NewWriter(body)
_ = wirter.WriteField("words", "123")
//上传文件
upload1Writer, _ := wirter.CreateFormFile("uploadfield1", "filename1")
filename1, _ := os.Open("filename1")
defer filename1.Close()
io.Copy(upload1Writer, filename1)
upload2Writer, _ := wirter.CreateFormFile("uploadfield2", "filename2")
filename2, _ := os.Open("filename2")
defer filename2.Close()
io.Copy(upload2Writer, filename2)
_ = wirter.Close()
fmt.Println(body)
// fmt.Println("边界信息boundary:", wirter.FormDataContentType())
r, _ := http.Post("http://httpbin.org/post", wirter.FormDataContentType(), body)
r.Body.Close()
content, _ := ioutil.ReadAll(r.Body)
fmt.Println(content)
}
PUT
request, err := http.NewRequest(http.MethodPut, "http://httpbin.org/put", nil)
if err != nil {
panic(err)
}
res, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer res.Body.Close()
result, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Printf("%s", result)
DELETE
func del() {
request, err := http.NewRequest(http.MethodDelete, "http://httpbin.org/delete", nil)
if err != nil {
panic(err)
}
res, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer res.Body.Close()
result, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Printf("%s", result)
}
HEAD
func head() {
request, err := http.NewRequest(http.MethodHead, "https://book.douban.com/", nil)
if err != nil {
panic(err)
}
res, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer res.Body.Close()
result, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Printf("%s", result)
fmt.Println("head", res.Header)
}
重定向
返回状态码:3xx 301 302 303 307 308
- 限制重定向次数
func redirectLimitTimes() {
//限制重写向次数
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) > 10 {
return errors.New("redirect too times")
}
return nil
},
}
request, _ := http.NewRequest(
http.MethodGet,
"http://httpbin.org/redirect/20",
nil,
)
request.Header.Add("accept", "text/html")
r, err := client.Do(request)
if err != nil {
panic(err)
}
fmt.Println(ioutil.ReadAll(r.Body))
}
- 禁止重定向
func redirectForbidden() {
//禁止重定向
//登陆请求,防止重定向到首页
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse //不会跳转
// retrun nil //跳转
},
}
request, _ := http.NewRequest(http.MethodGet, "http://httpbin.org/cokkies/set?name=wang", nil)
r, err := client.Do(request)
// r, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer r.Body.Close()
fmt.Printf("%s", r.Request.URL)
}
Client
var DefaultClient = &Client{}
type Client struct {
// Transport指定执行独立、单次HTTP请求的机制。
// 如果Transport为nil,则使用DefaultTransport。
Transport RoundTripper
// CheckRedirect指定处理重定向的策略。
// 如果CheckRedirect不为nil,客户端会在执行重定向之前调用本函数字段。
// 参数req和via是将要执行的请求和已经执行的请求(切片,越新的请求越靠后)。
// 如果CheckRedirect返回一个错误,本类型的Get方法不会发送请求req,
// 而是返回之前得到的最后一个回复和该错误。(包装进url.Error类型里)
//
// 如果CheckRedirect为nil,会采用默认策略:连续10此请求后停止。
CheckRedirect func(req *Request, via []*Request) error
// Jar指定cookie管理器。
// 如果Jar为nil,请求中不会发送cookie,回复中的cookie会被忽略。
Jar CookieJar
// Timeout指定本类型的值执行请求的时间限制。
// 该超时限制包括连接时间、重定向和读取回复主体的时间。
// 计时器会在Head、Get、Post或Do方法返回后继续运作并在超时后中断回复主体的读取。
//
// Timeout为零值表示不设置超时。
//
// Client实例的Transport字段必须支持CancelRequest方法,
// 否则Client会在试图用Head、Get、Post或Do方法执行请求时返回错误。
// 本类型的Transport字段默认值(DefaultTransport)支持CancelRequest方法。
Timeout time.Duration
}
Request
type Response struct {
Status string // 例如"200 OK"
StatusCode int // 例如200
Proto string // 例如"HTTP/1.0"
ProtoMajor int // 例如1
ProtoMinor int // 例如0
// Header保管头域的键值对。
// 如果回复中有多个头的键相同,Header中保存为该键对应用逗号分隔串联起来的这些头的值
// (参见RFC 2616 Section 4.2)
// 被本结构体中的其他字段复制保管的头(如ContentLength)会从Header中删掉。
//
// Header中的键都是规范化的,参见CanonicalHeaderKey函数
Header Header
// Body代表回复的主体。
// Client类型和Transport类型会保证Body字段总是非nil的,即使回复没有主体或主体长度为0。
// 关闭主体是调用者的责任。
// 如果服务端采用"chunked"传输编码发送的回复,Body字段会自动进行解码。
Body io.ReadCloser
// ContentLength记录相关内容的长度。
// 其值为-1表示长度未知(采用chunked传输编码)
// 除非对应的Request.Method是"HEAD",其值>=0表示可以从Body读取的字节数
ContentLength int64
// TransferEncoding按从最外到最里的顺序列出传输编码,空切片表示"identity"编码。
TransferEncoding []string
// Close记录头域是否指定应在读取完主体后关闭连接。(即Connection头)
// 该值是给客户端的建议,Response.Write方法的ReadResponse函数都不会关闭连接。
Close bool
// Trailer字段保存和头域相同格式的trailer键值对,和Header字段相同类型
Trailer Header
// Request是用来获取此回复的请求
// Request的Body字段是nil(因为已经被用掉了)
// 这个字段是被Client类型发出请求并获得回复后填充的
Request *Request
// TLS包含接收到该回复的TLS连接的信息。 对未加密的回复,本字段为nil。
// 返回的指针是被(同一TLS连接接收到的)回复共享的,不应被修改。
TLS *tls.ConnectionState
}
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
Response
type Response struct {
Status string // 例如"200 OK"
StatusCode int // 例如200
Proto string // 例如"HTTP/1.0"
ProtoMajor int // 例如1
ProtoMinor int // 例如0
// Header保管头域的键值对。
// 如果回复中有多个头的键相同,Header中保存为该键对应用逗号分隔串联起来的这些头的值
// (参见RFC 2616 Section 4.2)
// 被本结构体中的其他字段复制保管的头(如ContentLength)会从Header中删掉。
//
// Header中的键都是规范化的,参见CanonicalHeaderKey函数
Header Header
// Body代表回复的主体。
// Client类型和Transport类型会保证Body字段总是非nil的,即使回复没有主体或主体长度为0。
// 关闭主体是调用者的责任。
// 如果服务端采用"chunked"传输编码发送的回复,Body字段会自动进行解码。
Body io.ReadCloser
// ContentLength记录相关内容的长度。
// 其值为-1表示长度未知(采用chunked传输编码)
// 除非对应的Request.Method是"HEAD",其值>=0表示可以从Body读取的字节数
ContentLength int64
// TransferEncoding按从最外到最里的顺序列出传输编码,空切片表示"identity"编码。
TransferEncoding []string
// Close记录头域是否指定应在读取完主体后关闭连接。(即Connection头)
// 该值是给客户端的建议,Response.Write方法的ReadResponse函数都不会关闭连接。
Close bool
// Trailer字段保存和头域相同格式的trailer键值对,和Header字段相同类型
Trailer Header
// Request是用来获取此回复的请求
// Request的Body字段是nil(因为已经被用掉了)
// 这个字段是被Client类型发出请求并获得回复后填充的
Request *Request
// TLS包含接收到该回复的TLS连接的信息。 对未加密的回复,本字段为nil。
// 返回的指针是被(同一TLS连接接收到的)回复共享的,不应被修改。
TLS *tls.ConnectionState
}
- 响应体
func responseBody(r *http.Response) {
context, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Printf("read err %s", err)
}
fmt.Printf("bodyL%s", context)
}
- 返回状态
func status(r *http.Response) {
fmt.Println(r.Status) //状态码 string
fmt.Println(r.StatusCode) //状态码 int
}
- 响应头
func header(r *http.Response) {
fmt.Printf("Content-Type:%s\n", r.Header.Get("content-type")) //Get 忽略大小写
fmt.Println("header all:", r.Header)
}
- 响应编码信息
转码
go get golang.org/x/text/transform
package main
import (
"bufio"
"fmt"
"io/ioutil"
"net/http"
"golang.org/x/net/html/charset"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
const (
Ruike1 = "https://www.ruike1.com/"
DouBanUrl = "https://book.douban.com/"
)
func main() {
client := &http.Client{}
reqest, err := http.NewRequest(http.MethodGet, DouBanUrl, nil)
if err != nil {
panic(err)
}
reqest.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1;WOW64) AppleWebKit/537.36 (KHTML,like GeCKO) Chrome/45.0.2454.85 Safari/537.36 115Broswer/6.0.3")
resp, err := client.Do(reqest)
if err != nil {
panic(err)
}
defer resp.Body.Close()
//编码检测
bufReader := bufio.NewReader(resp.Body)
e := CheckEncoding(bufReader)
//转码
bodyReader := transform.NewReader(bufReader, e.NewDecoder())
content, _ := ioutil.ReadAll(bodyReader)
fmt.Printf("%s", content)
}
func CheckEncoding(bufReader *bufio.Reader) encoding.Encoding {
/*
获取网页编码方式:
1. content-type="text/html;charset=utf-8"
2. html head meta
<meta http-equiv=Content-Type content="text/html;charset=utf-8"
3. go get golang.org/x/net/html
参数1:网页前1024字节
参数2:Content-Type
charset.DetermineEncoding()
*/
bytes, err := bufReader.Peek(1024)
if err != nil {
fmt.Println("fetch err:", err)
return unicode.UTF8
}
e, name, certain := charset.DetermineEncoding(bytes, "content-type")
// e, name, certain := charset.DetermineEncoding(bytes, "")
fmt.Println(e, name, certain)
return e
}
Cookie
type Cookie struct {
Name string
Value string
Path string
Domain string
Expires time.Time
RawExpires string
// MaxAge=0表示未设置Max-Age属性
// MaxAge<0表示立刻删除该cookie,等价于"Max-Age: 0"
// MaxAge>0表示存在Max-Age属性,单位是秒
MaxAge int
Secure bool
HttpOnly bool
Raw string
Unparsed []string // 未解析的“属性-值”对的原始文本
}
func rrCookies() {
/* r, err := http.Get("http://httpbin.org/cookies/set?name=wang&pwd=123")
if err != nil {
fmt.Println(err)
}
defer r.Body.Close()
c, _ := ioutil.ReadAll(r.Body)
fmt.Printf("%s\n", c) //因为重定向 cookies为空
*/
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
request, _ := http.NewRequest(http.MethodGet, "http://httpbin.org/cookies/set?name=wang&pwd=123", nil)
r, err := client.Do(request)
// r, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer r.Body.Close()
/* for _, c := range r.Cookies() {
fmt.Printf("%s", c)
} */
// fmt.Println(r.Request.URL.String())
secondRequest, _ := http.NewRequest(http.MethodGet, "http://httpbin.org/cookies", nil)
for _, cookie := range r.Cookies() {
secondRequest.AddCookie(cookie)
}
rr, _ := client.Do(secondRequest)
defer rr.Body.Close()
c, _ := ioutil.ReadAll(rr.Body)
fmt.Printf("%s\n", c)
}
CookieJar
type CookieJar interface {
// SetCookies管理从u的回复中收到的cookie
// 根据其策略和实现,它可以选择是否存储cookie
SetCookies(u *url.URL, cookies []*Cookie)
// Cookies返回发送请求到u时应使用的cookie
// 本方法有责任遵守RFC 6265规定的标准cookie限制
Cookies(u *url.URL) []*Cookie
}
- 会话期cookie
func jarCookie() {
jar, _ := cookiejar.New(nil)
client := &http.Client{
Jar: jar,
}
r, _ := client.Get("http://httpbin.org/cookies/set?name=wang&pwd=123")
defer r.Body.Close()
_, _ = io.Copy(os.Stdout, r.Body)
}
- 持久cookie(保存到文件)
- 实现CookieJar接口可自处理cookies
- 使用第三方库
go get github.com/juju/persistent-cookiejar
client.go
import "cookiejar2 "github.com/juju/persistent-cookiejar"
func login(jar http.CookieJar) {
// jar, _ := cookiejar.New(nil)
client := &http.Client{
Jar: jar,
}
r, _ := client.PostForm(
"http://localhost:8585/login",
url.Values{"username": {"poloxue"}, "password": {"poloxue123"}},
)
defer r.Body.Close()
_, _ = io.Copy(os.Stdout, r.Body)
}
func center(jar http.CookieJar) {
client := &http.Client{
Jar: jar,
}
r, _ := client.Get("http://localhost:8585/center")
defer r.Body.Close()
_, _ = io.Copy(os.Stdout, r.Body)
}
func main() {
jar, _ := cookiejar2.New(&cookiejar2.Options{})
login(jar)
center(jar)
jar.Save()
}
默认保存在~/.go-cookies
cat ~/.go-cookies
[{"Name":"isLogin","Value":"1","Domain":"localhost","Path":"/","Secure":false,"HttpOnly":false,"Persistent":true,"HostOnly":true,"Expires":"2021-10-14T10:45:51Z","Creation":"2021-10-14T15:45:51.136921871+08:00","LastAccess":"2021-10-14T15:45:55.884299197+08:00","Updated":"2021-10-14T15:45:51.136921871+08:00","CanonicalHost":"localhost"}]
developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
server.go
package main
import (
"net/http"
"time"
)
func login(w http.ResponseWriter, req *http.Request) {
username := req.PostFormValue("username")
password := req.PostFormValue("password")
if username == "poloxue" && password == "poloxue123" {
http.SetCookie(w, &http.Cookie{
Name: "isLogin",
Value: "1",
Expires: time.Now().Add(3 * time.Hour),
})
_, _ = w.Write([]byte("登录成功\n"))
} else {
_, _ = w.Write([]byte("登录失败"))
}
return
}
func center(w http.ResponseWriter, r *http.Request) {
isLogin, err := r.Cookie("isLogin")
if err == http.ErrNoCookie {
_, _ = w.Write([]byte("无法访问"))
w.WriteHeader(http.StatusUnauthorized)
return
}
if isLogin.Value != "1" {
_, _ = w.Write([]byte("无法访问"))
w.WriteHeader(http.StatusUnauthorized)
return
}
_, _ = w.Write([]byte("个人主页\n"))
}
func main() {
http.HandleFunc("/login", login)
http.HandleFunc("/center", center)
_ = http.ListenAndServe(":8585", nil)
}
文件下载与进度
- 文件下载
func downloadFile(url, filename string) {
r, err := http.Get(url)
if err != nil {
panic(err)
}
defer r.Body.Close()
f, err := os.Create(filename)
if err != nil {
panic(err)
}
defer f.Close()
n, err := io.Copy(f, r.Body)
fmt.Println(n, err)
}
- 下载进度
func main(){
addr := "http://windows.agao.pro/down.php/local/Windows11_x64.iso"
filename := "Windows11_x64.iso"
downloadFileProgress(addr, filename)
}
type Reader struct {
io.Reader
Total int64
Current int64
}
func (r *Reader) Read(p []byte) (n int, err error) {
n, err = r.Reader.Read(p)
r.Current += int64(n)
fmt.Printf("\r进度:%.2f%%", float64(r.Current*10000/r.Total)/100)
// \r 每打印一串字符在回到行首继续打印
return
}
func downloadFileProgress(url, filename string) {
r, err := http.Get(url)
if err != nil {
panic(err)
}
defer r.Body.Close()
f, err := os.Create(filename)
if err != nil {
panic(err)
}
defer f.Close()
reader := &Reader{
Reader: r.Body,
Total: r.ContentLength,
}
n, err := io.Copy(f, reader)
fmt.Println(n, err)
}
客户端超时设置
type Transport struct {
// Proxy指定一个对给定请求返回代理的函数。
// 如果该函数返回了非nil的错误值,请求的执行就会中断并返回该错误。
// 如果Proxy为nil或返回nil的*URL置,将不使用代理。
Proxy func(*Request) (*url.URL, error)
// Dial指定创建TCP连接的拨号函数。如果Dial为nil,会使用net.Dial。
Dial func(network, addr string) (net.Conn, error)
// TLSClientConfig指定用于tls.Client的TLS配置信息。
// 如果该字段为nil,会使用默认的配置信息。
TLSClientConfig *tls.Config
// TLSHandshakeTimeout指定等待TLS握手完成的最长时间。零值表示不设置超时。
TLSHandshakeTimeout time.Duration
// 如果DisableKeepAlives为真,会禁止不同HTTP请求之间TCP连接的重用。
DisableKeepAlives bool
// 如果DisableCompression为真,会禁止Transport在请求中没有Accept-Encoding头时,
// 主动添加"Accept-Encoding: gzip"头,以获取压缩数据。
// 如果Transport自己请求gzip并得到了压缩后的回复,它会主动解压缩回复的主体。
// 但如果用户显式的请求gzip压缩数据,Transport是不会主动解压缩的。
DisableCompression bool
// 如果MaxIdleConnsPerHost!=0,会控制每个主机下的最大闲置连接。
// 如果MaxIdleConnsPerHost==0,会使用DefaultMaxIdleConnsPerHost。
MaxIdleConnsPerHost int
// ResponseHeaderTimeout指定在发送完请求(包括其可能的主体)之后,
// 等待接收服务端的回复的头域的最大时间。零值表示不设置超时。
// 该时间不包括获取回复主体的时间。
ResponseHeaderTimeout time.Duration
//TCP握手超时
DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
//TLS握手超时时间
TLSHandshakeTimeout time.Duratio
//接收响应头超时时间
ResponseHeaderTimeout time.Duration
//空闲连接超时时间
IdleConnTimeout time.Duration
// 内含隐藏或非导出字段
}
func transport() {
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, 2*time.Second)
},
ResponseHeaderTimeout: 5 * time.Second,
TLSHandshakeTimeout: 2 * time.Second,
IdleConnTimeout: 60 * time.Second,
},
}
r, _ := client.Get("http://httpbin.org/delay/10")
defer r.Body.Close()
_, _ = io.Copy(os.Stdout, r.Body)
}
http代理
func proxyurl() {
proxyUrl, _ := url.Parse("http://127.0.0.1:8585")
t := &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
}
client := http.Client{Transport: t}
r, err := client.Get("https://google.com")
if err != nil {
fmt.Println(err)
}
defer r.Body.Close()
_, _ = io.Copy(os.Stdout, r.Body)
}
header
type Header map[string][]string
服务端
type Server struct{}
随机推荐
- 【windows】 进程间通信 WM_COPYDATA消息
WM_COPYDATA消息可以实现window应用程序之间的数据传输,是同步传输方式. 需要用到结构体 COPYDATASTRUCT typedef struct tagCOPYDATASTRUCT ...
- nnlog 日志模块
python自带的logging模块理解起来比较费劲,直接pip install nnlog即可 1 import nnlog 2 log = nnlog.Logger(file_name='my.l ...
- 何同学新视频火了!找到减少沉迷手机的最佳方法:附免费APP
以优质原创视频吸引百万粉丝的 Up 主"何同学"昨晚(1 月 6 日)上线了最新作品,探讨了如何有效地减少现代人使用或者说沉迷手机的时间. 在视频开头,何同学提到,整理了 5000 ...
- moment 时区问题
moment.parseZone('2023-01-11T16:54:31.6864601').utc().format('YYYY-MM-DD HH:mm:ss') 文档 | Momen ...
- [转]C#的反射
一.反射简介 1.1.反射的说明 反射:指程序可以访问.检测和修改它本身状态或行为的一种能力(即:审查元数据并收集关于它的类型信息的能力). ①元数据(编译以后的最基本数据单元:指程序和程序类型本身的 ...
- [js函数] shallowEqual
const isBasicType = (t: any) => { return t === "number" || t === "string" || ...
- BUUCTF-[GXYCTF2019]Ping Ping Ping
一道命令执行题目 一.基础知识 Linux shell特殊字符(参考链接) [;]作为多个命令语句的分隔符(Command separator [semicolon]). 要在一个语句里面执行 ...
- Win10下VM虚拟机桥接模式无法上网的相关问题?
首先:在LR中点击虚拟机->设置,勾选桥接模式,勾选之后无法上网 1. 选中虚拟网卡VMnet8(NAT模式)和WLAN(本机)网卡,右键选择"桥接",然后系统创建一个 ...
- grep 查找字符串 在文件或者文件夹中
1, 命令行能做的事情很多, grep 'XXX' ./access.log 当前某个文件下下查找某个字符串grep 'xxx' ./ -r 当前目录文件夹下查找某个字符串
- vue html转pdf并打印
//文件名随便取一个如:htmlToPdf.js // 导出页面为PDF格式 import html2Canvas from 'html2canvas' import JsPDF from 'jspd ...