轉載golang中net/http包用法

codestacklinuxer發表於2024-04-29

轉自:https://studygolang.com/articles/5515

1. 前言

http包包含http客戶端和服務端的實現,利用Get,Head,Post,以及PostForm實現HTTP或者HTTPS的請求.

2. 本文分析內容安排

  • 函式
  • 結構

3. 函式

3.1 服務端函式

func Handle(pattern string, handler Handler)將handler按照指定的格式註冊到DefaultServeMux,ServeMux解釋了模式匹配規則
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))同上,主要用來實現動態檔案內容的展示,這點與ServerFile()不同的地方。
func ListenAndServe(addr string, handler Handler) error監聽TCP網路地址addr然後呼叫具有handler的Serve去處理連線請求.通常情況下Handler是nil,使用預設的DefaultServeMux
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error該函式與ListenAndServe功能基本相同,二者不同之處是該函式需要HTTPS連線.也就是說,必須給該服務Serve提供一個包含整數的秘鑰的檔案,如果證書是由證書機構簽署的,那麼證書檔案必須是服務證書之後跟著CA證書.
func ServeFile(w ResponseWriter, r *Request, name string)利用指定的檔案或者目錄的內容來響應相應的請求.
func SetCookie(w ResponseWriter, cookie *Cookie)給w設定cookie
func StatusText(code int) string對於http狀態碼返回文字表示,如果這個code未知,則返回空的字串
func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser該函式類似於io.LimitReader但是該函式是用來限制請求體的大小.與io.LimitReader不同的是,該函式返回一個ReaderCloser,當讀超過限制時,返回一個non-EOF,並且當Close方法呼叫時,關閉底層的reader.該函式組織客戶端惡意傳送大量請求,浪費伺服器資源.
func ParseHTTPVersion(vers string) (major, minor int, ok bool)解析http字串版本進行解析,”HTTP/1.0” 返回 (1, 0, true)
func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error)返回一個用於傳輸的代理函式,該函式總是返回相同的URL
func Redirect(w ResponseWriter, r *Request, urlStr string, code int)返回一個重定向的url給指定的請求,這個重定向url可能是一個相對請求路徑的一個相對路徑.
func Serve(l net.Listener, handler Handler) error該函式接受listener l的傳入http連線,對於每一個連線建立一個新的服務協程,這個服務協程讀取請求然後呼叫handler來給他們響應.handler一般為nil,這樣預設的DefaultServeMux被使用.

3.2 客戶端函式

Client具有Do,Get,Head,Post以及PostForm等方法。 其中Do方法可以對Request進行一系列的設定,而其他的對request設定較少。如果Client使用預設的Client,則其中的Get,Head,Post以及PostForm方法相當於預設的http.Get,http.Post,http.Head以及http.PostForm函式。
func (c *Client) Do(req *Request) (resp *Response, err error)Do傳送http請求並且返回一個http響應,遵守client的策略,如重定向,cookies以及auth等.錯誤經常是由於策略引起的,當err是nil時,resp總會包含一個非nil的resp.body.當呼叫者讀完resp.body之後應該關閉它,如果resp.body沒有關閉,則Client底層RoundTripper將無法重用存在的TCP連線去服務接下來的請求,如果resp.body非nil,則必須對其進行關閉.通常來說,經常使用Get,Post,或者PostForm來替代Do.
func (c *Client) Get(url string) (resp *Response, err error)利用get方法請求指定的url.Get請求指定的頁面資訊,並返回實體主體。
func (c *Client) Head(url string) (resp *Response, err error)利用head方法請求指定的url,Head只返回頁面的首部。
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error)利用post方法請求指定的URl,如果body也是一個io.Closer,則在請求之後關閉它
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error)利用post方法請求指定的url,利用data的key和value作為請求體.
Do方法可以靈活的對request進行配置,然後進行請求。利用http.Client以及http.NewRequest來模擬請求。模擬request中帶有cookie的請求。示例如下:

package main
import (
    // "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strconv"
)
func main() {
    client := &http.Client{}
    request, err := http.NewRequest("GET", "http://www.baidu.com", nil)
    if err != nil {
        fmt.Println(err)
    }
    cookie := &http.Cookie{Name: "userId", Value: strconv.Itoa(12345)}
    request.AddCookie(cookie) //request中新增cookie
    //設定request的header
    request.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
    request.Header.Set("Accept-Charset", "GBK,utf-8;q=0.7,*;q=0.3")
    request.Header.Set("Accept-Encoding", "gzip,deflate,sdch")
    request.Header.Set("Accept-Language", "zh-CN,zh;q=0.8")
    request.Header.Set("Cache-Control", "max-age=0")
    request.Header.Set("Connection", "keep-alive")
    response, err := client.Do(request)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer response.Body.Close()
    fmt.Println(response.StatusCode)
    if response.StatusCode == 200 {
        r, err := ioutil.ReadAll(response.Body)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println(string(r))
    }
}

4. 結構

4.1 Client

