golang之網絡開發

1. TCP/UDP Server/Client開發

net包提供network I/O開發接口,包括TCP/IPUDPDNSUnix domain socketshtml

經常使用開發通常僅須要最基礎接口或函數:golang

服務器:net.Listen()net.Accept()web

ln, err := net.Listen("tcp", ":8080") if err != nil { // handle error
} for { conn, err := ln.Accept() if err != nil { // handle error
 } go handleConnection(conn) }

客戶端:net.Dial()express

conn, err := net.Dial("tcp", "golang.org:80") if err != nil { // handle error
} fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n") status, err := bufio.NewReader(conn).ReadString('\n') // ...

相關API定義:編程

func Listen(network, address string) (Listener, error) func (l *TCPListener) Accept() (Conn, error) func Dial(network, address string) (Conn, error)

2. web開發

net/http包爲web開發提供支持,能很簡單地對Web的路由,靜態文件,模版,cookie等數據進行設置和操做。服務器

主要是兩步:設置訪問路由,設置監聽端口(轉發路由:根據用戶請求調用不一樣路由函數)。cookie

type HandlerFunc func(w http.ResponseWriter, r *http.Request)
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) // ServeHTTP calls f(w, r).
func HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request))
type Handler interface {
  ServeHTTP(http.ResponseWriter, *http.Request)
}
func Handle(pattern string, handler http.Handler)
func ListenAndServe(addr string, handler http.Handler) error

ListenAndServe starts an HTTP server with a given address and handler. The handler is usually nil, which means to use DefaultServeMux. 若此時給定Handler,則採用給定的Handler(給定的handler需實現ServeHTTP()函數的接口)。網絡

Handle and HandleFunc add handlers to DefaultServeMux.閉包

Handle()的參數爲地址匹配字符串和handler接口,handler須要實現ServeHTTP()函數的接口。併發

HandleFunc()的參數是地址匹配字符串和函數handle,函數handle等同http.Handler接口要實現的函數。

http.Handle("/foo", fooHandler) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) })
http.Handle("/zoo", http.HandlerFunc(zoo)) // 直接將函數zoo賦值爲http.HandlerFunc log.Fatal(http.ListenAndServe(
":8080", nil))
package main import ( "fmt"
    "net/http" _"html"
    "log" ) type fooHandler struct{ } func (f fooHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "Hello, first!\n") } func main(){ http.Handle("/foo", fooHandler{}) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "Hello, %q\n", r.URL.Path) }) log.Fatal(http.ListenAndServe(":8088", nil)) } # go build -o first first.go # ./first & $ curl localhost:8088/foo Hello, first! $ curl localhost:8088/bar?house=wang Hello, "/bar"
Handler示例

簡單應用

Get, Head, Post and PostForm make HTTP or HTTPS requests:

resp, err := http.Get("http://example.com/") ... resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) ... resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}}) ... defer resp.Body.Close() // when finished
body, err := ioutil.ReadAll(resp.Body)

複雜應用

client := &http.Client{ CheckRedirect: redirectPolicyFunc, } resp, err := client.Get("http://example.com") // ...
 req, err := http.NewRequest("GET", "http://example.com", nil) // ...
req.Header.Add("If-None-Match", `W/"wyzzy"`) resp, err := client.Do(req) // ...
1)經過建立Client控制HTTP客戶端client headers, redirect policy and other settings
tr := &http.Transport{ MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, DisableCompression: true, } client := &http.Client{Transport: tr} resp, err := client.Get("https://example.com")
2)經過建立Transport控制客戶端三層四層配置,如TLS,keepalives,compression等

注:Client和Transport可併發運行。

s := &http.Server{ Addr: ":8080", Handler: myHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } log.Fatal(s.ListenAndServe())
3)經過建立Server控制服務器端配置

經常使用量和函數

