| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- package resource
- import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "os"
- "strconv"
- "time"
- )
- // Provides access to resources through a http.Client.
- type HTTPProvider struct {
- Client *http.Client
- }
- var _ Provider = &HTTPProvider{}
- func (me *HTTPProvider) NewInstance(urlStr string) (r Instance, err error) {
- _r := new(httpInstance)
- _r.URL, err = url.Parse(urlStr)
- if err != nil {
- return
- }
- _r.Client = me.Client
- if _r.Client == nil {
- _r.Client = http.DefaultClient
- }
- r = _r
- return
- }
- type httpInstance struct {
- Client *http.Client
- URL *url.URL
- }
- var _ Instance = &httpInstance{}
- func mustNewRequest(method, urlStr string, body io.Reader) *http.Request {
- req, err := http.NewRequest(method, urlStr, body)
- if err != nil {
- panic(err)
- }
- return req
- }
- func responseError(r *http.Response) error {
- if r.StatusCode == http.StatusNotFound {
- return os.ErrNotExist
- }
- return errors.New(r.Status)
- }
- func (me *httpInstance) Get() (ret io.ReadCloser, err error) {
- resp, err := me.Client.Get(me.URL.String())
- if err != nil {
- return
- }
- if resp.StatusCode == http.StatusOK {
- ret = resp.Body
- return
- }
- resp.Body.Close()
- err = responseError(resp)
- return
- }
- func (me *httpInstance) Put(r io.Reader) (err error) {
- resp, err := me.Client.Do(mustNewRequest("PUT", me.URL.String(), r))
- if err != nil {
- return
- }
- resp.Body.Close()
- if resp.StatusCode == http.StatusOK {
- return
- }
- err = responseError(resp)
- return
- }
- func (me *httpInstance) ReadAt(b []byte, off int64) (n int, err error) {
- req := mustNewRequest("GET", me.URL.String(), nil)
- req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1))
- resp, err := me.Client.Do(req)
- if err != nil {
- return
- }
- defer resp.Body.Close()
- switch resp.StatusCode {
- case http.StatusPartialContent:
- case http.StatusRequestedRangeNotSatisfiable:
- err = io.EOF
- return
- default:
- err = responseError(resp)
- return
- }
- // TODO: This will crash if ContentLength was not provided (-1). Do
- // something about that.
- b = b[:resp.ContentLength]
- return io.ReadFull(resp.Body, b)
- }
- func (me *httpInstance) WriteAt(b []byte, off int64) (n int, err error) {
- req := mustNewRequest("PATCH", me.URL.String(), bytes.NewReader(b))
- req.ContentLength = int64(len(b))
- req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1))
- resp, err := me.Client.Do(req)
- if err != nil {
- return
- }
- resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- err = responseError(resp)
- }
- n = len(b)
- return
- }
- func (me *httpInstance) Stat() (fi os.FileInfo, err error) {
- resp, err := me.Client.Head(me.URL.String())
- if err != nil {
- return
- }
- resp.Body.Close()
- if resp.StatusCode == http.StatusNotFound {
- err = os.ErrNotExist
- return
- }
- if resp.StatusCode != http.StatusOK {
- err = errors.New(resp.Status)
- return
- }
- var _fi httpFileInfo
- if h := resp.Header.Get("Last-Modified"); h != "" {
- _fi.lastModified, err = time.Parse(http.TimeFormat, h)
- if err != nil {
- err = fmt.Errorf("error parsing Last-Modified header: %s", err)
- return
- }
- }
- if h := resp.Header.Get("Content-Length"); h != "" {
- _fi.contentLength, err = strconv.ParseInt(h, 10, 64)
- if err != nil {
- err = fmt.Errorf("error parsing Content-Length header: %s", err)
- return
- }
- }
- fi = _fi
- return
- }
- func (me *httpInstance) Delete() (err error) {
- resp, err := me.Client.Do(mustNewRequest("DELETE", me.URL.String(), nil))
- if err != nil {
- return
- }
- err = responseError(resp)
- resp.Body.Close()
- return
- }
- type httpFileInfo struct {
- lastModified time.Time
- contentLength int64
- }
- var _ os.FileInfo = httpFileInfo{}
- func (fi httpFileInfo) IsDir() bool {
- return false
- }
- func (fi httpFileInfo) Mode() os.FileMode {
- return 0
- }
- func (fi httpFileInfo) Name() string {
- return ""
- }
- func (fi httpFileInfo) Size() int64 {
- return fi.contentLength
- }
- func (fi httpFileInfo) ModTime() time.Time {
- return fi.lastModified
- }
- func (fi httpFileInfo) Sys() interface{} {
- return nil
- }
|