type ClientClient是一個http客戶端,預設客戶端(DefaultClient)將使用預設的傳送機制的客戶端.Client的Transport欄位一般會含有內部狀態(快取TCP連線),因此Client型別值應儘量被重用而不是建立新的。多個協程併發使用Clients是安全的.

type Client struct {
// Transport指定執行獨立、單次HTTP請求的機制如果Transport為nil,則使用DefaultTransport。
    Transport RoundTripper
// CheckRedirect指定處理重定向的策略,如果CheckRedirect非nil,client將會在呼叫重定向之前呼叫它。引數req和via是將要執行的請求和已經執行的請求(時間越久的請求優先執行),如果CheckRedirect返回一個錯誤,client的GetGet方法不會傳送請求req,而是回之前得到的響應和該錯誤。如果CheckRedirect為nil,會採用預設策略:在連續10次請求後停止。
    CheckRedirect func(req *Request, via []*Request) error
// Jar指定cookie管理器,如果Jar為nil,在請求中不會傳送cookie,在回覆中cookie也會被忽略。
    Jar CookieJar
// Timeout指定Client請求的時間限制,該超時限制包括連線時間、重定向和讀取response body時間。計時器會在Head,Get,Post或Do方法返回後開始計時並在讀到response.body後停止計時。Timeout為零值表示不設定超時。Client的Transport欄位必須支援CancelRequest方法,否則Client會在嘗試用Head,Get,Post或Do方法執行請求時返回錯誤。Client的Transport欄位預設值(DefaultTransport)支援CancelRequest方法。
    Timeout time.Duration
}

type ConnState表示客戶端連線服務端的狀態,其中ConnState常用狀態變數如下:

const (
    // StateNew代表一個新的連線,將要立刻傳送請求。
    // 連線從這個狀態開始,然後轉變為StateAlive或StateClosed。
    StateNew ConnState = iota
    // StateActive代表一個已經讀取了請求資料1到多個位元組的連線。
    // 用於StateAlive的Server.ConnState回撥函式在將連線交付給處理器之前被觸發,
    // 等到請求被處理完後,Server.ConnState回撥函式再次被觸發。
    // 在請求被處理後,連線狀態改變為StateClosed、StateHijacked或StateIdle。
    StateActive
    // StateIdle代表一個已經處理完了請求、處在閒置狀態、等待新請求的連線。
    // 連線狀態可以從StateIdle改變為StateActive或StateClosed。
    StateIdle
    // 代表一個被劫持的連線。這是一個終止狀態,不會轉變為StateClosed。
    StateHijacked
    // StateClosed代表一個關閉的連線。
    // 這是一個終止狀態。被劫持的連線不會轉變為StateClosed。
    StateClosed
) 

type Cookie常用SetCooker用來給http的請求或者http的response設定cookie

type Cookie struct {

        Name       string  //名字
        Value      string  //值
        Path       string   //路徑
        Domain     string   
        Expires    time.Time //過期時間
        RawExpires string

        // MaxAge=0 意味著 沒有'Max-Age'屬性指定.
        // MaxAge<0 意味著 立即刪除cookie
        // MaxAge>0 意味著設定了MaxAge屬性,並且其單位是秒
        MaxAge   int
        Secure   bool
        HttpOnly bool
        Raw      string
        Unparsed []string // 未解析的屬性值對
}

func (c *Cookie) String() string該函式返回cookie的序列化結果。如果只設定了Name和Value欄位,序列化結果可用於HTTP請求的Cookie頭或者HTTP回覆的Set-Cookie頭;如果設定了其他欄位,序列化結果只能用於HTTP回覆的Set-Cookie頭。
type CookieJar在http請求中,CookieJar管理儲存和使用cookies.Cookiejar的實現必須被多協程併發使用時是安全的.

type CookieJar interface {
        // SetCookies 處理從url接收到的cookie,是否儲存這個cookies取決於jar的策略和實現
        SetCookies(u *url.URL, cookies []*Cookie)

        // Cookies 返回傳送到指定url的cookies
        Cookies(u *url.URL) []*Cookie
}

type Dir使用一個侷限於指定目錄樹的本地檔案系統實現一個檔案系統.一個空目錄被當做當前目錄
type Dir string
func (d Dir) Open(name string) (File, error)type FileFile是透過FileSystem的Open方法返回的,並且能夠被FileServer實現.該方法與*os.File行為表現一樣

type File interface {
        io.Closer
        io.Reader
        Readdir(count int) ([]os.FileInfo, error)
        Seek(offset int64, whence int) (int64, error)
        Stat() (os.FileInfo, error)
}

type FileSystem實現了對一系列指定檔案的訪問,其中檔案路徑之間透過分隔符進行分割

type FileSystem interface {
        Open(name string) (File, error)
}

type Flusherresponsewriters允許http控制器將快取資料重新整理入client.然而如果client是透過http代理連線伺服器,這個快取資料也可能是在整個response結束後才能到達客戶端

type Flusher interface {
        // Flush將任何快取資料傳送到client
        Flush()
}