const ( MethodGet = "GET" MethodHead = "HEAD" MethodPost = "POST" MethodPut = "PUT" MethodPatch = "PATCH" // RFC 5789
    MethodDelete  = "DELETE" MethodConnect = "CONNECT" MethodOptions = "OPTIONS" MethodTrace = "TRACE" )const DefaultMaxHeaderBytes = 1 << 20 // 1 MB
const DefaultMaxIdleConnsPerHost = 2
var DefaultServeMux = &defaultServeMux var ErrHandlerTimeout = errors.New("http: Handler timeout")
const ( StatusContinue = 100 // RFC 7231, 6.2.1
    StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
    StatusProcessing         = 102 // RFC 2518, 10.1
    StatusEarlyHints         = 103 // RFC 8297
 StatusOK = 200 // RFC 7231, 6.3.1
    StatusCreated              = 201 // RFC 7231, 6.3.2
    StatusAccepted             = 202 // RFC 7231, 6.3.3
    StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
    StatusNoContent            = 204 // RFC 7231, 6.3.5
    StatusResetContent         = 205 // RFC 7231, 6.3.6
    StatusPartialContent       = 206 // RFC 7233, 4.1
    StatusMultiStatus          = 207 // RFC 4918, 11.1
    StatusAlreadyReported      = 208 // RFC 5842, 7.1
    StatusIMUsed               = 226 // RFC 3229, 10.4.1
 StatusMultipleChoices = 300 // RFC 7231, 6.4.1
    StatusMovedPermanently = 301 // RFC 7231, 6.4.2
    StatusFound            = 302 // RFC 7231, 6.4.3
    StatusSeeOther         = 303 // RFC 7231, 6.4.4
    StatusNotModified      = 304 // RFC 7232, 4.1
    StatusUseProxy         = 305 // RFC 7231, 6.4.5
 StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
    StatusPermanentRedirect = 308 // RFC 7538, 3
 StatusBadRequest = 400 // RFC 7231, 6.5.1
    StatusUnauthorized                 = 401 // RFC 7235, 3.1
    StatusPaymentRequired              = 402 // RFC 7231, 6.5.2
    StatusForbidden                    = 403 // RFC 7231, 6.5.3
    StatusNotFound                     = 404 // RFC 7231, 6.5.4
    StatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5
    StatusNotAcceptable                = 406 // RFC 7231, 6.5.6
    StatusProxyAuthRequired            = 407 // RFC 7235, 3.2
    StatusRequestTimeout               = 408 // RFC 7231, 6.5.7
    StatusConflict                     = 409 // RFC 7231, 6.5.8
    StatusGone                         = 410 // RFC 7231, 6.5.9
    StatusLengthRequired               = 411 // RFC 7231, 6.5.10
    StatusPreconditionFailed           = 412 // RFC 7232, 4.2
    StatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11
    StatusRequestURITooLong            = 414 // RFC 7231, 6.5.12
    StatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13
    StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
    StatusExpectationFailed            = 417 // RFC 7231, 6.5.14
    StatusTeapot                       = 418 // RFC 7168, 2.3.3
    StatusMisdirectedRequest           = 421 // RFC 7540, 9.1.2
    StatusUnprocessableEntity          = 422 // RFC 4918, 11.2
    StatusLocked                       = 423 // RFC 4918, 11.3
    StatusFailedDependency             = 424 // RFC 4918, 11.4
    StatusTooEarly                     = 425 // RFC 8470, 5.2.
    StatusUpgradeRequired              = 426 // RFC 7231, 6.5.15
    StatusPreconditionRequired         = 428 // RFC 6585, 3
    StatusTooManyRequests              = 429 // RFC 6585, 4
    StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
    StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3
 StatusInternalServerError = 500 // RFC 7231, 6.6.1
    StatusNotImplemented                = 501 // RFC 7231, 6.6.2
    StatusBadGateway                    = 502 // RFC 7231, 6.6.3
    StatusServiceUnavailable            = 503 // RFC 7231, 6.6.4
    StatusGatewayTimeout                = 504 // RFC 7231, 6.6.5
    StatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6
    StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
    StatusInsufficientStorage           = 507 // RFC 4918, 11.5
    StatusLoopDetected                  = 508 // RFC 5842, 7.2
    StatusNotExtended                   = 510 // RFC 2774, 7
    StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)
