客户端

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(保存到文件)
  1. 实现CookieJar接口可自处理cookies
  2. 使用第三方库

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{}

最新文章

  1. Unicode 和 UTF-8 有何区别?
  2. POJ 2226二分图最大匹配
  3. POJ 1840 Eqs 二分+map/hash
  4. Android开发之模拟器的选择
  5. Ansible安装配置及使用
  6. 1011. World Cup Betting (20)(最大值)
  7. WPF之Treeview控件简单用法
  8. XMPP个人信息展示
  9. oracle 管理
  10. Android 性能优化——之控件的优化
  11. cowboy源码分析(一)
  12. mysql常用运算符
  13. 阿里云.log
  14. netstat实现原理
  15. yaf
  16. Jenkins持续集成学习-Windows环境进行.Net开发1
  17. EJB3.0中的session bean以及MDB解析
  18. jenkins管理
  19. GNU和GPL的区别/关系
  20. mysql 数据库关于增加用户权限的问题

热门文章

  1. TM1621断码液晶驱动IC的原理、驱动代码
  2. drools规则的入门使用
  3. python菜鸟学习: 12. 装饰器的中级用法
  4. db2存储过程 动态拼接sql 、输出数据集示例
  5. OS-lab2
  6. 第八章用matplotlib、seaborn、pyecharts绘制散点图
  7. AndroidQ 打通应用层到HAL层(转)
  8. Springboot+thymeleaf结合Vue,通过thymeleaf给vue赋值解决Vue的SEO问题
  9. 【Java学习Day10】类型转换
  10. debian11用iso制作本地apt源