4.2 Server

type Handler實現Handler介面的物件可以註冊到HTTP服務端,為指定的路徑或者子樹提供服務。ServeHTTP應該將回復的header和資料寫入ResponseWriter介面然後返回。返回意味著該請求已經結束,HTTP服務端可以轉移向該連線上的下一個請求。如果ServeHTTP崩潰panic,那麼ServeHTTP的呼叫者假定這個panic的影響與活動請求是隔離的,二者互不影響.呼叫者恢復panic,將stack trace記錄到錯誤日誌中,然後掛起這個連線.

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

func FileServer(root FileSystem) HandlerFileServer返回一個使用FileSystem介面提供檔案訪問服務的HTTP處理器。可以使用httpDir來使用作業系統的FileSystem介面實現。其主要用來實現靜態檔案的展示。
func NotFoundHandler() Handler返回一個簡單的請求處理器,該處理器對任何請求都會返回”404 page not found”
func RedirectHandler(url string, code int) Handler使用給定的狀態碼將它接受到的任何請求都重定向到給定的url
func StripPrefix(prefix string, h Handler) Handler將請求url.path中移出指定的字首,然後將省下的請求交給handler h來處理,對於那些不是以指定字首開始的路徑請求,該函式返回一個http 404 not found 的錯誤.
func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler具有超時限制的handler,該函式返回的新Handler呼叫h中的ServerHTTP來處理每次請求,但是如果一次呼叫超出時間限制,那麼就會返回給請求者一個503服務請求不可達的訊息,並且在ResponseWriter返回超時錯誤.
其中FileServer經常和StripPrefix一起連用,用來實現靜態檔案展示,舉例如下:

package main
import (
    "fmt"
    "net/http"
)
func main() {
    http.Handle("/test/", http.FileServer(http.Dir("/home/work/"))) ///home/work/test/中必須有內容
    http.Handle("/download/", http.StripPrefix("/download/", http.FileServer(http.Dir("/home/work/"))))
    http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp")))) //127.0.0.1:9999/tmpfiles/訪問的本地檔案/tmp中的內容
    http.ListenAndServe(":9999", nil)
}

type HandlerFuncHandlerFunc type是一個介面卡,透過型別轉換我們可以將普通的函式作為HTTP處理器使用。如果f是一個具有適當簽名的函式,HandlerFunc(f)透過呼叫f實現了Handler介面。

type Hijacker interface {
        // Hijack讓呼叫者接管連線,在呼叫Hijack()後,http server庫將不再對該連線進行處理,對於該連線的管理和關閉責任將由呼叫者接管.
        Hijack() (net.Conn, *bufio.ReadWriter, error) //conn表示連線物件,bufrw代表該連線的讀寫快取物件。
}

Hijacker用法如下所示:

package main
import (
    "fmt"
    "net/http"
)
func HiJack(w http.ResponseWriter, r *http.Request) {
    hj, ok := w.(http.Hijacker)
    if !ok {
        http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
        return
    }
    conn, bufrw, err := hj.Hijack()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    defer conn.Close()
    bufrw.WriteString("Now we're speaking raw TCP. Say hi: \n")
    bufrw.Flush()

    fmt.Fprintf(bufrw, "You said: %s Bye.\n", "Good")
    bufrw.Flush()
}
func main() {
    http.HandleFunc("/hijack", HiJack)
    err := http.ListenAndServe(":9999", nil)
    if err != nil {
        fmt.Println(err)
    }
}

type Request代表客戶端給伺服器端傳送的一個請求.該欄位在伺服器端和客戶端使用時區別很大