StatusOK = 200 // RFC 7231, 6.3.1
func Error(w ResponseWriter, error string, code int) Error replies to the request with the specified error message and HTTP code. It does not otherwise end the request; the caller should ensure no further writes are done to w. The error message should be plain text. func DetectContentType(data []byte) string DetectContentType always returns a valid MIME type: if it cannot determine a more specific one, it returns "application/octet-stream". func Handle(pattern string, handler Handler) Handle registers the handler for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched. func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) HandleFunc registers the handler function for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched. func ListenAndServe(addr string, handler Handler) error The handler is typically nil, in which case the DefaultServeMux is used. ListenAndServe always returns a non-nil error. func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error func NotFound(w ResponseWriter, r *Request) NotFound replies to the request with an HTTP 404 not found error. ParseHTTPVersion parses a HTTP version string. "HTTP/1.0" returns (1, 0, true). func Redirect(w ResponseWriter, r *Request, url string, code int) The provided code should be in the 3xx range and is usually StatusMovedPermanently, StatusFound or StatusSeeOther. If the Content-Type header has not been set, Redirect sets it to "text/html; charset=utf-8" and writes a small HTML body. 
Setting the Content-Type header to any value, including nil, disables that behavior. func Serve(l net.Listener, handler Handler) error Serve accepts incoming HTTP connections on the listener l, creating a new service goroutine for each.
The service goroutines read requests and then call handler to reply to them. The handler
is typically nil, in which case the DefaultServeMux is used. Serve always returns a non-nil error. func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) ServeContent replies to the request using the content in the provided ReadSeeker.
The main benefit of ServeContent over io.Copy is that it handles Range requests properly, sets the MIME type, and handles If-Match,
If-Unmodified-Since, If-None-Match, If-Modified-Since, and If-Range requests. func ServeFile(w ResponseWriter, r *Request, name string) ServeFile replies to the request with the contents of the named file or directory. func StatusText(code int) string StatusText returns a text for the HTTP status code. It returns the empty string if the code is unknown.
func Error(w ResponseWriter, error string, code int) Error replies to the request with the specified error message and HTTP code. It does not otherwise end the request; the caller should ensure no further writes are done to w. The error message should be plain text. func DetectContentType(data []byte) string DetectContentType always returns a valid MIME type: if it cannot determine a more specific one, it returns "application/octet-stream".
type Response struct { Status string // e.g. "200 OK"
    StatusCode int    // e.g. 200
    Proto      string // e.g. "HTTP/1.0"
    ProtoMajor int    // e.g. 1
    ProtoMinor int    // e.g. 0 // Header maps header keys to values. If the response had multiple // headers with the same key, they may be concatenated, with comma // delimiters. (RFC 7230, section 3.2.2 requires that multiple headers // be semantically equivalent to a comma-delimited sequence.) When // Header values are duplicated by other fields in this struct (e.g., // ContentLength, TransferEncoding, Trailer), the field values are // authoritative. //
    // Keys in the map are canonicalized (see CanonicalHeaderKey).
 Header Header // Body represents the response body. //
    // The response body is streamed on demand as the Body field // is read. If the network connection fails or the server // terminates the response, Body.Read calls return an error. //
    // The http Client and Transport guarantee that Body is always // non-nil, even on responses without a body or responses with // a zero-length body. It is the caller's responsibility to // close Body. The default HTTP client's Transport may not // reuse HTTP/1.x "keep-alive" TCP connections if the Body is // not read to completion and closed. //
    // The Body is automatically dechunked if the server replied // with a "chunked" Transfer-Encoding. //
    // As of Go 1.12, the Body will also implement io.Writer // on a successful "101 Switching Protocols" response, // as used by WebSockets and HTTP/2's "h2c" mode.
 Body io.ReadCloser // ContentLength records the length of the associated content. The // value -1 indicates that the length is unknown. Unless Request.Method // is "HEAD", values >= 0 indicate that the given number of bytes may // be read from Body.
 ContentLength int64 // Contains transfer encodings from outer-most to inner-most. Value is // nil, means that "identity" encoding is used.
    TransferEncoding []string

    // Close records whether the header directed that the connection be // closed after reading Body. The value is advice for clients: neither // ReadResponse nor Response.Write ever closes a connection.
    Close bool

    // Uncompressed reports whether the response was sent compressed but // was decompressed by the http package. When true, reading from // Body yields the uncompressed content instead of the compressed // content actually set from the server, ContentLength is set to -1, // and the "Content-Length" and "Content-Encoding" fields are deleted // from the responseHeader. To get the original response from // the server, set Transport.DisableCompression to true.
    Uncompressed bool // Go 1.7 // Trailer maps trailer keys to values in the same // format as Header. //
    // The Trailer initially contains only nil values, one for // each key specified in the server's "Trailer" header // value. Those values are not added to Header. //
    // Trailer must not be accessed concurrently with Read calls // on the Body. //
    // After Body.Read has returned io.EOF, Trailer will contain // any trailer values sent by the server.
 Trailer Header // Request is the request that was sent to obtain this Response. // Request's Body is nil (having already been consumed). // This is only populated for Client requests.
    Request *Request // TLS contains information about the TLS connection on which the // response was received. It is nil for unencrypted responses. // The pointer is shared between responses and should not be // modified.
    TLS *tls.ConnectionState // Go 1.3
}
http.Response
type Request struct { // Method specifies the HTTP method (GET, POST, PUT, etc.). // For client requests, an empty string means GET. //
    // Go's HTTP client does not support sending a request with // the CONNECT method. See the documentation on Transport for // details.
    Method string

    // URL specifies either the URI being requested (for server // requests) or the URL to access (for client requests). //
    // For server requests, the URL is parsed from the URI // supplied on the Request-Line as stored in RequestURI. For // most requests, fields other than Path and RawQuery will be // empty. (See RFC 7230, Section 5.3) //
    // For client requests, the URL's Host specifies the server to // connect to, while the Request's Host field optionally // specifies the Host header value to send in the HTTP // request.
    URL *url.URL // The protocol version for incoming server requests. //
    // For client requests, these fields are ignored. The HTTP // client code always uses either HTTP/1.1 or HTTP/2. // See the docs on Transport for details.
    Proto      string // "HTTP/1.0"
    ProtoMajor int    // 1
    ProtoMinor int    // 0 // Header contains the request header fields either received // by the server or to be sent by the client. //
    // If a server received a request with header lines, //
    // Host: example.com // accept-encoding: gzip, deflate // Accept-Language: en-us // fOO: Bar // foo: two //
    // then //
    // Header = map[string][]string{ // "Accept-Encoding": {"gzip, deflate"}, // "Accept-Language": {"en-us"}, // "Foo": {"Bar", "two"}, // } //
    // For incoming requests, the Host header is promoted to the // Request.Host field and removed from the Header map. //
    // HTTP defines that header names are case-insensitive. The // request parser implements this by using CanonicalHeaderKey, // making the first character and any characters following a // hyphen uppercase and the rest lowercase. //
    // For client requests, certain headers such as Content-Length // and Connection are automatically written when needed and // values in Header may be ignored. See the documentation // for the Request.Write method.
 Header Header // Body is the request's body. //
    // For client requests, a nil body means the request has no // body, such as a GET request. The HTTP Client's Transport // is responsible for calling the Close method. //
    // For server requests, the Request Body is always non-nil // but will return EOF immediately when no body is present. // The Server will close the request body. The ServeHTTP // Handler does not need to.
 Body io.ReadCloser // GetBody defines an optional func to return a new copy of // Body. It is used for client requests when a redirect requires // reading the body more than once. Use of GetBody still // requires setting Body. //
    // For server requests, it is unused.
    GetBody func() (io.ReadCloser, error) // Go 1.8 // ContentLength records the length of the associated content. // The value -1 indicates that the length is unknown. // Values >= 0 indicate that the given number of bytes may // be read from Body. //
    // For client requests, a value of 0 with a non-nil Body is // also treated as unknown.
 ContentLength int64 // TransferEncoding lists the transfer encodings from outermost to // innermost. An empty list denotes the "identity" encoding. // TransferEncoding can usually be ignored; chunked encoding is // automatically added and removed as necessary when sending and // receiving requests.
    TransferEncoding []string

    // Close indicates whether to close the connection after // replying to this request (for servers) or after sending this // request and reading its response (for clients). //
    // For server requests, the HTTP server handles this automatically // and this field is not needed by Handlers. //
    // For client requests, setting this field prevents re-use of // TCP connections between requests to the same hosts, as if // Transport.DisableKeepAlives were set.
    Close bool

    // For server requests, Host specifies the host on which the // URL is sought. For HTTP/1 (per RFC 7230, section 5.4), this // is either the value of the "Host" header or the host name // given in the URL itself. For HTTP/2, it is the value of the // ":authority" pseudo-header field. // It may be of the form "host:port". For international domain // names, Host may be in Punycode or Unicode form. Use // golang.org/x/net/idna to convert it to either format if // needed. // To prevent DNS rebinding attacks, server Handlers should // validate that the Host header has a value for which the // Handler considers itself authoritative. The included // ServeMux supports patterns registered to particular host // names and thus protects its registered Handlers. //
    // For client requests, Host optionally overrides the Host // header to send. If empty, the Request.Write method uses // the value of URL.Host. Host may contain an international // domain name.
    Host string

    // Form contains the parsed form data, including both the URL // field's query parameters and the PATCH, POST, or PUT form data. // This field is only available after ParseForm is called. // The HTTP client ignores Form and uses Body instead.
 Form url.Values // PostForm contains the parsed form data from PATCH, POST // or PUT body parameters. //
    // This field is only available after ParseForm is called. // The HTTP client ignores PostForm and uses Body instead.
    PostForm url.Values // Go 1.1 // MultipartForm is the parsed multipart form, including file uploads. // This field is only available after ParseMultipartForm is called. // The HTTP client ignores MultipartForm and uses Body instead.
    MultipartForm *multipart.Form // Trailer specifies additional headers that are sent after the request // body. //
    // For server requests, the Trailer map initially contains only the // trailer keys, with nil values. (The client declares which trailers it // will later send.) While the handler is reading from Body, it must // not reference Trailer. After reading from Body returns EOF, Trailer // can be read again and will contain non-nil values, if they were sent // by the client. //
    // For client requests, Trailer must be initialized to a map containing // the trailer keys to later send. The values may be nil or their final // values. The ContentLength must be 0 or -1, to send a chunked request. // After the HTTP request is sent the map values can be updated while // the request body is read. Once the body returns EOF, the caller must // not mutate Trailer. //
    // Few HTTP clients, servers, or proxies support HTTP trailers.
 Trailer Header // RemoteAddr allows HTTP servers and other software to record // the network address that sent the request, usually for // logging. This field is not filled in by ReadRequest and // has no defined format. The HTTP server in this package // sets RemoteAddr to an "IP:port" address before invoking a // handler. // This field is ignored by the HTTP client.
    RemoteAddr string

    // RequestURI is the unmodified request-target of the // Request-Line (RFC 7230, Section 3.1.1) as sent by the client // to a server. Usually the URL field should be used instead. // It is an error to set this field in an HTTP client request.
    RequestURI string

    // TLS allows HTTP servers and other software to record // information about the TLS connection on which the request // was received. This field is not filled in by ReadRequest. // The HTTP server in this package sets the field for // TLS-enabled connections before invoking a handler; // otherwise it leaves the field nil. // This field is ignored by the HTTP client.
    TLS *tls.ConnectionState // Cancel is an optional channel whose closure indicates that the client // request should be regarded as canceled. Not all implementations of // RoundTripper may support Cancel. //
    // For server requests, this field is not applicable. //
    // Deprecated: Set the Request's context with NewRequestWithContext // instead. If a Request's Cancel field and context are both // set, it is undefined whether Cancel is respected.
    Cancel <-chan struct{} // Go 1.5 // Response is the redirect response which caused this request // to be created. This field is only populated during client // redirects.
    Response *Response // Go 1.7 // contains filtered or unexported fields
}
http.Request

