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

}

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)
}
type Header map[string][]string

服务端

type Server struct{}
posted @ 2021-10-10 19:37  wangzhilei  阅读(211)  评论(0)    收藏  举报