type Request struct {
    // Method指定HTTP方法(GET,POST,PUT等)預設使用GET方法。
    Method string
    // URL在服務端表示被請求的URI(uniform resource identifier,統一資源識別符號),在客戶端表示要訪問的URL。
    // 在服務端,URL欄位是解析請求行的URI(儲存在RequestURI欄位)得到的,對大多數請求來說,除了Path和RawQuery之外的欄位都是空字串。
    // 在客戶端,URL的Host欄位指定了要連線的伺服器,而Request的Host欄位指定要傳送的HTTP請求的Host頭的值。
    URL *url.URL
    // 接收到的請求的協議版本。client的Request總是使用HTTP/1.1
    Proto      string // "HTTP/1.0"
    ProtoMajor int    // 1
    ProtoMinor int    // 0
    // Header欄位用來表示HTTP請求的頭域。如果header(多行鍵值對格式)為:
    //    accept-encoding: gzip, deflate
    //    Accept-Language: en-us
    //    Connection: keep-alive
    // 則:
    //    Header = map[string][]string{
    //        "Accept-Encoding": {"gzip, deflate"},
    //        "Accept-Language": {"en-us"},
    //        "Connection": {"keep-alive"},
    //    }
    // HTTP規定header的鍵名(頭名)是區分大小寫的,請求的解析器透過規範化頭域的鍵名來實現這點,即首字母大寫,其他字母小寫,透過"-"進行分割。
    // 在客戶端的請求,可能會被自動新增或重寫Header中的特定的頭,參見Request.Write方法。
    Header Header
    // Body是請求的主體.對於客戶端請求來說,一個nil body意味著沒有body,http Client的Transport欄位負責呼叫Body的Close方法。
    // 在服務端,Body欄位總是非nil的;但在沒有主體時,讀取Body會立刻返回EOF.Server會關閉請求主體,而ServeHTTP處理器不需要關閉Body欄位。
    Body io.ReadCloser
    // ContentLength記錄相關內容的長度.如果為-1,表示長度未知,如果values>=0,表示可以從Body欄位讀取ContentLength位元組資料。
    // 在客戶端,如果Body非nil而該欄位為0,表示不知道Body的長度。
    ContentLength int64
    // TransferEncoding按從最外到最裡的順序列出傳輸編碼,空切片表示"identity"編碼。
    // 本欄位一般會被忽略。當傳送或接受請求時,會自動新增或移除"chunked"傳輸編碼。
    TransferEncoding []string
    // Close在服務端指定是否在回覆請求後關閉連線,在客戶端指定是否在傳送請求後關閉連線。
    Close bool
    // 對於伺服器端請求,Host指定URL指向的主機,可能的格式是host:port.對於客戶請求,Host用來重寫請求的Host頭,如過該欄位為"",Request.Write方法會使用URL.Host來進行賦值。
    Host string
    // Form是解析好的表單資料,包括URL欄位的query引數和POST或PUT的表單資料.本欄位只有在呼叫ParseForm後才有效。在客戶端,會忽略請求中的本欄位而使用Body替代。
    Form url.Values
    // PostForm是解析好的POST或PUT的表單資料.本欄位只有在呼叫ParseForm後才有效。在客戶端,會忽略請求中的本欄位而使用Body替代。
    PostForm url.Values
    // MultipartForm是解析好的多部件表單,包括上傳的檔案.本欄位只有在呼叫ParseMultipartForm後才有效。http客戶端中會忽略MultipartForm並且使用Body替代
    MultipartForm *multipart.Form
    // Trailer指定了在傳送請求體之後額外的headers,在服務端,Trailer欄位必須初始化為只有trailer鍵,所有鍵都對應nil值。
    // (客戶端會宣告哪些trailer會傳送)在處理器從Body讀取時,不能使用本欄位.在從Body的讀取返回EOF後,Trailer欄位會被更新完畢幷包含非nil的值。
    // (如果客戶端傳送了這些鍵值對),此時才可以訪問本欄位。
    // 在客戶端,Trail必須初始化為一個包含將要傳送的鍵值對的對映.(值可以是nil或其終值),ContentLength欄位必須是0或-1,以啟用"chunked"傳輸編碼傳送請求。
    // 在開始傳送請求後,Trailer可以在讀取請求主體期間被修改,一旦請求主體返回EOF,呼叫者就不可再修改Trailer。
    // 幾乎沒有HTTP客戶端、服務端或代理支援HTTP trailer。
    Trailer Header
    // RemoteAddr允許HTTP伺服器和其他軟體記錄該請求的來源地址,該欄位經常用於日誌.本欄位不是ReadRequest函式填寫的,也沒有定義格式。
    // 本包的HTTP伺服器會在呼叫處理器之前設定RemoteAddr為"IP:port"格式的地址.客戶端會忽略請求中的RemoteAddr欄位。
    RemoteAddr string
    // RequestURI是客戶端傳送到服務端的請求中未修改的URI(參見RFC 2616,Section 5.1),如果在http請求中設定該欄位便會報錯.
    RequestURI string
    // TLS欄位允許HTTP伺服器和其他軟體記錄接收到該請求的TLS連線的資訊,本欄位不是ReadRequest函式填寫的。
    // 對啟用了TLS的連線,本包的HTTP伺服器會在呼叫處理器之前設定TLS欄位,否則將設TLS為nil。
    // 客戶端會忽略請求中的TLS欄位。
    TLS *tls.ConnectionState
}

func NewRequest(method, urlStr string, body io.Reader) (*Request, error)利用指定的method,url以及可選的body返回一個新的請求.如果body引數實現了io.Closer介面,Request返回值的Body 欄位會被設定為body,並會被Client型別的Do、Post和PostForm方法以及Transport.RoundTrip方法關閉。
func ReadRequest(b *bufio.Reader) (req *Request, err error)從b中讀取和解析一個請求.
func (r *Request) AddCookie(c *Cookie)給request新增cookie,AddCookie向請求中新增一個cookie.按照RFC 6265 section 5.4的規則,AddCookie不會新增超過一個Cookie頭欄位.這表示所有的cookie都寫在同一行,用分號分隔(cookie內部用逗號分隔屬性)
func (r *Request) Cookie(name string) (*Cookie, error)返回request中指定名name的cookie,如果沒有發現,返回ErrNoCookie
func (r *Request) Cookies() []*Cookie返回該請求的所有cookies
func (r *Request) SetBasicAuth(username, password string)利用提供的使用者名稱和密碼給http基本許可權提供具有一定許可權的header。當使用http基本授權時,使用者名稱和密碼是不加密的
func (r *Request) UserAgent() string如果在request中傳送,該函式返回客戶端的user-Agent