HTTP/2

HTTP/2協議握手分2種方式,一種叫h2,一種叫h2c。

h2要求必須使用TLS加密,在TLS握手期間會順帶完成HTTPS/2協議的協商,若是協商失敗(好比客戶端不支持或者服務端不支持),則會使用HTTPS/1繼續後續通信。

h2c不使用TLS,而是多了一次基於HTTP協議的握手往返來完成向HTTP/2協議的升級,通常不建議使用

Golang的http庫在設計API時並無支持用戶使用h2c,而是鼓勵使用h2。

GO的http庫默認支持HTTP/2協議,只要咱們使用TLS則會默認啓動HTTP/2特性,若是協商失敗則蛻化爲HTTPS/1。。

對http Client或者Server作一些更加定製化的配置時,就會覆蓋掉http庫的默認行爲,從而致使沒法啓用HTTP/2協議。

只要server端開啓TLS服務便可啓用HTTP/2。

package main import ( "fmt"
    "net/http"
    "os"
    "strconv" ) var Addr string = ":8088" func handler(w http.ResponseWriter, r *http.Request){ r.ParseForm() // fmt.Println(r.Form) // fmt.Println(r.Form["numa"]) // fmt.Println(r.Form["numb"])
    numa, _:= strconv.Atoi(r.Form["numa"][0]) numb, _:= strconv.Atoi(r.Form["numb"][0]) fmt.Fprintf(w, "%d\n", numa+numb) } func main(){ http.HandleFunc("/", handler) _, err := os.Open("cert/server.crt") if err != nil { fmt.Println("Can't open server.crt") panic(err) } fmt.Printf("listen...[%s]\n", Addr) err = http.ListenAndServeTLS(Addr, "cert/server.crt", "cert/server.key", nil) if err != nil { fmt.Println(err) } }
