轉自: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,使用預設的DefaultServeMuxfunc 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設定cookiefunc 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)
返回一個用於傳輸的代理函式,該函式總是返回相同的URLfunc 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 Client
Client是一個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 stringfunc (d Dir) Open(name string) (File, error)type File
File是透過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 Flusher
responsewriters允許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) Handler
FileServer返回一個使用FileSystem介面提供檔案訪問服務的HTTP處理器。可以使用httpDir來使用作業系統的FileSystem介面實現。其主要用來實現靜態檔案的展示。func NotFoundHandler() Handler
返回一個簡單的請求處理器,該處理器對任何請求都會返回”404 page not found”func RedirectHandler(url string, code int) Handler
使用給定的狀態碼將它接受到的任何請求都重定向到給定的urlfunc 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 HandlerFunc
HandlerFunc 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,如果沒有發現,返回ErrNoCookiefunc (r *Request) Cookies() []*Cookie
返回該請求的所有cookiesfunc (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) error
ParseMultipartForm將請求的主體作為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.minorfunc (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) error
Write方法以有線格式將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
初始化一個新的ServeMuxfunc (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) error
ListenAndServeTLS監聽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型別。