package main
import (
    "fmt"
    "io/ioutil"
    "net/http"
)
func Test() {
    req, err := http.NewRequest("GET", "http://www.baidu.com", nil)
    if err != nil {
        fmt.Println(err)
        return
    }
    req.SetBasicAuth("test", "123456")
    fmt.Println(req.Proto)
    cookie := &http.Cookie{
        Name:  "test",
        Value: "12",
    }
    req.AddCookie(cookie)                     //新增cookie
    fmt.Println(req.Cookie("test"))           //獲取cookie
    fmt.Println(req.Cookies())                //獲取cookies
    req.Header.Set("User-Agent", "useragent") //設定ua
    fmt.Println(req.UserAgent())
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()
    if resp.StatusCode == 200 {
        content, _ := ioutil.ReadAll(resp.Body)
        fmt.Println(string(content))
    }
}
func main() {
    Test()
}

func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error)對於指定格式的key,FormFile返回符合條件的第一個檔案,如果有必要的話,該函式會呼叫ParseMultipartForm和ParseForm。
func (r *Request) FormValue(key string) string返回key獲取的佇列中第一個值。在查詢過程中post和put中的主題引數優先順序高於url中的value。為了訪問相同key的多個值,呼叫ParseForm然後直接檢查RequestForm。
func (r *Request) MultipartReader() (*multipart.Reader, error)如果這是一個有多部分組成的post請求,該函式將會返回一個MIME 多部分reader,否則的話將會返回一個nil和error。使用本函式代替ParseMultipartForm可以將請求body當做流stream來處理。
func (r *Request) ParseForm() error解析URL中的查詢字串,並將解析結果更新到r.Form欄位。對於POST或PUT請求,ParseForm還會將body當作表單解析,並將結果既更新到r.PostForm也更新到r.Form。解析結果中,POST或PUT請求主體要優先於URL查詢字串(同名變數,主體的值在查詢字串的值前面)。如果請求的主體的大小沒有被MaxBytesReader函式設定限制,其大小預設限制為開頭10MB。ParseMultipartForm會自動呼叫ParseForm。重複呼叫本方法是無意義的。
func (r *Request) ParseMultipartForm(maxMemory int64) errorParseMultipartForm將請求的主體作為multipart/form-data解析。請求的整個主體都會被解析,得到的檔案記錄最多 maxMemery位元組儲存在記憶體,其餘部分儲存在硬碟的temp檔案裡。如果必要,ParseMultipartForm會自行呼叫 ParseForm。重複呼叫本方法是無意義的。
func (r *Request) PostFormValue(key string) string返回post或者put請求body指定元素的第一個值,其中url中的引數被忽略。
func (r *Request) ProtoAtLeast(major, minor int) bool檢測在request中使用的http協議是否至少是major.minor
func (r *Request) Referer() string如果request中有refer,那麼refer返回相應的url。Referer在request中是拼錯的,這個錯誤從http初期就已經存在了。該值也可以從Headermap中利用Header[“Referer”]獲取;在使用過程中利用Referer這個方法而不是map的形式的好處是在編譯過程中可以檢查方法的錯誤,而無法檢查map中key的錯誤。
func (r *Request) Write(w io.Writer) errorWrite方法以有線格式將HTTP/1.1請求寫入w(用於將請求寫入下層TCPConn等)。本方法會考慮請求的如下欄位:Host URL Method (defaults to “GET”) Header ContentLength TransferEncoding Body如果存在Body,ContentLength欄位<= 0且TransferEncoding欄位未顯式設定為[“identity”],Write方法會顯式新增”Transfer-Encoding: chunked”到請求的頭域。Body欄位會在傳送完請求後關閉。
func (r *Request) WriteProxy(w io.Writer) error該函式與Write方法類似,但是該方法寫的request是按照http代理的格式去寫。尤其是,按照RFC 2616 Section 5.1.2,WriteProxy會使用絕對URI(包括協議和主機名)來初始化請求的第1行(Request-URI行)。無論何種情況,WriteProxy都會使用r.Host或r.URL.Host設定Host頭。
type Response指對於一個http請求的響應response

