├── README.md ├── examples ├── httpassembly ├── url3.go ├── url1.go ├── http1.go ├── url2.go ├── net2.go ├── net1.go ├── url4.go ├── jspcap.go ├── client_https.go ├── server.crt ├── cmd_key.txt ├── http2.go ├── server_https.go ├── http3.go ├── server.key ├── http4.go ├── postfile.go └── httpassembly.go ├── docs ├── index.md ├── url.md ├── gin.md ├── requests.md ├── net.md └── http.md ├── mkdocs.yml ├── type.go └── requests ├── requests_test.go └── requests.go /README.md: -------------------------------------------------------------------------------- 1 | # goexample 2 | -------------------------------------------------------------------------------- /examples/httpassembly: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asmcos/goexample/HEAD/examples/httpassembly -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ## 阅读并理解 2 | 3 | 阅读笔记主要针对 golang 源代码的 net代码 4 | 5 | [源代码](https://github.com/golang/go) 的src/net目录 6 | 7 | 在启动项目的时候,我找到了 一个golang 标准库项目 [godcoc.org](https://godoc.org/net) 8 | 9 | 中文 地址 [https://studygolang.com/pkgdoc](https://studygolang.com/pkgdoc) 10 | 11 | 所以我大部分思路就是抄袭,并理解前辈的文档。 12 | -------------------------------------------------------------------------------- /examples/url3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | ) 7 | 8 | func main(){ 9 | 10 | u := new(url.URL) 11 | 12 | u.Scheme = "https" 13 | u.Host = "google.com" 14 | q := u.Query() 15 | q.Set("q", "golang") 16 | u.RawQuery = q.Encode() 17 | fmt.Println(u) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: golang 部分代码解析 2 | pages: 3 | - 开始: index.md 4 | - net: net.md 5 | - net/url: url.md 6 | - net/http: http.md 7 | - requests: requests.md 8 | - gin: gin.md 9 | 10 | theme: 11 | name: 'material' 12 | markdown_extensions: 13 | - admonition 14 | - codehilite 15 | -------------------------------------------------------------------------------- /examples/url1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "fmt" 6 | "net/url" 7 | ) 8 | 9 | func main (){ 10 | u, err := url.Parse("http://example.com/path with spaces") 11 | if err != nil { 12 | // log.Fatal(err) 13 | } 14 | fmt.Println(u.EscapedPath()) 15 | 16 | fmt.Println(u.Path) 17 | fmt.Println(u.RawPath) 18 | fmt.Println(u.RawQuery) 19 | } 20 | -------------------------------------------------------------------------------- /examples/http1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "fmt" 7 | ) 8 | 9 | func main (){ 10 | 11 | resp, err := http.Get("https://cn.bing.com/") 12 | if err != nil { 13 | // handle error 14 | } 15 | defer resp.Body.Close() 16 | body, err := ioutil.ReadAll(resp.Body) 17 | // ... 18 | _ = string(body) 19 | fmt.Println(resp) 20 | 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /examples/url2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/url" 7 | ) 8 | 9 | func main() { 10 | 11 | // Parse + String preserve the original encoding. 12 | u, err := url.Parse("https://example.com/搜索") 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | fmt.Println(u.Path) //解码存储 17 | fmt.Println(u.RawPath) //原始存储 18 | fmt.Println(u.String()) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /type.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | var print = fmt.Println 9 | 10 | 11 | func typeof(i interface{}) string{ 12 | return fmt.Sprintf("%T",i) 13 | } 14 | 15 | 16 | func main (){ 17 | 18 | var obj interface {} 19 | 20 | obj = [] string{"1","2"} 21 | 22 | print(reflect.TypeOf(obj)) 23 | print(typeof(obj)) 24 | 25 | obj = map[string]string{"a":"b","c":"1"} 26 | print(reflect.TypeOf(obj)) 27 | 28 | print(typeof(obj)) 29 | return 30 | } 31 | -------------------------------------------------------------------------------- /examples/net2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "fmt" 6 | ) 7 | 8 | func handleConnection(conn net.Conn) { 9 | buf := make([]byte,2000) 10 | 11 | n,_ := conn.Read(buf) 12 | fmt.Println(conn.RemoteAddr().String()) 13 | fmt.Println(n) 14 | } 15 | 16 | func main (){ 17 | ln, err := net.Listen("tcp", ":8080") 18 | if err != nil { 19 | // handle error 20 | } 21 | for { 22 | conn, err := ln.Accept() 23 | if err != nil { 24 | // handle error 25 | } 26 | fmt.Println(conn) 27 | go handleConnection(conn) 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /examples/net1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "fmt" 6 | "time" 7 | // "bufio" 8 | ) 9 | 10 | func main (){ 11 | 12 | time.Sleep(10*time.Second) 13 | _, err := net.Dial("tcp", "go.xiulian.net.cn:80") 14 | if err != nil { 15 | // handle error 16 | } 17 | 18 | 19 | _, err1 := net.Dial("tcp", "go.xiulian.net.cn:80") 20 | if err1 != nil { 21 | fmt.Println( "handle error" ) 22 | } 23 | 24 | //fmt.Println(conn) 25 | //fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n") 26 | //status, _ := bufio.NewReader(conn).ReadString('\n') 27 | 28 | //fmt.Println(status) 29 | return 30 | } 31 | 32 | -------------------------------------------------------------------------------- /examples/url4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/url" 7 | ) 8 | 9 | func main() { 10 | 11 | // Parse + String preserve the original encoding. 12 | u, err := url.Parse("https://example.com/搜索") 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | fmt.Println(u.Path) //解码存储 17 | fmt.Println(u.RequestURI()) //压缩存储 18 | fmt.Println(u.String()) 19 | 20 | u, err = url.Parse("https://example.com/%E6%90%9C%E7%B4%A2") 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | fmt.Println(u.Path) //解码存储 25 | fmt.Println(u.RequestURI()) //压缩存储 26 | fmt.Println(u.String()) 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /examples/jspcap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/google/gopacket" 6 | "github.com/google/gopacket/pcap" 7 | "log" 8 | "time" 9 | ) 10 | var ( 11 | device string = "en0" 12 | snapshot_len int32 = 1024 13 | promiscuous bool = false 14 | err error 15 | timeout time.Duration = 30 * time.Second 16 | handle * pcap.Handle 17 | ) 18 | 19 | func main (){ 20 | handle,err = pcap.OpenLive(device,snapshot_len,promiscuous,timeout) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | defer handle.Close() 25 | 26 | packetSource := gopacket.NewPacketSource(handle,handle.LinkType()) 27 | 28 | for packet := range packetSource.Packets(){ 29 | fmt.Println(packet) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /examples/client_https.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "github.com/asmcos/tlsdump" 6 | ) 7 | 8 | func main() { 9 | log.SetFlags(log.Lshortfile) 10 | 11 | conf := &tlsdump.Config{ 12 | InsecureSkipVerify: true, 13 | } 14 | 15 | conn, err := tlsdump.Dial("tcp", "127.0.0.1:443", conf) 16 | if err != nil { 17 | log.Println(err) 18 | return 19 | } 20 | defer conn.Close() 21 | 22 | n, err := conn.Write([]byte("hello\n")) 23 | if err != nil { 24 | log.Println(n, err) 25 | return 26 | } 27 | 28 | buf := make([]byte, 100) 29 | n, err = conn.Read(buf) 30 | if err != nil { 31 | log.Println(n, err) 32 | return 33 | } 34 | 35 | println(string(buf[:n])) 36 | } 37 | 38 | -------------------------------------------------------------------------------- /examples/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpDCCAYwCCQDFM2h30uzzADANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAkx 3 | MjcuMC4wLjEwHhcNMTgwOTMwMDMxNTQ4WhcNMjgwOTI3MDMxNTQ4WjAUMRIwEAYD 4 | VQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0 5 | su8Iyp9mKEsjRB5pE6fOzk4Czj9iE4NAHr0PYr8p2u5e13VnzXmevRdI9SBWJXoU 6 | vAu8mmWxRMgv3S6rDSOnSprWq/4deFAP+9sDeSypXIZOqEFKYn55EWudnDE/BGPj 7 | Az9lrQgal3Tv9v855P9AcwGyPskFdWkrRfzviIZo4DBoMdvEg0xvJTXyBREu7zwb 8 | Lfg+pV8X9YlvwmhyfgbPCPJq290PpdezWcZ8rhotAc9JP13MCISdIjxYuFRkcplP 9 | eTmessWgbSQwz/TSf/IKMzXDdlZl9yMxplmB1AVOqffb9XzqI/UQjTJGH0/55ZXn 10 | cBSoY3fdNtSFghsxu4RFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGJN4m1Lajuy 11 | RG0JOEi3pGe1NTEY9pDjsKhZBK9mMx5P6PYbHb0p4+KPATj2p9VjkX7qo+kxaoHe 12 | x1osXU7KyFLnCoLYGxNC5rPoW0J44rxCLsxq/VdePPGMGhaG9EoYx/t97PHKUXSJ 13 | VnKu7ilZkGcdCGB09b2Xs/G6ps7wvM+6R2b+qu25KgMSkXLupVkkrgd5PgdM5HBm 14 | SD+vB+bLIBxY4RawW92OjdoV5yQ/X04fa2u9SOg7jDB/ms8G0t0iIJ2K9WyC9C7W 15 | O33Ml7xKam4unM47SkdEozTSK3cR8thNwoXDfF8BIM4561oaz5x6TKcxgQ7jKyDV 16 | jHO4qdfWmOg= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /examples/cmd_key.txt: -------------------------------------------------------------------------------- 1 | jiashengtekiMacBook-Air:examples jiashenghe$ openssl genrsa -out server.key 2048 2 | Generating RSA private key, 2048 bit long modulus 3 | ...............................................................+++ 4 | .........................+++ 5 | e is 65537 (0x10001) 6 | jiashengtekiMacBook-Air:examples jiashenghe$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 7 | You are about to be asked to enter information that will be incorporated 8 | into your certificate request. 9 | What you are about to enter is what is called a Distinguished Name or a DN. 10 | There are quite a few fields but you can leave some blank 11 | For some fields there will be a default value, 12 | If you enter '.', the field will be left blank. 13 | ----- 14 | Country Name (2 letter code) []: 15 | State or Province Name (full name) []: 16 | Locality Name (eg, city) []: 17 | Organization Name (eg, company) []: 18 | Organizational Unit Name (eg, section) []: 19 | Common Name (eg, fully qualified host name) []:127.0.0.1 20 | Email Address []: 21 | 22 | -------------------------------------------------------------------------------- /examples/http2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | //"io/ioutil" 5 | "net/http" 6 | "fmt" 7 | "log" 8 | "crypto/tls" 9 | ) 10 | 11 | func main (){ 12 | 13 | //proxyString := "http://127.0.0.1:8888" 14 | //proxyUrl, _ := url.Parse(proxyString) 15 | 16 | tr := &http.Transport{ 17 | //Proxy: http.ProxyURL(proxyUrl), 18 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 19 | } 20 | 21 | client := &http.Client{} 22 | client.Transport = tr 23 | 24 | request, err := http.NewRequest("HEAD", "http://cn.bing.com", nil) 25 | request.Header.Set("User-Agent", "Golang requests") 26 | //request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") 27 | 28 | resp, err := client.Do(request) 29 | 30 | if err != nil { 31 | log.Fatalln(err) 32 | return 33 | } 34 | defer resp.Body.Close() 35 | 36 | fmt.Println(resp) 37 | 38 | 39 | /* 40 | resp, err := http.Head("https://cn.bing.com/") 41 | if err != nil { 42 | // handle error 43 | } 44 | defer resp.Body.Close() 45 | fmt.Println(resp) 46 | */ 47 | return 48 | } 49 | -------------------------------------------------------------------------------- /examples/server_https.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "github.com/asmcos/tlsdump" 6 | "net" 7 | "bufio" 8 | ) 9 | 10 | func main() { 11 | log.SetFlags(log.Lshortfile) 12 | 13 | cer, err := tlsdump.LoadX509KeyPair("server.crt", "server.key") 14 | if err != nil { 15 | log.Println(err) 16 | return 17 | } 18 | 19 | config := &tlsdump.Config{Certificates: []tlsdump.Certificate{cer}} 20 | ln, err := tlsdump.Listen("tcp", ":443", config) 21 | if err != nil { 22 | log.Println(err) 23 | return 24 | } 25 | defer ln.Close() 26 | 27 | for { 28 | conn, err := ln.Accept() 29 | if err != nil { 30 | log.Println(err) 31 | continue 32 | } 33 | go handleConnection(conn) 34 | } 35 | } 36 | 37 | func handleConnection(conn net.Conn) { 38 | defer conn.Close() 39 | r := bufio.NewReader(conn) 40 | for { 41 | msg, err := r.ReadString('\n') 42 | if err != nil { 43 | log.Println(err) 44 | return 45 | } 46 | 47 | println(msg) 48 | 49 | n, err := conn.Write([]byte("world\n")) 50 | if err != nil { 51 | log.Println(n, err) 52 | return 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/http3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | // "crypto/tls" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "net/url" 10 | ) 11 | 12 | func main() { 13 | 14 | //creating the proxyURL 15 | 16 | proxyStr := "http://localhost:8888" 17 | proxyURL, err := url.Parse(proxyStr) 18 | if err != nil { 19 | log.Println(err) 20 | } 21 | 22 | //creating the URL to be loaded through the proxy 23 | urlStr := "http://cn.bing.com" 24 | url, err := url.Parse(urlStr) 25 | if err != nil { 26 | log.Println(err) 27 | } 28 | 29 | //adding the proxy settings to the Transport object 30 | transport := &http.Transport{ 31 | Proxy: http.ProxyURL(proxyURL), 32 | //TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 33 | } 34 | 35 | //adding the Transport object to the http Client 36 | client := &http.Client{ 37 | Transport: transport, 38 | } 39 | 40 | fmt.Println(client.Transport) 41 | //generating the HTTP GET request 42 | request, err := http.NewRequest("GET", url.String(), nil) 43 | if err != nil { 44 | log.Println(err) 45 | } 46 | request.Header.Set("User-Agent", "Golang requests") 47 | //request.Header.Set("Accept-Encoding", "gzip") 48 | //calling the URL 49 | response, err := client.Do(request) 50 | if err != nil { 51 | log.Println(err) 52 | } 53 | 54 | //getting the response 55 | data, err := ioutil.ReadAll(response.Body) 56 | if err != nil { 57 | log.Println(err) 58 | } 59 | //printing the response 60 | log.Println(string(data)) 61 | fmt.Println(response) 62 | } 63 | -------------------------------------------------------------------------------- /examples/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAtLLvCMqfZihLI0QeaROnzs5OAs4/YhODQB69D2K/KdruXtd1 3 | Z815nr0XSPUgViV6FLwLvJplsUTIL90uqw0jp0qa1qv+HXhQD/vbA3ksqVyGTqhB 4 | SmJ+eRFrnZwxPwRj4wM/Za0IGpd07/b/OeT/QHMBsj7JBXVpK0X874iGaOAwaDHb 5 | xINMbyU18gURLu88Gy34PqVfF/WJb8Jocn4GzwjyatvdD6XXs1nGfK4aLQHPST9d 6 | zAiEnSI8WLhUZHKZT3k5nrLFoG0kMM/00n/yCjM1w3ZWZfcjMaZZgdQFTqn32/V8 7 | 6iP1EI0yRh9P+eWV53AUqGN33TbUhYIbMbuERQIDAQABAoIBAE1wWYxU+ZbYMcs9 8 | SCMT71hrhXciWHt5jUgPZEqRsQcI8hrnD0ObBUIIG1CC7Ia+rODx7b24FViw93G7 9 | wmI/0UXCbTUOpGqQww72ilekuIUkR9KxvLlaGIVd99alt10gToZdP/AdBVoQCmdE 10 | XQC8qL9ACjwPOCsEtDt9VPZ6dc+/e2DGoB6VnBaBAcTVri2qFkzo1ISlt4ebrgep 11 | q8F1oxQagcBjdR96rgQ9HT5mV005m4J7uDod2z/DHN0HPr4Zj/0/3PZTz3dzCYxw 12 | spoyMA3DQ/mwRPyqdv9DE3I3gTRjR5Q144j6fA/uJlKMLdYhb7lZf+AOl1Y5KUsp 13 | DnGgs4kCgYEA5+BvHEGvUj2QLRotyxq48Yj8hbUqS2jyVGcT+j1Mz6GvG+IMACsv 14 | GIAUnx/8BN4f72cxn3nEES8lEtXUyGXNhk0zYXVtz+BsU1Ph/aBfPrdALF11jK90 15 | Saf7XpXBTp6tUQ5j062K2BbWdik1nVr531m7XnwV9lkiPpD2JyWEgX8CgYEAx398 16 | AjPb/gVYzRG+WC/h41O6UkZyq98cGBKn34Nwk8ADSNzzCE6eCVm00MZ8i5Ym+gq2 17 | hqY26Xh3u60I9NJTWX7t1FAgy0gQs/3sUMATTKLt6qLesTQjHD4o4uuJKEvKvShE 18 | RznPC6KcOfuaRHABCw8y3kz8AaCDpUiVIGBMVDsCgYEAqhkN6SCWSDI5LZzisver 19 | bCQdeshnemJGQnxLP2rwifAF0OKGAymqpsJuXjzMV5XKv4hn8qEUn7aB78tFCK0F 20 | k9vWMUw/fhsX3BjQ04lt1UgAn4r/zHB5Uf8Ue1JIODBW2/s+Z+4KyHIYaR3Z4tD2 21 | NE5TEQDd65V/dX5J1HXFOmkCgYEAoyuXXynRfqQa7Kfdmr2XMVsTIyYxtnfmv53K 22 | ppsYIhDD3v2w3hOHWTiBFt2xkm3+dTb78YfTlAQPUnaFw9Cfkayqk6PyHrJTzxQa 23 | DDX90wnUNLyADkoJiZkh0fA6H7ZWHX7o0aTrvqWCyD4NAHrnr4b/4SjwWdhStIZ4 24 | SqHY7G8CgYA5cCVAM7NamjVWyzvbrrwe9tQE2YtCDxxDXl9/OyzbE2V6WtIp9YvX 25 | MO54CqCTANQL4pVMm+saPdvT3eUr2455Eb4y7TzluwxNIOrwf7I39gmIs+SlycpN 26 | q98qemx6+hNtkCQKpXrUKFIFn3oh16gadtlkPY1Hpweqdd5QnQHqJA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /examples/http4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "net/http/httputil" 9 | "net/url" 10 | ) 11 | 12 | type Experiment struct { 13 | URL string 14 | ExtraHeaders map[string]string 15 | } 16 | 17 | func (e Experiment) Describe() string { 18 | s := e.URL 19 | for k, v := range e.ExtraHeaders { 20 | s += fmt.Sprintf(" %s=%s", k, v) 21 | } 22 | return s 23 | } 24 | 25 | func main() { 26 | /* addAcceptEncodingHeader := map[string]string{ 27 | "Accept-Encoding": "gzip", 28 | }*/ 29 | 30 | experiments := []Experiment{ 31 | {"http://httpbin.org/", nil}, 32 | } 33 | 34 | for _, e := range experiments { 35 | fmt.Printf("==== %s ====\n", e.Describe()) 36 | 37 | req, err := http.NewRequest(http.MethodGet, e.URL, nil) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | 42 | for k, v := range e.ExtraHeaders { 43 | req.Header.Add(k, v) 44 | } 45 | 46 | err = DebugRequest(req) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | fmt.Println("") 52 | } 53 | } 54 | 55 | func DebugRequest(req *http.Request) error { 56 | b, err := httputil.DumpRequestOut(req, false) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | proxyStr := "http://localhost:8888" 62 | proxyURL, err := url.Parse(proxyStr) 63 | if err != nil { 64 | log.Println(err) 65 | } 66 | fmt.Println(string(b)) 67 | 68 | //adding the proxy settings to the Transport object 69 | transport := &http.Transport{ 70 | Proxy: http.ProxyURL(proxyURL), 71 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 72 | } 73 | 74 | //adding the Transport object to the http Client 75 | client := &http.Client{ 76 | Transport: transport, 77 | } 78 | 79 | res, err := client.Do(req) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | b, err = httputil.DumpResponse(res, false) 85 | if err != nil { 86 | return err 87 | } 88 | 89 | fmt.Println(string(b)) 90 | fmt.Println(res) 91 | 92 | fmt.Println("Uncompressed", res.Uncompressed) 93 | 94 | return nil 95 | } 96 | -------------------------------------------------------------------------------- /examples/postfile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "mime/multipart" 8 | "net/http" 9 | "os" 10 | "strings" 11 | ) 12 | 13 | func main() { 14 | 15 | var client = &http.Client{} 16 | var remoteURL string 17 | 18 | remoteURL="http://httpbin.org/post" 19 | 20 | //prepare the reader instances to encode 21 | values := map[string]io.Reader{ 22 | "file": mustOpen("net2.go"), // lets assume its this file 23 | "file2": mustOpen("net1.go"), 24 | "other": strings.NewReader("hello world!"), 25 | } 26 | err := Upload(client, remoteURL, values) 27 | if err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | func Upload(client *http.Client, url string, values map[string]io.Reader) (err error) { 33 | // Prepare a form that you will submit to that URL. 34 | var b bytes.Buffer 35 | w := multipart.NewWriter(&b) 36 | for key, r := range values { 37 | var fw io.Writer 38 | if x, ok := r.(io.Closer); ok { 39 | defer x.Close() 40 | } 41 | // Add an image file 42 | if x, ok := r.(*os.File); ok { 43 | if fw, err = w.CreateFormFile(key, x.Name()); err != nil { 44 | return 45 | } 46 | } else { 47 | // Add other fields 48 | if fw, err = w.CreateFormField(key); err != nil { 49 | return 50 | } 51 | } 52 | if _, err = io.Copy(fw, r); err != nil { 53 | return err 54 | } 55 | 56 | } 57 | // Don't forget to close the multipart writer. 58 | // If you don't close it, your request will be missing the terminating boundary. 59 | w.Close() 60 | 61 | // Now that you have a form, you can submit it to your handler. 62 | req, err := http.NewRequest("POST", url, &b) 63 | if err != nil { 64 | return 65 | } 66 | // Don't forget to set the content type, this will contain the boundary. 67 | req.Header.Set("Content-Type", w.FormDataContentType()) 68 | 69 | // Submit the request 70 | res, err := client.Do(req) 71 | if err != nil { 72 | return 73 | } 74 | 75 | // Check the response 76 | if res.StatusCode != http.StatusOK { 77 | err = fmt.Errorf("bad status: %s", res.Status) 78 | } 79 | 80 | body := &bytes.Buffer{} 81 | _, err = body.ReadFrom(res.Body) 82 | if err != nil { 83 | // log.Fatal(err) 84 | } 85 | res.Body.Close() 86 | fmt.Println(body) 87 | 88 | return 89 | } 90 | 91 | func mustOpen(f string) *os.File { 92 | r, err := os.Open(f) 93 | if err != nil { 94 | panic(err) 95 | } 96 | return r 97 | } 98 | -------------------------------------------------------------------------------- /requests/requests_test.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "testing" 7 | "os" 8 | ) 9 | 10 | /* 11 | func TestGet(t *testing.T) { 12 | // example 1 13 | req := Requests() 14 | 15 | req.Header.Set("accept-encoding", "gzip, deflate, br") 16 | req.Get("http://go.xiulian.net.cn", Header{"Content-Length": "0"}, Params{"c": "d", "e": "f"}, Params{"c": "a"}) 17 | 18 | // example 2 19 | h := Header{ 20 | "Referer": "http://www.jeapedu.com", 21 | "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", 22 | } 23 | 24 | Get("http://jeapedu.com", h, Header{"Content-Length": "1024"}) 25 | 26 | // example 3 27 | p := Params{ 28 | "title": "The blog", 29 | "name": "file", 30 | "id": "12345", 31 | } 32 | resp := Requests().Get("http://www.cpython.org", p) 33 | 34 | resp.Text() 35 | fmt.Println(resp.Text()) 36 | 37 | // example 4 38 | // test authentication usernae,password 39 | //documentation https://www.httpwatch.com/httpgallery/authentication/#showExample10 40 | req = Requests() 41 | resp = req.Get("https://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.45874470316137206",Auth{"httpwatch","foo"}) 42 | fmt.Println(resp.httpresp) 43 | 44 | // this save file test PASS 45 | // resp.SaveFile("auth.jpeg") 46 | 47 | //example 5 test Json 48 | req = Requests() 49 | req.Header.Set("Content-Type","application/json") 50 | resp = req.Get("https://httpbin.org/json") 51 | 52 | var json map[string]interface{} 53 | resp.Json(&json) 54 | 55 | for k,v := range json{ 56 | fmt.Println(k,v) 57 | } 58 | 59 | // example 6 test gzip 60 | req = Requests() 61 | req.Debug = 1 62 | resp = req.Get("https://httpbin.org/gzip") 63 | 64 | fmt.Println(resp.Text()) 65 | 66 | // example 7 proxy and debug 67 | req = Requests() 68 | req.Debug = 1 69 | //req.Proxy("http://192.168.1.190:8888") 70 | 71 | resp = req.Get("https://www.sina.com.cn") 72 | // fmt.Println(resp.Text()) 73 | req.Get("https://www.sina.com.cn") 74 | 75 | //example 8 test auto Cookies 76 | req = Requests() 77 | req.Debug = 1 78 | // req.Proxy("http://192.168.1.190:8888") 79 | req.Get("https://www.httpbin.org/cookies/set?freeform=1234") 80 | req.Get("https://www.httpbin.org") 81 | req.Get("https://www.httpbin.org/cookies/set?a=33d") 82 | req.Get("https://www.httpbin.org") 83 | 84 | // example 9 test AddCookie 85 | req = Requests() 86 | req.Debug = 1 87 | 88 | cookie := &http.Cookie{} 89 | cookie.Name = "anewcookie" 90 | cookie.Value = "20180825" 91 | cookie.Path = "/" 92 | 93 | req.SetCookie(cookie) 94 | 95 | 96 | fmt.Println(req.Cookies) 97 | // req.Proxy("http://127.0.0.1:8888") 98 | req.Get("https://www.httpbin.org/cookies/set?freeform=1234") 99 | req.Get("https://www.httpbin.org") 100 | req.Get("https://www.httpbin.org/cookies/set?a=33d") 101 | req.Get("https://www.httpbin.org") 102 | 103 | } 104 | */ 105 | 106 | func TestPost(t *testing.T) { 107 | 108 | fmt.Println(&http.Cookie{}) 109 | 110 | // example 1 111 | // set post formdata 112 | req := Requests() 113 | req.Debug = 1 114 | 115 | 116 | data := Datas{ 117 | "comments": "ew", 118 | "custemail": "a@231.com", 119 | "custname": "1", 120 | "custtel": "2", 121 | "delivery": "12:45", 122 | "size": "small", 123 | "topping": "bacon", 124 | } 125 | 126 | resp := req.Post("https://www.httpbin.org/post",data) 127 | 128 | fmt.Println(resp.Text()) 129 | 130 | 131 | //example 2 upload files 132 | req = Requests() 133 | req.Debug = 1 134 | path, _ := os.Getwd() 135 | path1 := path + "/../examples/net1.go" 136 | path2 := path + "/../examples/net2.go" 137 | 138 | resp = req.Post("https://www.httpbin.org/post",data,Files{"a":path1,"b":path2}) 139 | 140 | fmt.Println(resp.Text()) 141 | 142 | 143 | 144 | } 145 | -------------------------------------------------------------------------------- /docs/url.md: -------------------------------------------------------------------------------- 1 | ## 定义 2 | 3 | !!! 解释 4 | URL Schemes 有两个单词: 5 | 6 | URL,我们都很清楚,http://go.xiulian.net.cn 就是个 URL,我们也叫它链接或网址; 7 | 8 | Schemes,表示的是一个 URL 中的一个位置——最初始的位置,即 ://之前的那段字符。比如 http://www.cpython.org 这个网址的 Schemes 是 http。 9 | 10 | ``` 11 | type URL struct { 12 | Scheme string 13 | Opaque string // encoded opaque data 14 | User *Userinfo // username and password information 15 | Host string // host or host:port 16 | Path string // path (relative paths may omit leading slash) 17 | RawPath string // encoded path hint (see EscapedPath method) 18 | ForceQuery bool // append a query ('?') even if RawQuery is empty 19 | RawQuery string // encoded query values, without '?' 20 | Fragment string // fragment for references, without '#' 21 | } 22 | ``` 23 | 24 | ### URL 例子 25 | 26 | ``` 27 | [scheme:][//[userinfo@]host][/]path[?query][#fragment] 28 | ``` 29 | 30 | 下面例子,我修改了原始例子,觉得原文档例子有歧义。 31 | 32 | ``` 33 | u := new(url.URL) 34 | 35 | u.Scheme = "https" 36 | u.Host = "google.com" 37 | q := u.Query() 38 | q.Set("q", "golang") 39 | u.RawQuery = q.Encode() 40 | fmt.Println(u) 41 | 42 | ``` 43 | 44 | 输入结果 45 | 46 | https://google.com/search?q=golang 47 | 48 | 49 | ## path 50 | 51 | Path 解码路径 52 | RawPath 原始路径 53 | 54 | 55 | ``` 56 | package main 57 | 58 | import ( 59 | "fmt" 60 | "log" 61 | "net/url" 62 | ) 63 | 64 | func main() { 65 | // Parse + String preserve the original encoding. 66 | u, err := url.Parse("https://example.com/foo%2fbar") 67 | if err != nil { 68 | log.Fatal(err) 69 | } 70 | fmt.Println(u.Path) //解码存储 71 | fmt.Println(u.RawPath) //原始存储 72 | fmt.Println(u.String()) 73 | } 74 | ``` 75 | 76 | 结果如下: 77 | 78 | ``` 79 | /foo/bar 80 | /foo%2fbar 81 | https://example.com/foo%2fbar 82 | ``` 83 | 84 | ## EscapedPath 85 | 86 | func (u *URL) EscapedPath() string 87 | 88 | EscapedPath 返回 u.Path 的转义形式。一般来说,任何路径都有多种可能的转义形式。EscapedPath 在 u.Path 有效转义时返回 u.RawPath 。否则,EscapedPath 将忽略 u.RawPath 并自行计算转义表单。 String 和 RequestURI 方法使用 EscapedPath 来构造它们的结果。通常,代码应该调用 EscapedPath ,而不是直接读取 u.RawPath 。 89 | 90 | 91 | ## RequestURI 92 | 93 | 将path 编码,无论原始URL是否编码都返回编码地址。 94 | 下面代码,我写了2个URL的例子。 95 | 96 | ``` 97 | package main 98 | 99 | import ( 100 | "fmt" 101 | "log" 102 | "net/url" 103 | ) 104 | 105 | func main() { 106 | 107 | // Parse + String preserve the original encoding. 108 | u, err := url.Parse("https://example.com/搜索") 109 | if err != nil { 110 | log.Fatal(err) 111 | } 112 | fmt.Println(u.Path) //解码存储 113 | fmt.Println(u.RequestURI()) //压缩存储 114 | fmt.Println(u.String()) 115 | 116 | u, err = url.Parse("https://example.com/%E6%90%9C%E7%B4%A2") 117 | if err != nil { 118 | log.Fatal(err) 119 | } 120 | fmt.Println(u.Path) //解码存储 121 | fmt.Println(u.RequestURI()) //压缩存储 122 | fmt.Println(u.String()) 123 | 124 | } 125 | 126 | ``` 127 | 128 | 129 | 结果 130 | 131 | ``` 132 | /搜索 133 | /%E6%90%9C%E7%B4%A2 134 | https://example.com/%E6%90%9C%E7%B4%A2 135 | /搜索 136 | /%E6%90%9C%E7%B4%A2 137 | https://example.com/%E6%90%9C%E7%B4%A2 138 | ``` 139 | 140 | 141 | ## Values 142 | type Values map[string][]string 143 | 144 | 用map来存储query 参数 145 | 146 | 操作方法如下: 147 | 148 | ``` 149 | v := url.Values{} 150 | v.Set("name", "Ava") 151 | v.Add("friend", "Jess") 152 | v.Add("friend", "Sarah") 153 | v.Add("friend", "Zoe") 154 | // v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe" 155 | fmt.Println(v.Get("name")) 156 | fmt.Println(v.Get("friend")) 157 | fmt.Println(v["friend"]) 158 | ``` 159 | 160 | 结果。 161 | 162 | ``` 163 | Ava 164 | Jess 165 | [Jess Sarah Zoe] 166 | ``` 167 | 168 | 这个功能可以对get参数进行转换编码。 169 | -------------------------------------------------------------------------------- /docs/gin.md: -------------------------------------------------------------------------------- 1 | ## gin 2 | 3 | gin 是一款轻量级的 golang web server框架。 4 | 5 | 源代码地址 [https://github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) 6 | 7 | 目前在github上star最多。这在一定层面上表示认可度最高。 8 | 9 | ## 开始 10 | 11 | go get github.com/gin-gonic/gin 12 | 13 | ## 写一个example.go 14 | 15 | ```go 16 | package main 17 | 18 | import ( 19 | "net/http" 20 | 21 | "github.com/gin-gonic/gin" 22 | ) 23 | 24 | func main (){ 25 | 26 | r := gin.Default() 27 | 28 | r.GET("/hello",func(c *gin.Context){ 29 | c.String(http.StatusOK,"Hello gocoin!") 30 | }) 31 | 32 | r.Run(":8080") 33 | } 34 | 35 | ``` 36 | 37 | 执行 go run example.go 38 | 39 | 打开浏览器访问: http://127.0.0.1:8080/hello 40 | 41 | ## 路由 42 | 43 | ## 模版 44 | 45 | ## 数据库 46 | 数据库模块常用的是 gorm。 47 | 48 | 安装 gorm 49 | go get github.com/jinzhu/gorm 50 | go get github.com/mattn/go-sqlite3 51 | 52 | 因为我例子用的sqlite3,所以也安装了sqlite3 依赖库。 53 | 54 | bogon:gocoin jiashenghe$ cat gorm_example.go 55 | 56 | ```go 57 | 58 | package main 59 | 60 | import ( 61 | "github.com/jinzhu/gorm" 62 | _ "github.com/jinzhu/gorm/dialects/sqlite" 63 | "fmt" 64 | ) 65 | 66 | type Person struct { 67 | gorm.Model 68 | FirstName string 69 | LastName string 70 | } 71 | 72 | func main() { 73 | db, _ := gorm.Open("sqlite3", "./gorm.db") 74 | defer db.Close() 75 | 76 | db.AutoMigrate(&Person{}) 77 | 78 | p1 := Person{FirstName: "John", LastName: "Doe"} 79 | p2 := Person{FirstName: "Jane", LastName: "Smith"} 80 | 81 | db.Create(&p1) 82 | var ps []Person 83 | 84 | fmt.Println("-------1------") 85 | db.Find(&ps) 86 | for _,k := range ps{ 87 | fmt.Println(k) 88 | } 89 | 90 | 91 | db.Create(&p2) 92 | fmt.Println("-------2------") 93 | db.Find(&ps) 94 | for _,k := range ps{ 95 | fmt.Println(k) 96 | } 97 | } 98 | 99 | ``` 100 | 101 | 执行结果: 102 | 103 | ``` 104 | -------1------ 105 | {{1 2018-11-08 10:12:48.913042 +0800 +0800 2018-11-08 10:12:48.913042 +0800 +0800 } John Doe} 106 | -------2------ 107 | {{1 2018-11-08 10:12:48.913042 +0800 +0800 2018-11-08 10:12:48.913042 +0800 +0800 } John Doe} 108 | {{2 2018-11-08 10:12:48.914945 +0800 +0800 2018-11-08 10:12:48.914945 +0800 +0800 } Jane Smith} 109 | ``` 110 | 111 | 112 | # gorm + gin 113 | 合并代码如下: 114 | ```go 115 | package main 116 | 117 | import ( 118 | _ "net/http" 119 | 120 | "github.com/gin-gonic/gin" 121 | "github.com/jinzhu/gorm" 122 | _ "github.com/jinzhu/gorm/dialects/sqlite" 123 | "fmt" 124 | ) 125 | 126 | var db *gorm.DB 127 | var err error 128 | 129 | type Person struct { 130 | gorm.Model 131 | FirstName string 132 | LastName string 133 | } 134 | 135 | func init_db (){ 136 | 137 | db, err = gorm.Open("sqlite3", "./gorm.db") 138 | if err != nil { 139 | fmt.Println(err) 140 | } 141 | db.AutoMigrate(&Person{}) 142 | 143 | } 144 | 145 | func main (){ 146 | 147 | init_db() 148 | defer db.Close() 149 | 150 | r := gin.Default() 151 | 152 | r.GET("/hello",func(c *gin.Context){ 153 | //c.String(http.StatusOK,"Hello gocoin!") 154 | var people []Person 155 | if err := db.Find(&people).Error; err != nil { 156 | c.AbortWithStatus(404) 157 | fmt.Println(err) 158 | } else { 159 | c.JSON(200, people) 160 | } 161 | 162 | 163 | }) 164 | 165 | r.Run(":8080") 166 | } 167 | ``` 168 | 169 | go run main.go 170 | 171 | 在浏览器浏览 http://127.0.0.1:8080/hello 172 | 173 | 结果如下: 174 | 175 | ``` 176 | [{"ID":1,"CreatedAt":"2018-11-08T10:12:48.913042+08:00","UpdatedAt":"2018-11-08T10:12:48.913042+08:00","DeletedAt":null,"FirstName":"John","LastName":"Doe"},{"ID":2,"CreatedAt":"2018-11-08T10:12:48.914945+08:00","UpdatedAt":"2018-11-08T10:12:48.914945+08:00","DeletedAt":null,"FirstName":"Jane","LastName":"Smith"}] 177 | ``` 178 | ## cookies 179 | 180 | ## 验证用户 181 | -------------------------------------------------------------------------------- /examples/httpassembly.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google, Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style license 4 | // that can be found in the LICENSE file in the root of the source 5 | // tree. 6 | 7 | // This binary provides sample code for using the gopacket TCP assembler and TCP 8 | // stream reader. It reads packets off the wire and reconstructs HTTP requests 9 | // it sees, logging them. 10 | package main 11 | 12 | import ( 13 | "bufio" 14 | "flag" 15 | "io" 16 | "log" 17 | "net/http" 18 | "time" 19 | "fmt" 20 | 21 | "github.com/google/gopacket" 22 | "github.com/google/gopacket/examples/util" 23 | "github.com/google/gopacket/layers" 24 | "github.com/google/gopacket/pcap" 25 | "github.com/google/gopacket/tcpassembly" 26 | "github.com/google/gopacket/tcpassembly/tcpreader" 27 | ) 28 | 29 | var iface = flag.String("i", "lo0", "Interface to get packets from") 30 | var fname = flag.String("r", "", "Filename to read from, overrides -i") 31 | var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture") 32 | var filter = flag.String("f", "tcp and portrange 80-443", "BPF filter for pcap") 33 | var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail") 34 | 35 | // Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces 36 | 37 | // httpStreamFactory implements tcpassembly.StreamFactory 38 | type httpStreamFactory struct{} 39 | 40 | // httpStream will handle the actual decoding of http requests. 41 | type httpStream struct { 42 | net, transport gopacket.Flow 43 | r tcpreader.ReaderStream 44 | } 45 | 46 | func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream { 47 | hstream := &httpStream{ 48 | net: net, 49 | transport: transport, 50 | r: tcpreader.NewReaderStream(), 51 | } 52 | 53 | 54 | src,dst := transport.Endpoints() 55 | if fmt.Sprintf("%v",src) == "80"{ 56 | go hstream.runResponse() // Important... we must guarantee that data from the reader stream is read. 57 | } else 58 | if fmt.Sprintf("%v",dst) == "80"{ 59 | go hstream.runRequest() // Important... we must guarantee that data from the reader stream is read. 60 | } else 61 | if fmt.Sprintf("%v",dst) == "443" { 62 | go hstream.runRequests() 63 | } else { 64 | go hstream.run() 65 | } 66 | 67 | // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it. 68 | return &hstream.r 69 | } 70 | 71 | func (h * httpStream) runRequests(){ 72 | reader := bufio.NewReader(&h.r) 73 | 74 | defer tcpreader.DiscardBytesToEOF(reader) 75 | 76 | log.Println(h.net, h.transport) 77 | 78 | for { 79 | data := make([]byte,1600) 80 | n,err := reader.Read(data) 81 | if err == io.EOF{ 82 | return 83 | } 84 | log.Printf("[% x]",data[:n]) 85 | } 86 | } 87 | 88 | func (h *httpStream) run(){ 89 | reader := bufio.NewReader(&h.r) 90 | defer tcpreader.DiscardBytesToEOF(reader) 91 | 92 | log.Println(h.net, h.transport) 93 | for { 94 | data := make([]byte,1600) 95 | n,err := reader.Read(data) 96 | if err == io.EOF{ 97 | return 98 | } 99 | log.Printf("[% x]",data[:n]) 100 | } 101 | 102 | } 103 | 104 | func (h *httpStream) runResponse() { 105 | 106 | buf := bufio.NewReader(&h.r) 107 | defer tcpreader.DiscardBytesToEOF(buf) 108 | for { 109 | resp, err := http.ReadResponse(buf,nil) 110 | if err == io.EOF || err == io.ErrUnexpectedEOF { 111 | // We must read until we see an EOF... very important! 112 | return 113 | } else if err != nil { 114 | log.Println("Error reading stream", h.net, h.transport, ":", err) 115 | return 116 | } else { 117 | bodyBytes := tcpreader.DiscardBytesToEOF(resp.Body) 118 | resp.Body.Close() 119 | printResponse(resp,h,bodyBytes) 120 | // log.Println("Received response from stream", h.net, h.transport, ":", resp, "with", bodyBytes, "bytes in response body") 121 | } 122 | } 123 | } 124 | func (h *httpStream) runRequest() { 125 | 126 | buf := bufio.NewReader(&h.r) 127 | defer tcpreader.DiscardBytesToEOF(buf) 128 | for { 129 | req, err := http.ReadRequest(buf) 130 | if err == io.EOF || err == io.ErrUnexpectedEOF { 131 | // We must read until we see an EOF... very important! 132 | return 133 | } else if err != nil { 134 | log.Println("Error reading stream", h.net, h.transport, ":", err) 135 | } else { 136 | bodyBytes := tcpreader.DiscardBytesToEOF(req.Body) 137 | req.Body.Close() 138 | printRequest(req,h,bodyBytes) 139 | // log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body") 140 | } 141 | } 142 | } 143 | 144 | func printHeader(h http.Header){ 145 | for k,v := range h{ 146 | fmt.Println(k,v) 147 | } 148 | } 149 | 150 | func printRequest(req *http.Request,h *httpStream,bodyBytes int){ 151 | 152 | fmt.Println("\n\r\n\r") 153 | fmt.Println(h.net,h.transport) 154 | fmt.Println("\n\r") 155 | fmt.Println(req.Method, req.RequestURI, req.Proto) 156 | printHeader(req.Header) 157 | 158 | } 159 | 160 | func printResponse(resp *http.Response,h *httpStream,bodyBytes int){ 161 | 162 | fmt.Println("\n\r") 163 | fmt.Println(resp.Proto, resp.Status) 164 | printHeader(resp.Header) 165 | } 166 | 167 | 168 | func main() { 169 | defer util.Run()() 170 | var handle *pcap.Handle 171 | var err error 172 | 173 | // Set up pcap packet capture 174 | if *fname != "" { 175 | log.Printf("Reading from pcap dump %q", *fname) 176 | handle, err = pcap.OpenOffline(*fname) 177 | } else { 178 | log.Printf("Starting capture on interface %q", *iface) 179 | handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever) 180 | } 181 | if err != nil { 182 | log.Fatal(err) 183 | } 184 | 185 | if err := handle.SetBPFFilter(*filter); err != nil { 186 | log.Fatal(err) 187 | } 188 | 189 | // Set up assembly 190 | streamFactory := &httpStreamFactory{} 191 | streamPool := tcpassembly.NewStreamPool(streamFactory) 192 | assembler := tcpassembly.NewAssembler(streamPool) 193 | 194 | log.Println("reading in packets") 195 | // Read in packets, pass to assembler. 196 | packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) 197 | packets := packetSource.Packets() 198 | ticker := time.Tick(time.Minute) 199 | for { 200 | select { 201 | case packet := <-packets: 202 | // A nil packet indicates the end of a pcap file. 203 | if packet == nil { 204 | return 205 | } 206 | if *logAllPackets { 207 | log.Println(packet) 208 | } 209 | if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP { 210 | log.Println("Unusable packet") 211 | continue 212 | } 213 | tcp := packet.TransportLayer().(*layers.TCP) 214 | assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp) 215 | case <-ticker: 216 | // Every minute, flush connections that haven't seen activity in the past 2 minutes. 217 | assembler.FlushOlderThan(time.Now().Add(time.Minute * -2)) 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /docs/requests.md: -------------------------------------------------------------------------------- 1 | ## requests 2 | 3 | `requests` 是一个用golang 语言clone python版本的requests库。 4 | golang 自带的net/http功能已经非常完善。它和python里面的urllib系列库一样,功能足够,但是使用起来非常繁琐。 5 | 6 | python版本的requests简直就是神器,让很多人非常敬仰。 7 | 8 | 因此我就动手按照python的requests的思想封装了一个 requests。 9 | 10 | 动手之前,我想尽量兼容 python的写法。但是golang不支持函数名称小写, 11 | 也不支持 参数命名。所以我只能用参数类型的方案来支持动态参数。 12 | 13 | ## 安装 14 | 15 | golang 1.11之前 16 | 17 | ``` 18 | go get -u github.com/asmcos/requests 19 | ``` 20 | 21 | golang 1.11之后,编辑一个go.mod文件 22 | 23 | ``` 24 | module github.com/asmcos/requests 25 | ``` 26 | 27 | !!! 特别说明 28 | 由于requests代码在更新,所有例子都有是当前有效。 29 | 并且部分片段有上下文。请大家阅读时参考原代码的requests_test.go 30 | 31 | 32 | ## 开始使用(带Auth) 33 | 34 | ``` go 35 | package main 36 | 37 | import ( 38 | "github.com/asmcos/requests" 39 | "fmt" 40 | ) 41 | 42 | func main (){ 43 | 44 | req := requests.Requests() 45 | resp,_ := req.Get("https://api.github.com/user",requests.Auth{"asmcos","password...."}) 46 | println(resp.Text()) 47 | fmt.Println(resp.R.StatusCode) 48 | fmt.Println(resp.R.Header["Content-Type"]) 49 | } 50 | 51 | ``` 52 | 53 | 第一个例子为什么是它? 因为python requests第一个例子是它。。。呵呵 54 | 55 | 注意::: `密码` 和用户名要用github真实用户才能测试。 56 | 57 | 58 | ## 创建请求的方法 59 | 60 | ### 例子1 61 | 62 | 极简使用 63 | 64 | ``` go 65 | package main 66 | 67 | import "github.com/asmcos/requests" 68 | 69 | func main (){ 70 | 71 | resp,_ := requests.Get("http://go.xiulian.net.cn") 72 | println(resp.Text()) 73 | } 74 | ``` 75 | 76 | 其实 requests实现都是先用Requests()函数创建一个 request 和 client, 77 | 再用req.Get去请求。 78 | 79 | requests.Get 是一个封装,对Requests()和req.Get的一个封装。 80 | 81 | ### 例子2 82 | 83 | 这个例子是分成2个步骤,来操作,这样的好处是可以通过req来设置各种请求参数。 84 | 后面的例子会展示各种设置。 85 | 86 | ``` go 87 | package main 88 | 89 | import "github.com/asmcos/requests" 90 | 91 | 92 | func main (){ 93 | 94 | req := requests.Requests() 95 | 96 | resp,_ := req.Get("http://go.xiulian.net.cn",requests.Header{"Referer":"http://www.jeapedu.com"}) 97 | 98 | println(resp.Text()) 99 | 100 | } 101 | ``` 102 | 103 | ## 设置Header 104 | 105 | ``` 106 | req := Requests() 107 | 108 | req.Header.Set("accept-encoding", "gzip, deflate, br") 109 | req.Get("http://go.xiulian.net.cn", requests.Header{"Referer": "http://www.jeapedu.com"}) 110 | ``` 111 | 112 | !!! 设置头的2种方法 113 | 1. 通过req.Header.Set函数直接设置 114 | 2. 调用req.Get 函数时,在函数参数里填写上 115 | 116 | Get 支持动态参数,但是参数前面要加类型标识。 117 | 118 | 函数里面根据类型判断参数的含义。 119 | 120 | 其实 函数的参数里关于Header参数是可以多次设置的。 121 | 122 | ``` go 123 | url1 := "http://go.xiulian.net.cn" 124 | req.Get(url1, requests.Header{"k0": "v0"},requests.Header{"k1":"v1"},requests.Header{"k2":"v2"}) 125 | 126 | h := requests.Header{ 127 | "k3":"v3", 128 | "k4":"v4", 129 | "k5":"v5", 130 | } 131 | req.Get(url1,h) 132 | ``` 133 | 134 | 这些都可以。灵活增加头。 135 | 136 | 137 | ## 设置参数 138 | 139 | 140 | ``` go 141 | p := requests.Params{ 142 | "title": "The blog", 143 | "name": "file", 144 | "id": "12345", 145 | } 146 | resp,_ := Requests().Get("http://www.cpython.org", p) 147 | ``` 148 | 149 | 其实参数设置。参数设置也是支持多次的。 150 | 151 | 类似如下: 152 | 153 | ``` go 154 | resp,_ := Requests().Get("http://www.cpython.org", p,p1,p2) 155 | ``` 156 | 157 | ## POST 的form表单 158 | 159 | ``` go 160 | data := Datas{ 161 | "comments": "ew", 162 | "custemail": "a@231.com", 163 | "custname": "1", 164 | "custtel": "2", 165 | "delivery": "12:45", 166 | "size": "small", 167 | "topping": "bacon", 168 | } 169 | 170 | resp,_ := req.Post("https://www.httpbin.org/post",data) 171 | 172 | fmt.Println(resp.Text()) 173 | ``` 174 | 175 | !!!! `注意` Post的form是放在body里面,而get的params 是放在url里面。 176 | 177 | 178 | ## Proxy 179 | 180 | 目前不支持带密码验证的代理。 181 | 182 | ``` go 183 | req = Requests() 184 | req.Proxy("http://192.168.1.190:8888") 185 | resp,_ = req.Get("https://www.sina.com.cn") 186 | ``` 187 | 188 | ## 设置Cookies 189 | 190 | requests 支持自身传递cookies。本质上是把cookies存在client.jar里面。 191 | 用户设置的cookies也会随着client.jar来传递。 192 | 193 | ``` go 194 | req = Requests() 195 | 196 | cookie := &http.Cookie{} 197 | cookie.Name = "anewcookie" 198 | cookie.Value = "20180825" 199 | cookie.Path = "/" 200 | 201 | req.SetCookie(cookie) 202 | 203 | fmt.Println(req.Cookies) 204 | req.Get("https://www.httpbin.org/cookies/set?freeform=1234") 205 | req.Get("https://www.httpbin.org") 206 | req.Get("https://www.httpbin.org/cookies/set?a=33d") 207 | ``` 208 | 209 | 210 | !!! 过程说明 211 | 代码中 首先使用http.Cookie生成一个用户自定义的cooie, 212 | req.SetCookie 实际上还没有把cookie放在client.jar里面。 213 | 在Get的时候requests会把req.Cookies里面的内容复制到client.jar里面,并且清空req.cookies 214 | 再一次Get的时候,requests都会处理Cookies。 215 | 216 | ## debug 217 | 218 | 当设置了Debug = 1,请求的时候会把request和response都打印出来, 219 | 220 | 包含request的cookie, 返回的cookie没有打印。 221 | 222 | ``` go 223 | req := Requests() 224 | req.Debug = 1 225 | 226 | data := Datas{ 227 | "comments": "ew", 228 | "custemail": "a@231.com", 229 | "custname": "1", 230 | "custtel": "2", 231 | "delivery": "12:45", 232 | "size": "small", 233 | "topping": "bacon", 234 | } 235 | 236 | resp,_ := req.Post("https://www.httpbin.org/post",data) 237 | 238 | fmt.Println(resp.Text()) 239 | ``` 240 | 241 | ### Debug 结果如下 242 | 243 | ``` json 244 | ===========Go RequestDebug ============ 245 | POST /post HTTP/1.1 246 | Host: www.httpbin.org 247 | User-Agent: Go-Requests 0.5 248 | Content-Length: 96 249 | Content-Type: application/x-www-form-urlencoded 250 | Accept-Encoding: gzip 251 | 252 | 253 | ===========Go ResponseDebug ============ 254 | HTTP/1.1 200 OK 255 | Content-Length: 560 256 | Access-Control-Allow-Credentials: true 257 | Access-Control-Allow-Origin: * 258 | Connection: keep-alive 259 | Content-Type: application/json 260 | Date: Sun, 02 Sep 2018 09:40:32 GMT 261 | Server: gunicorn/19.9.0 262 | Via: 1.1 vegur 263 | 264 | 265 | { 266 | "args": {}, 267 | "data": "", 268 | "files": {}, 269 | "form": { 270 | "comments": "ew", 271 | "custemail": "a@231.com", 272 | "custname": "1", 273 | "custtel": "2", 274 | "delivery": "12:45", 275 | "size": "small", 276 | "topping": "bacon" 277 | }, 278 | "headers": { 279 | "Accept-Encoding": "gzip", 280 | "Connection": "close", 281 | "Content-Length": "96", 282 | "Content-Type": "application/x-www-form-urlencoded", 283 | "Host": "www.httpbin.org", 284 | "User-Agent": "Go-Requests 0.5" 285 | }, 286 | "json": null, 287 | "origin": "219.143.154.50", 288 | "url": "https://www.httpbin.org/post" 289 | } 290 | ``` 291 | 292 | ## Authentication 验证用户名和密码。 293 | 294 | ``` go 295 | req = Requests() 296 | url1 := "https://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.45874470316137206" 297 | resp,_ = req.Get(url1,Auth{"httpwatch","foo"}) 298 | fmt.Println(resp.httpresp) 299 | ``` 300 | 301 | 用户名和密码的参数是加密存放在Header里面, 302 | 303 | 和params是不同的。 304 | 305 | ## saveFile 306 | 307 | 接着上面的例子,如果成功了。返回的数据实际是一个jpeg格式文件。 308 | 309 | 我们可以直接存文件。 310 | 311 | ``` 312 | resp.SaveFile("auth.jpeg") 313 | ``` 314 | 315 | ## JSON 316 | 317 | ``` go 318 | req = Requests() 319 | req.Header.Set("Content-Type","application/json") 320 | resp,_ = req.Get("https://httpbin.org/json") 321 | 322 | var json map[string]interface{} 323 | resp.Json(&json) 324 | 325 | for k,v := range json{ 326 | fmt.Println(k,v) 327 | } 328 | ``` 329 | 330 | ## 支持GZIP格式 331 | 332 | ``` go 333 | req = Requests() 334 | req.Debug = 1 335 | resp,_ = req.Get("https://httpbin.org/gzip") 336 | 337 | fmt.Println(resp.Text()) 338 | ``` 339 | 340 | 341 | ## 支持传文件POST 342 | 343 | ``` go 344 | req = Requests() 345 | req.Debug = 1 346 | path, _ := os.Getwd() 347 | path1 := path + "/../examples/net1.go" 348 | path2 := path + "/../examples/net2.go" 349 | 350 | resp,_ = req.Post("https://www.httpbin.org/post",data,Files{"a":path1,"b":path2}) 351 | 352 | fmt.Println(resp.Text()) 353 | 354 | ``` 355 | 356 | header,params,是不分 GET和POST的。 357 | 358 | 359 | ## 返回cookies 360 | 361 | 其实requests的cookies 一直存在client.jar里面, 362 | 363 | 所以需要从client.jar读取。 364 | 365 | ``` go 366 | resp,_ = req.Get("https://www.httpbin.org") 367 | coo := resp.Cookies() 368 | // coo is [] *http.Cookies 369 | println("********cookies*******") 370 | for _, c:= range coo{ 371 | fmt.Println(c.Name,c.Value) 372 | } 373 | ``` 374 | -------------------------------------------------------------------------------- /docs/net.md: -------------------------------------------------------------------------------- 1 | ## 引入包 2 | 3 | ``` 4 | import "net" 5 | ``` 6 | 7 | net 包是提供了一个可以移植的 network I/O 接口。包含 TCP/IP,UDP,域名解析,和Unix domain 8 | sockets 编程。 (注:这些在C语言和其他编程语言都有) 9 | 10 | 虽然 "net" 包提供了最底层的网络访问接口,但是大部clients编程需要的是基本接口例如:Dial,Listen,Accept和相关的Conn,Listener接口。另外"crypto/tls" 包也有类似:Dial,Listen函数。 11 | 12 | ## Dial 例子 13 | 14 | ``` 15 | package main 16 | 17 | import ( 18 | "net" 19 | "fmt" 20 | "bufio" 21 | ) 22 | 23 | func main (){ 24 | // 原文档golang.org 在国内访问不了,我换了一个域名 25 | conn, err := net.Dial("tcp", "go.xiulian.net.cn:80") 26 | if err != nil { 27 | // handle error 28 | } 29 | fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n") 30 | status, _ := bufio.NewReader(conn).ReadString('\n') 31 | 32 | fmt.Println(status) 33 | return 34 | } 35 | ``` 36 | 37 | !!! Dial源代码都调用了哪些系统调用 38 | sock_posix.go 文件里有posix 的系统调用方法 39 | 40 | * 创建socket 41 | * connect 42 | * getsockname 43 | * getpeername 44 | 45 | 46 | ## Listen 创建一个服务 47 | 48 | ``` 49 | package main 50 | 51 | import ( 52 | "net" 53 | "fmt" 54 | ) 55 | 56 | func handleConnection(conn net.Conn) { 57 | buf := make([]byte,2000) 58 | 59 | n,_ := conn.Read(buf) 60 | fmt.Println(conn.RemoteAddr().String()) 61 | fmt.Println(n) 62 | } 63 | 64 | func main (){ 65 | ln, err := net.Listen("tcp", ":8080") 66 | if err != nil { 67 | // handle error 68 | } 69 | for { 70 | conn, err := ln.Accept() 71 | if err != nil { 72 | // handle error 73 | } 74 | fmt.Println(conn) 75 | go handleConnection(conn) 76 | } 77 | 78 | } 79 | ``` 80 | 81 | 这个处理和其他语言的网络没有太多区别,就是Listen 8080端口,等待链接。 82 | 当Accept一个链接后就是 go(Goroutines)一个线程去处理这个链接conn。 83 | 84 | 85 | ## 域名解析(名字) 86 | 87 | 无论我们是调用Dial 来获得解析,还是直接调用LookupHost和LookupAddr来解析,这都是和OS息息相关的。 88 | 89 | 在Unix系列的OS上,解析器有两个方法解析域名。方法一就是用go发送到dns解析(dns在/etc/resolv.conf文件中配置)。方法二是cgo的解析器调用C库例程,如getaddrinfo和getnameinfo。 90 | 91 | 调用方法一时如果出现问题,会消耗 Goroutines 线程。而调用方法二如果出现出问题会消耗操作系统线程。因此go默认的情况是使用方法一。 92 | 93 | 94 | 可以通过将GODEBUG环境变量的netdns值(请参阅包运行时)设置为go或cgo来覆盖解析器决策,如下所示: 95 | 96 | ``` 97 | export GODEBUG = netdns = go#force pure Go resolver 98 | export GODEBUG = netdns = cgo#force cgo resolver 99 | ``` 100 | 101 | 在Windows上,解析器始终使用C库函数,例如GetAddrInfo和DnsQuery。 102 | 103 | ## func JoinHostPort 104 | 105 | func JoinHostPort(host, port string) string 106 | 107 | JoinHostPort 函数的目的是将host和port合成一个“host:port”格式的网络地址,如果IPV6这种的自身带有“:”,返回的格式应该是 "[host]:port" 108 | 109 | ## LookupAddr 110 | 111 | func LookupAddr(addr string) (names []string, err error) 112 | 113 | LookupAddr 函数是给定一个 地址反向查找,返回是的改名字对应的映射列表。 114 | 115 | ``` 116 | lt, _ := net.LookupAddr("127.0.0.1") 117 | fmt.Println(lt) //[localhost],根据地址查找到改地址的一个映射列表 118 | 119 | ``` 120 | 121 | ## 寻找主机名 122 | 123 | LookupCNAME 返回规范名,符合DNS的CNAME规则的。 124 | LookupHost或LookupIP 返回非规范名 125 | 126 | ## SplitHostPort 127 | 128 | func SplitHostPort(hostport string) (host, port string, err error) 129 | 130 | 把网络格式分析成host 和port ,网络格式包含:"host:port", "host%zone:port", "[host]:port" or "[host%zone]:port" 131 | 132 | 133 | ## Buffers 134 | 135 | type Buffers [][]byte 136 | func (v *Buffers) Read(p []byte) (n int, err error) 137 | func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) 138 | 139 | 包含0个,1个或者多个 要写的内容。 writev,read的时候使用。 140 | 141 | 142 | ## Conn 143 | 144 | 145 | ``` 146 | type Conn interface { 147 | // Read reads data from the connection. 148 | // Read can be made to time out and return an Error with Timeout() == true 149 | // after a fixed time limit; see SetDeadline and SetReadDeadline. 150 | Read(b []byte) (n int, err error) //从连接读书句 151 | 152 | // Write writes data to the connection. 153 | // Write can be made to time out and return an Error with Timeout() == true 154 | // after a fixed time limit; see SetDeadline and SetWriteDeadline. 155 | Write(b []byte) (n int, err error) //发送数据到连接 156 | 157 | // Close closes the connection. 158 | // Any blocked Read or Write operations will be unblocked and return errors. 159 | Close() error 160 | 161 | // LocalAddr returns the local network address. 162 | LocalAddr() Addr //本机地址 163 | 164 | // RemoteAddr returns the remote network address. 165 | RemoteAddr() Addr //远程地址 166 | 167 | // SetDeadline sets the read and write deadlines associated 168 | // with the connection. It is equivalent to calling both 169 | // SetReadDeadline and SetWriteDeadline. 170 | // 171 | // A deadline is an absolute time after which I/O operations 172 | // fail with a timeout (see type Error) instead of 173 | // blocking. The deadline applies to all future and pending 174 | // I/O, not just the immediately following call to Read or 175 | // Write. After a deadline has been exceeded, the connection 176 | // can be refreshed by setting a deadline in the future. 177 | // 178 | // An idle timeout can be implemented by repeatedly extending 179 | // the deadline after successful Read or Write calls. 180 | // 181 | // A zero value for t means I/O operations will not time out. 182 | SetDeadline(t time.Time) error //操作的timeout值,0表示不timeout 183 | 184 | // SetReadDeadline sets the deadline for future Read calls 185 | // and any currently-blocked Read call. 186 | // A zero value for t means Read will not time out. 187 | SetReadDeadline(t time.Time) error 188 | 189 | // SetWriteDeadline sets the deadline for future Write calls 190 | // and any currently-blocked Write call. 191 | // Even if write times out, it may return n > 0, indicating that 192 | // some of the data was successfully written. 193 | // A zero value for t means Write will not time out. 194 | SetWriteDeadline(t time.Time) error 195 | } 196 | 197 | ``` 198 | 199 | ## DialTimeout 200 | 201 | 带Timeout 的Dial 202 | 203 | ## Pipe 204 | func Pipe()(Conn,Conn) 205 | 206 | 在内存里将两个conn 同步,建立一个全双工网络。读的一端和写的一端是完全匹配的。两者的数据直接复制,没有任何缓存。 207 | 208 | 例如: Pipe(Conn1,Conn2) 209 | 如果程序向Conn1 写的内容,通过Conn2能读取。 210 | 如果程序向Conn2 写的内容,通过Conn1也能读取。 211 | 212 | ## Listener 213 | 214 | ``` 215 | type Listener interface { 216 | // Accept waits for and returns the next connection to the listener. 217 | Accept() (Conn, error) 218 | 219 | // Close closes the listener. 220 | // Any blocked Accept operations will be unblocked and return errors. 221 | Close() error 222 | 223 | // Addr returns the listener's network address. 224 | Addr() Addr 225 | } 226 | 227 | ``` 228 | 229 | 代码例子 230 | 231 | ``` 232 | // Listen on TCP port 2000 on all available unicast and 233 | // anycast IP addresses of the local system. 234 | l, err := net.Listen("tcp", ":2000") //监听2000端口,返回Listener 235 | if err != nil { 236 | log.Fatal(err) 237 | } 238 | defer l.Close() 239 | for { 240 | // Wait for a connection. 241 | conn, err := l.Accept() //接受到一个新的连接 242 | if err != nil { 243 | log.Fatal(err) 244 | } 245 | // Handle the connection in a new goroutine. 246 | // The loop then returns to accepting, so that 247 | // multiple connections may be served concurrently. 248 | go func(c net.Conn) { 249 | // Echo all incoming data. 250 | io.Copy(c, c) 251 | // Shut down the connection. 252 | c.Close() 253 | }(conn) // 启动一个线程处理这个新的连接,系统接着到Accept等待其他连接 254 | } 255 | ``` 256 | 257 | ## Listen 258 | 259 | func Listen(network, address string) (Listener, error) 260 | 261 | 监听本机网络消息,网络必须是“tcp”,“tcp4”,“tcp6”,“unix”或“unixpacket”。 262 | 263 | 对于TCP网络,如果address参数中的主机为空或文本未指定的IP地址,则Listen将侦听本地系统的所有可用单播和任播IP地址。 264 | 265 | 如果仅仅接收IPv4网络,请使用网络“tcp4”。地址可以使用主机名,但不推荐这样做,因为它将为主机的其中一个(most one)IP地址创建一个监听器。 266 | 267 | 如果address参数中的端口为空或“0”,如“127.0.0.1:”或“[:: 1]:0”,则自动选择端口号。Listener的Addr方法可用于发现所选端口。 268 | 269 | ## ListenPacket 270 | 271 | func ListenPacket(network, address string) (PacketConn, error) 272 | 273 | 返回的是PacketConn, PacketConn支持以下和Conn不同的接口 274 | 275 | ``` go 276 | ReadFrom(b []byte) (n int, addr Addr, err error) 277 | 278 | // WriteTo writes a packet with payload b to addr. 279 | // WriteTo can be made to time out and return 280 | // an Error with Timeout() == true after a fixed time limit; 281 | // see SetDeadline and SetWriteDeadline. 282 | // On packet-oriented connections, write timeouts are rare. 283 | WriteTo(b []byte, addr Addr) (n int, err error) 284 | ``` 285 | 286 | !!! 其他内容 287 | * TCPConn,ListenTCP 288 | * UDPConn,ListenUDP 289 | * UnixConn,ListenUnix 290 | 291 | 这里我不再一一列出。 292 | -------------------------------------------------------------------------------- /requests/requests.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "net/http/httputil" 7 | "net/url" 8 | "strings" 9 | "fmt" 10 | "compress/gzip" 11 | "encoding/json" 12 | "os" 13 | "crypto/tls" 14 | "net/http/cookiejar" 15 | "mime/multipart" 16 | "bytes" 17 | "io" 18 | 19 | ) 20 | 21 | var VERSION string = "0.4" 22 | 23 | type request struct { 24 | httpreq *http.Request 25 | Header *http.Header 26 | Client *http.Client 27 | Debug int 28 | Cookies []*http.Cookie 29 | Forms url.Values 30 | } 31 | 32 | type response struct { 33 | httpresp *http.Response 34 | content []byte 35 | text string 36 | req * request 37 | } 38 | 39 | type Header map[string]string 40 | type Params map[string]string 41 | type Datas map[string]string // for post form 42 | type Files map[string]string // name ,filename 43 | 44 | // {username,password} 45 | type Auth []string 46 | 47 | func Requests() *request { 48 | 49 | req := new(request) 50 | 51 | req.httpreq = &http.Request{ 52 | Method: "GET", 53 | Header: make(http.Header), 54 | Proto: "HTTP/1.1", 55 | ProtoMajor: 1, 56 | ProtoMinor: 1, 57 | } 58 | req.Header = &req.httpreq.Header 59 | req.httpreq.Header.Set("User-Agent", "Go-Requests "+VERSION) 60 | 61 | req.Client = &http.Client{} 62 | 63 | // auto with Cookies 64 | jar, err := cookiejar.New(nil) 65 | if err != nil { 66 | return nil 67 | } 68 | req.Client.Jar = jar 69 | 70 | return req 71 | } 72 | 73 | func Get(origurl string, args ...interface{}) (resp *response) { 74 | req := Requests() 75 | 76 | // call request Get 77 | resp = req.Get(origurl, args...) 78 | return resp 79 | } 80 | 81 | func (req *request) Get(origurl string, args ...interface{}) (resp *response) { 82 | // set params ?a=b&b=c 83 | //set Header 84 | params := []map[string]string{} 85 | 86 | //reset Cookies, 87 | //Client.Do can copy cookie from client.Jar to req.Header 88 | delete(req.httpreq.Header,"Cookie") 89 | 90 | for _, arg := range args { 91 | switch a := arg.(type) { 92 | // arg is Header , set to request header 93 | case Header: 94 | 95 | for k, v := range a { 96 | req.Header.Set(k, v) 97 | } 98 | // arg is "GET" params 99 | // ?title=website&id=1860&from=login 100 | case Params: 101 | params = append(params, a) 102 | case Auth: 103 | // a{username,password} 104 | req.httpreq.SetBasicAuth(a[0],a[1]) 105 | } 106 | } 107 | 108 | disturl, _ := buildURLParams(origurl, params...) 109 | 110 | //prepare to Do 111 | URL, err := url.Parse(disturl) 112 | if err != nil { 113 | return nil 114 | } 115 | req.httpreq.URL = URL 116 | 117 | req.ClientSetCookies() 118 | 119 | req.RequestDebug() 120 | 121 | res, err := req.Client.Do(req.httpreq) 122 | 123 | if err != nil { 124 | fmt.Println(err) 125 | return nil 126 | } 127 | 128 | resp = &response{} 129 | resp.httpresp = res 130 | resp.req = req 131 | resp.ResponseDebug() 132 | return resp 133 | } 134 | 135 | // handle URL params 136 | func buildURLParams(userURL string, params ...map[string]string) (string, error) { 137 | parsedURL, err := url.Parse(userURL) 138 | 139 | if err != nil { 140 | return "", err 141 | } 142 | 143 | parsedQuery, err := url.ParseQuery(parsedURL.RawQuery) 144 | 145 | if err != nil { 146 | return "", nil 147 | } 148 | 149 | for _, param := range params { 150 | for key, value := range param { 151 | parsedQuery.Add(key, value) 152 | } 153 | } 154 | return addQueryParams(parsedURL, parsedQuery), nil 155 | } 156 | 157 | func addQueryParams(parsedURL *url.URL, parsedQuery url.Values) string { 158 | if len(parsedQuery) > 0 { 159 | return strings.Join([]string{strings.Replace(parsedURL.String(), "?"+parsedURL.RawQuery, "", -1), parsedQuery.Encode()}, "?") 160 | } 161 | return strings.Replace(parsedURL.String(), "?"+parsedURL.RawQuery, "", -1) 162 | } 163 | 164 | func (req *request) RequestDebug(){ 165 | 166 | 167 | if req.Debug != 1{ 168 | return 169 | } 170 | 171 | fmt.Println("===========Go RequestDebug ============") 172 | 173 | message, err := httputil.DumpRequestOut(req.httpreq, false) 174 | if err != nil { 175 | return 176 | } 177 | fmt.Println(string(message)) 178 | 179 | if len(req.Client.Jar.Cookies(req.httpreq.URL)) > 0{ 180 | fmt.Println("Cookies:") 181 | for _, cookie := range req.Client.Jar.Cookies(req.httpreq.URL) { 182 | fmt.Println(cookie) 183 | } 184 | } 185 | } 186 | 187 | func (req *request ) SetCookie(cookie *http.Cookie){ 188 | req.Cookies = append(req.Cookies,cookie) 189 | } 190 | 191 | func (req * request) ClearCookies(){ 192 | req.Cookies = req.Cookies[0:0] 193 | } 194 | 195 | func (req * request) ClientSetCookies(){ 196 | 197 | if len(req.Cookies) > 0 { 198 | // 1. Cookies have content, Copy Cookies to Client.jar 199 | // 2. Clear Cookies 200 | req.Client.Jar.SetCookies(req.httpreq.URL, req.Cookies) 201 | req.ClearCookies() 202 | } 203 | 204 | } 205 | 206 | func (req * request) Proxy(proxyurl string){ 207 | 208 | urli := url.URL{} 209 | urlproxy, err:= urli.Parse(proxyurl) 210 | if err != nil { 211 | fmt.Println("Set proxy failed") 212 | return 213 | } 214 | req.Client.Transport = &http.Transport{ 215 | Proxy:http.ProxyURL(urlproxy), 216 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 217 | } 218 | 219 | } 220 | 221 | /**************/ 222 | func (resp *response) ResponseDebug(){ 223 | 224 | 225 | if resp.req.Debug != 1 { 226 | return 227 | } 228 | 229 | fmt.Println("===========Go ResponseDebug ============") 230 | 231 | message, err := httputil.DumpResponse(resp.httpresp, false) 232 | if err != nil { 233 | return 234 | } 235 | 236 | fmt.Println(string(message)) 237 | 238 | } 239 | 240 | func (resp *response) Content() []byte { 241 | 242 | defer resp.httpresp.Body.Close() 243 | var err error 244 | 245 | var Body = resp.httpresp.Body 246 | if resp.httpresp.Header.Get("Content-Encoding") == "gzip" && resp.req.Header.Get("Accept-Encoding") != "" { 247 | // fmt.Println("gzip") 248 | reader, err := gzip.NewReader(Body) 249 | if err != nil { 250 | return nil 251 | } 252 | Body = reader 253 | } 254 | 255 | resp.content, err = ioutil.ReadAll(Body) 256 | if err != nil { 257 | return nil 258 | } 259 | 260 | return resp.content 261 | } 262 | 263 | func (resp *response) Text() string { 264 | if resp.content == nil { 265 | resp.Content() 266 | } 267 | resp.text = string(resp.content) 268 | return resp.text 269 | } 270 | 271 | func (resp *response) SaveFile(filename string) error { 272 | if resp.content == nil { 273 | resp.Content() 274 | } 275 | f, err := os.Create(filename) 276 | if err != nil { 277 | return err 278 | } 279 | defer f.Close() 280 | 281 | _, err = f.Write(resp.content) 282 | f.Sync() 283 | 284 | return err 285 | } 286 | 287 | func (resp *response) Json(v interface{}) error { 288 | if resp.content == nil { 289 | resp.Content() 290 | } 291 | return json.Unmarshal(resp.content, v) 292 | } 293 | 294 | /**************post*************************/ 295 | func Post(origurl string, args ...interface{}) (resp *response) { 296 | req := Requests() 297 | 298 | // call request Get 299 | resp = req.Post(origurl, args...) 300 | return resp 301 | } 302 | 303 | func (req *request) Post(origurl string, args ...interface{}) (resp *response) { 304 | 305 | req.httpreq.Method = "POST" 306 | req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 307 | req.Forms = url.Values{} 308 | 309 | // set params ?a=b&b=c 310 | //set Header 311 | params := []map[string]string{} 312 | datas := []map[string]string{} // POST 313 | files := []map[string]string{} //post file 314 | 315 | //reset Cookies, 316 | //Client.Do can copy cookie from client.Jar to req.Header 317 | delete(req.httpreq.Header,"Cookie") 318 | 319 | for _, arg := range args { 320 | switch a := arg.(type) { 321 | // arg is Header , set to request header 322 | case Header: 323 | 324 | for k, v := range a { 325 | req.Header.Set(k, v) 326 | } 327 | // arg is "GET" params 328 | // ?title=website&id=1860&from=login 329 | case Params: 330 | params = append(params, a) 331 | 332 | case Datas: //Post form data,packaged in body. 333 | datas = append(datas,a) 334 | case Files: 335 | files = append(files,a) 336 | case Auth: 337 | // a{username,password} 338 | req.httpreq.SetBasicAuth(a[0],a[1]) 339 | } 340 | } 341 | 342 | disturl, _ := buildURLParams(origurl, params...) 343 | 344 | if len(files) > 0{ 345 | req.buildFilesAndForms(files,datas) 346 | 347 | } else { 348 | req.buildForms(datas...) 349 | req.setBodyBytes() // set forms to body 350 | } 351 | //prepare to Do 352 | URL, err := url.Parse(disturl) 353 | if err != nil { 354 | return nil 355 | } 356 | req.httpreq.URL = URL 357 | 358 | req.ClientSetCookies() 359 | 360 | req.RequestDebug() 361 | 362 | res, err := req.Client.Do(req.httpreq) 363 | 364 | if err != nil { 365 | fmt.Println(err) 366 | return nil 367 | } 368 | 369 | resp = &response{} 370 | resp.httpresp = res 371 | resp.req = req 372 | resp.ResponseDebug() 373 | return resp 374 | } 375 | 376 | func (req * request)setBodyBytes() { 377 | 378 | // maybe 379 | data := req.Forms.Encode() 380 | req.httpreq.Body = ioutil.NopCloser(strings.NewReader(data)) 381 | req.httpreq.ContentLength = int64(len(data)) 382 | } 383 | 384 | func (req * request) buildFilesAndForms(files []map[string]string,datas []map[string]string){ 385 | 386 | //handle file multipart 387 | 388 | var b bytes.Buffer 389 | w := multipart.NewWriter(&b) 390 | 391 | for _,file := range files{ 392 | for k,v := range file{ 393 | part, err := w.CreateFormFile(k, v) 394 | if err != nil { 395 | fmt.Printf("Upload %s failed!",v) 396 | panic(err) 397 | } 398 | file := openFile(v) 399 | _, err = io.Copy(part, file) 400 | } 401 | } 402 | 403 | for _,data := range datas{ 404 | for k,v := range data{ 405 | w.WriteField(k,v) 406 | } 407 | } 408 | 409 | 410 | w.Close() 411 | // set file header example: 412 | // "Content-Type": "multipart/form-data; boundary=------------------------7d87eceb5520850c", 413 | req.httpreq.Body = ioutil.NopCloser( bytes.NewReader(b.Bytes()) ) 414 | req.httpreq.ContentLength = int64(b.Len()) 415 | req.Header.Set("Content-Type", w.FormDataContentType()) 416 | } 417 | 418 | func (req * request)buildForms(datas ...map[string]string) { 419 | 420 | for _, data := range datas { 421 | for key, value := range data { 422 | req.Forms.Add(key, value) 423 | } 424 | } 425 | 426 | } 427 | 428 | func openFile(filename string)*os.File { 429 | r, err := os.Open(filename) 430 | if err != nil { 431 | panic(err) 432 | } 433 | return r 434 | } 435 | 436 | /*******/ 437 | // I don't want import fmt or delete fmt replay 438 | func empty (){ 439 | _ = fmt.Println 440 | } 441 | -------------------------------------------------------------------------------- /docs/http.md: -------------------------------------------------------------------------------- 1 | ## http 2 | 3 | http 包提供HTTP客户端和服务器的实现。 4 | 5 | Get, Head, Post, 和 PostForm 发出 HTTP (or HTTPS) 请求: 6 | 7 | 几个例子如下: 8 | 9 | ``` 10 | resp, err := http.Get("http://cpython.org/") 11 | ... 12 | resp, err := http.Post("http://cpython.org/upload", "image/jpeg", &buf) 13 | ... 14 | resp, err := http.PostForm("http://cpython.org/form", 15 | url.Values{"key": {"Value"}, "id": {"123"}}) 16 | ``` 17 | 18 | cpython.org 并没有Post接口,请勿测试!!! 19 | 20 | 21 | 如果client接收完数据,必须关闭client。 22 | 23 | ``` 24 | resp, err := http.Get("http://cpython.org/") 25 | if err != nil { 26 | // handle error 27 | } 28 | defer resp.Body.Close() //完成后关闭 29 | body, err := ioutil.ReadAll(resp.Body) 30 | // ... 31 | fmt.Println(body) 32 | ``` 33 | 34 | 35 | ## get 请求的例子 36 | 37 | ``` 38 | client := &http.Client{ 39 | CheckRedirect: redirectPolicyFunc, 40 | } 41 | // 定义一个client,再请求 42 | resp, err := client.Get("http://example.com") 43 | // ... 44 | 45 | // New 一个请求 46 | req, err := http.NewRequest("GET", "http://example.com", nil) 47 | // ... 增加header 48 | req.Header.Add("If-None-Match", `W/"wyzzy"`) 49 | resp, err := client.Do(req) //开始访问 50 | ``` 51 | 52 | 53 | TLS配置 54 | 55 | ``` 56 | tr := &http.Transport{ 57 | MaxIdleConns: 10, 58 | IdleConnTimeout: 30 * time.Second, 59 | DisableCompression: true, 60 | } 61 | client := &http.Client{Transport: tr} 62 | resp, err := client.Get("https://example.com") 63 | ``` 64 | 65 | 66 | ## server 67 | 68 | ``` 69 | http.Handle("/foo", fooHandler) 70 | 71 | http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { 72 | fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) 73 | }) 74 | 75 | log.Fatal(http.ListenAndServe(":8080", nil)) 76 | ``` 77 | 78 | 就看代码,监听了8080端口,等待http连接,根据path绑定了分配了处理函数。 79 | 80 | 81 | ``` 82 | s := &http.Server{ 83 | Addr: ":8080", 84 | Handler: myHandler, 85 | ReadTimeout: 10 * time.Second, 86 | WriteTimeout: 10 * time.Second, 87 | MaxHeaderBytes: 1 << 20, 88 | } 89 | log.Fatal(s.ListenAndServe()) 90 | // 配置了一些参数 91 | ``` 92 | 93 | ## 代码分析 94 | 95 | http 公共methods 96 | 97 | ``` 98 | const ( 99 | MethodGet = "GET" 100 | MethodHead = "HEAD" 101 | MethodPost = "POST" 102 | MethodPut = "PUT" 103 | MethodPatch = "PATCH" // RFC 5789 104 | MethodDelete = "DELETE" 105 | MethodConnect = "CONNECT" 106 | MethodOptions = "OPTIONS" 107 | MethodTrace = "TRACE" 108 | ) 109 | ``` 110 | 111 | var DefaultServeMux = &defaultServeMux //路由 112 | 113 | ``` 114 | Handle // 115 | HandleFunc // 116 | 注册DefaultServeMux 路由函数,处理path路由。 117 | ``` 118 | 119 | 120 | ## server 的两个例子 121 | 122 | ``` 123 | //1. 124 | package main 125 | 126 | import ( 127 | "io" 128 | "net/http" 129 | "log" 130 | ) 131 | 132 | // hello world, the web server 133 | func HelloServer(w http.ResponseWriter, req *http.Request) { 134 | io.WriteString(w, "hello, world!\n") 135 | } 136 | 137 | func main() { 138 | http.HandleFunc("/hello", HelloServer) 139 | log.Fatal(http.ListenAndServe(":12345", nil)) 140 | } 141 | ``` 142 | 143 | 144 | ``` 145 | //2. 146 | import ( 147 | "log" 148 | "net/http" 149 | ) 150 | 151 | func handler(w http.ResponseWriter, req *http.Request) { 152 | w.Header().Set("Content-Type", "text/plain") 153 | w.Write([]byte("This is an example server.\n")) 154 | } 155 | 156 | func main() { 157 | http.HandleFunc("/", handler) 158 | log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/") 159 | err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil) 160 | log.Fatal(err) 161 | } 162 | ``` 163 | 164 | ## Serve 165 | 166 | serve 接收Listener上HTTP接入(连接),为每一个连接着建立一个新的接收线程。每一个服务goroutines(线程)读requests并且调用处理函数。如果处理函数是nil,就是用默认的路由(DefaultServeMux)。 167 | 168 | 169 | func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error 170 | 171 | ServeTLS 接收HTTPS连接。但是参数需要提供CA证书和私钥(域名的私钥是要去专门的机构申请的)。 172 | 173 | ## SetCookie 174 | func SetCookie(w ResponseWriter, cookie *Cookie) 175 | 176 | 这是服务器端函数,给Response添加一个cookies,cookies的key必须正确,不正确cookie的key会被丢弃。 177 | 178 | 179 | ## Client 180 | 181 | ``` 182 | type Client struct { 183 | // Transport specifies the mechanism by which individual 184 | // HTTP requests are made. 185 | // If nil, DefaultTransport is used. 186 | Transport RoundTripper 187 | 188 | //这个部分,我查了一些资料,看了文档,实话讲,我并没有吃透这个部分。 189 | //我只记得有代理部分。其他未GET到其中的含义。希望以后我有机会来补充这个。2018.08.11 190 | 191 | // CheckRedirect specifies the policy for handling redirects. 192 | // If CheckRedirect is not nil, the client calls it before 193 | // following an HTTP redirect. The arguments req and via are 194 | // the upcoming request and the requests made already, oldest 195 | // first. If CheckRedirect returns an error, the Client's Get 196 | // method returns both the previous Response (with its Body 197 | // closed) and CheckRedirect's error (wrapped in a url.Error) 198 | // instead of issuing the Request req. 199 | // As a special case, if CheckRedirect returns ErrUseLastResponse, 200 | // then the most recent response is returned with its body 201 | // unclosed, along with a nil error. 202 | // 203 | // If CheckRedirect is nil, the Client uses its default policy, 204 | // which is to stop after 10 consecutive requests. 205 | CheckRedirect func(req *Request, via []*Request) error 206 | 207 | // 如果请求服务器返回是30x,会使用这个参数来解决redirect请求问题 208 | // Jar specifies the cookie jar. 209 | // 210 | // The Jar is used to insert relevant cookies into every 211 | // outbound Request and is updated with the cookie values 212 | // of every inbound Response. The Jar is consulted for every 213 | // redirect that the Client follows. 214 | // 215 | // If Jar is nil, cookies are only sent if they are explicitly 216 | // set on the Request. 217 | Jar CookieJar 218 | 219 | // 可以使用 setcookies来这是 cookie 220 | // Cookie操作server和client都有涉及到。 221 | 222 | // Timeout specifies a time limit for requests made by this 223 | // Client. The timeout includes connection time, any 224 | // redirects, and reading the response body. The timer remains 225 | // running after Get, Head, Post, or Do return and will 226 | // interrupt reading of the Response.Body. 227 | // 228 | // A Timeout of zero means no timeout. 229 | // 230 | // The Client cancels requests to the underlying Transport 231 | // using the Request.Cancel mechanism. Requests passed 232 | // to Client.Do may still set Request.Cancel; both will 233 | // cancel the request. 234 | // 235 | // For compatibility, the Client will also use the deprecated 236 | // CancelRequest method on Transport if found. New 237 | // RoundTripper implementations should use Request.Cancel 238 | // instead of implementing CancelRequest. 239 | Timeout time.Duration 240 | 241 | 242 | } 243 | 244 | ``` 245 | 246 | Client是一个http 客户端,DefaultClient 是一个使用DefaultTransport可使用客户端。 247 | Client的Transport 是具有内部标示的(缓存了TCP的连接),所以Clients是可以被重用(reused)的,而不是采取多次新建。Client可以安全地同时使用多个线程(goroutine)。 248 | 249 | Client在RoundTripper(Transport)上增加了http的一些细节处理,例如:Cookies和redirects。 250 | 251 | ## Do 252 | func (c *Client) Do(req *Request) (*Response, error) 253 | 254 | Do 发送一个HTTP 请求并且返回HTTP response(响应)。 并按照客户端上配置的策略(例如redirects, cookies,auth)返回HTTP response。 255 | 256 | 如果由客户端策略(如CheckRedirect)引起,或者无法说出HTTP(如网络连接问题),则返回错误。错误不是指:非2xx状态码。(状态码404,也是一种正确的返回值) 257 | 258 | 如果返回的错误为nil,则响应将包含用户应关闭的非零主体。如果Body未关闭,则客户端的基础RoundTripper(通常为Transport)可能无法重新使用到服务器的持久TCP连接以用于后续“保持活动”请求。 259 | 260 | 请求Body(如果非零)将由底层传输关闭,即使出现错误也是如此。 261 | 262 | 出错时,可以忽略任何响应。只有在CheckRedirect失败时才会出现带有非零错误的非零响应,即使这样,返回的Response.Body也已关闭。 263 | 264 | 通常使用Get,Post或PostForm代替Do. 265 | 266 | ## GET 267 | func (c *Client) Get(url string) (resp *Response, err error) 268 | 269 | 获取GET到指定URL的内容。如果响应是以下重定向代码之一,则在调用客户端的CheckRedirect函数后,Get将跟随重定向: 270 | 271 | 301(永久移动) 272 | 302(找到) 273 | 303(见其他) 274 | 307(临时重定向) 275 | 308(永久重定向) 276 | 277 | 如果客户端的CheckRedirect功能失败或者存在HTTP协议错误,则会返回错误。非2xx响应不会导致错误。 278 | 279 | 当err为nil时,resp总是包含一个非零的resp.Body。调用者在完成阅读后应该关闭resp.Body。 280 | 281 | 要使用自定义标头发出请求,请使用NewRequest和Client.Do。 282 | 283 | 284 | ## Head 285 | 286 | func (c *Client) Head(url string) (resp *Response, err error) 287 | 288 | Head向指定的URL发出HEAD。 289 | 290 | ## Post 291 | func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error) 292 | 293 | 发布POST到指定的URL。 294 | 295 | 调用者在完成read后应该关闭resp.Body。 296 | 297 | 如果提供的主体是io.Closer,则在请求后将其关闭。 298 | 299 | 要设置自定义标头,请使用NewRequest和Client.Do。 300 | 301 | 有关如何处理重定向的详细信息,请参阅Client.Do方法文档。 302 | 303 | ## PostForm 304 | 305 | func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) 306 | 307 | PostForm向指定的URL发出POST,数据的键和值URL编码为请求主体。 308 | 309 | Content-Type标头设置为application / x-www-form-urlencoded。要设置其他标头,请使用NewRequest和Client.Do。 310 | 311 | 当err为nil时,resp总是包含一个非零的resp.Body。调用者在完成阅读后应该关闭resp.Body。 312 | 313 | 有关如何处理重定向的详细信息,请参阅Client.Do方法文档。 314 | 315 | ## Cookie 316 | 317 | ``` 318 | type Cookie struct { 319 | Name string 320 | Value string 321 | 322 | Path string // optional 323 | Domain string // optional 324 | Expires time.Time // optional 325 | RawExpires string // for reading cookies only 326 | 327 | // MaxAge=0 means no 'Max-Age' attribute specified. 328 | // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' 329 | // MaxAge>0 means Max-Age attribute present and given in seconds 330 | MaxAge int 331 | Secure bool 332 | HttpOnly bool 333 | Raw string 334 | Unparsed []string // Raw text of unparsed attribute-value pairs 335 | } 336 | ``` 337 | 338 | Cookie表示在HTTP响应的Set-Cookie标头或HTTP请求的Cookie标头中发送的HTTP Cookie。 339 | 340 | 341 | ## CookieJar 342 | 343 | ``` 344 | type CookieJar interface { 345 | // SetCookies handles the receipt of the cookies in a reply for the 346 | // given URL. It may or may not choose to save the cookies, depending 347 | // on the jar's policy and implementation. 348 | SetCookies(u *url.URL, cookies []*Cookie) 349 | 350 | // Cookies returns the cookies to send in a request for the given URL. 351 | // It is up to the implementation to honor the standard cookie use 352 | // restrictions such as in RFC 6265. 353 | Cookies(u *url.URL) []*Cookie 354 | } 355 | ``` 356 | 357 | CookieJar管理HTTP请求中cookie的存储和使用。 358 | 359 | CookieJar的实现必须安全,以便多个goroutine并发使用。 360 | 361 | net / http / cookiejar包提供了一个CookieJar实现。 362 | 363 | 364 | ## Request 365 | 366 | ``` 367 | type Request struct { 368 | // 369 | // HTTP方法(GET,POST,PUT等)。 默认是GET 370 | // 371 | // 372 | // Go's HTTP 客户端请求不支持CONNECT, 373 | Method string 374 | 375 | 376 | // 对于服务器端URL,就是URI 377 | // 对于客户端请求的时候,URL就是要请求的URL地址 378 | URL *url.URL 379 | 380 | // The protocol version for incoming server requests. 381 | // 382 | // For client requests these fields are ignored. The HTTP 383 | // client code always uses either HTTP/1.1 or HTTP/2. 384 | // See the docs on Transport for details. 385 | Proto string // "HTTP/1.0" 386 | ProtoMajor int // 1 387 | ProtoMinor int // 0 388 | 389 | // Header contains the request header fields either received 390 | // by the server or to be sent by the client. 391 | // 392 | // If a server received a request with header lines, 393 | // 394 | // Host: example.com 395 | // accept-encoding: gzip, deflate 396 | // Accept-Language: en-us 397 | // fOO: Bar 398 | // foo: two 399 | // 400 | // then 401 | // 402 | // Header = map[string][]string{ 403 | // "Accept-Encoding": {"gzip, deflate"}, 404 | // "Accept-Language": {"en-us"}, 405 | // "Foo": {"Bar", "two"}, 406 | // } 407 | // 408 | // For incoming requests, the Host header is promoted to the 409 | // Request.Host field and removed from the Header map. 410 | // 411 | // HTTP defines that header names are case-insensitive. The 412 | // request parser implements this by using CanonicalHeaderKey, 413 | // making the first character and any characters following a 414 | // hyphen uppercase and the rest lowercase. 415 | // 416 | // For client requests, certain headers such as Content-Length 417 | // and Connection are automatically written when needed and 418 | // values in Header may be ignored. See the documentation 419 | // for the Request.Write method. 420 | 421 | // 是一个map[string][]string,key是字符串,v是字符串数组 422 | Header Header 423 | 424 | // Body 请求的正文. 425 | // 426 | // 对于客户端可以没有body 427 | 428 | // 服务器端必须返回body,没有就填写EOF,请求结束。 429 | Body io.ReadCloser 430 | 431 | // GetBody 客户端调用返回数据 432 | // 433 | // server unused. 434 | GetBody func() (io.ReadCloser, error) 435 | 436 | // ContentLength 内容的长度 . 437 | // -1 表示不确定. 438 | // >= 0 表示可以从Body中读取这些长度的内容。 439 | 440 | ContentLength int64 441 | 442 | // TransferEncoding lists the transfer encodings from outermost to 443 | // innermost. An empty list denotes the "identity" encoding. 444 | // TransferEncoding can usually be ignored; chunked encoding is 445 | // automatically added and removed as necessary when sending and 446 | // receiving requests. 447 | TransferEncoding []string 448 | 449 | // Close indicates whether to close the connection after 450 | // replying to this request (for servers) or after sending this 451 | // request and reading its response (for clients). 452 | // 453 | // For server requests, the HTTP server handles this automatically 454 | // and this field is not needed by Handlers. 455 | // 456 | // For client requests, setting this field prevents re-use of 457 | // TCP connections between requests to the same hosts, as if 458 | // Transport.DisableKeepAlives were set. 459 | Close bool 460 | 461 | // For server requests Host specifies the host on which the 462 | // URL is sought. Per RFC 2616, this is either the value of 463 | // the "Host" header or the host name given in the URL itself. 464 | // It may be of the form "host:port". For international domain 465 | // names, Host may be in Punycode or Unicode form. Use 466 | // golang.org/x/net/idna to convert it to either format if 467 | // needed. 468 | // 469 | // For client requests Host optionally overrides the Host 470 | // header to send. If empty, the Request.Write method uses 471 | // the value of URL.Host. Host may contain an international 472 | // domain name. 473 | Host string 474 | 475 | // get 请求的时候是 参数 476 | // POST请求的时候是form 数据 477 | Form url.Values 478 | 479 | // PostForm contains the parsed form data from POST, PATCH, 480 | // or PUT body parameters. 481 | // 482 | // This field is only available after ParseForm is called. 483 | // The HTTP client ignores PostForm and uses Body instead. 484 | // server 中使用 485 | PostForm url.Values 486 | 487 | // MultipartForm is the parsed multipart form, including file uploads. 488 | // This field is only available after ParseMultipartForm is called. 489 | // The HTTP client ignores MultipartForm and uses Body instead. 490 | // server中使用 491 | MultipartForm *multipart.Form 492 | 493 | // Trailer specifies additional headers that are sent after the request 494 | // body. 495 | // 496 | // For server requests the Trailer map initially contains only the 497 | // trailer keys, with nil values. (The client declares which trailers it 498 | // will later send.) While the handler is reading from Body, it must 499 | // not reference Trailer. After reading from Body returns EOF, Trailer 500 | // can be read again and will contain non-nil values, if they were sent 501 | // by the client. 502 | // 503 | // For client requests Trailer must be initialized to a map containing 504 | // the trailer keys to later send. The values may be nil or their final 505 | // values. The ContentLength must be 0 or -1, to send a chunked request. 506 | // After the HTTP request is sent the map values can be updated while 507 | // the request body is read. Once the body returns EOF, the caller must 508 | // not mutate Trailer. 509 | // 510 | // Few HTTP clients, servers, or proxies support HTTP trailers. 511 | Trailer Header 512 | 513 | // RemoteAddr allows HTTP servers and other software to record 514 | // the network address that sent the request, usually for 515 | // logging. This field is not filled in by ReadRequest and 516 | // has no defined format. The HTTP server in this package 517 | // sets RemoteAddr to an "IP:port" address before invoking a 518 | // handler. 519 | // This field is ignored by the HTTP client. 520 | RemoteAddr string 521 | 522 | // RequestURI is the unmodified Request-URI of the 523 | // Request-Line (RFC 2616, Section 5.1) as sent by the client 524 | // to a server. Usually the URL field should be used instead. 525 | // It is an error to set this field in an HTTP client request. 526 | RequestURI string 527 | 528 | // TLS allows HTTP servers and other software to record 529 | // information about the TLS connection on which the request 530 | // was received. This field is not filled in by ReadRequest. 531 | // The HTTP server in this package sets the field for 532 | // TLS-enabled connections before invoking a handler; 533 | // otherwise it leaves the field nil. 534 | // This field is ignored by the HTTP client. 535 | TLS *tls.ConnectionState 536 | 537 | // Cancel is an optional channel whose closure indicates that the client 538 | // request should be regarded as canceled. Not all implementations of 539 | // RoundTripper may support Cancel. 540 | // 541 | // For server requests, this field is not applicable. 542 | // 543 | // Deprecated: Use the Context and WithContext methods 544 | // instead. If a Request's Cancel field and context are both 545 | // set, it is undefined whether Cancel is respected. 546 | Cancel <-chan struct{} 547 | 548 | // Response is the redirect response which caused this request 549 | // to be created. This field is only populated during client 550 | // redirects. 551 | Response *Response 552 | // contains filtered or unexported fields 553 | } 554 | 555 | ``` 556 | 557 | 558 | ## NewRequest 559 | 560 | func NewRequest(method, url string, body io.Reader) (*Request, error) 561 | 562 | NewRequest在给定方法,URL和可选主体的情况下返回新请求。 563 | 564 | 如果提供的正文也是io.Closer,则返回的Request.Body设置为body,将由客户端方法Do,Post和PostForm以及Transport.RoundTrip关闭。 565 | 566 | NewRequest返回适用于Client.Do或Transport.RoundTrip的请求。要创建用于测试服务器处理程序的请求,请使用net / http / httptest包中的NewRequest函数,使用ReadRequest,或手动更新Request字段。有关入站和出站请求字段之间的区别,请参阅请求类型的文档。 567 | 568 | 如果body的类型为* bytes.Buffer,* bytes.Reader或* strings.Reader,则返回的请求的ContentLength设置为其精确值(而不是-1),GetBody将被填充(因此307和308重定向可以重放如果ContentLength为0,则Body设置为NoBody。 569 | 570 | 571 | ## AddCookie 572 | func(r * Request)AddCookie(c * Cookie) 573 | 574 | AddCookie为请求添加了一个cookie。根据RFC 6265第5.4节,AddCookie不会附加多个Cookie标头字段。这意味着所有cookie(如果有的话)都写入同一行,用分号分隔。 575 | 576 | ## Cookies, Cookie 577 | 578 | func (r *Request) Cookies() []*Cookie 579 | func (r *Request) Cookie(name string) (*Cookie, error) 580 | 581 | 582 | 583 | ## form 表单或者文件传输 584 | 585 | func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) 586 | 587 | func (r *Request) FormValue(key string) string 588 | 589 | 590 | ## Response 591 | 592 | 593 | ``` 594 | type Response struct { 595 | Status string // e.g. "200 OK" 596 | StatusCode int // e.g. 200 597 | Proto string // e.g. "HTTP/1.0" 598 | ProtoMajor int // e.g. 1 599 | ProtoMinor int // e.g. 0 600 | 601 | // Header maps header keys to values. If the response had multiple 602 | // headers with the same key, they may be concatenated, with comma 603 | // delimiters. (Section 4.2 of RFC 2616 requires that multiple headers 604 | // be semantically equivalent to a comma-delimited sequence.) When 605 | // Header values are duplicated by other fields in this struct (e.g., 606 | // ContentLength, TransferEncoding, Trailer), the field values are 607 | // authoritative. 608 | // 609 | // Keys in the map are canonicalized (see CanonicalHeaderKey). 610 | Header Header 611 | 612 | // Body represents the response body. 613 | // 614 | // The response body is streamed on demand as the Body field 615 | // is read. If the network connection fails or the server 616 | // terminates the response, Body.Read calls return an error. 617 | // 618 | // The http Client and Transport guarantee that Body is always 619 | // non-nil, even on responses without a body or responses with 620 | // a zero-length body. It is the caller's responsibility to 621 | // close Body. The default HTTP client's Transport may not 622 | // reuse HTTP/1.x "keep-alive" TCP connections if the Body is 623 | // not read to completion and closed. 624 | // 625 | // The Body is automatically dechunked if the server replied 626 | // with a "chunked" Transfer-Encoding. 627 | Body io.ReadCloser 628 | 629 | // ContentLength records the length of the associated content. The 630 | // value -1 indicates that the length is unknown. Unless Request.Method 631 | // is "HEAD", values >= 0 indicate that the given number of bytes may 632 | // be read from Body. 633 | ContentLength int64 634 | 635 | // Contains transfer encodings from outer-most to inner-most. Value is 636 | // nil, means that "identity" encoding is used. 637 | TransferEncoding []string 638 | 639 | // Close records whether the header directed that the connection be 640 | // closed after reading Body. The value is advice for clients: neither 641 | // ReadResponse nor Response.Write ever closes a connection. 642 | Close bool 643 | 644 | // Uncompressed reports whether the response was sent compressed but 645 | // was decompressed by the http package. When true, reading from 646 | // Body yields the uncompressed content instead of the compressed 647 | // content actually set from the server, ContentLength is set to -1, 648 | // and the "Content-Length" and "Content-Encoding" fields are deleted 649 | // from the responseHeader. To get the original response from 650 | // the server, set Transport.DisableCompression to true. 651 | Uncompressed bool 652 | // 是否压缩 653 | 654 | // Trailer maps trailer keys to values in the same 655 | // format as Header. 656 | // 657 | // The Trailer initially contains only nil values, one for 658 | // each key specified in the server's "Trailer" header 659 | // value. Those values are not added to Header. 660 | // 661 | // Trailer must not be accessed concurrently with Read calls 662 | // on the Body. 663 | // 664 | // After Body.Read has returned io.EOF, Trailer will contain 665 | // any trailer values sent by the server. 666 | Trailer Header 667 | 668 | // Request is the request that was sent to obtain this Response. 669 | // Request's Body is nil (having already been consumed). 670 | // This is only populated for Client requests. 671 | Request *Request 672 | 673 | // TLS contains information about the TLS connection on which the 674 | // response was received. It is nil for unencrypted responses. 675 | // The pointer is shared between responses and should not be 676 | // modified. 677 | TLS *tls.ConnectionState 678 | } 679 | ``` 680 | 681 | 682 | ## Transfer 例子 683 | 684 | ``` 685 | var DefaultTransport RoundTripper = &Transport{ 686 | Proxy: ProxyFromEnvironment, 687 | DialContext: (&net.Dialer{ 688 | Timeout: 30 * time.Second, 689 | KeepAlive: 30 * time.Second, 690 | DualStack: true, 691 | }).DialContext, 692 | MaxIdleConns: 100, 693 | IdleConnTimeout: 90 * time.Second, 694 | TLSHandshakeTimeout: 10 * time.Second, 695 | ExpectContinueTimeout: 1 * time.Second, 696 | } 697 | ``` 698 | 699 | 原文档后面有sever部分。此次,我没有写。因为我要写几个httpclient的例子了。 700 | --------------------------------------------------------------------------------