This commit is contained in:
2018-11-04 15:58:15 +01:00
commit f956bcee28
1178 changed files with 584552 additions and 0 deletions

196
vendor/github.com/anacrolix/missinggo/resource/http.go generated vendored Normal file
View File

@ -0,0 +1,196 @@
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
}

View File

@ -0,0 +1,61 @@
package resource
import (
"io"
"os"
)
// Provides access to resources through the native OS filesystem.
type OSFileProvider struct{}
var _ Provider = OSFileProvider{}
func (me OSFileProvider) NewInstance(filePath string) (r Instance, err error) {
return &osFileInstance{filePath}, nil
}
type osFileInstance struct {
path string
}
var _ Instance = &osFileInstance{}
func (me *osFileInstance) Get() (ret io.ReadCloser, err error) {
return os.Open(me.path)
}
func (me *osFileInstance) Put(r io.Reader) (err error) {
f, err := os.OpenFile(me.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
if err != nil {
return
}
defer f.Close()
_, err = io.Copy(f, r)
return
}
func (me *osFileInstance) ReadAt(b []byte, off int64) (n int, err error) {
f, err := os.Open(me.path)
if err != nil {
return
}
defer f.Close()
return f.ReadAt(b, off)
}
func (me *osFileInstance) WriteAt(b []byte, off int64) (n int, err error) {
f, err := os.OpenFile(me.path, os.O_CREATE|os.O_WRONLY, 0640)
if err != nil {
return
}
defer f.Close()
return f.WriteAt(b, off)
}
func (me *osFileInstance) Stat() (fi os.FileInfo, err error) {
return os.Stat(me.path)
}
func (me *osFileInstance) Delete() error {
return os.Remove(me.path)
}

View File

@ -0,0 +1,21 @@
package resource
type Provider interface {
NewInstance(string) (Instance, error)
}
// TranslatedProvider manipulates resource locations, so as to allow
// sandboxing, or relative paths for example.
type TranslatedProvider struct {
// The underlying Provider.
BaseProvider Provider
// Some location used in calculating final locations.
BaseLocation string
// Function that takes BaseLocation, and the caller location and returns
// the location to be used with the BaseProvider.
JoinLocations func(base, rel string) string
}
func (me *TranslatedProvider) NewInstance(rel string) (Instance, error) {
return me.BaseProvider.NewInstance(me.JoinLocations(me.BaseLocation, rel))
}

View File

@ -0,0 +1,46 @@
package resource
import (
"io"
"os"
)
// An Instance represents the content at some location accessed through some
// Provider. It's the data at some URL.
type Instance interface {
Get() (io.ReadCloser, error)
Put(io.Reader) error
Stat() (os.FileInfo, error)
ReadAt([]byte, int64) (int, error)
WriteAt([]byte, int64) (int, error)
Delete() error
}
// Creates a io.ReadSeeker to an Instance.
func ReadSeeker(r Instance) io.ReadSeeker {
fi, err := r.Stat()
if err != nil {
return nil
}
return io.NewSectionReader(r, 0, fi.Size())
}
// Move instance content, deleting the source if it succeeds.
func Move(from, to Instance) (err error) {
rc, err := from.Get()
if err != nil {
return
}
defer rc.Close()
err = to.Put(rc)
if err != nil {
return
}
from.Delete()
return
}
func Exists(i Instance) bool {
_, err := i.Stat()
return err == nil
}