type Response struct {
    Status     string // 例如"200 OK"
    StatusCode int    // 例如200
    Proto      string // 例如"HTTP/1.0"
    ProtoMajor int    // 主協議號:例如1
    ProtoMinor int    // 副協議號:例如0
    // Header保管header的key values,如果response中有多個header中具有相同的key,那麼Header中儲存為該鍵對應用逗號分隔串聯起來的這些頭的值// 被本結構體中的其他欄位複製保管的頭(如ContentLength)會從Header中刪掉。Header中的鍵都是規範化的,參見CanonicalHeaderKey函式
    Header Header
    // Body代表response的主體。http的client和Transport確保這個body永遠非nil,即使response沒有body或body長度為0。呼叫者也需要關閉這個body// 如果服務端採用"chunked"傳輸編碼傳送的回覆,Body欄位會自動進行解碼。
    Body io.ReadCloser
    // ContentLength記錄相關內容的長度。
    // 其值為-1表示長度未知(採用chunked傳輸編碼)
    // 除非對應的Request.Method是"HEAD",其值>=0表示可以從Body讀取的位元組數
    ContentLength int64
    // TransferEncoding按從最外到最裡的順序列出傳輸編碼,空切片表示"identity"編碼。
    TransferEncoding []string
    // Close記錄頭域是否指定應在讀取完主體後關閉連線。(即Connection頭)
    // 該值是給客戶端的建議,Response.Write方法的ReadResponse函式都不會關閉連線。
    Close bool
    // Trailer欄位儲存和頭域相同格式的trailer鍵值對,和Header欄位相同型別
    Trailer Header
    // Request是用來獲取此回覆的請求,Request的Body欄位是nil(因為已經被用掉了)這個欄位是被Client型別發出請求並獲得回覆後填充的
    Request *Request
    // TLS包含接收到該回復的TLS連線的資訊。 對未加密的回覆,本欄位為nil。返回的指標是被(同一TLS連線接收到的)回覆共享的,不應被修改。
    TLS *tls.ConnectionState
}

func Get(url string) (resp *Response, err error)利用GET方法對一個指定的URL進行請求,如果response是如下重定向中的一個程式碼,則Get之後將會呼叫重定向內容,最多10次重定向。
301 (永久重定向,告訴客戶端以後應該從新地址訪問)
302 (暫時性重定向,作為HTTP1.0的標準,以前叫做Moved Temporarily,現在叫做Found。現在使用只是為了相容性處理,包括PHP的預設Location重定向用到也是302),注:303和307其實是對302的細化。
303 (對於Post請求,它表示請求已經被處理,客戶端可以接著使用GET方法去請求Location裡的URl)
307 (臨時重定向,對於Post請求,表示請求還沒有被處理,客戶端應該向Location裡的URL重新發起Post請求)
如果有太多次重定向或者有一個http協議錯誤將會導致錯誤。當err為nil時,resp總是包含一個非nil的resp.body,Get是對DefaultClient.Get的一個包裝。
func Head(url string) (resp *Response, err error)該函式功能見net中Head方法功能。該方法與預設的defaultClient中Head方法一致。
func Post(url string, bodyType string, body io.Reader) (resp *Response, err error)該方法與預設的defaultClient中Post方法一致。
func PostForm(url string, data url.Values) (resp *Response, err error)該方法與預設的defaultClient中PostForm方法一致。
func ReadResponse(r *bufio.Reader, req *Request) (*Response, error)ReadResponse從r讀取並返回一個HTTP 回覆。req引數是可選的,指定該回復對應的請求(即是對該請求的回覆)。如果是nil,將假設請 求是GET請求。客戶端必須在結束resp.Body的讀取後關閉它。讀取完畢並關閉後,客戶端可以檢查resp.Trailer欄位獲取回覆的 trailer的鍵值對。(本函式主要用在客戶端從下層獲取回覆)
func (r *Response) Cookies() []*Cookie解析cookie並返回在header中利用set-Cookie設定的cookie值。
func (r *Response) Location() (*url.URL, error)返回response中Location的header值的url。如果該值存在的話,則對於請求問題可以解決相對重定向的問題,如果該值為nil,則返回ErrNOLocation的錯誤。
func (r *Response) ProtoAtLeast(major, minor int) bool判定在response中使用的http協議是否至少是major.minor的形式。
func (r *Response) Write(w io.Writer) error將response中資訊按照線性格式寫入w中。
type ResponseWriter該介面被http handler用來構建一個http response

type ResponseWriter interface {
    // Header返回一個Header型別值,該值會被WriteHeader方法傳送.在呼叫WriteHeader或Write方法後再改變header值是不起作用的。
    Header() Header
    // WriteHeader該方法傳送HTTP回覆的頭域和狀態碼。如果沒有被顯式呼叫,第一次呼叫Write時會觸發隱式呼叫WriteHeader(http.StatusOK)
    // 因此,顯示呼叫WriterHeader主要用於傳送錯誤狀態碼。
    WriteHeader(int)
    // Write向連線中寫入資料,該資料作為HTTP response的一部分。如果被呼叫時還沒有呼叫WriteHeader,本方法會先呼叫WriteHeader(http.StatusOK)
    // 如果Header中沒有"Content-Type"鍵,本方法會使用包函式DetectContentType檢查資料的前512位元組,將返回值作為該鍵的值。
    Write([]byte) (int, error)
}

type RoundTripper該函式是一個執行簡單http事務的介面,該介面在被多協程併發使用時必須是安全的。

