conn.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. package oss
  2. import (
  3. "bytes"
  4. "crypto/md5"
  5. "encoding/base64"
  6. "encoding/xml"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "net"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. "strconv"
  15. "strings"
  16. "time"
  17. )
  18. // Conn oss conn
  19. type Conn struct {
  20. config *Config
  21. url *urlMaker
  22. }
  23. // Response Http response from oss
  24. type Response struct {
  25. statusCode int
  26. headers http.Header
  27. body io.ReadCloser
  28. }
  29. // Do 处理请求,返回响应结果。
  30. func (conn Conn) Do(method, bucketName, objectName, urlParams, subResource string,
  31. headers map[string]string, data io.Reader) (*Response, error) {
  32. uri := conn.url.getURL(bucketName, objectName, urlParams)
  33. resource := conn.url.getResource(bucketName, objectName, subResource)
  34. return conn.doRequest(method, uri, resource, headers, data)
  35. }
  36. func (conn Conn) doRequest(method string, uri *url.URL, canonicalizedResource string,
  37. headers map[string]string, data io.Reader) (*Response, error) {
  38. httpTimeOut := conn.config.HTTPTimeout
  39. method = strings.ToUpper(method)
  40. uri.Opaque = uri.Path
  41. req := &http.Request{
  42. Method: method,
  43. URL: uri,
  44. Proto: "HTTP/1.1",
  45. ProtoMajor: 1,
  46. ProtoMinor: 1,
  47. Header: make(http.Header),
  48. Host: uri.Host,
  49. }
  50. conn.handleBody(req, data)
  51. date := time.Now().UTC().Format(http.TimeFormat)
  52. req.Header.Set(HTTPHeaderDate, date)
  53. req.Header.Set(HTTPHeaderHost, conn.config.Endpoint)
  54. req.Header.Set(HTTPHeaderUserAgent, conn.config.UserAgent)
  55. if conn.config.SecurityToken != "" {
  56. req.Header.Set(HTTPHeaderOssSecurityToken, conn.config.SecurityToken)
  57. }
  58. if headers != nil {
  59. for k, v := range headers {
  60. req.Header.Set(k, v)
  61. }
  62. }
  63. conn.signHeader(req, canonicalizedResource)
  64. timeoutClient := &http.Client{Transport: &http.Transport{
  65. Dial: func(netw, addr string) (net.Conn, error) {
  66. conn, err := net.DialTimeout(netw, addr, httpTimeOut.ConnectTimeout)
  67. if err != nil {
  68. return nil, err
  69. }
  70. return newTimeoutConn(conn, httpTimeOut.ReadWriteTimeout, httpTimeOut.LongTimeout), nil
  71. },
  72. ResponseHeaderTimeout: httpTimeOut.HeaderTimeout,
  73. MaxIdleConnsPerHost: 2000,
  74. }}
  75. resp, err := timeoutClient.Do(req)
  76. if err != nil {
  77. return nil, err
  78. }
  79. return conn.handleResponse(resp)
  80. }
  81. // handle request body
  82. func (conn Conn) handleBody(req *http.Request, body io.Reader) {
  83. rc, ok := body.(io.ReadCloser)
  84. if !ok && body != nil {
  85. rc = ioutil.NopCloser(body)
  86. }
  87. req.Body = rc
  88. switch v := body.(type) {
  89. case *bytes.Buffer:
  90. req.ContentLength = int64(v.Len())
  91. case *bytes.Reader:
  92. req.ContentLength = int64(v.Len())
  93. case *strings.Reader:
  94. req.ContentLength = int64(v.Len())
  95. case *os.File:
  96. req.ContentLength = tryGetFileSize(v)
  97. }
  98. req.Header.Set(HTTPHeaderContentLength, strconv.FormatInt(req.ContentLength, 10))
  99. // md5
  100. if req.Body != nil && conn.config.IsEnableMD5 {
  101. buf, _ := ioutil.ReadAll(req.Body)
  102. req.Body = ioutil.NopCloser(bytes.NewReader(buf))
  103. sum := md5.Sum(buf)
  104. b64 := base64.StdEncoding.EncodeToString(sum[:])
  105. req.Header.Set(HTTPHeaderContentMD5, b64)
  106. }
  107. }
  108. func tryGetFileSize(f *os.File) int64 {
  109. fInfo, _ := f.Stat()
  110. return fInfo.Size()
  111. }
  112. // handle response
  113. func (conn Conn) handleResponse(resp *http.Response) (*Response, error) {
  114. statusCode := resp.StatusCode
  115. if statusCode >= 400 && statusCode <= 505 {
  116. // 4xx and 5xx indicate that the operation has error occurred
  117. var respBody []byte
  118. respBody, err := readResponseBody(resp)
  119. if err != nil {
  120. return nil, err
  121. }
  122. if len(respBody) == 0 {
  123. // no error in response body
  124. err = fmt.Errorf("oss: service returned without a response body (%s)", resp.Status)
  125. } else {
  126. // response contains storage service error object, unmarshal
  127. srvErr, errIn := serviceErrFromXML(respBody, resp.StatusCode,
  128. resp.Header.Get(HTTPHeaderOssRequestID))
  129. if err != nil { // error unmarshaling the error response
  130. err = errIn
  131. }
  132. err = srvErr
  133. }
  134. return &Response{
  135. statusCode: resp.StatusCode,
  136. headers: resp.Header,
  137. body: ioutil.NopCloser(bytes.NewReader(respBody)), // restore the body//
  138. }, err
  139. } else if statusCode >= 300 && statusCode <= 307 {
  140. // oss use 3xx, but response has no body
  141. err := fmt.Errorf("oss: service returned %d,%s", resp.StatusCode, resp.Status)
  142. return &Response{
  143. statusCode: resp.StatusCode,
  144. headers: resp.Header,
  145. body: resp.Body,
  146. }, err
  147. }
  148. // 2xx, successful
  149. return &Response{
  150. statusCode: resp.StatusCode,
  151. headers: resp.Header,
  152. body: resp.Body,
  153. }, nil
  154. }
  155. func readResponseBody(resp *http.Response) ([]byte, error) {
  156. defer resp.Body.Close()
  157. out, err := ioutil.ReadAll(resp.Body)
  158. if err == io.EOF {
  159. err = nil
  160. }
  161. return out, err
  162. }
  163. func serviceErrFromXML(body []byte, statusCode int, requestID string) (ServiceError, error) {
  164. var storageErr ServiceError
  165. if err := xml.Unmarshal(body, &storageErr); err != nil {
  166. return storageErr, err
  167. }
  168. storageErr.StatusCode = statusCode
  169. storageErr.RequestID = requestID
  170. storageErr.RawMessage = string(body)
  171. return storageErr, nil
  172. }
  173. func xmlUnmarshal(body io.Reader, v interface{}) error {
  174. data, err := ioutil.ReadAll(body)
  175. if err != nil {
  176. return err
  177. }
  178. return xml.Unmarshal(data, v)
  179. }
  180. // Handle http timeout
  181. type timeoutConn struct {
  182. conn net.Conn
  183. timeout time.Duration
  184. longTimeout time.Duration
  185. }
  186. func newTimeoutConn(conn net.Conn, timeout time.Duration, longTimeout time.Duration) *timeoutConn {
  187. conn.SetReadDeadline(time.Now().Add(longTimeout))
  188. return &timeoutConn{
  189. conn: conn,
  190. timeout: timeout,
  191. longTimeout: longTimeout,
  192. }
  193. }
  194. func (c *timeoutConn) Read(b []byte) (n int, err error) {
  195. c.SetReadDeadline(time.Now().Add(c.timeout))
  196. n, err = c.conn.Read(b)
  197. c.SetReadDeadline(time.Now().Add(c.longTimeout))
  198. return n, err
  199. }
  200. func (c *timeoutConn) Write(b []byte) (n int, err error) {
  201. c.SetWriteDeadline(time.Now().Add(c.timeout))
  202. n, err = c.conn.Write(b)
  203. c.SetReadDeadline(time.Now().Add(c.longTimeout))
  204. return n, err
  205. }
  206. func (c *timeoutConn) Close() error {
  207. return c.conn.Close()
  208. }
  209. func (c *timeoutConn) LocalAddr() net.Addr {
  210. return c.conn.LocalAddr()
  211. }
  212. func (c *timeoutConn) RemoteAddr() net.Addr {
  213. return c.conn.RemoteAddr()
  214. }
  215. func (c *timeoutConn) SetDeadline(t time.Time) error {
  216. return c.conn.SetDeadline(t)
  217. }
  218. func (c *timeoutConn) SetReadDeadline(t time.Time) error {
  219. return c.conn.SetReadDeadline(t)
  220. }
  221. func (c *timeoutConn) SetWriteDeadline(t time.Time) error {
  222. return c.conn.SetWriteDeadline(t)
  223. }
  224. // UrlMaker - build url and resource
  225. const (
  226. urlTypeCname = 1
  227. urlTypeIP = 2
  228. urlTypeAliyun = 3
  229. )
  230. type urlMaker struct {
  231. Scheme string // http or https
  232. NetLoc string // host or ip
  233. Type int // 1 CNAME 2 IP 3 ALIYUN
  234. }
  235. // Parse endpoint
  236. func (um *urlMaker) Init(endpoint string, isCname bool) {
  237. if strings.HasPrefix(endpoint, "http://") {
  238. um.Scheme = "http"
  239. um.NetLoc = endpoint[len("http://"):]
  240. } else if strings.HasPrefix(endpoint, "https://") {
  241. um.Scheme = "https"
  242. um.NetLoc = endpoint[len("https://"):]
  243. } else {
  244. um.Scheme = "http"
  245. um.NetLoc = endpoint
  246. }
  247. host, _, err := net.SplitHostPort(um.NetLoc)
  248. if err != nil {
  249. host = um.NetLoc
  250. }
  251. ip := net.ParseIP(host)
  252. if ip != nil {
  253. um.Type = urlTypeIP
  254. } else if isCname {
  255. um.Type = urlTypeCname
  256. } else {
  257. um.Type = urlTypeAliyun
  258. }
  259. }
  260. // Build URL
  261. func (um urlMaker) getURL(bucket, object, params string) *url.URL {
  262. var host = ""
  263. var path = ""
  264. object = url.QueryEscape(object)
  265. if um.Type == urlTypeCname {
  266. host = um.NetLoc
  267. path = "/" + object
  268. } else if um.Type == urlTypeIP {
  269. if bucket == "" {
  270. host = um.NetLoc
  271. path = "/"
  272. } else {
  273. host = um.NetLoc
  274. path = fmt.Sprintf("/%s/%s", bucket, object)
  275. }
  276. } else {
  277. if bucket == "" {
  278. host = um.NetLoc
  279. path = "/"
  280. } else {
  281. host = bucket + "." + um.NetLoc
  282. path = "/" + object
  283. }
  284. }
  285. uri := &url.URL{
  286. Scheme: um.Scheme,
  287. Host: host,
  288. Path: path,
  289. RawQuery: params,
  290. }
  291. return uri
  292. }
  293. // Canonicalized Resource
  294. func (um urlMaker) getResource(bucketName, objectName, subResource string) string {
  295. if subResource != "" {
  296. subResource = "?" + subResource
  297. }
  298. if bucketName == "" {
  299. return fmt.Sprintf("/%s%s", bucketName, subResource)
  300. }
  301. return fmt.Sprintf("/%s/%s%s", bucketName, objectName, subResource)
  302. }