github.com/puellanivis/breton@v0.2.16/lib/files/httpfiles/reader.go (about) 1 package httpfiles 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "net/http" 8 "net/url" 9 "os" 10 "time" 11 12 "github.com/puellanivis/breton/lib/files" 13 "github.com/puellanivis/breton/lib/files/wrapper" 14 ) 15 16 type reader struct { 17 r io.Reader 18 s io.Seeker 19 info *wrapper.Info 20 21 *request 22 header http.Header 23 24 err error 25 loading <-chan struct{} 26 } 27 28 func (r *reader) Header() (http.Header, error) { 29 for range r.loading { 30 } 31 32 if r.err != nil { 33 return nil, r.err 34 } 35 36 return r.header, nil 37 } 38 39 func (r *reader) Stat() (os.FileInfo, error) { 40 for range r.loading { 41 } 42 43 if r.err != nil { 44 return nil, r.err 45 } 46 47 return r.info, nil 48 } 49 50 func (r *reader) Read(b []byte) (n int, err error) { 51 for range r.loading { 52 } 53 54 if r.err != nil { 55 return 0, r.err 56 } 57 58 return r.r.Read(b) 59 } 60 61 func (r *reader) Seek(offset int64, whence int) (int64, error) { 62 // TODO: if we’ve not loaded yet, it’s possible to try and use http Range header? (header has poor support, so… blech for now. 63 for range r.loading { 64 } 65 66 if r.err != nil { 67 return 0, r.err 68 } 69 70 if r.s == nil { 71 switch s := r.r.(type) { 72 case io.Seeker: 73 r.s = s 74 default: 75 return 0, os.ErrInvalid 76 } 77 } 78 79 return r.s.Seek(offset, whence) 80 } 81 82 func (r *reader) Close() error { 83 for range r.loading { 84 } 85 86 // Ignore the r.err, as it is a request-scope error, and not relevant to closing. 87 88 if c, ok := r.r.(io.Closer); ok { 89 return c.Close() 90 } 91 92 return nil 93 } 94 95 func (h *handler) Open(ctx context.Context, uri *url.URL) (files.Reader, error) { 96 uri = elideDefaultPort(uri) 97 98 cl, ok := getClient(ctx) 99 if !ok { 100 cl = http.DefaultClient 101 } 102 103 req := newHTTPRequest(http.MethodGet, uri) 104 req = req.WithContext(ctx) 105 106 if ua, ok := getUserAgent(ctx); ok { 107 req.Header.Set("User-Agent", ua) 108 } 109 110 loading := make(chan struct{}) 111 r := &reader{ 112 loading: loading, 113 114 request: &request{ 115 name: uri.String(), 116 req: req, 117 }, 118 } 119 120 go func() { 121 // So, all of the file operations block on a range over the loading channel. 122 // They will not end this blocking until loading is closed. 123 // But they will also swallow any sends, though sends will block until someone is receiving. 124 // 125 // So, we will block on the first send until someone receives from the loading channel, 126 // or the context expires. 127 // 128 // But none of the receivers will actually unblock until the loading channel is _closed_. 129 // And once the channel is closed, each range over loading won’t even stop to block. 130 131 defer close(loading) 132 133 select { 134 case loading <- struct{}{}: 135 case <-ctx.Done(): 136 r.err = files.PathError("open", r.name, ctx.Err()) 137 return 138 } 139 140 // So, we will not arrive here until someone is ranging over the loading channel. 141 // 142 // This ensures the actual http request HAPPENS AFTER the first file operation is called, 143 // but that all file operation behavior HAPPENS AFTER the actual http request is made. 144 // 145 // This lets us apply files.Option functions after files.Open, 146 // and change the http.Request before actually doing it. 147 148 resp, err := cl.Do(req) 149 if err != nil { 150 r.err = files.PathError("open", r.name, err) 151 return 152 } 153 154 r.header = resp.Header 155 uri := resp.Request.URL 156 157 t := time.Now() 158 if lastmod := r.header.Get("Last-Modified"); lastmod != "" { 159 if t1, err := http.ParseTime(lastmod); err == nil { 160 t = t1 161 } 162 } 163 164 r.info = wrapper.NewInfo(uri, int(resp.ContentLength), t) 165 166 if err := getErr(resp); err != nil { 167 resp.Body.Close() 168 169 r.err = files.PathError("open", uri.String(), err) 170 return 171 } 172 173 if resp.ContentLength < 0 { 174 r.r = resp.Body 175 return 176 } 177 178 b, err := files.ReadFrom(resp.Body) 179 if err != nil { 180 r.err = files.PathError("read", uri.String(), err) 181 return 182 } 183 resp.Body.Close() 184 185 r.r = bytes.NewReader(b) 186 }() 187 188 return r, nil 189 }