File: driver.go

package info (click to toggle)
golang-gocloud 0.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 15,036 kB
  • sloc: sh: 477; xml: 22; sql: 14; makefile: 4
file content (397 lines) | stat: -rw-r--r-- 17,133 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
// Copyright 2018 The Go Cloud Development Kit Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package driver defines interfaces to be implemented by blob drivers, which
// will be used by the blob package to interact with the underlying services.
// Application code should use package blob.
package driver // import "gocloud.dev/blob/driver"

import (
	"context"
	"errors"
	"io"
	"strings"
	"time"

	"gocloud.dev/gcerrors"
)

// ReaderOptions controls Reader behaviors.
type ReaderOptions struct {
	// BeforeRead is a callback that must be called exactly once before
	// any data is read, unless NewRangeReader returns an error before then, in
	// which case it should not be called at all.
	// asFunc allows drivers to expose driver-specific types;
	// see Bucket.As for more details.
	BeforeRead func(asFunc func(interface{}) bool) error
}

// Reader reads an object from the blob.
type Reader interface {
	io.ReadCloser

	// Attributes returns a subset of attributes about the blob.
	// The portable type will not modify the returned ReaderAttributes.
	Attributes() *ReaderAttributes

	// As allows drivers to expose driver-specific types;
	// see Bucket.As for more details.
	As(interface{}) bool
}

// Writer writes an object to the blob.
type Writer interface {
	io.WriteCloser
}

// WriterOptions controls behaviors of Writer.
type WriterOptions struct {
	// BufferSize changes the default size in byte of the maximum part Writer can
	// write in a single request, if supported. Larger objects will be split into
	// multiple requests.
	BufferSize int
	// CacheControl specifies caching attributes that services may use
	// when serving the blob.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
	CacheControl string
	// ContentDisposition specifies whether the blob content is expected to be
	// displayed inline or as an attachment.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
	ContentDisposition string
	// ContentEncoding specifies the encoding used for the blob's content, if any.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
	ContentEncoding string
	// ContentLanguage specifies the language used in the blob's content, if any.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language
	ContentLanguage string
	// ContentMD5 is used as a message integrity check.
	// The portable type checks that the MD5 hash of the bytes written matches
	// ContentMD5.
	// If len(ContentMD5) > 0, driver implementations may pass it to their
	// underlying network service to guarantee the integrity of the bytes in
	// transit.
	ContentMD5 []byte
	// Metadata holds key/value strings to be associated with the blob.
	// Keys are guaranteed to be non-empty and lowercased.
	Metadata map[string]string
	// BeforeWrite is a callback that must be called exactly once before
	// any data is written, unless NewTypedWriter returns an error, in
	// which case it should not be called.
	// asFunc allows drivers to expose driver-specific types;
	// see Bucket.As for more details.
	BeforeWrite func(asFunc func(interface{}) bool) error
}

// CopyOptions controls options for Copy.
type CopyOptions struct {
	// BeforeCopy is a callback that must be called before initiating the Copy.
	// asFunc allows drivers to expose driver-specific types;
	// see Bucket.As for more details.
	BeforeCopy func(asFunc func(interface{}) bool) error
}

// ReaderAttributes contains a subset of attributes about a blob that are
// accessible from Reader.
type ReaderAttributes struct {
	// ContentType is the MIME type of the blob object. It must not be empty.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
	ContentType string
	// ModTime is the time the blob object was last modified.
	ModTime time.Time
	// Size is the size of the object in bytes.
	Size int64
}

// Attributes contains attributes about a blob.
type Attributes struct {
	// CacheControl specifies caching attributes that services may use
	// when serving the blob.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
	CacheControl string
	// ContentDisposition specifies whether the blob content is expected to be
	// displayed inline or as an attachment.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
	ContentDisposition string
	// ContentEncoding specifies the encoding used for the blob's content, if any.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
	ContentEncoding string
	// ContentLanguage specifies the language used in the blob's content, if any.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language
	ContentLanguage string
	// ContentType is the MIME type of the blob object. It must not be empty.
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
	ContentType string
	// Metadata holds key/value pairs associated with the blob.
	// Keys will be lowercased by the portable type before being returned
	// to the user. If there are duplicate case-insensitive keys (e.g.,
	// "foo" and "FOO"), only one value will be kept, and it is undefined
	// which one.
	Metadata map[string]string
	// CreateTime is the time the blob object was created. If not available,
	// leave as the zero time.
	CreateTime time.Time
	// ModTime is the time the blob object was last modified.
	ModTime time.Time
	// Size is the size of the object in bytes.
	Size int64
	// MD5 is an MD5 hash of the blob contents or nil if not available.
	MD5 []byte
	// ETag for the blob; see https://en.wikipedia.org/wiki/HTTP_ETag.
	ETag string
	// AsFunc allows drivers to expose driver-specific types;
	// see Bucket.As for more details.
	// If not set, no driver-specific types are supported.
	AsFunc func(interface{}) bool
}

// ListOptions sets options for listing objects in the bucket.
type ListOptions struct {
	// Prefix indicates that only results with the given prefix should be
	// returned.
	Prefix string
	// Delimiter sets the delimiter used to define a hierarchical namespace,
	// like a filesystem with "directories".
	//
	// An empty delimiter means that the bucket is treated as a single flat
	// namespace.
	//
	// A non-empty delimiter means that any result with the delimiter in its key
	// after Prefix is stripped will be returned with ListObject.IsDir = true,
	// ListObject.Key truncated after the delimiter, and zero values for other
	// ListObject fields. These results represent "directories". Multiple results
	// in a "directory" are returned as a single result.
	Delimiter string
	// PageSize sets the maximum number of objects to be returned.
	// 0 means no maximum; driver implementations should choose a reasonable
	// max. It is guaranteed to be >= 0.
	PageSize int
	// PageToken may be filled in with the NextPageToken from a previous
	// ListPaged call.
	PageToken []byte
	// BeforeList is a callback that must be called exactly once during ListPaged,
	// before the underlying service's list is executed.
	// asFunc allows drivers to expose driver-specific types;
	// see Bucket.As for more details.
	BeforeList func(asFunc func(interface{}) bool) error
}

// ListObject represents a specific blob object returned from ListPaged.
type ListObject struct {
	// Key is the key for this blob.
	Key string
	// ModTime is the time the blob object was last modified.
	ModTime time.Time
	// Size is the size of the object in bytes.
	Size int64
	// MD5 is an MD5 hash of the blob contents or nil if not available.
	MD5 []byte
	// IsDir indicates that this result represents a "directory" in the
	// hierarchical namespace, ending in ListOptions.Delimiter. Key can be
	// passed as ListOptions.Prefix to list items in the "directory".
	// Fields other than Key and IsDir will not be set if IsDir is true.
	IsDir bool
	// AsFunc allows drivers to expose driver-specific types;
	// see Bucket.As for more details.
	// If not set, no driver-specific types are supported.
	AsFunc func(interface{}) bool
}

// ListPage represents a page of results return from ListPaged.
type ListPage struct {
	// Objects is the slice of objects found. If ListOptions.PageSize > 0,
	// it should have at most ListOptions.PageSize entries.
	//
	// Objects should be returned in lexicographical order of UTF-8 encoded keys,
	// including across pages. I.e., all objects returned from a ListPage request
	// made using a PageToken from a previous ListPage request's NextPageToken
	// should have Key >= the Key for all objects from the previous request.
	Objects []*ListObject
	// NextPageToken should be left empty unless there are more objects
	// to return. The value may be returned as ListOptions.PageToken on a
	// subsequent ListPaged call, to fetch the next page of results.
	// It can be an arbitrary []byte; it need not be a valid key.
	NextPageToken []byte
}

// Bucket provides read, write and delete operations on objects within it on the
// blob service.
type Bucket interface {
	// ErrorCode should return a code that describes the error, which was returned by
	// one of the other methods in this interface.
	ErrorCode(error) gcerrors.ErrorCode

	// As converts i to driver-specific types.
	// See https://gocloud.dev/concepts/as/ for background information.
	As(i interface{}) bool

	// ErrorAs allows drivers to expose driver-specific types for returned
	// errors.
	// See https://gocloud.dev/concepts/as/ for background information.
	ErrorAs(error, interface{}) bool

	// Attributes returns attributes for the blob. If the specified object does
	// not exist, Attributes must return an error for which ErrorCode returns
	// gcerrors.NotFound.
	// The portable type will not modify the returned Attributes.
	Attributes(ctx context.Context, key string) (*Attributes, error)

	// ListPaged lists objects in the bucket, in lexicographical order by
	// UTF-8-encoded key, returning pages of objects at a time.
	// Services are only required to be eventually consistent with respect
	// to recently written or deleted objects. That is to say, there is no
	// guarantee that an object that's been written will immediately be returned
	// from ListPaged.
	// opts is guaranteed to be non-nil.
	ListPaged(ctx context.Context, opts *ListOptions) (*ListPage, error)

	// NewRangeReader returns a Reader that reads part of an object, reading at
	// most length bytes starting at the given offset. If length is negative, it
	// will read until the end of the object. If the specified object does not
	// exist, NewRangeReader must return an error for which ErrorCode returns
	// gcerrors.NotFound.
	// opts is guaranteed to be non-nil.
	NewRangeReader(ctx context.Context, key string, offset, length int64, opts *ReaderOptions) (Reader, error)

	// NewTypedWriter returns Writer that writes to an object associated with key.
	//
	// A new object will be created unless an object with this key already exists.
	// Otherwise any previous object with the same key will be replaced.
	// The object may not be available (and any previous object will remain)
	// until Close has been called.
	//
	// contentType sets the MIME type of the object to be written. It must not be
	// empty. opts is guaranteed to be non-nil.
	//
	// The caller must call Close on the returned Writer when done writing.
	//
	// Implementations should abort an ongoing write if ctx is later canceled,
	// and do any necessary cleanup in Close. Close should then return ctx.Err().
	NewTypedWriter(ctx context.Context, key, contentType string, opts *WriterOptions) (Writer, error)

	// Copy copies the object associated with srcKey to dstKey.
	//
	// If the source object does not exist, Copy must return an error for which
	// ErrorCode returns gcerrors.NotFound.
	//
	// If the destination object already exists, it should be overwritten.
	//
	// opts is guaranteed to be non-nil.
	Copy(ctx context.Context, dstKey, srcKey string, opts *CopyOptions) error

	// Delete deletes the object associated with key. If the specified object does
	// not exist, Delete must return an error for which ErrorCode returns
	// gcerrors.NotFound.
	Delete(ctx context.Context, key string) error

	// SignedURL returns a URL that can be used to GET the blob for the duration
	// specified in opts.Expiry. opts is guaranteed to be non-nil.
	// If not supported, return an error for which ErrorCode returns
	// gcerrors.Unimplemented.
	SignedURL(ctx context.Context, key string, opts *SignedURLOptions) (string, error)

	// Close cleans up any resources used by the Bucket. Once Close is called,
	// there will be no method calls to the Bucket other than As, ErrorAs, and
	// ErrorCode. There may be open readers or writers that will receive calls.
	// It is up to the driver as to how these will be handled.
	Close() error
}

// SignedURLOptions sets options for SignedURL.
type SignedURLOptions struct {
	// Expiry sets how long the returned URL is valid for. It is guaranteed to be > 0.
	Expiry time.Duration

	// Method is the HTTP method that can be used on the URL; one of "GET", "PUT",
	// or "DELETE". Drivers must implement all 3.
	Method string

	// ContentType specifies the Content-Type HTTP header the user agent is
	// permitted to use in the PUT request. It must match exactly. See
	// EnforceAbsentContentType for behavior when ContentType is the empty string.
	// If this field is not empty and the bucket cannot enforce the Content-Type
	// header, it must return an Unimplemented error.
	//
	// This field will not be set for any non-PUT requests.
	ContentType string

	// If EnforceAbsentContentType is true and ContentType is the empty string,
	// then PUTing to the signed URL must fail if the Content-Type header is
	// present or the implementation must return an error if it cannot enforce
	// this. If EnforceAbsentContentType is false and ContentType is the empty
	// string, implementations should validate the Content-Type header if possible.
	// If EnforceAbsentContentType is true and the bucket cannot enforce the
	// Content-Type header, it must return an Unimplemented error.
	//
	// This field will always be false for non-PUT requests.
	EnforceAbsentContentType bool

	// BeforeSign is a callback that will be called before each call to the
	// the underlying service's sign functionality.
	// asFunc converts its argument to driver-specific types.
	// See https://gocloud.dev/concepts/as/ for background information.
	BeforeSign func(asFunc func(interface{}) bool) error
}

// prefixedBucket implements Bucket by prepending prefix to all keys.
type prefixedBucket struct {
	base   Bucket
	prefix string
}

// NewPrefixedBucket returns a Bucket based on b with all keys modified to have
// prefix.
func NewPrefixedBucket(b Bucket, prefix string) Bucket {
	return &prefixedBucket{base: b, prefix: prefix}
}

func (b *prefixedBucket) ErrorCode(err error) gcerrors.ErrorCode { return b.base.ErrorCode(err) }
func (b *prefixedBucket) As(i interface{}) bool                  { return b.base.As(i) }
func (b *prefixedBucket) ErrorAs(err error, i interface{}) bool  { return b.base.ErrorAs(err, i) }
func (b *prefixedBucket) Attributes(ctx context.Context, key string) (*Attributes, error) {
	return b.base.Attributes(ctx, b.prefix+key)
}
func (b *prefixedBucket) ListPaged(ctx context.Context, opts *ListOptions) (*ListPage, error) {
	var myopts ListOptions
	if opts != nil {
		myopts = *opts
	}
	myopts.Prefix = b.prefix + myopts.Prefix
	page, err := b.base.ListPaged(ctx, &myopts)
	if err != nil {
		return nil, err
	}
	for _, p := range page.Objects {
		p.Key = strings.TrimPrefix(p.Key, b.prefix)
	}
	return page, nil
}
func (b *prefixedBucket) NewRangeReader(ctx context.Context, key string, offset, length int64, opts *ReaderOptions) (Reader, error) {
	return b.base.NewRangeReader(ctx, b.prefix+key, offset, length, opts)
}
func (b *prefixedBucket) NewTypedWriter(ctx context.Context, key, contentType string, opts *WriterOptions) (Writer, error) {
	if key == "" {
		return nil, errors.New("invalid key (empty string)")
	}
	return b.base.NewTypedWriter(ctx, b.prefix+key, contentType, opts)
}
func (b *prefixedBucket) Copy(ctx context.Context, dstKey, srcKey string, opts *CopyOptions) error {
	return b.base.Copy(ctx, b.prefix+dstKey, b.prefix+srcKey, opts)
}
func (b *prefixedBucket) Delete(ctx context.Context, key string) error {
	return b.base.Delete(ctx, b.prefix+key)
}
func (b *prefixedBucket) SignedURL(ctx context.Context, key string, opts *SignedURLOptions) (string, error) {
	return b.base.SignedURL(ctx, b.prefix+key, opts)
}
func (b *prefixedBucket) Close() error { return b.base.Close() }