type RoundTripper interface {
    // RoundTrip執行單次HTTP事務,返回request的response,RoundTrip不應試圖解析該回復。
    // 尤其要注意,只要RoundTrip獲得了一個回覆,不管該回復的HTTP狀態碼如何,它必須將返回值err設定為nil。
    // 非nil的返回值err應該留給獲取回覆失敗的情況。類似的,RoundTrip不能試圖管理高層協議,如重定向、認證或者cookie。
    // RoundTrip除了從請求的主體讀取並關閉主體之外,不能夠對請求做任何修改,包括(請求的)錯誤。
    // RoundTrip函式接收的請求的URL和Header欄位必須保證是初始化了的。
    RoundTrip(*Request) (*Response, error)
}

func NewFileTransport(fs FileSystem) RoundTripper該函式返回一個RoundTripper介面,服務指定的檔案系統。 返回的RoundTripper介面會忽略接收的請求中的URL主機及其他絕大多數屬性。該函式的典型應用是給Transport型別的值註冊”file”協議。如下所示:

t := &http.Transport{}
t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
c := &http.Client{Transport: t}
res, err := c.Get("file:///etc/passwd")

type ServeMux該函式是一個http請求多路複用器,它將每一個請求的URL和一個註冊模式的列表進行匹配,然後呼叫和URL最匹配的模式的處理器進行後續操作。模式是固定的、由根開始的路徑,如”/favicon.ico”,或由根開始的子樹,如”/images/” (注意結尾的斜槓)。較長的模式優先於較短的模式,因此如果模式”/images/”和”/images/thumbnails/”都註冊了處理器,後一 個處理器會用於路徑以”/images/thumbnails/”開始的請求,前一個處理器會接收到其餘的路徑在”/images/”子樹下的請求。
注意,因為以斜槓結尾的模式代表一個由根開始的子樹,模式”/”會匹配所有的未被其他註冊的模式匹配的路徑,而不僅僅是路徑”/”。
模式也能(可選地)以主機名開始,表示只匹配該主機上的路徑。指定主機的模式優先於一般的模式,因此一個註冊了兩個模式”/codesearch”和”codesearch.google.com/”的處理器不會接管目標為”http://www.google.com/“的請求。
ServeMux還會負責對URL路徑的過濾,將任何路徑中包含”.”或”..”元素的請求重定向到等價的沒有這兩種元素的URL。(參見path.Clean函式)
func NewServeMux() *ServeMux初始化一個新的ServeMux
func (mux *ServeMux) Handle(pattern string, handler Handler)將handler註冊為指定的模式,如果該模式已經有了handler,則會出錯panic。
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))將handler註冊為指定的模式
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)根據指定的r.Method,r.Host以及r.RUL.Path返回一個用來處理給定請求的handler。該函式總是返回一個非nil的 handler,如果path不是一個規範格式,則handler會重定向到其規範path。Handler總是返回匹配該請求的的已註冊模式;在內建重 定向處理器的情況下,pattern會在重定向後進行匹配。如果沒有已註冊模式可以應用於該請求,本方法將返回一個內建的”404 page not found”處理器和一個空字串模式。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)該函式用於將最接近請求url模式的handler分配給指定的請求。
舉例說明servemux的用法:

package main
import (
    "fmt"
    "net/http"
)
func Test(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "just for test!")
}
func main() {
    mux := http.NewServeMux()
    mux.Handle("/", http.FileServer(http.Dir("/home")))
    mux.HandleFunc("/test", Test)
    err := http.ListenAndServe(":9999", mux)
    if err != nil {
        fmt.Println(err)
    }
}

type Server該結構體定義一些用來執行HTTP Server的引數,如果Server預設為0的話,那麼這也是一個有效的配置。

type Server struct {
    Addr           string        // 監聽的TCP地址,如果為空字串會使用":http"
    Handler        Handler       // 呼叫的處理器,如為nil會呼叫http.DefaultServeMux
    ReadTimeout    time.Duration // 請求的讀取操作在超時前的最大持續時間
    WriteTimeout   time.Duration // 回覆的寫入操作在超時前的最大持續時間
    MaxHeaderBytes int           // 請求的頭域最大長度,如為0則用DefaultMaxHeaderBytes
    TLSConfig      *tls.Config   // 可選的TLS配置,用於ListenAndServeTLS方法
    // TLSNextProto(可選地)指定一個函式來在一個NPN型協議升級出現時接管TLS連線的所有權。
    // 對映的鍵為商談的協議名;對映的值為函式,該函式的Handler引數應處理HTTP請求,
    // 並且初始化Handler.ServeHTTP的*Request引數的TLS和RemoteAddr欄位(如果未設定)。
    // 連線在函式返回時會自動關閉。
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
    // ConnState欄位指定一個可選的回撥函式,該函式會在一個與客戶端的連線改變狀態時被呼叫。
    // 參見ConnState型別和相關常數獲取細節。
    ConnState func(net.Conn, ConnState)
    // ErrorLog指定一個可選的日誌記錄器,用於記錄接收連線時的錯誤和處理器不正常的行為。
    // 如果本欄位為nil,日誌會透過log包的標準日誌記錄器寫入os.Stderr。
    ErrorLog *log.Logger
    // 內含隱藏或非匯出欄位
}

