入门 简单get请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" "io/ioutil" "net/http" ) func main () { resp, err := http.Get("https://www.liwenzhou.com/" ) if err != nil { fmt.Printf("get failed, err:%v\n" , err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("read from resp.Body failed, err:%v\n" , err) return } fmt.Print(string (body)) }
带参数get请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import ( "fmt" "io/ioutil" "net/http" "net/url" ) func main () { apiUrl := "http://127.0.0.1:9090/get" data := url.Values{} data.Set("name" , "小王子" ) data.Set("age" , "18" ) u, err := url.ParseRequestURI(apiUrl) if err != nil { fmt.Printf("parse url requestUrl failed, err:%v\n" , err) } u.RawQuery = data.Encode() fmt.Println(u.String()) resp, err := http.Get(u.String()) if err != nil { fmt.Printf("post failed, err:%v\n" , err) return } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("get resp failed, err:%v\n" , err) return } fmt.Println(string (b)) }
对应的Server端HandlerFunc
1 2 3 4 5 6 7 8 func getHandler (w http.ResponseWriter, r *http.Request) { defer r.Body.Close() data := r.URL.Query() fmt.Println(data.Get("name" )) fmt.Println(data.Get("age" )) answer := `{"status": "ok"}` w.Write([]byte (answer)) }
post请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport ( "fmt" "io/ioutil" "net/http" "strings" ) func main () { url := "http://127.0.0.1:9090/post" contentType := "application/json" data := `{"name":"小王子","age":18}` resp, err := http.Post(url, contentType, strings.NewReader(data)) if err != nil { fmt.Printf("post failed, err:%v\n" , err) return } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("get resp failed, err:%v\n" , err) return } fmt.Println(string (b)) }
对应的Server端HandlerFunc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func postHandler (w http.ResponseWriter, r *http.Request) { defer r.Body.Close() r.ParseForm() fmt.Println(r.PostForm) fmt.Println(r.PostForm.Get("name" ), r.PostForm.Get("age" )) b, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Printf("read request.Body failed, err:%v\n" , err) return } fmt.Println(string (b)) answer := `{"status": "ok"}` w.Write([]byte (answer)) }
postForm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "net/http" "fmt" "io/ioutil" "net/url" ) func main () { postParam := url.Values{ "mobile" : {"xxxxxx" }, "isRemberPwd" : {"1" }, } resp, err := http.PostForm("http://www.maimaiche.com/loginRegister/login.do" , postParam) if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } fmt.Println(string (body)) }
Go Http客户端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package mainimport ( "fmt" "net/http" "log" "reflect" "bytes" ) func main () { resp, err := http.Get("http://www.baidu.com" ) if err != nil { log.Println(err) return } defer resp.Body.Close() headers := resp.Header for k, v := range headers { fmt.Printf("k=%v, v=%v\n" , k, v) } fmt.Printf("resp status %s,statusCode %d\n" , resp.Status, resp.StatusCode) fmt.Printf("resp Proto %s\n" , resp.Proto) fmt.Printf("resp content length %d\n" , resp.ContentLength) fmt.Printf("resp transfer encoding %v\n" , resp.TransferEncoding) fmt.Printf("resp Uncompressed %t\n" , resp.Uncompressed) fmt.Println(reflect.TypeOf(resp.Body)) buf := bytes.NewBuffer(make ([]byte , 0 , 512 )) length, _ := buf.ReadFrom(resp.Body) fmt.Println(len (buf.Bytes())) fmt.Println(length) fmt.Println(string (buf.Bytes())) }
设置头参数、cookie 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package mainimport ( "net/http" "strings" "fmt" "io/ioutil" "log" "encoding/json" ) func main () { client := &http.Client{} req, err := http.NewRequest("POST" , "http://www.maimaiche.com/loginRegister/login.do" , strings.NewReader("mobile=xxxxxxxxx&isRemberPwd=1" )) if err != nil { log.Println(err) return } req.Header.Set("Content-Type" , "application/x-www-form-urlencoded; charset=UTF-8" ) resp, err := client.Do(req) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Println(err) return } fmt.Println(resp.Header.Get("Content-Type" )) type Result struct { Msg string Status string Obj string } result := &Result{} json.Unmarshal(body, result) if result.Status == "1" { fmt.Println(result.Msg) } else { fmt.Println("login error" ) } fmt.Println(result) }
server 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" "net/http" ) func hello (w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n" ) } func headers (w http.ResponseWriter, req *http.Request) { for name, headers := range req.Header { for _, h := range headers { fmt.Fprintf(w, "%v: %v\n" , name, h) } } } func main () { http.HandleFunc("/hello" , hello) http.HandleFunc("/headers" , headers) http.ListenAndServe(":8090" , nil ) }
HandlerFunc HandlerFunc
实际上是以函数类型 func(ResponseWriter, *Request)
为底层类型,为 HandlerFunc
类型定义了方法 ServeHTTP
。是的,Go 语言允许为(基于)函数的类型定义方法。Serve.Handle()
方法只接受类型为接口 Handler
的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 type Handler interface { ServeHTTP(ResponseWriter, *Request) } func (mux *ServeMux) Handle (pattern string , handler Handler) { if mux.m == nil { mux.m = make (map [string ]muxEntry) } e := muxEntry{h: handler, pattern: pattern} if pattern[len (pattern)-1 ] == '/' { mux.es = appendSorted(mux.es, e) } mux.m[pattern] = e }
显然 HandlerFunc
实现了接口 Handler
。HandlerFunc
类型只是为了方便注册函数类型的处理器。我们当然可以直接定义一个实现 Handler
接口的类型,然后注册该类型的实例:
1 2 3 4 5 6 7 type greeting string func (g greeting) ServeHTTP (w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, g) } http.Handle("/greeting" , greeting("Welcome, dj" ))
Web 服务器创建启动流程
Hijacker 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type Hijacker interface { Hijack() (net.Conn, *bufio.ReadWriter, error) }
Hijack()
可以将 HTTP 对应的 TCP 连接取出,连接在 Hijack()
之后,HTTP 的相关操作就会受到影响,调用方需要负责去关闭连接。
也就是说,Hijack 接管了 HTTP 的 Socket 连接,之后 Golang 的内置 HTTP 库和 HTTPServer 库将不会管理这个 Socket 连接的生命周期,这个生命周期已经划给 Hijacker 了。
看一个简单的例子。
1 2 3 4 5 6 7 8 9 10 11 func handle1 (w http.ResponseWriter, r *http.Request) { hj, _ := w.(http.Hijacker) conn, buf, _ := hj.Hijack() defer conn.Close() buf.WriteString("hello world" ) buf.Flush() } func handle2 (w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world" ) }
问题来了,上面两个 handle
方法有什么区别呢?很简单,同样是 http 请求,返回的结果一个遵循 http 协议,一个不遵循。
1 2 3 4 5 6 7 8 ➜ ~ curl -i http: hello world% ➜ ~ curl -i http: HTTP/1.1 200 OK Date: Thu, 14 Jun 2018 07 :51 :31 GMT Content-Length: 11 Content-Type : text/plain; charset=utf-8 hello world%
分别是以上两者的返回,可以看到,hijack 之后的返回,虽然 body 是相同的,但是完全没有遵循 http 协议。(废话,别人都说了 hijack 之后返回了 body 然后直接关闭了,哪来的 headers)
但我们还是要看看为啥..
1 2 3 4 5 6 7 8 9 10 func (c *conn) serve (ctx context.Context) { ... serverHandler{c.server}.ServeHTTP(w, w.req) w.cancelCtx() if c.hijacked() { return } w.finishRequest() ... }
这是 net/http 包中的方法,也是 http 路由的核心方法。调用 ServeHTTP
(也就是上边的 handle 方法)方法,如果被 hijack
了就直接 return 了,而一般的 http 请求会经过后边的 finishRequest
方法,加入 headers 等并关闭连接。
Hijack
方法,一般在在创建连接阶段使用 HTTP 连接,后续自己完全处理 connection。符合这样的使用场景的并不多,基于 HTTP 协议的 rpc 算一个,从 HTTP 升级到 WebSocket 也算一个。
go 中自带的 rpc 可以直接复用 http server 处理请求的那一套流程去创建连接,连接创建完毕后再使用 Hijack
方法拿到连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func (server *server) servehttp (w http.responsewriter, req *http.request) { if req.method != "connect" { w.header().set("content-type" , "text/plain; charset=utf-8" ) w.writeheader(http.statusmethodnotallowed) io.writestring(w, "405 must connect\n" ) return } conn, _, err := w.(http.hijacker).hijack() if err != nil { log.print ("rpc hijacking " , req.remoteaddr, ": " , err.error()) return } io.writestring(conn, "http/1.0 " +connected+"\n\n" ) server.serveconn(conn) }
客户端通过向服务端发送 method 为 connect 的请求创建连接,创建成功后即可开始 rpc 调用。
websocket 中的应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func (s Server) ServeHTTP (w http.ResponseWriter, req *http.Request) { s.serveWebSocket(w, req) } func (s Server) serveWebSocket (w http.ResponseWriter, req *http.Request) { rwc, buf, err := w.(http.Hijacker).Hijack() if err != nil { panic ("Hijack failed: " + err.Error()) } defer rwc.Close() conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) if err != nil { return } if conn == nil { panic ("unexpected nil conn" ) } s.Handler(conn) }
websocket 在创建连接的阶段与 http 使用相同的协议,而在后边的数据传输的过程中使用了他自己的协议,符合了 Hijack
的用途。通过 serveWebSocket
方法将 HTTP 协议升级到 Websocket 协议。