sever.go
package main import ( "log"
    "net/http" ) func main(){ srv := &http.Server{Addr: ":8088", Handler: http.HandlerFunc(handle)} log.Printf("Serving on https://server:8088") log.Fatal(srv.ListenAndServeTLS("cert/server.crt", "cert/server.key")) } func handle(w http.ResponseWriter, r *http.Request){ w.Write([]byte("Hello")) }

除了使用ServeTLS/ListenAndServeTLS來啓動支持HTTPS/2特性的服務端以外,還能夠經過http2.ConfigureServer來爲http.Server啓動HTTPS/2特性並直接使用Serve來啓動服務。

package main import ( "fmt"
    "crypto/tls"
    "crypto/x509"
    "flag"
    "io/ioutil"
    "log"
    "net/http"
    "golang.org/x/net/http2" ) var addr = flag.String("addr", "https://server:8088?numa=4&numb=6", "connect to") var httpVer = flag.Int("httpVer", 2, "HTTP version") func main(){ flag.Parse() client := &http.Client{} caCert, err := ioutil.ReadFile("cert/ca.crt") if err != nil { log.Fatalf("Reading server certificate: %s", err) } pool := x509.NewCertPool() pool.AppendCertsFromPEM(caCert) tlsConfig := &tls.Config{ RootCAs: pool, } switch *httpVer { case 1: client.Transport = &http.Transport { TLSClientConfig: tlsConfig, } case 2: client.Transport = &http2.Transport { TLSClientConfig: tlsConfig, } } resp, err := client.Get(*addr) if err != nil { log.Fatalf("Failed get: %s", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatalf("Failed reading response body: %s", err) } fmt.Printf("Response %d: %s\nbody: %s\n", resp.StatusCode, resp.Proto, string(body)) }

client定製後要開啓HTTP/2,須要配置Transport(或使用http2.ConfigureTransport(transport))。

$ ./cli -httpVer=1 Response 200: HTTP/1.1 body: 10 $ ./cli -httpVer=20 Response 200: HTTP/2.0 body: 10 

cookie相關

A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an HTTP response or the Cookie header of an HTTP request.

See https://tools.ietf.org/html/rfc6265 for details.

type Cookie struct { Name string Value string Path string    // optional
    Domain     string    // optional
    Expires    time.Time // optional
    RawExpires string    // for reading cookies only // MaxAge=0 means no 'Max-Age' attribute specified. // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' // MaxAge>0 means Max-Age attribute present and given in seconds
    MaxAge   int Secure bool HttpOnly bool SameSite SameSite // Go 1.11
    Raw      string Unparsed []string // Raw text of unparsed attribute-value pairs
} func (c *Cookie) String() string String returns the serialization of the cookie for use in a Cookie header (if only Name and Value are set) or 
a Set-Cookie response header (if other fields are set). If c is nil or c.Name is invalid, the empty string is returned. func SetCookie(w ResponseWriter, cookie *Cookie) SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
The provided cookie must have a valid Name. Invalid cookies may be silently dropped.

r.URL參考net/url包,定義以下:

type URL struct { Scheme string Opaque string    // encoded opaque data
    User       *Userinfo // username and password information
    Host       string    // host or host:port
    Path       string    // path (relative paths may omit leading slash)
    RawPath    string    // encoded path hint (see EscapedPath method); added in Go 1.5
    ForceQuery bool      // append a query ('?') even if RawQuery is empty; added in Go 1.7
    RawQuery   string    // encoded query values, without '?'
    Fragment   string    // fragment for references, without '#'
}
resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}})