func (srv *Server) ListenAndServe() error監聽TCP網路地址srv.Addr然後呼叫Serve來處理接下來連線的請求。如果srv.Addr是空的話,則使用“:http”。
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) errorListenAndServeTLS監聽srv.Addr確定的TCP地址,並且會呼叫Serve方法處理接收到的連線。必須提供證書檔案和對應的私鑰文 件。如果證書是由權威機構簽發的,certFile引數必須是順序串聯的服務端證書和CA證書。如果srv.Addr為空字串,會使 用”:https”。
func (srv *Server) Serve(l net.Listener) error接受Listener l的連線,建立一個新的服務協程。該服務協程讀取請求然後呼叫srv.Handler來應答。實際上就是實現了對某個埠進行監聽,然後建立相應的連線。
func (s *Server) SetKeepAlivesEnabled(v bool)該函式控制是否http的keep-alives能夠使用,預設情況下,keep-alives總是可用的。只有資源非常緊張的環境或者服務端在關閉程序中時,才應該關閉該功能。
舉例說明Server的用法:

package main
import (
    "fmt"
    "net/http"
)
func Test(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "just for test!")
}
func main() {
    newserver := http.Server{
        Addr:         ":9992",
        ReadTimeout:  0,
        WriteTimeout: 0,
    }
    mux := http.NewServeMux()
    mux.Handle("/", http.FileServer(http.Dir("/home")))
    mux.HandleFunc("/test", Test)
    newserver.Handler = mux
    err := newserver.ListenAndServe()
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(err)
    // err := http.ListenAndServe(":9999", mux)
    // if err != nil {
    //     fmt.Println(err)
    // }
}

type Transport該結構體實現了RoundTripper介面,支援HTTP,HTTPS以及HTTP代理,TranSport也能快取連線供將來使用。

type Transport struct {
    // Proxy指定一個對給定請求返回代理的函式。如果該函式返回了非nil的錯誤值,請求的執行就會中斷並返回該錯誤。
    // 如果Proxy為nil或返回nil的*URL值,將不使用代理。
    Proxy func(*Request) (*url.URL, error)
    // Dial指定建立未加密TCP連線的dial函式。如果Dial為nil,會使用net.Dial。
    Dial func(network, addr string) (net.Conn, error)
  // DialTls利用一個可選的dial函式來為非代理的https請求建立一個TLS連線。如果DialTLS為nil的話,那麼使用Dial和TLSClientConfig。
  //如果DialTLS被設定,那麼Dial鉤子不被用於HTTPS請求和TLSClientConfig並且TLSHandshakeTimeout被忽略。返回的net.conn預設已經經過了TLS握手協議。
  DialTLS func(network, addr string) (net.Conn, error) 
    // TLSClientConfig指定用於tls.Client的TLS配置資訊。如果該欄位為nil,會使用預設的配置資訊。
    TLSClientConfig *tls.Config
    // TLSHandshakeTimeout指定等待TLS握手完成的最長時間。零值表示不設定超時。
    TLSHandshakeTimeout time.Duration
    // 如果DisableKeepAlives為真,不同HTTP請求之間TCP連線的重用將被阻止。
    DisableKeepAlives bool
    // 如果DisableCompression為真,會禁止Transport在請求中沒有Accept-Encoding頭時,
    // 主動新增"Accept-Encoding: gzip"頭,以獲取壓縮資料。
    // 如果Transport自己請求gzip並得到了壓縮後的回覆,它會主動解壓縮回復的主體。
    // 但如果使用者顯式的請求gzip壓縮資料,Transport是不會主動解壓縮的。
    DisableCompression bool
    // 如果MaxIdleConnsPerHost不為0,會控制每個主機下的最大閒置連線數目。
    // 如果MaxIdleConnsPerHost為0,會使用DefaultMaxIdleConnsPerHost。
    MaxIdleConnsPerHost int
    // ResponseHeaderTimeout指定在傳送完請求(包括其可能的主體)之後,
    // 等待接收服務端的回覆的頭域的最大時間。零值表示不設定超時。
    // 該時間不包括讀取回復主體的時間。
    ResponseHeaderTimeout time.Duration
}

func (t *Transport) CancelRequest(req *Request)透過關閉連線來取消傳送中的請求。
func (t *Transport) CloseIdleConnections()關閉所有之前請求但目前處於空閒狀態的連線。該方法並不中斷任何正在使用的連線。
func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper)RegisterProtocol註冊一個新的名為scheme的協議。t會將使用scheme協議的請求轉交給rt。rt有責任模擬HTTP請求的語義。RegisterProtocol可以被其他包用於提供”ftp”或”file”等協議的實現。
func (t *Transport) RoundTrip(req *Request) (resp *Response, err error)該函式實現了RoundTripper介面,對於高層http客戶端支援,例如處理cookies以及重定向,檢視Get,Post以及Client型別。

相關文章