http.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package resource
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "os"
  10. "strconv"
  11. "time"
  12. )
  13. // Provides access to resources through a http.Client.
  14. type HTTPProvider struct {
  15. Client *http.Client
  16. }
  17. var _ Provider = &HTTPProvider{}
  18. func (me *HTTPProvider) NewInstance(urlStr string) (r Instance, err error) {
  19. _r := new(httpInstance)
  20. _r.URL, err = url.Parse(urlStr)
  21. if err != nil {
  22. return
  23. }
  24. _r.Client = me.Client
  25. if _r.Client == nil {
  26. _r.Client = http.DefaultClient
  27. }
  28. r = _r
  29. return
  30. }
  31. type httpInstance struct {
  32. Client *http.Client
  33. URL *url.URL
  34. }
  35. var _ Instance = &httpInstance{}
  36. func mustNewRequest(method, urlStr string, body io.Reader) *http.Request {
  37. req, err := http.NewRequest(method, urlStr, body)
  38. if err != nil {
  39. panic(err)
  40. }
  41. return req
  42. }
  43. func responseError(r *http.Response) error {
  44. if r.StatusCode == http.StatusNotFound {
  45. return os.ErrNotExist
  46. }
  47. return errors.New(r.Status)
  48. }
  49. func (me *httpInstance) Get() (ret io.ReadCloser, err error) {
  50. resp, err := me.Client.Get(me.URL.String())
  51. if err != nil {
  52. return
  53. }
  54. if resp.StatusCode == http.StatusOK {
  55. ret = resp.Body
  56. return
  57. }
  58. resp.Body.Close()
  59. err = responseError(resp)
  60. return
  61. }
  62. func (me *httpInstance) Put(r io.Reader) (err error) {
  63. resp, err := me.Client.Do(mustNewRequest("PUT", me.URL.String(), r))
  64. if err != nil {
  65. return
  66. }
  67. resp.Body.Close()
  68. if resp.StatusCode == http.StatusOK {
  69. return
  70. }
  71. err = responseError(resp)
  72. return
  73. }
  74. func (me *httpInstance) ReadAt(b []byte, off int64) (n int, err error) {
  75. req := mustNewRequest("GET", me.URL.String(), nil)
  76. req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1))
  77. resp, err := me.Client.Do(req)
  78. if err != nil {
  79. return
  80. }
  81. defer resp.Body.Close()
  82. switch resp.StatusCode {
  83. case http.StatusPartialContent:
  84. case http.StatusRequestedRangeNotSatisfiable:
  85. err = io.EOF
  86. return
  87. default:
  88. err = responseError(resp)
  89. return
  90. }
  91. // TODO: This will crash if ContentLength was not provided (-1). Do
  92. // something about that.
  93. b = b[:resp.ContentLength]
  94. return io.ReadFull(resp.Body, b)
  95. }
  96. func (me *httpInstance) WriteAt(b []byte, off int64) (n int, err error) {
  97. req := mustNewRequest("PATCH", me.URL.String(), bytes.NewReader(b))
  98. req.ContentLength = int64(len(b))
  99. req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1))
  100. resp, err := me.Client.Do(req)
  101. if err != nil {
  102. return
  103. }
  104. resp.Body.Close()
  105. if resp.StatusCode != http.StatusOK {
  106. err = responseError(resp)
  107. }
  108. n = len(b)
  109. return
  110. }
  111. func (me *httpInstance) Stat() (fi os.FileInfo, err error) {
  112. resp, err := me.Client.Head(me.URL.String())
  113. if err != nil {
  114. return
  115. }
  116. resp.Body.Close()
  117. if resp.StatusCode == http.StatusNotFound {
  118. err = os.ErrNotExist
  119. return
  120. }
  121. if resp.StatusCode != http.StatusOK {
  122. err = errors.New(resp.Status)
  123. return
  124. }
  125. var _fi httpFileInfo
  126. if h := resp.Header.Get("Last-Modified"); h != "" {
  127. _fi.lastModified, err = time.Parse(http.TimeFormat, h)
  128. if err != nil {
  129. err = fmt.Errorf("error parsing Last-Modified header: %s", err)
  130. return
  131. }
  132. }
  133. if h := resp.Header.Get("Content-Length"); h != "" {
  134. _fi.contentLength, err = strconv.ParseInt(h, 10, 64)
  135. if err != nil {
  136. err = fmt.Errorf("error parsing Content-Length header: %s", err)
  137. return
  138. }
  139. }
  140. fi = _fi
  141. return
  142. }
  143. func (me *httpInstance) Delete() (err error) {
  144. resp, err := me.Client.Do(mustNewRequest("DELETE", me.URL.String(), nil))
  145. if err != nil {
  146. return
  147. }
  148. err = responseError(resp)
  149. resp.Body.Close()
  150. return
  151. }
  152. type httpFileInfo struct {
  153. lastModified time.Time
  154. contentLength int64
  155. }
  156. var _ os.FileInfo = httpFileInfo{}
  157. func (fi httpFileInfo) IsDir() bool {
  158. return false
  159. }
  160. func (fi httpFileInfo) Mode() os.FileMode {
  161. return 0
  162. }
  163. func (fi httpFileInfo) Name() string {
  164. return ""
  165. }
  166. func (fi httpFileInfo) Size() int64 {
  167. return fi.contentLength
  168. }
  169. func (fi httpFileInfo) ModTime() time.Time {
  170. return fi.lastModified
  171. }
  172. func (fi httpFileInfo) Sys() interface{} {
  173. return nil
  174. }