Created
May 15, 2015 21:18
-
-
Save tiborvass/8d946eec4ace5c706851 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package registry | |
| import ( | |
| "bufio" | |
| "bytes" | |
| "errors" | |
| "fmt" | |
| "io" | |
| "io/ioutil" | |
| "net/http" | |
| "os" | |
| ) | |
| type ReadSeekCloser interface { | |
| io.ReadSeeker | |
| io.Closer | |
| } | |
| func NewHTTPReadSeeker(client *http.Client, url string, size int64) ReadSeekCloser { | |
| return &httpReadSeeker{ | |
| client: client, | |
| url: url, | |
| size: size, | |
| } | |
| } | |
| type httpReadSeeker struct { | |
| client *http.Client | |
| url string | |
| size int64 | |
| rc io.ReadCloser // remote read closer | |
| brd *bufio.Reader // internal buffered io | |
| offset int64 | |
| err error | |
| } | |
| func (hrs *httpReadSeeker) Read(p []byte) (n int, err error) { | |
| if hrs.err != nil { | |
| return 0, hrs.err | |
| } | |
| rd, err := hrs.reader() | |
| if err != nil { | |
| return 0, err | |
| } | |
| n, err = rd.Read(p) | |
| hrs.offset += int64(n) | |
| // Simulate io.EOF error if we reach filesize. | |
| if err == nil && hrs.offset >= hrs.size { | |
| err = io.EOF | |
| } | |
| return n, err | |
| } | |
| func (hrs *httpReadSeeker) Seek(offset int64, whence int) (int64, error) { | |
| if hrs.err != nil { | |
| return 0, hrs.err | |
| } | |
| var err error | |
| newOffset := hrs.offset | |
| switch whence { | |
| case os.SEEK_CUR: | |
| newOffset += int64(offset) | |
| case os.SEEK_END: | |
| newOffset = hrs.size + int64(offset) | |
| case os.SEEK_SET: | |
| newOffset = int64(offset) | |
| } | |
| if newOffset < 0 { | |
| err = errors.New("cannot seek to negative position") | |
| } else { | |
| if hrs.offset != newOffset { | |
| hrs.reset() | |
| } | |
| // No problems, set the offset. | |
| hrs.offset = newOffset | |
| } | |
| return hrs.offset, err | |
| } | |
| func (hrs *httpReadSeeker) Close() error { | |
| if hrs.err != nil { | |
| return hrs.err | |
| } | |
| // close and release reader chain | |
| if hrs.rc != nil { | |
| hrs.rc.Close() | |
| } | |
| hrs.rc = nil | |
| hrs.brd = nil | |
| hrs.err = errors.New("httpLayer: closed") | |
| return nil | |
| } | |
| func (hrs *httpReadSeeker) reset() { | |
| if hrs.err != nil { | |
| return | |
| } | |
| if hrs.rc != nil { | |
| hrs.rc.Close() | |
| hrs.rc = nil | |
| } | |
| } | |
| func (hrs *httpReadSeeker) reader() (io.Reader, error) { | |
| if hrs.err != nil { | |
| return nil, hrs.err | |
| } | |
| if hrs.rc != nil { | |
| return hrs.brd, nil | |
| } | |
| // If the offset is great than or equal to size, return a empty, noop reader. | |
| if hrs.offset >= hrs.size { | |
| return ioutil.NopCloser(bytes.NewReader([]byte{})), nil | |
| } | |
| req, err := http.NewRequest("GET", hrs.url, nil) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if hrs.offset > 0 { | |
| // TODO(stevvooe): Get this working correctly. | |
| // If we are at different offset, issue a range request from there. | |
| req.Header.Add("Range", "1-") | |
| // TODO: get context in here | |
| // context.GetLogger(hrs.context).Infof("Range: %s", req.Header.Get("Range")) | |
| } | |
| resp, err := hrs.client.Do(req) | |
| if err != nil { | |
| return nil, err | |
| } | |
| switch { | |
| case resp.StatusCode == 200: | |
| hrs.rc = resp.Body | |
| default: | |
| defer resp.Body.Close() | |
| return nil, fmt.Errorf("unexpected status resolving reader: %v", resp.Status) | |
| } | |
| if hrs.brd == nil { | |
| hrs.brd = bufio.NewReader(hrs.rc) | |
| } else { | |
| hrs.brd.Reset(hrs.rc) | |
| } | |
| return hrs.brd, nil | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment