github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/test/fetcher.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package test 18 19 import ( 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "sort" 25 "strings" 26 "sync" 27 28 "camlistore.org/pkg/blob" 29 "camlistore.org/pkg/blobserver" 30 "camlistore.org/pkg/context" 31 "camlistore.org/pkg/types" 32 ) 33 34 // Fetcher is an in-memory implementation of the blobserver Storage 35 // interface. It started as just a fetcher and grew. It also includes 36 // other convenience methods for testing. 37 type Fetcher struct { 38 l sync.Mutex 39 m map[string]*Blob // keyed by blobref string 40 sorted []string // blobrefs sorted 41 42 // ReceiveErr optionally returns the error to return on receive. 43 ReceiveErr error 44 45 // FetchErr, if non-nil, specifies the error to return on the next fetch call. 46 // If it returns nil, fetches proceed as normal. 47 FetchErr func() error 48 } 49 50 var _ blobserver.Storage = (*Fetcher)(nil) 51 52 func (tf *Fetcher) AddBlob(b *Blob) { 53 tf.l.Lock() 54 defer tf.l.Unlock() 55 if tf.m == nil { 56 tf.m = make(map[string]*Blob) 57 } 58 key := b.BlobRef().String() 59 _, had := tf.m[key] 60 tf.m[key] = b 61 if !had { 62 tf.sorted = append(tf.sorted, key) 63 sort.Strings(tf.sorted) 64 } 65 } 66 67 func (tf *Fetcher) FetchStreaming(ref blob.Ref) (file io.ReadCloser, size int64, err error) { 68 return tf.Fetch(ref) 69 } 70 71 var dummyCloser = ioutil.NopCloser(nil) 72 73 func (tf *Fetcher) Fetch(ref blob.Ref) (file types.ReadSeekCloser, size int64, err error) { 74 if tf.FetchErr != nil { 75 if err = tf.FetchErr(); err != nil { 76 return 77 } 78 } 79 tf.l.Lock() 80 defer tf.l.Unlock() 81 if tf.m == nil { 82 err = os.ErrNotExist 83 return 84 } 85 tb, ok := tf.m[ref.String()] 86 if !ok { 87 err = os.ErrNotExist 88 return 89 } 90 size = int64(len(tb.Contents)) 91 return struct { 92 *io.SectionReader 93 io.Closer 94 }{ 95 io.NewSectionReader(strings.NewReader(tb.Contents), 0, size), 96 dummyCloser, 97 }, size, nil 98 } 99 100 func (tf *Fetcher) BlobContents(br blob.Ref) (contents string, ok bool) { 101 tf.l.Lock() 102 defer tf.l.Unlock() 103 b, ok := tf.m[br.String()] 104 if !ok { 105 return 106 } 107 return b.Contents, true 108 } 109 110 func (tf *Fetcher) ReceiveBlob(br blob.Ref, source io.Reader) (blob.SizedRef, error) { 111 sb := blob.SizedRef{} 112 h := br.Hash() 113 if h == nil { 114 return sb, fmt.Errorf("Unsupported blobref hash for %s", br) 115 } 116 all, err := ioutil.ReadAll(io.TeeReader(source, h)) 117 if err != nil { 118 return sb, err 119 } 120 if !br.HashMatches(h) { 121 // This is a somewhat redundant check, since 122 // blobserver.Receive now does it. But for testing code, 123 // it's worth the cost. 124 return sb, fmt.Errorf("Hash mismatch receiving blob %s", br) 125 } 126 if err := tf.ReceiveErr; err != nil { 127 return sb, err 128 } 129 b := &Blob{Contents: string(all)} 130 tf.AddBlob(b) 131 return blob.SizedRef{br, int64(len(all))}, nil 132 } 133 134 func (tf *Fetcher) StatBlobs(dest chan<- blob.SizedRef, blobs []blob.Ref) error { 135 for _, br := range blobs { 136 tf.l.Lock() 137 b, ok := tf.m[br.String()] 138 tf.l.Unlock() 139 if ok { 140 dest <- blob.SizedRef{br, int64(len(b.Contents))} 141 } 142 } 143 return nil 144 } 145 146 // BlobrefStrings returns the sorted stringified blobrefs stored in this fetcher. 147 func (tf *Fetcher) BlobrefStrings() []string { 148 tf.l.Lock() 149 defer tf.l.Unlock() 150 s := make([]string, len(tf.sorted)) 151 copy(s, tf.sorted) 152 return s 153 } 154 155 func (tf *Fetcher) EnumerateBlobs(ctx *context.Context, dest chan<- blob.SizedRef, after string, limit int) error { 156 defer close(dest) 157 tf.l.Lock() 158 defer tf.l.Unlock() 159 n := 0 160 for _, k := range tf.sorted { 161 if k <= after { 162 continue 163 } 164 b := tf.m[k] 165 select { 166 case dest <- blob.SizedRef{b.BlobRef(), b.Size()}: 167 case <-ctx.Done(): 168 return context.ErrCanceled 169 } 170 n++ 171 if limit > 0 && n == limit { 172 break 173 } 174 } 175 return nil 176 } 177 178 func (tf *Fetcher) RemoveBlobs(blobs []blob.Ref) error { 179 tf.l.Lock() 180 defer tf.l.Unlock() 181 for _, br := range blobs { 182 delete(tf.m, br.String()) 183 } 184 tf.sorted = tf.sorted[:0] 185 for k := range tf.m { 186 tf.sorted = append(tf.sorted, k) 187 } 188 sort.Strings(tf.sorted) 189 return nil 190 }