示例應用以下:

表單form可由兩種方式提交GET或POST,GET方式form表單鍵值對直接跟在url後(?分割,&連接),POST方式Form以請求body方式發送。

import ( "fmt"
    "net/http"
    "strings"
    "log" ) func main(){ http.HandleFunc("/", sayhelloName)  // 設置訪問路由
    err := http.ListenAndServe(":9090", nil) // 設置監聽端口
    if err != nil { log.Fatal("ListenAndServe: ", err) } } func sayhelloName(w http.ResponseWriter, r *http.Request){ r.ParseForm() fmt.Println("---------------") fmt.Println(r.Form) fmt.Println("path:", r.URL.Path) fmt.Println("scheme:", r.URL.Scheme) fmt.Println(r.Form["url_long"]) fmt.Println("====") for k, v := range r.Form{ fmt.Println("key:", k) fmt.Println("val:", strings.Join(v, " ")) } fmt.Fprintf(w, "Hello wang!") }

godoc提供了一個web開發示例Writing Web Applications,用到了net/http,html/template, regexp,閉包等技術:

package main import ( _"fmt"
    "io/ioutil"
    "net/http"
    "log"
    "html/template"
    "regexp" _"errors" ) type Page struct { Title string Body []byte } func (p *Page) save() error { filename := p.Title + ".txt"
    return ioutil.WriteFile(filename, p.Body, 0600) } func loadPage(title string) (*Page, error){ filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return &Page{Title: title, Body : body}, nil } var templates *template.Template var validPath  *regexp.Regexp func main(){ /* p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")} p1.save() p2, _ := loadPage("TestPage") fmt.Println(string(p2.Body)) */
    // import templates // panic when failed
    templates = template.Must(template.ParseFiles("edit.html", "view.html")) // validation
    validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$") /* http.HandleFunc("/view/", viewHandler) http.HandleFunc("/edit/", editHandler) http.HandleFunc("/save/", saveHandler) */ http.HandleFunc("/view/", makeHandler(viewHandler)) http.HandleFunc("/edit/", makeHandler(editHandler)) http.HandleFunc("/save/", makeHandler(saveHandler)) log.Fatal(http.ListenAndServe(":8090", nil)) } /* func getTitle(w http.ResponseWriter, r *http.Request)(string, error){ m := validPath.FindStringSubmatch(r.URL.Path) if m == nil { http.NotFound(w, r) return "", errors.New("Invalid Page Title") } return m[2], nil // The title is the second subexpression } */ func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request){ m := validPath.FindStringSubmatch(r.URL.Path) if m == nil { http.NotFound(w, r) return } fn(w, r, m[2]) } } /* func viewHandler(w http.ResponseWriter, r *http.Request){ title := r.URL.Path[len("/view/"):] p, _ := loadPage(title) fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body) } */

//func viewHandler(w http.ResponseWriter, r *http.Request){
func viewHandler(w http.ResponseWriter, r *http.Request, title string){ // title := r.URL.Path[len("/view/"):] /* title, err := getTitle(w, r) if err != nil { return } */ p, err:= loadPage(title) if err != nil { http.Redirect(w, r, "/edit/"+title, http.StatusFound) return } //t, _:= template.ParseFiles("view.html") //t.Execute(w, p)
    renderTemplate(w, "view", p) } /* func editHandler(w http.ResponseWriter, r *http.Request){ title := r.URL.Path[len("/edit/"):] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } fmt.Fprintf(w, "<h1>Editing %s</h1>" + "<form action=\"/save/%s\" method=\"POST\">" + "<textarea name=\"body\">%s</textarea><br>" + "<input type=\"submit\" value=\"Save\">" + "</form>", p.Title, p.Title, p.Body) } */
//func editHandler(w http.ResponseWriter, r*http.Request){
func editHandler(w http.ResponseWriter, r*http.Request, title string){ // title := r.URL.Path[len("/edit/"):] /* title, err := getTitle(w, r) if err != nil { return } */ p, err := loadPage(title) if err != nil { p = &Page{Title: title} } // t, _:= template.ParseFiles(edit.html) // t.Execute(w, p)
    renderTemplate(w, "edit", p) } //func saveHandler(w http.ResponseWriter, r *http.Request){
func saveHandler(w http.ResponseWriter, r *http.Request, title string){ // title := r.URL.Path[len("/save/"):] /* title , err := getTitle(w, r) if err != nil { return } */ body := r.FormValue("body") p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } http.Redirect(w, r, "/view/"+title, http.StatusFound) } func renderTemplate(w http.ResponseWriter, tmpl string, p *Page){ /* t, err := template.ParseFiles(tmpl + ".html") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } err = t.Execute(w, p) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } */ err := templates.ExecuteTemplate(w, tmpl+".html", p) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
View Code

 

參考:

  1. https://golang.google.cn/pkg/net/

  2. https://www.kancloud.cn/kancloud/the-way-to-go/165098 go入門指南

  3. http://wiki.jikexueyuan.com/project/go-web-programming/03.3.html go web編程

  4. Writing Web Applications

  5. Go語言練習:網絡編程實例——簡易圖片上傳網站 photoweb
  6. Go 源碼學習之--net/http

  7. Go語言的http/2服務器功能及客戶端使用

  8. Golang爬蟲全攻略  網絡應用解析

相關文章
相關標籤/搜索