/http/client.go

http://github.com/petar/GoHTTP · Go · 295 lines · 147 code · 28 blank · 120 comment · 38 complexity · 3c80cbd24656d69bf86582d2113076b3 MD5 · raw file

  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Primitive HTTP client. See RFC 2616.
  5. package http
  6. import (
  7. "encoding/base64"
  8. "fmt"
  9. "io"
  10. "os"
  11. "strings"
  12. "url"
  13. )
  14. // A Client is an HTTP client. Its zero value (DefaultClient) is a usable client
  15. // that uses DefaultTransport.
  16. //
  17. // The Client's Transport typically has internal state (cached
  18. // TCP connections), so Clients should be reused instead of created as
  19. // needed. Clients are safe for concurrent use by multiple goroutines.
  20. //
  21. // Client is not yet very configurable.
  22. type Client struct {
  23. Transport RoundTripper // if nil, DefaultTransport is used
  24. // If CheckRedirect is not nil, the client calls it before
  25. // following an HTTP redirect. The arguments req and via
  26. // are the upcoming request and the requests made already,
  27. // oldest first. If CheckRedirect returns an error, the client
  28. // returns that error instead of issue the Request req.
  29. //
  30. // If CheckRedirect is nil, the Client uses its default policy,
  31. // which is to stop after 10 consecutive requests.
  32. CheckRedirect func(req *Request, via []*Request) os.Error
  33. }
  34. // DefaultClient is the default Client and is used by Get, Head, and Post.
  35. var DefaultClient = &Client{}
  36. // RoundTripper is an interface representing the ability to execute a
  37. // single HTTP transaction, obtaining the Response for a given Request.
  38. //
  39. // A RoundTripper must be safe for concurrent use by multiple
  40. // goroutines.
  41. type RoundTripper interface {
  42. // RoundTrip executes a single HTTP transaction, returning
  43. // the Response for the request req. RoundTrip should not
  44. // attempt to interpret the response. In particular,
  45. // RoundTrip must return err == nil if it obtained a response,
  46. // regardless of the response's HTTP status code. A non-nil
  47. // err should be reserved for failure to obtain a response.
  48. // Similarly, RoundTrip should not attempt to handle
  49. // higher-level protocol details such as redirects,
  50. // authentication, or cookies.
  51. //
  52. // RoundTrip may modify the request. The request Headers field is
  53. // guaranteed to be initialized.
  54. RoundTrip(req *Request) (resp *Response, err os.Error)
  55. }
  56. // Given a string of the form "host", "host:port", or "[ipv6::address]:port",
  57. // return true if the string includes a port.
  58. func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
  59. // Used in Send to implement io.ReadCloser by bundling together the
  60. // bufio.Reader through which we read the response, and the underlying
  61. // network connection.
  62. type readClose struct {
  63. io.Reader
  64. io.Closer
  65. }
  66. // Do sends an HTTP request and returns an HTTP response, following
  67. // policy (e.g. redirects, cookies, auth) as configured on the client.
  68. //
  69. // A non-nil response always contains a non-nil resp.Body.
  70. //
  71. // Callers should close resp.Body when done reading from it. If
  72. // resp.Body is not closed, the Client's underlying RoundTripper
  73. // (typically Transport) may not be able to re-use a persistent TCP
  74. // connection to the server for a subsequent "keep-alive" request.
  75. //
  76. // Generally Get, Post, or PostForm will be used instead of Do.
  77. func (c *Client) Do(req *Request) (resp *Response, err os.Error) {
  78. if req.Method == "GET" || req.Method == "HEAD" {
  79. return c.doFollowingRedirects(req)
  80. }
  81. return send(req, c.Transport)
  82. }
  83. // send issues an HTTP request. Caller should close resp.Body when done reading from it.
  84. func send(req *Request, t RoundTripper) (resp *Response, err os.Error) {
  85. if t == nil {
  86. t = DefaultTransport
  87. if t == nil {
  88. err = os.NewError("no http.Client.Transport or http.DefaultTransport")
  89. return
  90. }
  91. }
  92. // Most the callers of send (Get, Post, et al) don't need
  93. // Headers, leaving it uninitialized. We guarantee to the
  94. // Transport that this has been initialized, though.
  95. if req.Header == nil {
  96. req.Header = make(Header)
  97. }
  98. info := req.URL.RawUserinfo
  99. if len(info) > 0 {
  100. if req.Header == nil {
  101. req.Header = make(Header)
  102. }
  103. req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(info)))
  104. }
  105. return t.RoundTrip(req)
  106. }
  107. // True if the specified HTTP status code is one for which the Get utility should
  108. // automatically redirect.
  109. func shouldRedirect(statusCode int) bool {
  110. switch statusCode {
  111. case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
  112. return true
  113. }
  114. return false
  115. }
  116. // Get issues a GET to the specified URL. If the response is one of the following
  117. // redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
  118. //
  119. // 301 (Moved Permanently)
  120. // 302 (Found)
  121. // 303 (See Other)
  122. // 307 (Temporary Redirect)
  123. //
  124. // Caller should close r.Body when done reading from it.
  125. //
  126. // Get is a convenience wrapper around DefaultClient.Get.
  127. func Get(url string) (r *Response, err os.Error) {
  128. return DefaultClient.Get(url)
  129. }
  130. // Get issues a GET to the specified URL. If the response is one of the
  131. // following redirect codes, Get follows the redirect after calling the
  132. // Client's CheckRedirect function.
  133. //
  134. // 301 (Moved Permanently)
  135. // 302 (Found)
  136. // 303 (See Other)
  137. // 307 (Temporary Redirect)
  138. //
  139. // Caller should close r.Body when done reading from it.
  140. func (c *Client) Get(url string) (r *Response, err os.Error) {
  141. req, err := NewRequest("GET", url, nil)
  142. if err != nil {
  143. return nil, err
  144. }
  145. return c.doFollowingRedirects(req)
  146. }
  147. func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) {
  148. // TODO: if/when we add cookie support, the redirected request shouldn't
  149. // necessarily supply the same cookies as the original.
  150. var base *url.URL
  151. redirectChecker := c.CheckRedirect
  152. if redirectChecker == nil {
  153. redirectChecker = defaultCheckRedirect
  154. }
  155. var via []*Request
  156. req := ireq
  157. urlStr := "" // next relative or absolute URL to fetch (after first request)
  158. for redirect := 0; ; redirect++ {
  159. if redirect != 0 {
  160. req = new(Request)
  161. req.Method = ireq.Method
  162. req.Header = make(Header)
  163. req.URL, err = base.Parse(urlStr)
  164. if err != nil {
  165. break
  166. }
  167. if len(via) > 0 {
  168. // Add the Referer header.
  169. lastReq := via[len(via)-1]
  170. if lastReq.URL.Scheme != "https" {
  171. req.Header.Set("Referer", lastReq.URL.String())
  172. }
  173. err = redirectChecker(req, via)
  174. if err != nil {
  175. break
  176. }
  177. }
  178. }
  179. urlStr = req.URL.String()
  180. if r, err = send(req, c.Transport); err != nil {
  181. break
  182. }
  183. if shouldRedirect(r.StatusCode) {
  184. r.Body.Close()
  185. if urlStr = r.Header.Get("Location"); urlStr == "" {
  186. err = os.NewError(fmt.Sprintf("%d response missing Location header", r.StatusCode))
  187. break
  188. }
  189. base = req.URL
  190. via = append(via, req)
  191. continue
  192. }
  193. return
  194. }
  195. method := ireq.Method
  196. err = &url.Error{method[0:1] + strings.ToLower(method[1:]), urlStr, err}
  197. return
  198. }
  199. func defaultCheckRedirect(req *Request, via []*Request) os.Error {
  200. if len(via) >= 10 {
  201. return os.NewError("stopped after 10 redirects")
  202. }
  203. return nil
  204. }
  205. // Post issues a POST to the specified URL.
  206. //
  207. // Caller should close r.Body when done reading from it.
  208. //
  209. // Post is a wrapper around DefaultClient.Post
  210. func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
  211. return DefaultClient.Post(url, bodyType, body)
  212. }
  213. // Post issues a POST to the specified URL.
  214. //
  215. // Caller should close r.Body when done reading from it.
  216. func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
  217. req, err := NewRequest("POST", url, body)
  218. if err != nil {
  219. return nil, err
  220. }
  221. req.Header.Set("Content-Type", bodyType)
  222. return send(req, c.Transport)
  223. }
  224. // PostForm issues a POST to the specified URL,
  225. // with data's keys and values urlencoded as the request body.
  226. //
  227. // Caller should close r.Body when done reading from it.
  228. //
  229. // PostForm is a wrapper around DefaultClient.PostForm
  230. func PostForm(url string, data url.Values) (r *Response, err os.Error) {
  231. return DefaultClient.PostForm(url, data)
  232. }
  233. // PostForm issues a POST to the specified URL,
  234. // with data's keys and values urlencoded as the request body.
  235. //
  236. // Caller should close r.Body when done reading from it.
  237. func (c *Client) PostForm(url string, data url.Values) (r *Response, err os.Error) {
  238. return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
  239. }
  240. // Head issues a HEAD to the specified URL. If the response is one of the
  241. // following redirect codes, Head follows the redirect after calling the
  242. // Client's CheckRedirect function.
  243. //
  244. // 301 (Moved Permanently)
  245. // 302 (Found)
  246. // 303 (See Other)
  247. // 307 (Temporary Redirect)
  248. //
  249. // Head is a wrapper around DefaultClient.Head
  250. func Head(url string) (r *Response, err os.Error) {
  251. return DefaultClient.Head(url)
  252. }
  253. // Head issues a HEAD to the specified URL. If the response is one of the
  254. // following redirect codes, Head follows the redirect after calling the
  255. // Client's CheckRedirect function.
  256. //
  257. // 301 (Moved Permanently)
  258. // 302 (Found)
  259. // 303 (See Other)
  260. // 307 (Temporary Redirect)
  261. func (c *Client) Head(url string) (r *Response, err os.Error) {
  262. req, err := NewRequest("HEAD", url, nil)
  263. if err != nil {
  264. return nil, err
  265. }
  266. return c.doFollowingRedirects(req)
  267. }