github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/client/blob_writer.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "os" 10 "time" 11 12 "github.com/docker/distribution" 13 "github.com/docker/distribution/context" 14 ) 15 16 type httpBlobUpload struct { 17 statter distribution.BlobStatter 18 client *http.Client 19 20 uuid string 21 startedAt time.Time 22 23 location string // always the last value of the location header. 24 offset int64 25 closed bool 26 } 27 28 func (hbu *httpBlobUpload) Reader() (io.ReadCloser, error) { 29 panic("Not implemented") 30 } 31 32 func (hbu *httpBlobUpload) handleErrorResponse(resp *http.Response) error { 33 if resp.StatusCode == http.StatusNotFound { 34 return distribution.ErrBlobUploadUnknown 35 } 36 return HandleErrorResponse(resp) 37 } 38 39 func (hbu *httpBlobUpload) ReadFrom(r io.Reader) (n int64, err error) { 40 req, err := http.NewRequest("PATCH", hbu.location, ioutil.NopCloser(r)) 41 if err != nil { 42 return 0, err 43 } 44 defer req.Body.Close() 45 46 resp, err := hbu.client.Do(req) 47 if err != nil { 48 return 0, err 49 } 50 51 if !SuccessStatus(resp.StatusCode) { 52 return 0, hbu.handleErrorResponse(resp) 53 } 54 55 hbu.uuid = resp.Header.Get("Docker-Upload-UUID") 56 hbu.location, err = sanitizeLocation(resp.Header.Get("Location"), hbu.location) 57 if err != nil { 58 return 0, err 59 } 60 rng := resp.Header.Get("Range") 61 var start, end int64 62 if n, err := fmt.Sscanf(rng, "%d-%d", &start, &end); err != nil { 63 return 0, err 64 } else if n != 2 || end < start { 65 return 0, fmt.Errorf("bad range format: %s", rng) 66 } 67 68 return (end - start + 1), nil 69 70 } 71 72 func (hbu *httpBlobUpload) Write(p []byte) (n int, err error) { 73 req, err := http.NewRequest("PATCH", hbu.location, bytes.NewReader(p)) 74 if err != nil { 75 return 0, err 76 } 77 req.Header.Set("Content-Range", fmt.Sprintf("%d-%d", hbu.offset, hbu.offset+int64(len(p)-1))) 78 req.Header.Set("Content-Length", fmt.Sprintf("%d", len(p))) 79 req.Header.Set("Content-Type", "application/octet-stream") 80 81 resp, err := hbu.client.Do(req) 82 if err != nil { 83 return 0, err 84 } 85 86 if !SuccessStatus(resp.StatusCode) { 87 return 0, hbu.handleErrorResponse(resp) 88 } 89 90 hbu.uuid = resp.Header.Get("Docker-Upload-UUID") 91 hbu.location, err = sanitizeLocation(resp.Header.Get("Location"), hbu.location) 92 if err != nil { 93 return 0, err 94 } 95 rng := resp.Header.Get("Range") 96 var start, end int 97 if n, err := fmt.Sscanf(rng, "%d-%d", &start, &end); err != nil { 98 return 0, err 99 } else if n != 2 || end < start { 100 return 0, fmt.Errorf("bad range format: %s", rng) 101 } 102 103 return (end - start + 1), nil 104 105 } 106 107 func (hbu *httpBlobUpload) Seek(offset int64, whence int) (int64, error) { 108 newOffset := hbu.offset 109 110 switch whence { 111 case os.SEEK_CUR: 112 newOffset += int64(offset) 113 case os.SEEK_END: 114 newOffset += int64(offset) 115 case os.SEEK_SET: 116 newOffset = int64(offset) 117 } 118 119 hbu.offset = newOffset 120 121 return hbu.offset, nil 122 } 123 124 func (hbu *httpBlobUpload) ID() string { 125 return hbu.uuid 126 } 127 128 func (hbu *httpBlobUpload) StartedAt() time.Time { 129 return hbu.startedAt 130 } 131 132 func (hbu *httpBlobUpload) Commit(ctx context.Context, desc distribution.Descriptor) (distribution.Descriptor, error) { 133 // TODO(dmcgowan): Check if already finished, if so just fetch 134 req, err := http.NewRequest("PUT", hbu.location, nil) 135 if err != nil { 136 return distribution.Descriptor{}, err 137 } 138 139 values := req.URL.Query() 140 values.Set("digest", desc.Digest.String()) 141 req.URL.RawQuery = values.Encode() 142 143 resp, err := hbu.client.Do(req) 144 if err != nil { 145 return distribution.Descriptor{}, err 146 } 147 defer resp.Body.Close() 148 149 if !SuccessStatus(resp.StatusCode) { 150 return distribution.Descriptor{}, hbu.handleErrorResponse(resp) 151 } 152 153 return hbu.statter.Stat(ctx, desc.Digest) 154 } 155 156 func (hbu *httpBlobUpload) Cancel(ctx context.Context) error { 157 req, err := http.NewRequest("DELETE", hbu.location, nil) 158 if err != nil { 159 return err 160 } 161 resp, err := hbu.client.Do(req) 162 if err != nil { 163 return err 164 } 165 defer resp.Body.Close() 166 167 if resp.StatusCode == http.StatusNotFound || SuccessStatus(resp.StatusCode) { 168 return nil 169 } 170 return hbu.handleErrorResponse(resp) 171 } 172 173 func (hbu *httpBlobUpload) Close() error { 174 hbu.closed = true 175 return nil 176 }