vendor, internal/build: fix OpenBSD by bumping Azure libs (#17966)

* bump azure-storage-blob-go dependency to 0.3.0 release

* update azure-storage-blob-go module import path

* fix multiple return values on azblob.NewSharedKeyCredential

* vendor: bump Azure libs to latest from upstream
This commit is contained in:
Alexander van der Meij
2019-07-23 14:06:44 +02:00
committed by Péter Szilágyi
parent 4f56790efc
commit 57d9c93dcd
97 changed files with 37890 additions and 1075 deletions

View File

@@ -2,6 +2,7 @@ package pipeline
import (
"context"
"github.com/mattn/go-ieproxy"
"net"
"net/http"
"os"
@@ -204,7 +205,7 @@ func newDefaultHTTPClient() *http.Client {
// We want the Transport to have a large connection pool
return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
Proxy: ieproxy.GetProxyFunc(),
// We use Dial instead of DialContext as DialContext has been reported to cause slower performance.
Dial /*Context*/ : (&net.Dialer{
Timeout: 30 * time.Second,
@@ -253,3 +254,31 @@ type methodFactoryMarker struct {
func (methodFactoryMarker) New(next Policy, po *PolicyOptions) Policy {
panic("methodFactoryMarker policy should have been replaced with a method policy")
}
// LogSanitizer can be implemented to clean secrets from lines logged by ForceLog
// By default no implemetation is provided here, because pipeline may be used in many different
// contexts, so the correct implementation is context-dependent
type LogSanitizer interface {
SanitizeLogMessage(raw string) string
}
var sanitizer LogSanitizer
var enableForceLog bool = true
// SetLogSanitizer can be called to supply a custom LogSanitizer.
// There is no threadsafety or locking on the underlying variable,
// so call this function just once at startup of your application
// (Don't later try to change the sanitizer on the fly).
func SetLogSanitizer(s LogSanitizer)(){
sanitizer = s
}
// SetForceLogEnabled can be used to disable ForceLog
// There is no threadsafety or locking on the underlying variable,
// so call this function just once at startup of your application
// (Don't later try to change the setting on the fly).
func SetForceLogEnabled(enable bool)() {
enableForceLog = enable
}

View File

@@ -0,0 +1,14 @@
package pipeline
// ForceLog should rarely be used. It forceable logs an entry to the
// Windows Event Log (on Windows) or to the SysLog (on Linux)
func ForceLog(level LogLevel, msg string) {
if !enableForceLog {
return
}
if sanitizer != nil {
msg = sanitizer.SanitizeLogMessage(msg)
}
forceLog(level, msg)
}

View File

@@ -7,9 +7,9 @@ import (
"log/syslog"
)
// ForceLog should rarely be used. It forceable logs an entry to the
// forceLog should rarely be used. It forceable logs an entry to the
// Windows Event Log (on Windows) or to the SysLog (on Linux)
func ForceLog(level LogLevel, msg string) {
func forceLog(level LogLevel, msg string) {
if defaultLogger == nil {
return // Return fast if we failed to create the logger.
}

View File

@@ -6,9 +6,9 @@ import (
"unsafe"
)
// ForceLog should rarely be used. It forceable logs an entry to the
// forceLog should rarely be used. It forceable logs an entry to the
// Windows Event Log (on Windows) or to the SysLog (on Linux)
func ForceLog(level LogLevel, msg string) {
func forceLog(level LogLevel, msg string) {
var el eventType
switch level {
case LogError, LogFatal, LogPanic:
@@ -35,7 +35,7 @@ const (
)
var reportEvent = func() func(eventType eventType, eventID int32, msg string) {
advAPI32 := syscall.MustLoadDLL("AdvAPI32.dll")
advAPI32 := syscall.MustLoadDLL("advapi32.dll") // lower case to tie in with Go's sysdll registration
registerEventSource := advAPI32.MustFindProc("RegisterEventSourceW")
sourceName, _ := os.Executable()

View File

@@ -9,6 +9,23 @@ type causer interface {
Cause() error
}
func errorWithPC(msg string, pc uintptr) string {
s := ""
if fn := runtime.FuncForPC(pc); fn != nil {
file, line := fn.FileLine(pc)
s = fmt.Sprintf("-> %v, %v:%v\n", fn.Name(), file, line)
}
s += msg + "\n\n"
return s
}
func getPC(callersToSkip int) uintptr {
// Get the PC of Initialize method's caller.
pc := [1]uintptr{}
_ = runtime.Callers(callersToSkip, pc[:])
return pc[0]
}
// ErrorNode can be an embedded field in a private error object. This field
// adds Program Counter support and a 'cause' (reference to a preceding error).
// When initializing a error type with this embedded field, initialize the
@@ -22,12 +39,7 @@ type ErrorNode struct {
// When defining a new error type, have its Error method call this one passing
// it the string representation of the error.
func (e *ErrorNode) Error(msg string) string {
s := ""
if fn := runtime.FuncForPC(e.pc); fn != nil {
file, line := fn.FileLine(e.pc)
s = fmt.Sprintf("-> %v, %v:%v\n", fn.Name(), file, line)
}
s += msg + "\n\n"
s := errorWithPC(msg, e.pc)
if e.cause != nil {
s += e.cause.Error() + "\n"
}
@@ -83,10 +95,8 @@ func (e ErrorNode) Timeout() bool {
// value of 3 is very common; but, depending on your code nesting, you may need
// a different value.
func (ErrorNode) Initialize(cause error, callersToSkip int) ErrorNode {
// Get the PC of Initialize method's caller.
pc := [1]uintptr{}
_ = runtime.Callers(callersToSkip, pc[:])
return ErrorNode{pc: pc[0], cause: cause}
pc := getPC(callersToSkip)
return ErrorNode{pc: pc, cause: cause}
}
// Cause walks all the preceding errors and return the originating error.
@@ -101,12 +111,53 @@ func Cause(err error) error {
return err
}
// ErrorNodeNoCause can be an embedded field in a private error object. This field
// adds Program Counter support.
// When initializing a error type with this embedded field, initialize the
// ErrorNodeNoCause field by calling ErrorNodeNoCause{}.Initialize().
type ErrorNodeNoCause struct {
pc uintptr // Represents a Program Counter that you can get symbols for.
}
// Error returns a string with the PC's symbols or "" if the PC is invalid.
// When defining a new error type, have its Error method call this one passing
// it the string representation of the error.
func (e *ErrorNodeNoCause) Error(msg string) string {
return errorWithPC(msg, e.pc)
}
// Temporary returns true if the error occurred due to a temporary condition.
func (e ErrorNodeNoCause) Temporary() bool {
return false
}
// Timeout returns true if the error occurred due to time expiring.
func (e ErrorNodeNoCause) Timeout() bool {
return false
}
// Initialize is used to initialize an embedded ErrorNode field.
// It captures the caller's program counter.
// To initialize the field, use "ErrorNodeNoCause{}.Initialize(3)". A callersToSkip
// value of 3 is very common; but, depending on your code nesting, you may need
// a different value.
func (ErrorNodeNoCause) Initialize(callersToSkip int) ErrorNodeNoCause {
pc := getPC(callersToSkip)
return ErrorNodeNoCause{pc: pc}
}
// NewError creates a simple string error (like Error.New). But, this
// error also captures the caller's Program Counter and the preceding error.
// error also captures the caller's Program Counter and the preceding error (if provided).
func NewError(cause error, msg string) error {
return &pcError{
ErrorNode: ErrorNode{}.Initialize(cause, 3),
msg: msg,
if cause != nil {
return &pcError{
ErrorNode: ErrorNode{}.Initialize(cause, 3),
msg: msg,
}
}
return &pcErrorNoCause{
ErrorNodeNoCause: ErrorNodeNoCause{}.Initialize(3),
msg: msg,
}
}
@@ -119,3 +170,12 @@ type pcError struct {
// Error satisfies the error interface. It shows the error with Program Counter
// symbols and calls Error on the preceding error so you can see the full error chain.
func (e *pcError) Error() string { return e.ErrorNode.Error(e.msg) }
// pcErrorNoCause is a simple string error (like error.New) with an ErrorNode (PC).
type pcErrorNoCause struct {
ErrorNodeNoCause
msg string
}
// Error satisfies the error interface. It shows the error with Program Counter symbols.
func (e *pcErrorNoCause) Error() string { return e.ErrorNodeNoCause.Error(e.msg) }

View File

@@ -5,5 +5,5 @@ const (
UserAgent = "azure-pipeline-go/" + Version
// Version is the semantic version (see http://semver.org) of the pipeline package.
Version = "0.1.0"
Version = "0.2.1"
)

View File

@@ -1,3 +0,0 @@
package azblob
const serviceLibVersion = "0.1"

View File

@@ -1,122 +0,0 @@
package azblob
import (
"context"
"io"
"net"
"net/http"
)
const CountToEnd = 0
// HTTPGetter is a function type that refers to a method that performs an HTTP GET operation.
type HTTPGetter func(ctx context.Context, i HTTPGetterInfo) (*http.Response, error)
// HTTPGetterInfo is passed to an HTTPGetter function passing it parameters
// that should be used to make an HTTP GET request.
type HTTPGetterInfo struct {
// Offset specifies the start offset that should be used when
// creating the HTTP GET request's Range header
Offset int64
// Count specifies the count of bytes that should be used to calculate
// the end offset when creating the HTTP GET request's Range header
Count int64
// ETag specifies the resource's etag that should be used when creating
// the HTTP GET request's If-Match header
ETag ETag
}
// RetryReaderOptions contains properties which can help to decide when to do retry.
type RetryReaderOptions struct {
// MaxRetryRequests specifies the maximum number of HTTP GET requests that will be made
// while reading from a RetryReader. A value of zero means that no additional HTTP
// GET requests will be made.
MaxRetryRequests int
doInjectError bool
doInjectErrorRound int
}
// retryReader implements io.ReaderCloser methods.
// retryReader tries to read from response, and if there is retriable network error
// returned during reading, it will retry according to retry reader option through executing
// user defined action with provided data to get a new response, and continue the overall reading process
// through reading from the new response.
type retryReader struct {
ctx context.Context
response *http.Response
info HTTPGetterInfo
countWasBounded bool
o RetryReaderOptions
getter HTTPGetter
}
// NewRetryReader creates a retry reader.
func NewRetryReader(ctx context.Context, initialResponse *http.Response,
info HTTPGetterInfo, o RetryReaderOptions, getter HTTPGetter) io.ReadCloser {
if getter == nil {
panic("getter must not be nil")
}
if info.Count < 0 {
panic("info.Count must be >= 0")
}
if o.MaxRetryRequests < 0 {
panic("o.MaxRetryRequests must be >= 0")
}
return &retryReader{ctx: ctx, getter: getter, info: info, countWasBounded: info.Count != CountToEnd, response: initialResponse, o: o}
}
func (s *retryReader) Read(p []byte) (n int, err error) {
for try := 0; ; try++ {
//fmt.Println(try) // Comment out for debugging.
if s.countWasBounded && s.info.Count == CountToEnd {
// User specified an original count and the remaining bytes are 0, return 0, EOF
return 0, io.EOF
}
if s.response == nil { // We don't have a response stream to read from, try to get one.
response, err := s.getter(s.ctx, s.info)
if err != nil {
return 0, err
}
// Successful GET; this is the network stream we'll read from.
s.response = response
}
n, err := s.response.Body.Read(p) // Read from the stream
// Injection mechanism for testing.
if s.o.doInjectError && try == s.o.doInjectErrorRound {
err = &net.DNSError{IsTemporary: true}
}
// We successfully read data or end EOF.
if err == nil || err == io.EOF {
s.info.Offset += int64(n) // Increments the start offset in case we need to make a new HTTP request in the future
if s.info.Count != CountToEnd {
s.info.Count -= int64(n) // Decrement the count in case we need to make a new HTTP request in the future
}
return n, err // Return the return to the caller
}
s.Close() // Error, close stream
s.response = nil // Our stream is no longer good
// Check the retry count and error code, and decide whether to retry.
if try >= s.o.MaxRetryRequests {
return n, err // All retries exhausted
}
if netErr, ok := err.(net.Error); ok && (netErr.Timeout() || netErr.Temporary()) {
continue
// Loop around and try to get and read from new stream.
}
return n, err // Not retryable, just return
}
}
func (s *retryReader) Close() error {
if s.response != nil && s.response.Body != nil {
return s.response.Body.Close()
}
return nil
}

View File

@@ -4,8 +4,8 @@ import (
"time"
)
// HTTPAccessConditions identifies standard HTTP access conditions which you optionally set.
type HTTPAccessConditions struct {
// ModifiedAccessConditions identifies standard HTTP access conditions which you optionally set.
type ModifiedAccessConditions struct {
IfModifiedSince time.Time
IfUnmodifiedSince time.Time
IfMatch ETag
@@ -13,7 +13,7 @@ type HTTPAccessConditions struct {
}
// pointers is for internal infrastructure. It returns the fields as pointers.
func (ac HTTPAccessConditions) pointers() (ims *time.Time, ius *time.Time, ime *ETag, inme *ETag) {
func (ac ModifiedAccessConditions) pointers() (ims *time.Time, ius *time.Time, ime *ETag, inme *ETag) {
if !ac.IfModifiedSince.IsZero() {
ims = &ac.IfModifiedSince
}
@@ -31,16 +31,14 @@ func (ac HTTPAccessConditions) pointers() (ims *time.Time, ius *time.Time, ime *
// ContainerAccessConditions identifies container-specific access conditions which you optionally set.
type ContainerAccessConditions struct {
HTTPAccessConditions
ModifiedAccessConditions
LeaseAccessConditions
}
// BlobAccessConditions identifies blob-specific access conditions which you optionally set.
type BlobAccessConditions struct {
HTTPAccessConditions
ModifiedAccessConditions
LeaseAccessConditions
AppendBlobAccessConditions
PageBlobAccessConditions
}
// LeaseAccessConditions identifies lease access conditions for a container or blob which you optionally set.

View File

@@ -5,13 +5,12 @@ import "sync/atomic"
// AtomicMorpherInt32 identifies a method passed to and invoked by the AtomicMorphInt32 function.
// The AtomicMorpher callback is passed a startValue and based on this value it returns
// what the new value should be and the result that AtomicMorph should return to its caller.
type AtomicMorpherInt32 func(startVal int32) (val int32, morphResult interface{})
type atomicMorpherInt32 func(startVal int32) (val int32, morphResult interface{})
const targetAndMorpherMustNotBeNil = "target and morpher must not be nil"
// AtomicMorph atomically morphs target in to new value (and result) as indicated bythe AtomicMorpher callback function.
func AtomicMorphInt32(target *int32, morpher AtomicMorpherInt32) interface{} {
if target == nil || morpher == nil {
panic("target and morpher mut not be nil")
}
func atomicMorphInt32(target *int32, morpher atomicMorpherInt32) interface{} {
for {
currentVal := atomic.LoadInt32(target)
desiredVal, morphResult := morpher(currentVal)
@@ -24,13 +23,10 @@ func AtomicMorphInt32(target *int32, morpher AtomicMorpherInt32) interface{} {
// AtomicMorpherUint32 identifies a method passed to and invoked by the AtomicMorph function.
// The AtomicMorpher callback is passed a startValue and based on this value it returns
// what the new value should be and the result that AtomicMorph should return to its caller.
type AtomicMorpherUint32 func(startVal uint32) (val uint32, morphResult interface{})
type atomicMorpherUint32 func(startVal uint32) (val uint32, morphResult interface{})
// AtomicMorph atomically morphs target in to new value (and result) as indicated bythe AtomicMorpher callback function.
func AtomicMorphUint32(target *uint32, morpher AtomicMorpherUint32) interface{} {
if target == nil || morpher == nil {
panic("target and morpher mut not be nil")
}
func atomicMorphUint32(target *uint32, morpher atomicMorpherUint32) interface{} {
for {
currentVal := atomic.LoadUint32(target)
desiredVal, morphResult := morpher(currentVal)
@@ -43,13 +39,10 @@ func AtomicMorphUint32(target *uint32, morpher AtomicMorpherUint32) interface{}
// AtomicMorpherUint64 identifies a method passed to and invoked by the AtomicMorphUint64 function.
// The AtomicMorpher callback is passed a startValue and based on this value it returns
// what the new value should be and the result that AtomicMorph should return to its caller.
type AtomicMorpherInt64 func(startVal int64) (val int64, morphResult interface{})
type atomicMorpherInt64 func(startVal int64) (val int64, morphResult interface{})
// AtomicMorph atomically morphs target in to new value (and result) as indicated bythe AtomicMorpher callback function.
func AtomicMorphInt64(target *int64, morpher AtomicMorpherInt64) interface{} {
if target == nil || morpher == nil {
panic("target and morpher mut not be nil")
}
func atomicMorphInt64(target *int64, morpher atomicMorpherInt64) interface{} {
for {
currentVal := atomic.LoadInt64(target)
desiredVal, morphResult := morpher(currentVal)
@@ -62,13 +55,10 @@ func AtomicMorphInt64(target *int64, morpher AtomicMorpherInt64) interface{} {
// AtomicMorpherUint64 identifies a method passed to and invoked by the AtomicMorphUint64 function.
// The AtomicMorpher callback is passed a startValue and based on this value it returns
// what the new value should be and the result that AtomicMorph should return to its caller.
type AtomicMorpherUint64 func(startVal uint64) (val uint64, morphResult interface{})
type atomicMorpherUint64 func(startVal uint64) (val uint64, morphResult interface{})
// AtomicMorph atomically morphs target in to new value (and result) as indicated bythe AtomicMorpher callback function.
func AtomicMorphUint64(target *uint64, morpher AtomicMorpherUint64) interface{} {
if target == nil || morpher == nil {
panic("target and morpher mut not be nil")
}
func atomicMorphUint64(target *uint64, morpher atomicMorpherUint64) interface{} {
for {
currentVal := atomic.LoadUint64(target)
desiredVal, morphResult := morpher(currentVal)

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@ package azblob
import (
"context"
"encoding/base64"
"fmt"
"io"
"net/http"
@@ -12,10 +11,12 @@ import (
"sync"
"time"
"errors"
"github.com/Azure/azure-pipeline-go/pipeline"
)
// CommonResponseHeaders returns the headers common to all blob REST API responses.
// CommonResponse returns the headers common to all blob REST API responses.
type CommonResponse interface {
// ETag returns the value for header ETag.
ETag() ETag
@@ -42,6 +43,7 @@ type UploadToBlockBlobOptions struct {
BlockSize int64
// Progress is a function that is invoked periodically as bytes are sent to the BlockBlobURL.
// Note that the progress reporting is not always increasing; it can go down when retrying a request.
Progress pipeline.ProgressReceiver
// BlobHTTPHeaders indicates the HTTP headers to be associated with the blob.
@@ -60,17 +62,25 @@ type UploadToBlockBlobOptions struct {
// UploadBufferToBlockBlob uploads a buffer in blocks to a block blob.
func UploadBufferToBlockBlob(ctx context.Context, b []byte,
blockBlobURL BlockBlobURL, o UploadToBlockBlobOptions) (CommonResponse, error) {
// Validate parameters and set defaults
if o.BlockSize < 0 || o.BlockSize > BlockBlobMaxUploadBlobBytes {
panic(fmt.Sprintf("BlockSize option must be > 0 and <= %d", BlockBlobMaxUploadBlobBytes))
}
bufferSize := int64(len(b))
if o.BlockSize == 0 {
o.BlockSize = BlockBlobMaxUploadBlobBytes // Default if unspecified
// If bufferSize > (BlockBlobMaxStageBlockBytes * BlockBlobMaxBlocks), then error
if bufferSize > BlockBlobMaxStageBlockBytes*BlockBlobMaxBlocks {
return nil, errors.New("Buffer is too large to upload to a block blob")
}
// If bufferSize <= BlockBlobMaxUploadBlobBytes, then Upload should be used with just 1 I/O request
if bufferSize <= BlockBlobMaxUploadBlobBytes {
o.BlockSize = BlockBlobMaxUploadBlobBytes // Default if unspecified
} else {
o.BlockSize = bufferSize / BlockBlobMaxBlocks // buffer / max blocks = block size to use all 50,000 blocks
if o.BlockSize < BlobDefaultDownloadBlockSize { // If the block size is smaller than 4MB, round up to 4MB
o.BlockSize = BlobDefaultDownloadBlockSize
}
// StageBlock will be called with blockSize blocks and a parallelism of (BufferSize / BlockSize).
}
}
size := int64(len(b))
if size <= BlockBlobMaxUploadBlobBytes {
if bufferSize <= BlockBlobMaxUploadBlobBytes {
// If the size can fit in 1 Upload call, do it this way
var body io.ReadSeeker = bytes.NewReader(b)
if o.Progress != nil {
@@ -79,10 +89,7 @@ func UploadBufferToBlockBlob(ctx context.Context, b []byte,
return blockBlobURL.Upload(ctx, body, o.BlobHTTPHeaders, o.Metadata, o.AccessConditions)
}
var numBlocks = uint16(((size - 1) / o.BlockSize) + 1)
if numBlocks > BlockBlobMaxBlocks {
panic(fmt.Sprintf("The buffer's size is too big or the BlockSize is too small; the number of blocks must be <= %d", BlockBlobMaxBlocks))
}
var numBlocks = uint16(((bufferSize - 1) / o.BlockSize) + 1)
blockIDList := make([]string, numBlocks) // Base-64 encoded block IDs
progress := int64(0)
@@ -90,7 +97,7 @@ func UploadBufferToBlockBlob(ctx context.Context, b []byte,
err := doBatchTransfer(ctx, batchTransferOptions{
operationName: "UploadBufferToBlockBlob",
transferSize: size,
transferSize: bufferSize,
chunkSize: o.BlockSize,
parallelism: o.Parallelism,
operation: func(offset int64, count int64) error {
@@ -115,7 +122,7 @@ func UploadBufferToBlockBlob(ctx context.Context, b []byte,
// Block IDs are unique values to avoid issue if 2+ clients are uploading blocks
// at the same time causing PutBlockList to get a mix of blocks from all the clients.
blockIDList[blockNum] = base64.StdEncoding.EncodeToString(newUUID().bytes())
_, err := blockBlobURL.StageBlock(ctx, blockIDList[blockNum], body, o.AccessConditions.LeaseAccessConditions)
_, err := blockBlobURL.StageBlock(ctx, blockIDList[blockNum], body, o.AccessConditions.LeaseAccessConditions, nil)
return err
},
})
@@ -147,10 +154,9 @@ func UploadFileToBlockBlob(ctx context.Context, file *os.File,
///////////////////////////////////////////////////////////////////////////////
const BlobDefaultDownloadBlockSize = int64(4 * 1024 * 1024) // 4MB
// DownloadFromAzureFileOptions identifies options used by the DownloadAzureFileToBuffer and DownloadAzureFileToFile functions.
// DownloadFromBlobOptions identifies options used by the DownloadBlobToBuffer and DownloadBlobToFile functions.
type DownloadFromBlobOptions struct {
// BlockSize specifies the block size to use for each parallel download; the default size is BlobDefaultDownloadBlockSize.
BlockSize int64
@@ -168,32 +174,19 @@ type DownloadFromBlobOptions struct {
RetryReaderOptionsPerBlock RetryReaderOptions
}
// downloadAzureFileToBuffer downloads an Azure file to a buffer with parallel.
// downloadBlobToBuffer downloads an Azure blob to a buffer with parallel.
func downloadBlobToBuffer(ctx context.Context, blobURL BlobURL, offset int64, count int64,
ac BlobAccessConditions, b []byte, o DownloadFromBlobOptions,
initialDownloadResponse *DownloadResponse) error {
// Validate parameters, and set defaults.
if o.BlockSize < 0 {
panic("BlockSize option must be >= 0")
}
b []byte, o DownloadFromBlobOptions, initialDownloadResponse *DownloadResponse) error {
if o.BlockSize == 0 {
o.BlockSize = BlobDefaultDownloadBlockSize
}
if offset < 0 {
panic("offset option must be >= 0")
}
if count < 0 {
panic("count option must be >= 0")
}
if count == CountToEnd { // If size not specified, calculate it
if initialDownloadResponse != nil {
count = initialDownloadResponse.ContentLength() - offset // if we have the length, use it
} else {
// If we don't have the length at all, get it
dr, err := blobURL.Download(ctx, 0, CountToEnd, ac, false)
dr, err := blobURL.Download(ctx, 0, CountToEnd, o.AccessConditions, false)
if err != nil {
return err
}
@@ -201,21 +194,20 @@ func downloadBlobToBuffer(ctx context.Context, blobURL BlobURL, offset int64, co
}
}
if int64(len(b)) < count {
panic(fmt.Errorf("the buffer's size should be equal to or larger than the request count of bytes: %d", count))
}
// Prepare and do parallel download.
progress := int64(0)
progressLock := &sync.Mutex{}
err := doBatchTransfer(ctx, batchTransferOptions{
operationName: "downloadBlobToBuffer",
transferSize: count,
transferSize: count,
chunkSize: o.BlockSize,
parallelism: o.Parallelism,
operation: func(chunkStart int64, count int64) error {
dr, err := blobURL.Download(ctx, chunkStart+ offset, count, ac, false)
dr, err := blobURL.Download(ctx, chunkStart+offset, count, o.AccessConditions, false)
if err != nil {
return err
}
body := dr.Body(o.RetryReaderOptionsPerBlock)
if o.Progress != nil {
rangeProgress := int64(0)
@@ -241,29 +233,24 @@ func downloadBlobToBuffer(ctx context.Context, blobURL BlobURL, offset int64, co
return nil
}
// DownloadAzureFileToBuffer downloads an Azure file to a buffer with parallel.
// DownloadBlobToBuffer downloads an Azure blob to a buffer with parallel.
// Offset and count are optional, pass 0 for both to download the entire blob.
func DownloadBlobToBuffer(ctx context.Context, blobURL BlobURL, offset int64, count int64,
ac BlobAccessConditions, b []byte, o DownloadFromBlobOptions) error {
return downloadBlobToBuffer(ctx, blobURL, offset, count, ac, b, o, nil)
b []byte, o DownloadFromBlobOptions) error {
return downloadBlobToBuffer(ctx, blobURL, offset, count, b, o, nil)
}
// DownloadBlobToFile downloads an Azure file to a local file.
// DownloadBlobToFile downloads an Azure blob to a local file.
// The file would be truncated if the size doesn't match.
// Offset and count are optional, pass 0 for both to download the entire blob.
func DownloadBlobToFile(ctx context.Context, blobURL BlobURL, offset int64, count int64,
ac BlobAccessConditions, file *os.File, o DownloadFromBlobOptions) error {
// 1. Validate parameters.
if file == nil {
panic("file must not be nil")
}
// 2. Calculate the size of the destination file
file *os.File, o DownloadFromBlobOptions) error {
// 1. Calculate the size of the destination file
var size int64
if count == CountToEnd {
// Try to get Azure file's size
props, err := blobURL.GetProperties(ctx, ac)
// Try to get Azure blob's size
props, err := blobURL.GetProperties(ctx, o.AccessConditions)
if err != nil {
return err
}
@@ -272,7 +259,7 @@ func DownloadBlobToFile(ctx context.Context, blobURL BlobURL, offset int64, coun
size = count
}
// 3. Compare and try to resize local file's size if it doesn't match Azure file's size.
// 2. Compare and try to resize local file's size if it doesn't match Azure blob's size.
stat, err := file.Stat()
if err != nil {
return err
@@ -284,19 +271,18 @@ func DownloadBlobToFile(ctx context.Context, blobURL BlobURL, offset int64, coun
}
if size > 0 {
// 4. Set mmap and call DownloadAzureFileToBuffer.
// 3. Set mmap and call downloadBlobToBuffer.
m, err := newMMF(file, true, 0, int(size))
if err != nil {
return err
}
defer m.unmap()
return downloadBlobToBuffer(ctx, blobURL, offset, size, ac, m, o, nil)
return downloadBlobToBuffer(ctx, blobURL, offset, size, m, o, nil)
} else { // if the blob's size is 0, there is no need in downloading it
return nil
}
}
///////////////////////////////////////////////////////////////////////////////
// BatchTransferOptions identifies options used by doBatchTransfer.
@@ -374,7 +360,10 @@ func UploadStreamToBlockBlob(ctx context.Context, reader io.Reader, blockBlobURL
result, err := uploadStream(ctx, reader,
UploadStreamOptions{BufferSize: o.BufferSize, MaxBuffers: o.MaxBuffers},
&uploadStreamToBlockBlobOptions{b: blockBlobURL, o: o, blockIDPrefix: newUUID()})
return result.(CommonResponse), err
if err != nil {
return nil, err
}
return result.(CommonResponse), nil
}
type uploadStreamToBlockBlobOptions struct {
@@ -390,13 +379,17 @@ func (t *uploadStreamToBlockBlobOptions) start(ctx context.Context) (interface{}
}
func (t *uploadStreamToBlockBlobOptions) chunk(ctx context.Context, num uint32, buffer []byte) error {
if num == 0 && len(buffer) < t.o.BufferSize {
// If whole payload fits in 1 block, don't stage it; End will upload it with 1 I/O operation
if num == 0 {
t.firstBlock = buffer
return nil
// If whole payload fits in 1 block, don't stage it; End will upload it with 1 I/O operation
// If the payload is exactly the same size as the buffer, there may be more content coming in.
if len(buffer) < t.o.BufferSize {
return nil
}
}
// Else, upload a staged block...
AtomicMorphUint32(&t.maxBlockNum, func(startVal uint32) (val uint32, morphResult interface{}) {
atomicMorphUint32(&t.maxBlockNum, func(startVal uint32) (val uint32, morphResult interface{}) {
// Atomically remember (in t.numBlocks) the maximum block num we've ever seen
if startVal < num {
return num, nil
@@ -404,19 +397,21 @@ func (t *uploadStreamToBlockBlobOptions) chunk(ctx context.Context, num uint32,
return startVal, nil
})
blockID := newUuidBlockID(t.blockIDPrefix).WithBlockNumber(num).ToBase64()
_, err := t.b.StageBlock(ctx, blockID, bytes.NewReader(buffer), LeaseAccessConditions{})
_, err := t.b.StageBlock(ctx, blockID, bytes.NewReader(buffer), LeaseAccessConditions{}, nil)
return err
}
func (t *uploadStreamToBlockBlobOptions) end(ctx context.Context) (interface{}, error) {
if t.maxBlockNum == 0 {
// If the first block had the exact same size as the buffer
// we would have staged it as a block thinking that there might be more data coming
if t.maxBlockNum == 0 && len(t.firstBlock) != t.o.BufferSize {
// If whole payload fits in 1 block (block #0), upload it with 1 I/O operation
return t.b.Upload(ctx, bytes.NewReader(t.firstBlock),
t.o.BlobHTTPHeaders, t.o.Metadata, t.o.AccessConditions)
}
// Multiple blocks staged, commit them all now
blockID := newUuidBlockID(t.blockIDPrefix)
blockIDs := make([]string, t.maxBlockNum + 1)
blockIDs := make([]string, t.maxBlockNum+1)
for bn := uint32(0); bn <= t.maxBlockNum; bn++ {
blockIDs[bn] = blockID.WithBlockNumber(bn).ToBase64()
}
@@ -436,7 +431,28 @@ type UploadStreamOptions struct {
BufferSize int
}
type firstErr struct {
lock sync.Mutex
finalError error
}
func (fe *firstErr) set(err error) {
fe.lock.Lock()
if fe.finalError == nil {
fe.finalError = err
}
fe.lock.Unlock()
}
func (fe *firstErr) get() (err error) {
fe.lock.Lock()
err = fe.finalError
fe.lock.Unlock()
return
}
func uploadStream(ctx context.Context, reader io.Reader, o UploadStreamOptions, t iTransfer) (interface{}, error) {
firstErr := firstErr{}
ctx, cancel := context.WithCancel(ctx) // New context so that any failure cancels everything
defer cancel()
wg := sync.WaitGroup{} // Used to know when all outgoing messages have finished processing
@@ -463,9 +479,12 @@ func uploadStream(ctx context.Context, reader io.Reader, o UploadStreamOptions,
err := t.chunk(ctx, outgoingMsg.chunkNum, outgoingMsg.buffer)
wg.Done() // Indicate this buffer was sent
if nil != err {
// NOTE: finalErr could be assigned to multiple times here which is OK,
// some error will be returned.
firstErr.set(err)
cancel()
}
incoming <- outgoingMsg.buffer // The goroutine reading from the stream can use reuse this buffer now
incoming <- outgoingMsg.buffer // The goroutine reading from the stream can reuse this buffer now
}
}()
}
@@ -490,7 +509,7 @@ func uploadStream(ctx context.Context, reader io.Reader, o UploadStreamOptions,
buffer = <-incoming
}
n, err := io.ReadFull(reader, buffer)
if err != nil {
if err != nil { // Less than len(buffer) bytes were read
buffer = buffer[:n] // Make slice match the # of read bytes
}
if len(buffer) > 0 {
@@ -499,12 +518,21 @@ func uploadStream(ctx context.Context, reader io.Reader, o UploadStreamOptions,
outgoing <- OutgoingMsg{chunkNum: c, buffer: buffer}
}
if err != nil { // The reader is done, no more outgoing buffers
if err == io.EOF || err == io.ErrUnexpectedEOF {
err = nil // This function does NOT return an error if io.ReadFull returns io.EOF or io.ErrUnexpectedEOF
} else {
firstErr.set(err)
}
break
}
}
// NOTE: Don't close the incoming channel because the outgoing goroutines post buffers into it when they are done
close(outgoing) // Make all the outgoing goroutines terminate when this channel is empty
wg.Wait() // Wait for all pending outgoing messages to complete
// After all blocks uploaded, commit them to the blob & return the result
return t.end(ctx)
err := firstErr.get()
if err == nil {
// If no error, after all blocks uploaded, commit them to the blob & return the result
return t.end(ctx)
}
return nil, err
}

View File

@@ -1,6 +1,7 @@
package azblob
import (
"net"
"net/url"
"strings"
)
@@ -14,13 +15,39 @@ const (
// existing URL into its parts by calling NewBlobURLParts(). You construct a URL from parts by calling URL().
// NOTE: Changing any SAS-related field requires computing a new SAS signature.
type BlobURLParts struct {
Scheme string // Ex: "https://"
Host string // Ex: "account.blob.core.windows.net"
ContainerName string // "" if no container
BlobName string // "" if no blob
Snapshot string // "" if not a snapshot
SAS SASQueryParameters
UnparsedParams string
Scheme string // Ex: "https://"
Host string // Ex: "account.blob.core.windows.net", "10.132.141.33", "10.132.141.33:80"
IPEndpointStyleInfo IPEndpointStyleInfo
ContainerName string // "" if no container
BlobName string // "" if no blob
Snapshot string // "" if not a snapshot
SAS SASQueryParameters
UnparsedParams string
}
// IPEndpointStyleInfo is used for IP endpoint style URL when working with Azure storage emulator.
// Ex: "https://10.132.141.33/accountname/containername"
type IPEndpointStyleInfo struct {
AccountName string // "" if not using IP endpoint style
}
// isIPEndpointStyle checkes if URL's host is IP, in this case the storage account endpoint will be composed as:
// http(s)://IP(:port)/storageaccount/container/...
// As url's Host property, host could be both host or host:port
func isIPEndpointStyle(host string) bool {
if host == "" {
return false
}
if h, _, err := net.SplitHostPort(host); err == nil {
host = h
}
// For IPv6, there could be case where SplitHostPort fails for cannot finding port.
// In this case, eliminate the '[' and ']' in the URL.
// For details about IPv6 URL, please refer to https://tools.ietf.org/html/rfc2732
if host[0] == '[' && host[len(host)-1] == ']' {
host = host[1 : len(host)-1]
}
return net.ParseIP(host) != nil
}
// NewBlobURLParts parses a URL initializing BlobURLParts' fields including any SAS-related & snapshot query parameters. Any other
@@ -37,10 +64,17 @@ func NewBlobURLParts(u url.URL) BlobURLParts {
if path[0] == '/' {
path = path[1:] // If path starts with a slash, remove it
}
if isIPEndpointStyle(up.Host) {
if accountEndIndex := strings.Index(path, "/"); accountEndIndex == -1 { // Slash not found; path has account name & no container name or blob
up.IPEndpointStyleInfo.AccountName = path
} else {
up.IPEndpointStyleInfo.AccountName = path[:accountEndIndex] // The account name is the part between the slashes
path = path[accountEndIndex+1:] // path refers to portion after the account name now (container & blob names)
}
}
// Find the next slash (if it exists)
containerEndIndex := strings.Index(path, "/")
if containerEndIndex == -1 { // Slash not found; path has container name & no blob name
containerEndIndex := strings.Index(path, "/") // Find the next slash (if it exists)
if containerEndIndex == -1 { // Slash not found; path has container name & no blob name
up.ContainerName = path
} else {
up.ContainerName = path[:containerEndIndex] // The container name is the part between the slashes
@@ -77,6 +111,9 @@ func (values caseInsensitiveValues) Get(key string) ([]string, bool) {
// field contains the SAS, snapshot, and unparsed query parameters.
func (up BlobURLParts) URL() url.URL {
path := ""
if isIPEndpointStyle(up.Host) && up.IPEndpointStyleInfo.AccountName != "" {
path += "/" + up.IPEndpointStyleInfo.AccountName
}
// Concatenate container & blob names (if they exist)
if up.ContainerName != "" {
path += "/" + up.ContainerName
@@ -87,6 +124,11 @@ func (up BlobURLParts) URL() url.URL {
rawQuery := up.UnparsedParams
//If no snapshot is initially provided, fill it in from the SAS query properties to help the user
if up.Snapshot == "" && !up.SAS.snapshotTime.IsZero() {
up.Snapshot = up.SAS.snapshotTime.Format(SnapshotTimeFormat)
}
// Concatenate blob snapshot query parameter (if it exists)
if up.Snapshot != "" {
if len(rawQuery) > 0 {

View File

@@ -8,14 +8,16 @@ import (
)
// BlobSASSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage container or blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/constructing-a-service-sas
type BlobSASSignatureValues struct {
Version string `param:"sv"` // If not specified, this defaults to SASVersion
Protocol SASProtocol `param:"spr"` // See the SASProtocol* constants
StartTime time.Time `param:"st"` // Not specified if IsZero
ExpiryTime time.Time `param:"se"` // Not specified if IsZero
Permissions string `param:"sp"` // Create by initializing a ContainerSASPermissions or BlobSASPermissions and then call String()
IPRange IPRange `param:"sip"`
Identifier string `param:"si"`
SnapshotTime time.Time
Permissions string `param:"sp"` // Create by initializing a ContainerSASPermissions or BlobSASPermissions and then call String()
IPRange IPRange `param:"sip"`
Identifier string `param:"si"`
ContainerName string
BlobName string // Use "" to create a Container SAS
CacheControl string // rscc
@@ -25,19 +27,28 @@ type BlobSASSignatureValues struct {
ContentType string // rsct
}
// NewSASQueryParameters uses an account's shared key credential to sign this signature values to produce
// NewSASQueryParameters uses an account's StorageAccountCredential to sign this signature values to produce
// the proper SAS query parameters.
func (v BlobSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) SASQueryParameters {
if sharedKeyCredential == nil {
panic("sharedKeyCredential can't be nil")
// See: StorageAccountCredential. Compatible with both UserDelegationCredential and SharedKeyCredential
func (v BlobSASSignatureValues) NewSASQueryParameters(credential StorageAccountCredential) (SASQueryParameters, error) {
resource := "c"
if credential == nil {
return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without StorageAccountCredential")
}
resource := "c"
if v.BlobName == "" {
if !v.SnapshotTime.IsZero() {
resource = "bs"
//Make sure the permission characters are in the correct order
perms := &BlobSASPermissions{}
if err := perms.Parse(v.Permissions); err != nil {
return SASQueryParameters{}, err
}
v.Permissions = perms.String()
} else if v.BlobName == "" {
// Make sure the permission characters are in the correct order
perms := &ContainerSASPermissions{}
if err := perms.Parse(v.Permissions); err != nil {
panic(err)
return SASQueryParameters{}, err
}
v.Permissions = perms.String()
} else {
@@ -45,32 +56,54 @@ func (v BlobSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *Share
// Make sure the permission characters are in the correct order
perms := &BlobSASPermissions{}
if err := perms.Parse(v.Permissions); err != nil {
panic(err)
return SASQueryParameters{}, err
}
v.Permissions = perms.String()
}
if v.Version == "" {
v.Version = SASVersion
}
startTime, expiryTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime)
startTime, expiryTime, snapshotTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime, v.SnapshotTime)
signedIdentifier := v.Identifier
udk := credential.getUDKParams()
if udk != nil {
udkStart, udkExpiry, _ := FormatTimesForSASSigning(udk.SignedStart, udk.SignedExpiry, time.Time{})
//I don't like this answer to combining the functions
//But because signedIdentifier and the user delegation key strings share a place, this is an _OK_ way to do it.
signedIdentifier = strings.Join([]string{
udk.SignedOid,
udk.SignedTid,
udkStart,
udkExpiry,
udk.SignedService,
udk.SignedVersion,
}, "\n")
}
// String to sign: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
stringToSign := strings.Join([]string{
v.Permissions,
startTime,
expiryTime,
getCanonicalName(sharedKeyCredential.AccountName(), v.ContainerName, v.BlobName),
v.Identifier,
getCanonicalName(credential.AccountName(), v.ContainerName, v.BlobName),
signedIdentifier,
v.IPRange.String(),
string(v.Protocol),
v.Version,
resource,
snapshotTime, // signed timestamp
v.CacheControl, // rscc
v.ContentDisposition, // rscd
v.ContentEncoding, // rsce
v.ContentLanguage, // rscl
v.ContentType}, // rsct
"\n")
signature := sharedKeyCredential.ComputeHMACSHA256(stringToSign)
signature := ""
signature = credential.ComputeHMACSHA256(stringToSign)
p := SASQueryParameters{
// Common SAS parameters
@@ -82,13 +115,30 @@ func (v BlobSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *Share
ipRange: v.IPRange,
// Container/Blob-specific SAS parameters
resource: resource,
identifier: v.Identifier,
resource: resource,
identifier: v.Identifier,
cacheControl: v.CacheControl,
contentDisposition: v.ContentDisposition,
contentEncoding: v.ContentEncoding,
contentLanguage: v.ContentLanguage,
contentType: v.ContentType,
snapshotTime: v.SnapshotTime,
// Calculated SAS signature
signature: signature,
}
return p
//User delegation SAS specific parameters
if udk != nil {
p.signedOid = udk.SignedOid
p.signedTid = udk.SignedTid
p.signedStart = udk.SignedStart
p.signedExpiry = udk.SignedExpiry
p.signedService = udk.SignedService
p.signedVersion = udk.SignedVersion
}
return p, nil
}
// getCanonicalName computes the canonical name for a container or blob resource for SAS signing.

View File

@@ -0,0 +1,8 @@
package azblob
// StorageAccountCredential is a wrapper interface for SharedKeyCredential and UserDelegationCredential
type StorageAccountCredential interface {
AccountName() string
ComputeHMACSHA256(message string) (base64String string)
getUDKParams() *UserDelegationKey
}

View File

@@ -45,7 +45,7 @@ func (ab AppendBlobURL) WithSnapshot(snapshot string) AppendBlobURL {
// Create creates a 0-length append blob. Call AppendBlock to append data to an append blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob.
func (ab AppendBlobURL) Create(ctx context.Context, h BlobHTTPHeaders, metadata Metadata, ac BlobAccessConditions) (*AppendBlobCreateResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.ModifiedAccessConditions.pointers()
return ab.abClient.Create(ctx, 0, nil,
&h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5,
&h.CacheControl, metadata, ac.LeaseAccessConditions.pointers(), &h.ContentDisposition,
@@ -56,17 +56,39 @@ func (ab AppendBlobURL) Create(ctx context.Context, h BlobHTTPHeaders, metadata
// This method panics if the stream is not at position 0.
// Note that the http client closes the body stream after the request is sent to the service.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/append-block.
func (ab AppendBlobURL) AppendBlock(ctx context.Context, body io.ReadSeeker, ac BlobAccessConditions) (*AppendBlobAppendBlockResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifAppendPositionEqual, ifMaxSizeLessThanOrEqual := ac.AppendBlobAccessConditions.pointers()
return ab.abClient.AppendBlock(ctx, body, validateSeekableStreamAt0AndGetCount(body), nil,
ac.LeaseAccessConditions.pointers(),
func (ab AppendBlobURL) AppendBlock(ctx context.Context, body io.ReadSeeker, ac AppendBlobAccessConditions, transactionalMD5 []byte) (*AppendBlobAppendBlockResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
ifAppendPositionEqual, ifMaxSizeLessThanOrEqual := ac.AppendPositionAccessConditions.pointers()
count, err := validateSeekableStreamAt0AndGetCount(body)
if err != nil {
return nil, err
}
return ab.abClient.AppendBlock(ctx, body, count, nil,
transactionalMD5, ac.LeaseAccessConditions.pointers(),
ifMaxSizeLessThanOrEqual, ifAppendPositionEqual,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
}
// AppendBlobAccessConditions identifies append blob-specific access conditions which you optionally set.
// AppendBlockFromURL copies a new block of data from source URL to the end of the existing append blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/append-block-from-url.
func (ab AppendBlobURL) AppendBlockFromURL(ctx context.Context, sourceURL url.URL, offset int64, count int64, destinationAccessConditions AppendBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions, transactionalMD5 []byte) (*AppendBlobAppendBlockFromURLResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := destinationAccessConditions.ModifiedAccessConditions.pointers()
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers()
ifAppendPositionEqual, ifMaxSizeLessThanOrEqual := destinationAccessConditions.AppendPositionAccessConditions.pointers()
return ab.abClient.AppendBlockFromURL(ctx, sourceURL.String(), 0, httpRange{offset: offset, count: count}.pointers(),
transactionalMD5, nil, destinationAccessConditions.LeaseAccessConditions.pointers(),
ifMaxSizeLessThanOrEqual, ifAppendPositionEqual,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil)
}
type AppendBlobAccessConditions struct {
ModifiedAccessConditions
LeaseAccessConditions
AppendPositionAccessConditions
}
// AppendPositionAccessConditions identifies append blob-specific access conditions which you optionally set.
type AppendPositionAccessConditions struct {
// IfAppendPositionEqual ensures that the AppendBlock operation succeeds
// only if the append position is equal to a value.
// IfAppendPositionEqual=0 means no 'IfAppendPositionEqual' header specified.
@@ -83,13 +105,7 @@ type AppendBlobAccessConditions struct {
}
// pointers is for internal infrastructure. It returns the fields as pointers.
func (ac AppendBlobAccessConditions) pointers() (iape *int64, imsltoe *int64) {
if ac.IfAppendPositionEqual < -1 {
panic("IfAppendPositionEqual can't be less than -1")
}
if ac.IfMaxSizeLessThanOrEqual < -1 {
panic("IfMaxSizeLessThanOrEqual can't be less than -1")
}
func (ac AppendPositionAccessConditions) pointers() (iape *int64, imsltoe *int64) {
var zero int64 // defaults to 0
switch ac.IfAppendPositionEqual {
case -1:

View File

@@ -14,9 +14,6 @@ type BlobURL struct {
// NewBlobURL creates a BlobURL object using the specified URL and request policy pipeline.
func NewBlobURL(url url.URL, p pipeline.Pipeline) BlobURL {
if p == nil {
panic("p can't be nil")
}
blobClient := newBlobClient(url, p)
return BlobURL{blobClient: blobClient}
}
@@ -34,9 +31,6 @@ func (b BlobURL) String() string {
// WithPipeline creates a new BlobURL object identical to the source but with the specified request policy pipeline.
func (b BlobURL) WithPipeline(p pipeline.Pipeline) BlobURL {
if p == nil {
panic("p can't be nil")
}
return NewBlobURL(b.blobClient.URL(), p)
}
@@ -64,13 +58,14 @@ func (b BlobURL) ToPageBlobURL() PageBlobURL {
}
// DownloadBlob reads a range of bytes from a blob. The response also includes the blob's properties and metadata.
// Passing azblob.CountToEnd (0) for count will download the blob from the offset to the end.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-blob.
func (b BlobURL) Download(ctx context.Context, offset int64, count int64, ac BlobAccessConditions, rangeGetContentMD5 bool) (*DownloadResponse, error) {
var xRangeGetContentMD5 *bool
if rangeGetContentMD5 {
xRangeGetContentMD5 = &rangeGetContentMD5
}
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
dr, err := b.blobClient.Download(ctx, nil, nil,
httpRange{offset: offset, count: count}.pointers(),
ac.LeaseAccessConditions.pointers(), xRangeGetContentMD5,
@@ -90,7 +85,7 @@ func (b BlobURL) Download(ctx context.Context, offset int64, count int64, ac Blo
// Note that deleting a blob also deletes all its snapshots.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/delete-blob.
func (b BlobURL) Delete(ctx context.Context, deleteOptions DeleteSnapshotsOptionType, ac BlobAccessConditions) (*BlobDeleteResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return b.blobClient.Delete(ctx, nil, nil, ac.LeaseAccessConditions.pointers(), deleteOptions,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
}
@@ -107,14 +102,14 @@ func (b BlobURL) Undelete(ctx context.Context) (*BlobUndeleteResponse, error) {
// bandwidth of the blob. A block blob's tier determines Hot/Cool/Archive storage type. This operation
// does not update the blob's ETag.
// For detailed information about block blob level tiering see https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-storage-tiers.
func (b BlobURL) SetTier(ctx context.Context, tier AccessTierType) (*BlobSetTierResponse, error) {
return b.blobClient.SetTier(ctx, tier, nil, nil)
func (b BlobURL) SetTier(ctx context.Context, tier AccessTierType, lac LeaseAccessConditions) (*BlobSetTierResponse, error) {
return b.blobClient.SetTier(ctx, tier, nil, nil, lac.pointers())
}
// GetBlobProperties returns the blob's properties.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-blob-properties.
func (b BlobURL) GetProperties(ctx context.Context, ac BlobAccessConditions) (*BlobGetPropertiesResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return b.blobClient.GetProperties(ctx, nil, nil, ac.LeaseAccessConditions.pointers(),
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
}
@@ -122,7 +117,7 @@ func (b BlobURL) GetProperties(ctx context.Context, ac BlobAccessConditions) (*B
// SetBlobHTTPHeaders changes a blob's HTTP headers.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/set-blob-properties.
func (b BlobURL) SetHTTPHeaders(ctx context.Context, h BlobHTTPHeaders, ac BlobAccessConditions) (*BlobSetHTTPHeadersResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return b.blobClient.SetHTTPHeaders(ctx, nil,
&h.CacheControl, &h.ContentType, h.ContentMD5, &h.ContentEncoding, &h.ContentLanguage,
ac.LeaseAccessConditions.pointers(), ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag,
@@ -132,7 +127,7 @@ func (b BlobURL) SetHTTPHeaders(ctx context.Context, h BlobHTTPHeaders, ac BlobA
// SetBlobMetadata changes a blob's metadata.
// https://docs.microsoft.com/rest/api/storageservices/set-blob-metadata.
func (b BlobURL) SetMetadata(ctx context.Context, metadata Metadata, ac BlobAccessConditions) (*BlobSetMetadataResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return b.blobClient.SetMetadata(ctx, nil, metadata, ac.LeaseAccessConditions.pointers(),
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
}
@@ -143,14 +138,14 @@ func (b BlobURL) CreateSnapshot(ctx context.Context, metadata Metadata, ac BlobA
// CreateSnapshot does NOT panic if the user tries to create a snapshot using a URL that already has a snapshot query parameter
// because checking this would be a performance hit for a VERY unusual path and I don't think the common case should suffer this
// performance hit.
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return b.blobClient.CreateSnapshot(ctx, nil, metadata, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, ac.LeaseAccessConditions.pointers(), nil)
}
// AcquireLease acquires a lease on the blob for write and delete operations. The lease duration must be between
// 15 to 60 seconds, or infinite (-1).
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
func (b BlobURL) AcquireLease(ctx context.Context, proposedID string, duration int32, ac HTTPAccessConditions) (*BlobAcquireLeaseResponse, error) {
func (b BlobURL) AcquireLease(ctx context.Context, proposedID string, duration int32, ac ModifiedAccessConditions) (*BlobAcquireLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
return b.blobClient.AcquireLease(ctx, nil, &duration, &proposedID,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
@@ -158,7 +153,7 @@ func (b BlobURL) AcquireLease(ctx context.Context, proposedID string, duration i
// RenewLease renews the blob's previously-acquired lease.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
func (b BlobURL) RenewLease(ctx context.Context, leaseID string, ac HTTPAccessConditions) (*BlobRenewLeaseResponse, error) {
func (b BlobURL) RenewLease(ctx context.Context, leaseID string, ac ModifiedAccessConditions) (*BlobRenewLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
return b.blobClient.RenewLease(ctx, leaseID, nil,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
@@ -166,7 +161,7 @@ func (b BlobURL) RenewLease(ctx context.Context, leaseID string, ac HTTPAccessCo
// ReleaseLease releases the blob's previously-acquired lease.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
func (b BlobURL) ReleaseLease(ctx context.Context, leaseID string, ac HTTPAccessConditions) (*BlobReleaseLeaseResponse, error) {
func (b BlobURL) ReleaseLease(ctx context.Context, leaseID string, ac ModifiedAccessConditions) (*BlobReleaseLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
return b.blobClient.ReleaseLease(ctx, leaseID, nil,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
@@ -175,7 +170,7 @@ func (b BlobURL) ReleaseLease(ctx context.Context, leaseID string, ac HTTPAccess
// BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1)
// constant to break a fixed-duration lease when it expires or an infinite lease immediately.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
func (b BlobURL) BreakLease(ctx context.Context, breakPeriodInSeconds int32, ac HTTPAccessConditions) (*BlobBreakLeaseResponse, error) {
func (b BlobURL) BreakLease(ctx context.Context, breakPeriodInSeconds int32, ac ModifiedAccessConditions) (*BlobBreakLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
return b.blobClient.BreakLease(ctx, nil, leasePeriodPointer(breakPeriodInSeconds),
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
@@ -183,7 +178,7 @@ func (b BlobURL) BreakLease(ctx context.Context, breakPeriodInSeconds int32, ac
// ChangeLease changes the blob's lease ID.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
func (b BlobURL) ChangeLease(ctx context.Context, leaseID string, proposedID string, ac HTTPAccessConditions) (*BlobChangeLeaseResponse, error) {
func (b BlobURL) ChangeLease(ctx context.Context, leaseID string, proposedID string, ac ModifiedAccessConditions) (*BlobChangeLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
return b.blobClient.ChangeLease(ctx, leaseID, proposedID,
nil, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
@@ -201,10 +196,9 @@ func leasePeriodPointer(period int32) (p *int32) {
// StartCopyFromURL copies the data at the source URL to a blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/copy-blob.
func (b BlobURL) StartCopyFromURL(ctx context.Context, source url.URL, metadata Metadata, srcac BlobAccessConditions, dstac BlobAccessConditions) (*BlobStartCopyFromURLResponse, error) {
srcIfModifiedSince, srcIfUnmodifiedSince, srcIfMatchETag, srcIfNoneMatchETag := srcac.HTTPAccessConditions.pointers()
dstIfModifiedSince, dstIfUnmodifiedSince, dstIfMatchETag, dstIfNoneMatchETag := dstac.HTTPAccessConditions.pointers()
srcLeaseID := srcac.LeaseAccessConditions.pointers()
func (b BlobURL) StartCopyFromURL(ctx context.Context, source url.URL, metadata Metadata, srcac ModifiedAccessConditions, dstac BlobAccessConditions) (*BlobStartCopyFromURLResponse, error) {
srcIfModifiedSince, srcIfUnmodifiedSince, srcIfMatchETag, srcIfNoneMatchETag := srcac.pointers()
dstIfModifiedSince, dstIfUnmodifiedSince, dstIfMatchETag, dstIfNoneMatchETag := dstac.ModifiedAccessConditions.pointers()
dstLeaseID := dstac.LeaseAccessConditions.pointers()
return b.blobClient.StartCopyFromURL(ctx, source.String(), nil, metadata,
@@ -212,7 +206,7 @@ func (b BlobURL) StartCopyFromURL(ctx context.Context, source url.URL, metadata
srcIfMatchETag, srcIfNoneMatchETag,
dstIfModifiedSince, dstIfUnmodifiedSince,
dstIfMatchETag, dstIfNoneMatchETag,
dstLeaseID, srcLeaseID, nil)
dstLeaseID, nil)
}
// AbortCopyFromURL stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata.

View File

@@ -12,7 +12,7 @@ import (
)
const (
// BlockBlobMaxPutBlobBytes indicates the maximum number of bytes that can be sent in a call to Upload.
// BlockBlobMaxUploadBlobBytes indicates the maximum number of bytes that can be sent in a call to Upload.
BlockBlobMaxUploadBlobBytes = 256 * 1024 * 1024 // 256MB
// BlockBlobMaxStageBlockBytes indicates the maximum number of bytes that can be sent in a call to StageBlock.
@@ -30,9 +30,6 @@ type BlockBlobURL struct {
// NewBlockBlobURL creates a BlockBlobURL object using the specified URL and request policy pipeline.
func NewBlockBlobURL(url url.URL, p pipeline.Pipeline) BlockBlobURL {
if p == nil {
panic("p can't be nil")
}
blobClient := newBlobClient(url, p)
bbClient := newBlockBlobClient(url, p)
return BlockBlobURL{BlobURL: BlobURL{blobClient: blobClient}, bbClient: bbClient}
@@ -59,8 +56,12 @@ func (bb BlockBlobURL) WithSnapshot(snapshot string) BlockBlobURL {
// Note that the http client closes the body stream after the request is sent to the service.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob.
func (bb BlockBlobURL) Upload(ctx context.Context, body io.ReadSeeker, h BlobHTTPHeaders, metadata Metadata, ac BlobAccessConditions) (*BlockBlobUploadResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
return bb.bbClient.Upload(ctx, body, validateSeekableStreamAt0AndGetCount(body), nil,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
count, err := validateSeekableStreamAt0AndGetCount(body)
if err != nil {
return nil, err
}
return bb.bbClient.Upload(ctx, body, count, nil,
&h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5,
&h.CacheControl, metadata, ac.LeaseAccessConditions.pointers(),
&h.ContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag,
@@ -70,16 +71,20 @@ func (bb BlockBlobURL) Upload(ctx context.Context, body io.ReadSeeker, h BlobHTT
// StageBlock uploads the specified block to the block blob's "staging area" to be later committed by a call to CommitBlockList.
// Note that the http client closes the body stream after the request is sent to the service.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-block.
func (bb BlockBlobURL) StageBlock(ctx context.Context, base64BlockID string, body io.ReadSeeker, ac LeaseAccessConditions) (*BlockBlobStageBlockResponse, error) {
return bb.bbClient.StageBlock(ctx, base64BlockID, validateSeekableStreamAt0AndGetCount(body), body, nil, ac.pointers(), nil)
func (bb BlockBlobURL) StageBlock(ctx context.Context, base64BlockID string, body io.ReadSeeker, ac LeaseAccessConditions, transactionalMD5 []byte) (*BlockBlobStageBlockResponse, error) {
count, err := validateSeekableStreamAt0AndGetCount(body)
if err != nil {
return nil, err
}
return bb.bbClient.StageBlock(ctx, base64BlockID, count, body, transactionalMD5, nil, ac.pointers(), nil)
}
// StageBlockFromURL copies the specified block from a source URL to the block blob's "staging area" to be later committed by a call to CommitBlockList.
// If count is CountToEnd (0), then data is read from specified offset to the end.
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/put-block-from-url.
func (bb BlockBlobURL) StageBlockFromURL(ctx context.Context, base64BlockID string, sourceURL url.URL, offset int64, count int64, ac LeaseAccessConditions) (*BlockBlobStageBlockFromURLResponse, error) {
sourceURLStr := sourceURL.String()
return bb.bbClient.StageBlockFromURL(ctx, base64BlockID, 0, &sourceURLStr, httpRange{offset: offset, count: count}.pointers(), nil, nil, ac.pointers(), nil)
func (bb BlockBlobURL) StageBlockFromURL(ctx context.Context, base64BlockID string, sourceURL url.URL, offset int64, count int64, destinationAccessConditions LeaseAccessConditions, sourceAccessConditions ModifiedAccessConditions) (*BlockBlobStageBlockFromURLResponse, error) {
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers()
return bb.bbClient.StageBlockFromURL(ctx, base64BlockID, 0, sourceURL.String(), httpRange{offset: offset, count: count}.pointers(), nil, nil, destinationAccessConditions.pointers(), sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil)
}
// CommitBlockList writes a blob by specifying the list of block IDs that make up the blob.
@@ -90,7 +95,7 @@ func (bb BlockBlobURL) StageBlockFromURL(ctx context.Context, base64BlockID stri
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-block-list.
func (bb BlockBlobURL) CommitBlockList(ctx context.Context, base64BlockIDs []string, h BlobHTTPHeaders,
metadata Metadata, ac BlobAccessConditions) (*BlockBlobCommitBlockListResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return bb.bbClient.CommitBlockList(ctx, BlockLookupList{Latest: base64BlockIDs}, nil,
&h.CacheControl, &h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5,
metadata, ac.LeaseAccessConditions.pointers(), &h.ContentDisposition,

View File

@@ -3,6 +3,7 @@ package azblob
import (
"bytes"
"context"
"errors"
"fmt"
"net/url"
@@ -16,9 +17,6 @@ type ContainerURL struct {
// NewContainerURL creates a ContainerURL object using the specified URL and request policy pipeline.
func NewContainerURL(url url.URL, p pipeline.Pipeline) ContainerURL {
if p == nil {
panic("p can't be nil")
}
client := newContainerClient(url, p)
return ContainerURL{client: client}
}
@@ -89,10 +87,10 @@ func (c ContainerURL) Create(ctx context.Context, metadata Metadata, publicAcces
// For more information, see https://docs.microsoft.com/rest/api/storageservices/delete-container.
func (c ContainerURL) Delete(ctx context.Context, ac ContainerAccessConditions) (*ContainerDeleteResponse, error) {
if ac.IfMatch != ETagNone || ac.IfNoneMatch != ETagNone {
panic("the IfMatch and IfNoneMatch access conditions must have their default values because they are ignored by the service")
return nil, errors.New("the IfMatch and IfNoneMatch access conditions must have their default values because they are ignored by the service")
}
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.ModifiedAccessConditions.pointers()
return c.client.Delete(ctx, nil, ac.LeaseAccessConditions.pointers(),
ifModifiedSince, ifUnmodifiedSince, nil)
}
@@ -109,9 +107,9 @@ func (c ContainerURL) GetProperties(ctx context.Context, ac LeaseAccessCondition
// For more information, see https://docs.microsoft.com/rest/api/storageservices/set-container-metadata.
func (c ContainerURL) SetMetadata(ctx context.Context, metadata Metadata, ac ContainerAccessConditions) (*ContainerSetMetadataResponse, error) {
if !ac.IfUnmodifiedSince.IsZero() || ac.IfMatch != ETagNone || ac.IfNoneMatch != ETagNone {
panic("the IfUnmodifiedSince, IfMatch, and IfNoneMatch must have their default values because they are ignored by the blob service")
return nil, errors.New("the IfUnmodifiedSince, IfMatch, and IfNoneMatch must have their default values because they are ignored by the blob service")
}
ifModifiedSince, _, _, _ := ac.HTTPAccessConditions.pointers()
ifModifiedSince, _, _, _ := ac.ModifiedAccessConditions.pointers()
return c.client.SetMetadata(ctx, nil, ac.LeaseAccessConditions.pointers(), metadata, ifModifiedSince, nil)
}
@@ -181,16 +179,16 @@ func (p *AccessPolicyPermission) Parse(s string) error {
func (c ContainerURL) SetAccessPolicy(ctx context.Context, accessType PublicAccessType, si []SignedIdentifier,
ac ContainerAccessConditions) (*ContainerSetAccessPolicyResponse, error) {
if ac.IfMatch != ETagNone || ac.IfNoneMatch != ETagNone {
panic("the IfMatch and IfNoneMatch access conditions must have their default values because they are ignored by the service")
return nil, errors.New("the IfMatch and IfNoneMatch access conditions must have their default values because they are ignored by the service")
}
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.ModifiedAccessConditions.pointers()
return c.client.SetAccessPolicy(ctx, si, nil, ac.LeaseAccessConditions.pointers(),
accessType, ifModifiedSince, ifUnmodifiedSince, nil)
}
// AcquireLease acquires a lease on the container for delete operations. The lease duration must be between 15 to 60 seconds, or infinite (-1).
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-container.
func (c ContainerURL) AcquireLease(ctx context.Context, proposedID string, duration int32, ac HTTPAccessConditions) (*ContainerAcquireLeaseResponse, error) {
func (c ContainerURL) AcquireLease(ctx context.Context, proposedID string, duration int32, ac ModifiedAccessConditions) (*ContainerAcquireLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.pointers()
return c.client.AcquireLease(ctx, nil, &duration, &proposedID,
ifModifiedSince, ifUnmodifiedSince, nil)
@@ -198,28 +196,28 @@ func (c ContainerURL) AcquireLease(ctx context.Context, proposedID string, durat
// RenewLease renews the container's previously-acquired lease.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-container.
func (c ContainerURL) RenewLease(ctx context.Context, leaseID string, ac HTTPAccessConditions) (*ContainerRenewLeaseResponse, error) {
func (c ContainerURL) RenewLease(ctx context.Context, leaseID string, ac ModifiedAccessConditions) (*ContainerRenewLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.pointers()
return c.client.RenewLease(ctx, leaseID, nil, ifModifiedSince, ifUnmodifiedSince, nil)
}
// ReleaseLease releases the container's previously-acquired lease.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-container.
func (c ContainerURL) ReleaseLease(ctx context.Context, leaseID string, ac HTTPAccessConditions) (*ContainerReleaseLeaseResponse, error) {
func (c ContainerURL) ReleaseLease(ctx context.Context, leaseID string, ac ModifiedAccessConditions) (*ContainerReleaseLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.pointers()
return c.client.ReleaseLease(ctx, leaseID, nil, ifModifiedSince, ifUnmodifiedSince, nil)
}
// BreakLease breaks the container's previously-acquired lease (if it exists).
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-container.
func (c ContainerURL) BreakLease(ctx context.Context, period int32, ac HTTPAccessConditions) (*ContainerBreakLeaseResponse, error) {
func (c ContainerURL) BreakLease(ctx context.Context, period int32, ac ModifiedAccessConditions) (*ContainerBreakLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.pointers()
return c.client.BreakLease(ctx, nil, leasePeriodPointer(period), ifModifiedSince, ifUnmodifiedSince, nil)
}
// ChangeLease changes the container's lease ID.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-container.
func (c ContainerURL) ChangeLease(ctx context.Context, leaseID string, proposedID string, ac HTTPAccessConditions) (*ContainerChangeLeaseResponse, error) {
func (c ContainerURL) ChangeLease(ctx context.Context, leaseID string, proposedID string, ac ModifiedAccessConditions) (*ContainerChangeLeaseResponse, error) {
ifModifiedSince, ifUnmodifiedSince, _, _ := ac.pointers()
return c.client.ChangeLease(ctx, leaseID, proposedID, nil, ifModifiedSince, ifUnmodifiedSince, nil)
}
@@ -231,7 +229,7 @@ func (c ContainerURL) ChangeLease(ctx context.Context, leaseID string, proposedI
// For more information, see https://docs.microsoft.com/rest/api/storageservices/list-blobs.
func (c ContainerURL) ListBlobsFlatSegment(ctx context.Context, marker Marker, o ListBlobsSegmentOptions) (*ListBlobsFlatSegmentResponse, error) {
prefix, include, maxResults := o.pointers()
return c.client.ListBlobFlatSegment(ctx, prefix, marker.val, maxResults, include, nil, nil)
return c.client.ListBlobFlatSegment(ctx, prefix, marker.Val, maxResults, include, nil, nil)
}
// ListBlobsHierarchySegment returns a single segment of blobs starting from the specified Marker. Use an empty
@@ -241,10 +239,10 @@ func (c ContainerURL) ListBlobsFlatSegment(ctx context.Context, marker Marker, o
// For more information, see https://docs.microsoft.com/rest/api/storageservices/list-blobs.
func (c ContainerURL) ListBlobsHierarchySegment(ctx context.Context, marker Marker, delimiter string, o ListBlobsSegmentOptions) (*ListBlobsHierarchySegmentResponse, error) {
if o.Details.Snapshots {
panic("snapshots are not supported in this listing operation")
return nil, errors.New("snapshots are not supported in this listing operation")
}
prefix, include, maxResults := o.pointers()
return c.client.ListBlobHierarchySegment(ctx, delimiter, prefix, marker.val, maxResults, include, nil, nil)
return c.client.ListBlobHierarchySegment(ctx, delimiter, prefix, marker.Val, maxResults, include, nil, nil)
}
// ListBlobsSegmentOptions defines options available when calling ListBlobs.
@@ -264,9 +262,6 @@ func (o *ListBlobsSegmentOptions) pointers() (prefix *string, include []ListBlob
}
include = o.Details.slice()
if o.MaxResults != 0 {
if o.MaxResults < 0 {
panic("MaxResults must be >= 0")
}
maxResults = &o.MaxResults
}
return

View File

@@ -26,9 +26,6 @@ type PageBlobURL struct {
// NewPageBlobURL creates a PageBlobURL object using the specified URL and request policy pipeline.
func NewPageBlobURL(url url.URL, p pipeline.Pipeline) PageBlobURL {
if p == nil {
panic("p can't be nil")
}
blobClient := newBlobClient(url, p)
pbClient := newPageBlobClient(url, p)
return PageBlobURL{BlobURL: BlobURL{blobClient: blobClient}, pbClient: pbClient}
@@ -47,39 +44,54 @@ func (pb PageBlobURL) WithSnapshot(snapshot string) PageBlobURL {
return NewPageBlobURL(p.URL(), pb.blobClient.Pipeline())
}
// CreatePageBlob creates a page blob of the specified length. Call PutPage to upload data data to a page blob.
// Create creates a page blob of the specified length. Call PutPage to upload data data to a page blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob.
func (pb PageBlobURL) Create(ctx context.Context, size int64, sequenceNumber int64, h BlobHTTPHeaders, metadata Metadata, ac BlobAccessConditions) (*PageBlobCreateResponse, error) {
if sequenceNumber < 0 {
panic("sequenceNumber must be greater than or equal to 0")
}
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
return pb.pbClient.Create(ctx, 0, nil,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return pb.pbClient.Create(ctx, 0, size, nil,
&h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5, &h.CacheControl,
metadata, ac.LeaseAccessConditions.pointers(),
&h.ContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, &size, &sequenceNumber, nil)
&h.ContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, &sequenceNumber, nil)
}
// UploadPages writes 1 or more pages to the page blob. The start offset and the stream size must be a multiple of 512 bytes.
// This method panics if the stream is not at position 0.
// Note that the http client closes the body stream after the request is sent to the service.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page.
func (pb PageBlobURL) UploadPages(ctx context.Context, offset int64, body io.ReadSeeker, ac BlobAccessConditions) (*PageBlobUploadPagesResponse, error) {
count := validateSeekableStreamAt0AndGetCount(body)
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.PageBlobAccessConditions.pointers()
return pb.pbClient.UploadPages(ctx, body, count, nil,
func (pb PageBlobURL) UploadPages(ctx context.Context, offset int64, body io.ReadSeeker, ac PageBlobAccessConditions, transactionalMD5 []byte) (*PageBlobUploadPagesResponse, error) {
count, err := validateSeekableStreamAt0AndGetCount(body)
if err != nil {
return nil, err
}
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.SequenceNumberAccessConditions.pointers()
return pb.pbClient.UploadPages(ctx, body, count, transactionalMD5, nil,
PageRange{Start: offset, End: offset + count - 1}.pointers(),
ac.LeaseAccessConditions.pointers(),
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
}
// UploadPagesFromURL copies 1 or more pages from a source URL to the page blob.
// The sourceOffset specifies the start offset of source data to copy from.
// The destOffset specifies the start offset of data in page blob will be written to.
// The count must be a multiple of 512 bytes.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page-from-url.
func (pb PageBlobURL) UploadPagesFromURL(ctx context.Context, sourceURL url.URL, sourceOffset int64, destOffset int64, count int64, transactionalMD5 []byte, destinationAccessConditions PageBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions) (*PageBlobUploadPagesFromURLResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := destinationAccessConditions.ModifiedAccessConditions.pointers()
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers()
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := destinationAccessConditions.SequenceNumberAccessConditions.pointers()
return pb.pbClient.UploadPagesFromURL(ctx, sourceURL.String(), *PageRange{Start: sourceOffset, End: sourceOffset + count - 1}.pointers(), 0,
*PageRange{Start: destOffset, End: destOffset + count - 1}.pointers(), transactionalMD5, nil, destinationAccessConditions.LeaseAccessConditions.pointers(),
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil)
}
// ClearPages frees the specified pages from the page blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page.
func (pb PageBlobURL) ClearPages(ctx context.Context, offset int64, count int64, ac BlobAccessConditions) (*PageBlobClearPagesResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.PageBlobAccessConditions.pointers()
func (pb PageBlobURL) ClearPages(ctx context.Context, offset int64, count int64, ac PageBlobAccessConditions) (*PageBlobClearPagesResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.SequenceNumberAccessConditions.pointers()
return pb.pbClient.ClearPages(ctx, 0, nil,
PageRange{Start: offset, End: offset + count - 1}.pointers(),
ac.LeaseAccessConditions.pointers(),
@@ -90,7 +102,7 @@ func (pb PageBlobURL) ClearPages(ctx context.Context, offset int64, count int64,
// GetPageRanges returns the list of valid page ranges for a page blob or snapshot of a page blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges.
func (pb PageBlobURL) GetPageRanges(ctx context.Context, offset int64, count int64, ac BlobAccessConditions) (*PageList, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return pb.pbClient.GetPageRanges(ctx, nil, nil,
httpRange{offset: offset, count: count}.pointers(),
ac.LeaseAccessConditions.pointers(),
@@ -100,7 +112,7 @@ func (pb PageBlobURL) GetPageRanges(ctx context.Context, offset int64, count int
// GetPageRangesDiff gets the collection of page ranges that differ between a specified snapshot and this page blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges.
func (pb PageBlobURL) GetPageRangesDiff(ctx context.Context, offset int64, count int64, prevSnapshot string, ac BlobAccessConditions) (*PageList, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return pb.pbClient.GetPageRangesDiff(ctx, nil, nil, &prevSnapshot,
httpRange{offset: offset, count: count}.pointers(),
ac.LeaseAccessConditions.pointers(),
@@ -111,10 +123,7 @@ func (pb PageBlobURL) GetPageRangesDiff(ctx context.Context, offset int64, count
// Resize resizes the page blob to the specified size (which must be a multiple of 512).
// For more information, see https://docs.microsoft.com/rest/api/storageservices/set-blob-properties.
func (pb PageBlobURL) Resize(ctx context.Context, size int64, ac BlobAccessConditions) (*PageBlobResizeResponse, error) {
if size%PageBlobPageBytes != 0 {
panic("Size must be a multiple of PageBlobPageBytes (512)")
}
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
return pb.pbClient.Resize(ctx, size, nil, ac.LeaseAccessConditions.pointers(),
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
}
@@ -122,14 +131,11 @@ func (pb PageBlobURL) Resize(ctx context.Context, size int64, ac BlobAccessCondi
// SetSequenceNumber sets the page blob's sequence number.
func (pb PageBlobURL) UpdateSequenceNumber(ctx context.Context, action SequenceNumberActionType, sequenceNumber int64,
ac BlobAccessConditions) (*PageBlobUpdateSequenceNumberResponse, error) {
if sequenceNumber < 0 {
panic("sequenceNumber must be greater than or equal to 0")
}
sn := &sequenceNumber
if action == SequenceNumberActionIncrement {
sn = nil
}
ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.ModifiedAccessConditions.pointers()
return pb.pbClient.UpdateSequenceNumber(ctx, action, nil,
ac.LeaseAccessConditions.pointers(), ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch,
sn, nil)
@@ -141,37 +147,28 @@ func (pb PageBlobURL) UpdateSequenceNumber(ctx context.Context, action SequenceN
// For more information, see https://docs.microsoft.com/rest/api/storageservices/incremental-copy-blob and
// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/incremental-snapshots.
func (pb PageBlobURL) StartCopyIncremental(ctx context.Context, source url.URL, snapshot string, ac BlobAccessConditions) (*PageBlobCopyIncrementalResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
qp := source.Query()
qp.Set("snapshot", snapshot)
source.RawQuery = qp.Encode()
return pb.pbClient.CopyIncremental(ctx, source.String(), nil, nil,
return pb.pbClient.CopyIncremental(ctx, source.String(), nil,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
}
func (pr PageRange) pointers() *string {
if pr.Start < 0 {
panic("PageRange's Start value must be greater than or equal to 0")
}
if pr.End <= 0 {
panic("PageRange's End value must be greater than 0")
}
if pr.Start%PageBlobPageBytes != 0 {
panic("PageRange's Start value must be a multiple of 512")
}
if pr.End%PageBlobPageBytes != (PageBlobPageBytes - 1) {
panic("PageRange's End value must be 1 less than a multiple of 512")
}
if pr.End <= pr.Start {
panic("PageRange's End value must be after the start")
}
endOffset := strconv.FormatInt(int64(pr.End), 10)
asString := fmt.Sprintf("bytes=%v-%s", pr.Start, endOffset)
return &asString
}
// PageBlobAccessConditions identifies page blob-specific access conditions which you optionally set.
type PageBlobAccessConditions struct {
ModifiedAccessConditions
LeaseAccessConditions
SequenceNumberAccessConditions
}
// SequenceNumberAccessConditions identifies page blob-specific access conditions which you optionally set.
type SequenceNumberAccessConditions struct {
// IfSequenceNumberLessThan ensures that the page blob operation succeeds
// only if the blob's sequence number is less than a value.
// IfSequenceNumberLessThan=0 means no 'IfSequenceNumberLessThan' header specified.
@@ -195,17 +192,7 @@ type PageBlobAccessConditions struct {
}
// pointers is for internal infrastructure. It returns the fields as pointers.
func (ac PageBlobAccessConditions) pointers() (snltoe *int64, snlt *int64, sne *int64) {
if ac.IfSequenceNumberLessThan < -1 {
panic("Ifsequencenumberlessthan can't be less than -1")
}
if ac.IfSequenceNumberLessThanOrEqual < -1 {
panic("IfSequenceNumberLessThanOrEqual can't be less than -1")
}
if ac.IfSequenceNumberEqual < -1 {
panic("IfSequenceNumberEqual can't be less than -1")
}
func (ac SequenceNumberAccessConditions) pointers() (snltoe *int64, snlt *int64, sne *int64) {
var zero int64 // Defaults to 0
switch ac.IfSequenceNumberLessThan {
case -1:

View File

@@ -23,13 +23,21 @@ type ServiceURL struct {
// NewServiceURL creates a ServiceURL object using the specified URL and request policy pipeline.
func NewServiceURL(primaryURL url.URL, p pipeline.Pipeline) ServiceURL {
if p == nil {
panic("p can't be nil")
}
client := newServiceClient(primaryURL, p)
return ServiceURL{client: client}
}
//GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object.
//OAuth is required for this call, as well as any role that can delegate access to the storage account.
func (s ServiceURL) GetUserDelegationCredential(ctx context.Context, info KeyInfo, timeout *int32, requestID *string) (UserDelegationCredential, error) {
sc := newServiceClient(s.client.url, s.client.p)
udk, err := sc.GetUserDelegationKey(ctx, info, timeout, requestID)
if err != nil {
return UserDelegationCredential{}, err
}
return NewUserDelegationCredential(strings.Split(s.client.url.Host, ".")[0], *udk), nil
}
// URL returns the URL endpoint used by the ServiceURL object.
func (s ServiceURL) URL() url.URL {
return s.client.URL()
@@ -81,16 +89,16 @@ func appendToURLPath(u url.URL, name string) url.URL {
// After getting a segment, process it, and then call ListContainersFlatSegment again (passing the the
// previously-returned Marker) to get the next segment. For more information, see
// https://docs.microsoft.com/rest/api/storageservices/list-containers2.
func (s ServiceURL) ListContainersSegment(ctx context.Context, marker Marker, o ListContainersSegmentOptions) (*ListContainersResponse, error) {
func (s ServiceURL) ListContainersSegment(ctx context.Context, marker Marker, o ListContainersSegmentOptions) (*ListContainersSegmentResponse, error) {
prefix, include, maxResults := o.pointers()
return s.client.ListContainersSegment(ctx, prefix, marker.val, maxResults, include, nil, nil)
return s.client.ListContainersSegment(ctx, prefix, marker.Val, maxResults, include, nil, nil)
}
// ListContainersOptions defines options available when calling ListContainers.
type ListContainersSegmentOptions struct {
Detail ListContainersDetail // No IncludeType header is produced if ""
Prefix string // No Prefix header is produced if ""
MaxResults int32 // 0 means unspecified
Prefix string // No Prefix header is produced if ""
MaxResults int32 // 0 means unspecified
// TODO: update swagger to generate this type?
}
@@ -99,9 +107,6 @@ func (o *ListContainersSegmentOptions) pointers() (prefix *string, include ListC
prefix = &o.Prefix
}
if o.MaxResults != 0 {
if o.MaxResults < 0 {
panic("MaxResults must be >= 0")
}
maxResults = &o.MaxResults
}
include = ListContainersIncludeType(o.Detail.string())

View File

@@ -0,0 +1,38 @@
package azblob
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
)
// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it
func NewUserDelegationCredential(accountName string, key UserDelegationKey) UserDelegationCredential {
return UserDelegationCredential{
accountName: accountName,
accountKey: key,
}
}
type UserDelegationCredential struct {
accountName string
accountKey UserDelegationKey
}
// AccountName returns the Storage account's name
func (f UserDelegationCredential) AccountName() string {
return f.accountName
}
// ComputeHMAC
func (f UserDelegationCredential) ComputeHMACSHA256(message string) (base64String string) {
bytes, _ := base64.StdEncoding.DecodeString(f.accountKey.Value)
h := hmac.New(sha256.New, bytes)
h.Write([]byte(message))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
// Private method to return important parameters for NewSASQueryParameters
func (f UserDelegationCredential) getUDKParams() *UserDelegationKey {
return &f.accountKey
}

View File

@@ -0,0 +1,3 @@
package azblob
const serviceLibVersion = "0.7"

View File

@@ -6,6 +6,7 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"net/http"
"net/url"
"sort"
@@ -17,12 +18,12 @@ import (
// NewSharedKeyCredential creates an immutable SharedKeyCredential containing the
// storage account's name and either its primary or secondary key.
func NewSharedKeyCredential(accountName, accountKey string) *SharedKeyCredential {
func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredential, error) {
bytes, err := base64.StdEncoding.DecodeString(accountKey)
if err != nil {
panic(err)
return &SharedKeyCredential{}, err
}
return &SharedKeyCredential{accountName: accountName, accountKey: bytes}
return &SharedKeyCredential{accountName: accountName, accountKey: bytes}, nil
}
// SharedKeyCredential contains an account's name and its primary or secondary key.
@@ -38,6 +39,15 @@ func (f SharedKeyCredential) AccountName() string {
return f.accountName
}
func (f SharedKeyCredential) getAccountKey() []byte {
return f.accountKey
}
// noop function to satisfy StorageAccountCredential interface
func (f SharedKeyCredential) getUDKParams() *UserDelegationKey {
return nil
}
// New creates a credential policy object.
func (f *SharedKeyCredential) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
return pipeline.PolicyFunc(func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
@@ -45,7 +55,10 @@ func (f *SharedKeyCredential) New(next pipeline.Policy, po *pipeline.PolicyOptio
if d := request.Header.Get(headerXmsDate); d == "" {
request.Header[headerXmsDate] = []string{time.Now().UTC().Format(http.TimeFormat)}
}
stringToSign := f.buildStringToSign(request)
stringToSign, err := f.buildStringToSign(request)
if err != nil {
return nil, err
}
signature := f.ComputeHMACSHA256(stringToSign)
authHeader := strings.Join([]string{"SharedKey ", f.accountName, ":", signature}, "")
request.Header[headerAuthorization] = []string{authHeader}
@@ -84,13 +97,13 @@ const (
)
// ComputeHMACSHA256 generates a hash signature for an HTTP request or for a SAS.
func (f *SharedKeyCredential) ComputeHMACSHA256(message string) (base64String string) {
func (f SharedKeyCredential) ComputeHMACSHA256(message string) (base64String string) {
h := hmac.New(sha256.New, f.accountKey)
h.Write([]byte(message))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func (f *SharedKeyCredential) buildStringToSign(request pipeline.Request) string {
func (f *SharedKeyCredential) buildStringToSign(request pipeline.Request) (string, error) {
// https://docs.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services
headers := request.Header
contentLength := headers.Get(headerContentLength)
@@ -98,6 +111,11 @@ func (f *SharedKeyCredential) buildStringToSign(request pipeline.Request) string
contentLength = ""
}
canonicalizedResource, err := f.buildCanonicalizedResource(request.URL)
if err != nil {
return "", err
}
stringToSign := strings.Join([]string{
request.Method,
headers.Get(headerContentEncoding),
@@ -112,9 +130,9 @@ func (f *SharedKeyCredential) buildStringToSign(request pipeline.Request) string
headers.Get(headerIfUnmodifiedSince),
headers.Get(headerRange),
buildCanonicalizedHeader(headers),
f.buildCanonicalizedResource(request.URL),
canonicalizedResource,
}, "\n")
return stringToSign
return stringToSign, nil
}
func buildCanonicalizedHeader(headers http.Header) string {
@@ -146,7 +164,7 @@ func buildCanonicalizedHeader(headers http.Header) string {
return string(ch.Bytes())
}
func (f *SharedKeyCredential) buildCanonicalizedResource(u *url.URL) string {
func (f *SharedKeyCredential) buildCanonicalizedResource(u *url.URL) (string, error) {
// https://docs.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services
cr := bytes.NewBufferString("/")
cr.WriteString(f.accountName)
@@ -164,7 +182,7 @@ func (f *SharedKeyCredential) buildCanonicalizedResource(u *url.URL) string {
// params is a map[string][]string; param name is key; params values is []string
params, err := url.ParseQuery(u.RawQuery) // Returns URL decoded values
if err != nil {
panic(err)
return "", errors.New("parsing query parameters must succeed, otherwise there might be serious problems in the SDK/generated code")
}
if len(params) > 0 { // There is at least 1 query parameter
@@ -183,5 +201,5 @@ func (f *SharedKeyCredential) buildCanonicalizedResource(u *url.URL) string {
cr.WriteString("\n" + paramName + ":" + strings.Join(paramValues, ","))
}
}
return string(cr.Bytes())
return string(cr.Bytes()), nil
}

View File

@@ -2,6 +2,7 @@ package azblob
import (
"context"
"errors"
"sync/atomic"
"runtime"
@@ -11,6 +12,10 @@ import (
"github.com/Azure/azure-pipeline-go/pipeline"
)
// TokenRefresher represents a callback method that you write; this method is called periodically
// so you can refresh the token credential's value.
type TokenRefresher func(credential TokenCredential) time.Duration
// TokenCredential represents a token credential (which is also a pipeline.Factory).
type TokenCredential interface {
Credential
@@ -20,12 +25,15 @@ type TokenCredential interface {
// NewTokenCredential creates a token credential for use with role-based access control (RBAC) access to Azure Storage
// resources. You initialize the TokenCredential with an initial token value. If you pass a non-nil value for
// tokenRefresher, then the function you pass will be called immediately (so it can refresh and change the
// TokenCredential's token value by calling SetToken; your tokenRefresher function must return a time.Duration
// tokenRefresher, then the function you pass will be called immediately so it can refresh and change the
// TokenCredential's token value by calling SetToken. Your tokenRefresher function must return a time.Duration
// indicating how long the TokenCredential object should wait before calling your tokenRefresher function again.
func NewTokenCredential(initialToken string, tokenRefresher func(credential TokenCredential) time.Duration) TokenCredential {
// If your tokenRefresher callback fails to refresh the token, you can return a duration of 0 to stop your
// TokenCredential object from ever invoking tokenRefresher again. Also, oen way to deal with failing to refresh a
// token is to cancel a context.Context object used by requests that have the TokenCredential object in their pipeline.
func NewTokenCredential(initialToken string, tokenRefresher TokenRefresher) TokenCredential {
tc := &tokenCredential{}
tc.SetToken(initialToken) // We dont' set it above to guarantee atomicity
tc.SetToken(initialToken) // We don't set it above to guarantee atomicity
if tokenRefresher == nil {
return tc // If no callback specified, return the simple tokenCredential
}
@@ -68,7 +76,7 @@ type tokenCredential struct {
// The members below are only used if the user specified a tokenRefresher callback function.
timer *time.Timer
tokenRefresher func(c TokenCredential) time.Duration
tokenRefresher TokenRefresher
lock sync.Mutex
stopped bool
}
@@ -84,7 +92,7 @@ func (f *tokenCredential) SetToken(token string) { f.token.Store(token) }
// startRefresh calls refresh which immediately calls tokenRefresher
// and then starts a timer to call tokenRefresher in the future.
func (f *tokenCredential) startRefresh(tokenRefresher func(c TokenCredential) time.Duration) {
func (f *tokenCredential) startRefresh(tokenRefresher TokenRefresher) {
f.tokenRefresher = tokenRefresher
f.stopped = false // In case user calls StartRefresh, StopRefresh, & then StartRefresh again
f.refresh()
@@ -95,11 +103,13 @@ func (f *tokenCredential) startRefresh(tokenRefresher func(c TokenCredential) ti
// in order to refresh the token again in the future.
func (f *tokenCredential) refresh() {
d := f.tokenRefresher(f) // Invoke the user's refresh callback outside of the lock
f.lock.Lock()
if !f.stopped {
f.timer = time.AfterFunc(d, f.refresh)
if d > 0 { // If duration is 0 or negative, refresher wants to not be called again
f.lock.Lock()
if !f.stopped {
f.timer = time.AfterFunc(d, f.refresh)
}
f.lock.Unlock()
}
f.lock.Unlock()
}
// stopRefresh stops any pending timer and sets stopped field to true to prevent
@@ -118,7 +128,8 @@ func (f *tokenCredential) stopRefresh() {
func (f *tokenCredential) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
return pipeline.PolicyFunc(func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
if request.URL.Scheme != "https" {
panic("Token credentials require a URL using the https protocol scheme.")
// HTTPS must be used, otherwise the tokens are at the risk of being exposed
return nil, errors.New("token credentials require a URL using the https protocol scheme")
}
request.Header[headerAuthorization] = []string{"Bearer " + f.Token()}
return next.Do(ctx, request)

View File

@@ -1,4 +1,4 @@
// +build linux darwin freebsd
// +build linux darwin freebsd openbsd netbsd dragonfly
package azblob
@@ -22,6 +22,6 @@ func (m *mmf) unmap() {
err := syscall.Munmap(*m)
*m = nil
if err != nil {
panic(err)
panic("if we are unable to unmap the memory-mapped file, there is serious concern for memory corruption")
}
}

View File

@@ -33,6 +33,6 @@ func (m *mmf) unmap() {
*m = mmf{}
err := syscall.UnmapViewOfFile(addr)
if err != nil {
panic(err)
panic("if we are unable to unmap the memory-mapped file, there is serious concern for memory corruption")
}
}

View File

@@ -17,14 +17,13 @@ type PipelineOptions struct {
// Telemetry configures the built-in telemetry policy behavior.
Telemetry TelemetryOptions
// HTTPSender configures the sender of HTTP requests
HTTPSender pipeline.Factory
}
// NewPipeline creates a Pipeline using the specified credentials and options.
func NewPipeline(c Credential, o PipelineOptions) pipeline.Pipeline {
if c == nil {
panic("c can't be nil")
}
// Closest to API goes first; closest to the wire goes last
f := []pipeline.Factory{
NewTelemetryPolicyFactory(o.Telemetry),
@@ -39,8 +38,9 @@ func NewPipeline(c Credential, o PipelineOptions) pipeline.Pipeline {
f = append(f, c)
}
f = append(f,
pipeline.MethodFactoryMarker(), // indicates at what stage in the pipeline the method factory is invoked
NewRequestLogPolicyFactory(o.RequestLog))
NewRequestLogPolicyFactory(o.RequestLog),
pipeline.MethodFactoryMarker()) // indicates at what stage in the pipeline the method factory is invoked
return pipeline.NewPipeline(f, pipeline.Options{HTTPSender: nil, Log: o.Log})
return pipeline.NewPipeline(f, pipeline.Options{HTTPSender: o.HTTPSender, Log: o.Log})
}

View File

@@ -109,7 +109,8 @@ func NewRequestLogPolicyFactory(o RequestLogOptions) pipeline.Factory {
})
}
func redactSigQueryParam(rawQuery string) (bool, string) {
// RedactSigQueryParam redacts the 'sig' query parameter in URL's raw query to protect secret.
func RedactSigQueryParam(rawQuery string) (bool, string) {
rawQuery = strings.ToLower(rawQuery) // lowercase the string so we can look for ?sig= and &sig=
sigFound := strings.Contains(rawQuery, "?sig=")
if !sigFound {
@@ -130,12 +131,13 @@ func redactSigQueryParam(rawQuery string) (bool, string) {
func prepareRequestForLogging(request pipeline.Request) *http.Request {
req := request
if sigFound, rawQuery := redactSigQueryParam(req.URL.RawQuery); sigFound {
if sigFound, rawQuery := RedactSigQueryParam(req.URL.RawQuery); sigFound {
// Make copy so we don't destroy the query parameters we actually need to send in the request
req = request.Copy()
req.Request.URL.RawQuery = rawQuery
}
return req.Request
return prepareRequestForServiceLogging(req)
}
func stack() []byte {
@@ -148,3 +150,33 @@ func stack() []byte {
buf = make([]byte, 2*len(buf))
}
}
///////////////////////////////////////////////////////////////////////////////////////
// Redact phase useful for blob and file service only. For other services,
// this method can directly return request.Request.
///////////////////////////////////////////////////////////////////////////////////////
func prepareRequestForServiceLogging(request pipeline.Request) *http.Request {
req := request
if exist, key := doesHeaderExistCaseInsensitive(req.Header, xMsCopySourceHeader); exist {
req = request.Copy()
url, err := url.Parse(req.Header.Get(key))
if err == nil {
if sigFound, rawQuery := RedactSigQueryParam(url.RawQuery); sigFound {
url.RawQuery = rawQuery
req.Header.Set(xMsCopySourceHeader, url.String())
}
}
}
return req.Request
}
const xMsCopySourceHeader = "x-ms-copy-source"
func doesHeaderExistCaseInsensitive(header http.Header, key string) (bool, string) {
for keyInHeader := range header {
if strings.EqualFold(keyInHeader, key) {
return true, keyInHeader
}
}
return false, ""
}

View File

@@ -2,15 +2,17 @@ package azblob
import (
"context"
"errors"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"strconv"
"strings"
"time"
"github.com/Azure/azure-pipeline-go/pipeline"
"io/ioutil"
"io"
)
// RetryPolicy tells the pipeline what kind of retry policy to use. See the RetryPolicy* constants.
@@ -57,30 +59,21 @@ type RetryOptions struct {
// If RetryReadsFromSecondaryHost is "" (the default) then operations are not retried against another host.
// NOTE: Before setting this field, make sure you understand the issues around reading stale & potentially-inconsistent
// data at this webpage: https://docs.microsoft.com/en-us/azure/storage/common/storage-designing-ha-apps-with-ragrs
RetryReadsFromSecondaryHost string // Comment this our for non-Blob SDKs
RetryReadsFromSecondaryHost string // Comment this our for non-Blob SDKs
}
func (o RetryOptions) retryReadsFromSecondaryHost() string {
return o.RetryReadsFromSecondaryHost // This is for the Blob SDK only
return o.RetryReadsFromSecondaryHost // This is for the Blob SDK only
//return "" // This is for non-blob SDKs
}
func (o RetryOptions) defaults() RetryOptions {
if o.Policy != RetryPolicyExponential && o.Policy != RetryPolicyFixed {
panic("RetryPolicy must be RetryPolicyExponential or RetryPolicyFixed")
}
if o.MaxTries < 0 {
panic("MaxTries must be >= 0")
}
if o.TryTimeout < 0 || o.RetryDelay < 0 || o.MaxRetryDelay < 0 {
panic("TryTimeout, RetryDelay, and MaxRetryDelay must all be >= 0")
}
if o.RetryDelay > o.MaxRetryDelay {
panic("RetryDelay must be <= MaxRetryDelay")
}
if (o.RetryDelay == 0 && o.MaxRetryDelay != 0) || (o.RetryDelay != 0 && o.MaxRetryDelay == 0) {
panic("Both RetryDelay and MaxRetryDelay must be 0 or neither can be 0")
}
// We assume the following:
// 1. o.Policy should either be RetryPolicyExponential or RetryPolicyFixed
// 2. o.MaxTries >= 0
// 3. o.TryTimeout, o.RetryDelay, and o.MaxRetryDelay >=0
// 4. o.RetryDelay <= o.MaxRetryDelay
// 5. Both o.RetryDelay and o.MaxRetryDelay must be 0 or neither can be 0
IfDefault := func(current *time.Duration, desired time.Duration) {
if *current == time.Duration(0) {
@@ -127,7 +120,8 @@ func (o RetryOptions) calcDelay(try int32) time.Duration { // try is >=1; never
}
// Introduce some jitter: [0.0, 1.0) / 2 = [0.0, 0.5) + 0.8 = [0.8, 1.3)
delay = time.Duration(delay.Seconds() * (rand.Float64()/2 + 0.8) * float64(time.Second)) // NOTE: We want math/rand; not crypto/rand
// For casts and rounding - be careful, as per https://github.com/golang/go/issues/20757
delay = time.Duration(float32(delay) * (rand.Float32()/2 + 0.8)) // NOTE: We want math/rand; not crypto/rand
if delay > o.MaxRetryDelay {
delay = o.MaxRetryDelay
}
@@ -164,7 +158,8 @@ func NewRetryPolicyFactory(o RetryOptions) pipeline.Factory {
logf("Primary try=%d, Delay=%v\n", primaryTry, delay)
time.Sleep(delay) // The 1st try returns 0 delay
} else {
delay := time.Second * time.Duration(rand.Float32()/2+0.8)
// For casts and rounding - be careful, as per https://github.com/golang/go/issues/20757
delay := time.Duration(float32(time.Second) * (rand.Float32()/2 + 0.8))
logf("Secondary try=%d, Delay=%v\n", try-primaryTry, delay)
time.Sleep(delay) // Delay with some jitter before trying secondary
}
@@ -175,11 +170,14 @@ func NewRetryPolicyFactory(o RetryOptions) pipeline.Factory {
// For each try, seek to the beginning of the Body stream. We do this even for the 1st try because
// the stream may not be at offset 0 when we first get it and we want the same behavior for the
// 1st try as for additional tries.
if err = requestCopy.RewindBody(); err != nil {
panic(err)
err = requestCopy.RewindBody()
if err != nil {
return nil, errors.New("we must be able to seek on the Body Stream, otherwise retries would cause data corruption")
}
if !tryingPrimary {
requestCopy.Request.URL.Host = o.retryReadsFromSecondaryHost()
requestCopy.URL.Host = o.retryReadsFromSecondaryHost()
requestCopy.Host = o.retryReadsFromSecondaryHost()
}
// Set the server-side timeout query parameter "timeout=[seconds]"
@@ -214,16 +212,34 @@ func NewRetryPolicyFactory(o RetryOptions) pipeline.Factory {
switch {
case ctx.Err() != nil:
action = "NoRetry: Op timeout"
case !tryingPrimary && response != nil && response.Response().StatusCode == http.StatusNotFound:
case !tryingPrimary && response != nil && response.Response() != nil && response.Response().StatusCode == http.StatusNotFound:
// If attempt was against the secondary & it returned a StatusNotFound (404), then
// the resource was not found. This may be due to replication delay. So, in this
// case, we'll never try the secondary again for this operation.
considerSecondary = false
action = "Retry: Secondary URL returned 404"
case err != nil:
// NOTE: Protocol Responder returns non-nil if REST API returns invalid status code for the invoked operation
if netErr, ok := err.(net.Error); ok && (netErr.Temporary() || netErr.Timeout()) {
action = "Retry: net.Error and Temporary() or Timeout()"
// NOTE: Protocol Responder returns non-nil if REST API returns invalid status code for the invoked operation.
// Use ServiceCode to verify if the error is related to storage service-side,
// ServiceCode is set only when error related to storage service happened.
if stErr, ok := err.(StorageError); ok {
if stErr.Temporary() {
action = "Retry: StorageError with error service code and Temporary()"
} else if stErr.Response() != nil && isSuccessStatusCode(stErr.Response()) { // TODO: This is a temporarily work around, remove this after protocol layer fix the issue that net.Error is wrapped as storageError
action = "Retry: StorageError with success status code"
} else {
action = "NoRetry: StorageError not Temporary() and without retriable status code"
}
} else if netErr, ok := err.(net.Error); ok {
// Use non-retriable net.Error list, but not retriable list.
// As there are errors without Temporary() implementation,
// while need be retried, like 'connection reset by peer', 'transport connection broken' and etc.
// So the SDK do retry for most of the case, unless the error should not be retried for sure.
if !isNotRetriable(netErr) {
action = "Retry: net.Error and not in the non-retriable list"
} else {
action = "NoRetry: net.Error and in the non-retriable list"
}
} else {
action = "NoRetry: unrecognized error"
}
@@ -237,11 +253,17 @@ func NewRetryPolicyFactory(o RetryOptions) pipeline.Factory {
if err != nil {
tryCancel() // If we're returning an error, cancel this current/last per-retry timeout context
} else {
// TODO: Right now, we've decided to leak the per-try Context until the user's Context is canceled.
// Another option is that we wrap the last per-try context in a body and overwrite the Response's Body field with our wrapper.
// We wrap the last per-try context in a body and overwrite the Response's Body field with our wrapper.
// So, when the user closes the Body, the our per-try context gets closed too.
// Another option, is that the Last Policy do this wrapping for a per-retry context (not for the user's context)
_ = tryCancel // So, for now, we don't call cancel: cancel()
if response == nil || response.Response() == nil {
// We do panic in the case response or response.Response() is nil,
// as for client, the response should not be nil if request is sent and the operations is executed successfully.
// Another option, is that execute the cancel function when response or response.Response() is nil,
// as in this case, current per-try has nothing to do in future.
return nil, errors.New("invalid state, response should not be nil when the operation is executed successfully")
}
response.Response().Body = &contextCancelReadCloser{cf: tryCancel, body: response.Response().Body}
}
break // Don't retry
}
@@ -259,6 +281,78 @@ func NewRetryPolicyFactory(o RetryOptions) pipeline.Factory {
})
}
// contextCancelReadCloser helps to invoke context's cancelFunc properly when the ReadCloser is closed.
type contextCancelReadCloser struct {
cf context.CancelFunc
body io.ReadCloser
}
func (rc *contextCancelReadCloser) Read(p []byte) (n int, err error) {
return rc.body.Read(p)
}
func (rc *contextCancelReadCloser) Close() error {
err := rc.body.Close()
if rc.cf != nil {
rc.cf()
}
return err
}
// isNotRetriable checks if the provided net.Error isn't retriable.
func isNotRetriable(errToParse net.Error) bool {
// No error, so this is NOT retriable.
if errToParse == nil {
return true
}
// The error is either temporary or a timeout so it IS retriable (not not retriable).
if errToParse.Temporary() || errToParse.Timeout() {
return false
}
genericErr := error(errToParse)
// From here all the error are neither Temporary() nor Timeout().
switch err := errToParse.(type) {
case *net.OpError:
// The net.Error is also a net.OpError but the inner error is nil, so this is not retriable.
if err.Err == nil {
return true
}
genericErr = err.Err
}
switch genericErr.(type) {
case *net.AddrError, net.UnknownNetworkError, *net.DNSError, net.InvalidAddrError, *net.ParseError, *net.DNSConfigError:
// If the error is one of the ones listed, then it is NOT retriable.
return true
}
// If it's invalid header field name/value error thrown by http module, then it is NOT retriable.
// This could happen when metadata's key or value is invalid. (RoundTrip in transport.go)
if strings.Contains(genericErr.Error(), "invalid header field") {
return true
}
// Assume the error is retriable.
return false
}
var successStatusCodes = []int{http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent, http.StatusPartialContent}
func isSuccessStatusCode(resp *http.Response) bool {
if resp == nil {
return false
}
for _, i := range successStatusCodes {
if i == resp.StatusCode {
return true
}
}
return false
}
// According to https://github.com/golang/go/wiki/CompilerOptimizations, the compiler will inline this method and hopefully optimize all calls to it away
var logf = func(format string, a ...interface{}) {}

View File

@@ -0,0 +1,178 @@
package azblob
import (
"context"
"io"
"net"
"net/http"
"strings"
"sync"
)
const CountToEnd = 0
// HTTPGetter is a function type that refers to a method that performs an HTTP GET operation.
type HTTPGetter func(ctx context.Context, i HTTPGetterInfo) (*http.Response, error)
// HTTPGetterInfo is passed to an HTTPGetter function passing it parameters
// that should be used to make an HTTP GET request.
type HTTPGetterInfo struct {
// Offset specifies the start offset that should be used when
// creating the HTTP GET request's Range header
Offset int64
// Count specifies the count of bytes that should be used to calculate
// the end offset when creating the HTTP GET request's Range header
Count int64
// ETag specifies the resource's etag that should be used when creating
// the HTTP GET request's If-Match header
ETag ETag
}
// FailedReadNotifier is a function type that represents the notification function called when a read fails
type FailedReadNotifier func(failureCount int, lastError error, offset int64, count int64, willRetry bool)
// RetryReaderOptions contains properties which can help to decide when to do retry.
type RetryReaderOptions struct {
// MaxRetryRequests specifies the maximum number of HTTP GET requests that will be made
// while reading from a RetryReader. A value of zero means that no additional HTTP
// GET requests will be made.
MaxRetryRequests int
doInjectError bool
doInjectErrorRound int
// NotifyFailedRead is called, if non-nil, after any failure to read. Expected usage is diagnostic logging.
NotifyFailedRead FailedReadNotifier
// TreatEarlyCloseAsError can be set to true to prevent retries after "read on closed response body". By default,
// retryReader has the following special behaviour: closing the response body before it is all read is treated as a
// retryable error. This is to allow callers to force a retry by closing the body from another goroutine (e.g. if the =
// read is too slow, caller may want to force a retry in the hope that the retry will be quicker). If
// TreatEarlyCloseAsError is true, then retryReader's special behaviour is suppressed, and "read on closed body" is instead
// treated as a fatal (non-retryable) error.
// Note that setting TreatEarlyCloseAsError only guarantees that Closing will produce a fatal error if the Close happens
// from the same "thread" (goroutine) as Read. Concurrent Close calls from other goroutines may instead produce network errors
// which will be retried.
TreatEarlyCloseAsError bool
}
// retryReader implements io.ReaderCloser methods.
// retryReader tries to read from response, and if there is retriable network error
// returned during reading, it will retry according to retry reader option through executing
// user defined action with provided data to get a new response, and continue the overall reading process
// through reading from the new response.
type retryReader struct {
ctx context.Context
info HTTPGetterInfo
countWasBounded bool
o RetryReaderOptions
getter HTTPGetter
// we support Close-ing during Reads (from other goroutines), so we protect the shared state, which is response
responseMu *sync.Mutex
response *http.Response
}
// NewRetryReader creates a retry reader.
func NewRetryReader(ctx context.Context, initialResponse *http.Response,
info HTTPGetterInfo, o RetryReaderOptions, getter HTTPGetter) io.ReadCloser {
return &retryReader{
ctx: ctx,
getter: getter,
info: info,
countWasBounded: info.Count != CountToEnd,
response: initialResponse,
responseMu: &sync.Mutex{},
o: o}
}
func (s *retryReader) setResponse(r *http.Response) {
s.responseMu.Lock()
defer s.responseMu.Unlock()
s.response = r
}
func (s *retryReader) Read(p []byte) (n int, err error) {
for try := 0; ; try++ {
//fmt.Println(try) // Comment out for debugging.
if s.countWasBounded && s.info.Count == CountToEnd {
// User specified an original count and the remaining bytes are 0, return 0, EOF
return 0, io.EOF
}
s.responseMu.Lock()
resp := s.response
s.responseMu.Unlock()
if resp == nil { // We don't have a response stream to read from, try to get one.
newResponse, err := s.getter(s.ctx, s.info)
if err != nil {
return 0, err
}
// Successful GET; this is the network stream we'll read from.
s.setResponse(newResponse)
resp = newResponse
}
n, err := resp.Body.Read(p) // Read from the stream (this will return non-nil err if forceRetry is called, from another goroutine, while it is running)
// Injection mechanism for testing.
if s.o.doInjectError && try == s.o.doInjectErrorRound {
err = &net.DNSError{IsTemporary: true}
}
// We successfully read data or end EOF.
if err == nil || err == io.EOF {
s.info.Offset += int64(n) // Increments the start offset in case we need to make a new HTTP request in the future
if s.info.Count != CountToEnd {
s.info.Count -= int64(n) // Decrement the count in case we need to make a new HTTP request in the future
}
return n, err // Return the return to the caller
}
s.Close() // Error, close stream
s.setResponse(nil) // Our stream is no longer good
// Check the retry count and error code, and decide whether to retry.
retriesExhausted := try >= s.o.MaxRetryRequests
_, isNetError := err.(net.Error)
willRetry := (isNetError || s.wasRetryableEarlyClose(err)) && !retriesExhausted
// Notify, for logging purposes, of any failures
if s.o.NotifyFailedRead != nil {
failureCount := try + 1 // because try is zero-based
s.o.NotifyFailedRead(failureCount, err, s.info.Offset, s.info.Count, willRetry)
}
if willRetry {
continue
// Loop around and try to get and read from new stream.
}
return n, err // Not retryable, or retries exhausted, so just return
}
}
// By default, we allow early Closing, from another concurrent goroutine, to be used to force a retry
// Is this safe, to close early from another goroutine? Early close ultimately ends up calling
// net.Conn.Close, and that is documented as "Any blocked Read or Write operations will be unblocked and return errors"
// which is exactly the behaviour we want.
// NOTE: that if caller has forced an early Close from a separate goroutine (separate from the Read)
// then there are two different types of error that may happen - either the one one we check for here,
// or a net.Error (due to closure of connection). Which one happens depends on timing. We only need this routine
// to check for one, since the other is a net.Error, which our main Read retry loop is already handing.
func (s *retryReader) wasRetryableEarlyClose(err error) bool {
if s.o.TreatEarlyCloseAsError {
return false // user wants all early closes to be errors, and so not retryable
}
// unfortunately, http.errReadOnClosedResBody is private, so the best we can do here is to check for its text
return strings.HasSuffix(err.Error(), ReadOnClosedBodyMessage)
}
const ReadOnClosedBodyMessage = "read on closed response body"
func (s *retryReader) Close() error {
s.responseMu.Lock()
defer s.responseMu.Unlock()
if s.response != nil && s.response.Body != nil {
return s.response.Body.Close()
}
return nil
}

View File

@@ -2,6 +2,7 @@ package azblob
import (
"bytes"
"errors"
"fmt"
"strings"
"time"
@@ -22,21 +23,21 @@ type AccountSASSignatureValues struct {
// NewSASQueryParameters uses an account's shared key credential to sign this signature values to produce
// the proper SAS query parameters.
func (v AccountSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) SASQueryParameters {
func (v AccountSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) {
// https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS
if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" {
panic("Account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType")
return SASQueryParameters{}, errors.New("account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType")
}
if v.Version == "" {
v.Version = SASVersion
}
perms := &AccountSASPermissions{}
if err := perms.Parse(v.Permissions); err != nil {
panic(err)
return SASQueryParameters{}, err
}
v.Permissions = perms.String()
startTime, expiryTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime)
startTime, expiryTime, _ := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime, time.Time{})
stringToSign := strings.Join([]string{
sharedKeyCredential.AccountName(),
@@ -68,7 +69,8 @@ func (v AccountSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *Sh
// Calculated SAS signature
signature: signature,
}
return p
return p, nil
}
// The AccountSASPermissions type simplifies creating the permissions string for an Azure Storage Account SAS.
@@ -205,7 +207,7 @@ func (rt *AccountSASResourceTypes) Parse(s string) error {
switch r {
case 's':
rt.Service = true
case 'q':
case 'c':
rt.Container = true
case 'o':
rt.Object = true

View File

@@ -22,7 +22,7 @@ const (
// FormatTimesForSASSigning converts a time.Time to a snapshotTimeFormat string suitable for a
// SASField's StartTime or ExpiryTime fields. Returns "" if value.IsZero().
func FormatTimesForSASSigning(startTime, expiryTime time.Time) (string, string) {
func FormatTimesForSASSigning(startTime, expiryTime, snapshotTime time.Time) (string, string, string) {
ss := ""
if !startTime.IsZero() {
ss = startTime.Format(SASTimeFormat) // "yyyy-MM-ddTHH:mm:ssZ"
@@ -31,7 +31,11 @@ func FormatTimesForSASSigning(startTime, expiryTime time.Time) (string, string)
if !expiryTime.IsZero() {
se = expiryTime.Format(SASTimeFormat) // "yyyy-MM-ddTHH:mm:ssZ"
}
return ss, se
sh := ""
if !snapshotTime.IsZero() {
sh = snapshotTime.Format(SnapshotTimeFormat)
}
return ss, se, sh
}
// SASTimeFormat represents the format of a SAS start or expiry time. Use it when formatting/parsing a time.Time.
@@ -47,17 +51,57 @@ const SASTimeFormat = "2006-01-02T15:04:05Z" //"2017-07-27T00:00:00Z" // ISO 860
// This type defines the components used by all Azure Storage resources (Containers, Blobs, Files, & Queues).
type SASQueryParameters struct {
// All members are immutable or values so copies of this struct are goroutine-safe.
version string `param:"sv"`
services string `param:"ss"`
resourceTypes string `param:"srt"`
protocol SASProtocol `param:"spr"`
startTime time.Time `param:"st"`
expiryTime time.Time `param:"se"`
ipRange IPRange `param:"sip"`
identifier string `param:"si"`
resource string `param:"sr"`
permissions string `param:"sp"`
signature string `param:"sig"`
version string `param:"sv"`
services string `param:"ss"`
resourceTypes string `param:"srt"`
protocol SASProtocol `param:"spr"`
startTime time.Time `param:"st"`
expiryTime time.Time `param:"se"`
snapshotTime time.Time `param:"snapshot"`
ipRange IPRange `param:"sip"`
identifier string `param:"si"`
resource string `param:"sr"`
permissions string `param:"sp"`
signature string `param:"sig"`
cacheControl string `param:"rscc"`
contentDisposition string `param:"rscd"`
contentEncoding string `param:"rsce"`
contentLanguage string `param:"rscl"`
contentType string `param:"rsct"`
signedOid string `param:"skoid"`
signedTid string `param:"sktid"`
signedStart time.Time `param:"skt"`
signedExpiry time.Time `param:"ske"`
signedService string `param:"sks"`
signedVersion string `param:"skv"`
}
func (p *SASQueryParameters) SignedOid() string {
return p.signedOid
}
func (p *SASQueryParameters) SignedTid() string {
return p.signedTid
}
func (p *SASQueryParameters) SignedStart() time.Time {
return p.signedStart
}
func (p *SASQueryParameters) SignedExpiry() time.Time {
return p.signedExpiry
}
func (p *SASQueryParameters) SignedService() string {
return p.signedService
}
func (p *SASQueryParameters) SignedVersion() string {
return p.signedVersion
}
func (p *SASQueryParameters) SnapshotTime() time.Time {
return p.snapshotTime
}
func (p *SASQueryParameters) Version() string {
@@ -99,6 +143,26 @@ func (p *SASQueryParameters) Signature() string {
return p.signature
}
func (p *SASQueryParameters) CacheControl() string {
return p.cacheControl
}
func (p *SASQueryParameters) ContentDisposition() string {
return p.contentDisposition
}
func (p *SASQueryParameters) ContentEncoding() string {
return p.contentEncoding
}
func (p *SASQueryParameters) ContentLanguage() string {
return p.contentLanguage
}
func (p *SASQueryParameters) ContentType() string {
return p.contentType
}
// IPRange represents a SAS IP range's start IP and (optionally) end IP.
type IPRange struct {
Start net.IP // Not specified if length = 0
@@ -135,6 +199,8 @@ func newSASQueryParameters(values url.Values, deleteSASParametersFromValues bool
p.resourceTypes = val
case "spr":
p.protocol = SASProtocol(val)
case "snapshot":
p.snapshotTime, _ = time.Parse(SnapshotTimeFormat, val)
case "st":
p.startTime, _ = time.Parse(SASTimeFormat, val)
case "se":
@@ -155,6 +221,28 @@ func newSASQueryParameters(values url.Values, deleteSASParametersFromValues bool
p.permissions = val
case "sig":
p.signature = val
case "rscc":
p.cacheControl = val
case "rscd":
p.contentDisposition = val
case "rsce":
p.contentEncoding = val
case "rscl":
p.contentLanguage = val
case "rsct":
p.contentType = val
case "skoid":
p.signedOid = val
case "sktid":
p.signedTid = val
case "skt":
p.signedStart, _ = time.Parse(SASTimeFormat, val)
case "ske":
p.signedExpiry, _ = time.Parse(SASTimeFormat, val)
case "sks":
p.signedService = val
case "skv":
p.signedVersion = val
default:
isSASKey = false // We didn't recognize the query parameter
}
@@ -197,9 +285,32 @@ func (p *SASQueryParameters) addToValues(v url.Values) url.Values {
if p.permissions != "" {
v.Add("sp", p.permissions)
}
if p.signedOid != "" {
v.Add("skoid", p.signedOid)
v.Add("sktid", p.signedTid)
v.Add("skt", p.signedStart.Format(SASTimeFormat))
v.Add("ske", p.signedExpiry.Format(SASTimeFormat))
v.Add("sks", p.signedService)
v.Add("skv", p.signedVersion)
}
if p.signature != "" {
v.Add("sig", p.signature)
}
if p.cacheControl != "" {
v.Add("rscc", p.cacheControl)
}
if p.contentDisposition != "" {
v.Add("rscd", p.contentDisposition)
}
if p.contentEncoding != "" {
v.Add("rsce", p.contentEncoding)
}
if p.contentLanguage != "" {
v.Add("rscl", p.contentLanguage)
}
if p.contentType != "" {
v.Add("rsct", p.contentType)
}
return v
}

View File

@@ -43,11 +43,14 @@ func newStorageError(cause error, response *http.Response, description string) e
response: response,
description: description,
},
serviceCode: ServiceCodeType(response.Header.Get("x-ms-error-code")),
}
}
// ServiceCode returns service-error information. The caller may examine these values but should not modify any of them.
func (e *storageError) ServiceCode() ServiceCodeType { return e.serviceCode }
func (e *storageError) ServiceCode() ServiceCodeType {
return e.serviceCode
}
// Error implements the error interface's Error method to return a string representation of the error.
func (e *storageError) Error() string {
@@ -94,8 +97,6 @@ func (e *storageError) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err
break
case xml.CharData:
switch tokName {
case "Code":
e.serviceCode = ServiceCodeType(tt)
case "Message":
e.description = string(tt)
default:

View File

@@ -16,16 +16,10 @@ type httpRange struct {
}
func (r httpRange) pointers() *string {
if r.offset == 0 && r.count == 0 { // Do common case first for performance
return nil // No specified range
if r.offset == 0 && r.count == CountToEnd { // Do common case first for performance
return nil // No specified range
}
if r.offset < 0 {
panic("The range offset must be >= 0")
}
if r.count < 0 {
panic("The range count must be >= 0")
}
endOffset := "" // if count == 0
endOffset := "" // if count == CountToEnd (0)
if r.count > 0 {
endOffset = strconv.FormatInt((r.offset+r.count)-1, 10)
}
@@ -35,27 +29,36 @@ func (r httpRange) pointers() *string {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func validateSeekableStreamAt0AndGetCount(body io.ReadSeeker) int64 {
func validateSeekableStreamAt0AndGetCount(body io.ReadSeeker) (int64, error) {
if body == nil { // nil body's are "logically" seekable to 0 and are 0 bytes long
return 0
return 0, nil
}
validateSeekableStreamAt0(body)
err := validateSeekableStreamAt0(body)
if err != nil {
return 0, err
}
count, err := body.Seek(0, io.SeekEnd)
if err != nil {
panic("failed to seek stream")
return 0, errors.New("body stream must be seekable")
}
body.Seek(0, io.SeekStart)
return count
return count, nil
}
func validateSeekableStreamAt0(body io.ReadSeeker) {
// return an error if body is not a valid seekable stream at 0
func validateSeekableStreamAt0(body io.ReadSeeker) error {
if body == nil { // nil body's are "logically" seekable to 0
return
return nil
}
if pos, err := body.Seek(0, io.SeekCurrent); pos != 0 || err != nil {
// Help detect programmer error
if err != nil {
panic(err)
return errors.New("body stream must be seekable")
}
panic(errors.New("stream must be set to position 0"))
return errors.New("body stream must be set to position 0")
}
return nil
}

View File

@@ -21,10 +21,7 @@ type uuid [16]byte
func newUUID() (u uuid) {
u = uuid{}
// Set all bits to randomly (or pseudo-randomly) chosen values.
_, err := rand.Read(u[:])
if err != nil {
panic("ran.Read failed")
}
rand.Read(u[:])
u[8] = (u[8] | reservedRFC4122) & 0x7F // u.setVariant(ReservedRFC4122)
var version byte = 4

View File

@@ -33,20 +33,21 @@ func newAppendBlobClient(url url.URL, p pipeline.Pipeline) appendBlobClient {
// error.contentLength is the length of the request. timeout is the timeout parameter is expressed in seconds. For more
// information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// lease is active and matches this ID. maxSize is optional conditional header. The max length in bytes permitted for
// the append blob. If the Append Block operation would cause the blob to exceed that limit or if the blob size is
// already greater than the value specified in this header, the request will fail with MaxBlobSizeConditionNotMet error
// (HTTP status code 412 - Precondition Failed). appendPosition is optional conditional header, used only for the
// Append Block operation. A number indicating the byte offset to compare. Append Block will succeed only if the append
// position is equal to this number. If it is not, the request will fail with the AppendPositionConditionNotMet error
// (HTTP status code 412 - Precondition Failed). ifModifiedSince is specify this header value to operate only on a blob
// if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate
// only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to
// operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a
// matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded
// in the analytics logs when storage analytics logging is enabled.
func (client appendBlobClient) AppendBlock(ctx context.Context, body io.ReadSeeker, contentLength int64, timeout *int32, leaseID *string, maxSize *int64, appendPosition *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*AppendBlobAppendBlockResponse, error) {
// Timeouts for Blob Service Operations.</a> transactionalContentMD5 is specify the transactional md5 for the body, to
// be validated by the service. leaseID is if specified, the operation only succeeds if the resource's lease is active
// and matches this ID. maxSize is optional conditional header. The max length in bytes permitted for the append blob.
// If the Append Block operation would cause the blob to exceed that limit or if the blob size is already greater than
// the value specified in this header, the request will fail with MaxBlobSizeConditionNotMet error (HTTP status code
// 412 - Precondition Failed). appendPosition is optional conditional header, used only for the Append Block operation.
// A number indicating the byte offset to compare. Append Block will succeed only if the append position is equal to
// this number. If it is not, the request will fail with the AppendPositionConditionNotMet error (HTTP status code 412
// - Precondition Failed). ifModifiedSince is specify this header value to operate only on a blob if it has been
// modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if
// it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on blobs
// with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client appendBlobClient) AppendBlock(ctx context.Context, body io.ReadSeeker, contentLength int64, timeout *int32, transactionalContentMD5 []byte, leaseID *string, maxSize *int64, appendPosition *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*AppendBlobAppendBlockResponse, error) {
if err := validate([]validation{
{targetValue: body,
constraints: []constraint{{target: "body", name: null, rule: true, chain: nil}}},
@@ -55,7 +56,7 @@ func (client appendBlobClient) AppendBlock(ctx context.Context, body io.ReadSeek
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.appendBlockPreparer(body, contentLength, timeout, leaseID, maxSize, appendPosition, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.appendBlockPreparer(body, contentLength, timeout, transactionalContentMD5, leaseID, maxSize, appendPosition, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -67,7 +68,7 @@ func (client appendBlobClient) AppendBlock(ctx context.Context, body io.ReadSeek
}
// appendBlockPreparer prepares the AppendBlock request.
func (client appendBlobClient) appendBlockPreparer(body io.ReadSeeker, contentLength int64, timeout *int32, leaseID *string, maxSize *int64, appendPosition *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client appendBlobClient) appendBlockPreparer(body io.ReadSeeker, contentLength int64, timeout *int32, transactionalContentMD5 []byte, leaseID *string, maxSize *int64, appendPosition *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, body)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -79,6 +80,9 @@ func (client appendBlobClient) appendBlockPreparer(body io.ReadSeeker, contentLe
params.Set("comp", "appendblock")
req.URL.RawQuery = params.Encode()
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
if transactionalContentMD5 != nil {
req.Header.Set("Content-MD5", base64.StdEncoding.EncodeToString(transactionalContentMD5))
}
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
@@ -94,8 +98,8 @@ func (client appendBlobClient) appendBlockPreparer(body io.ReadSeeker, contentLe
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -118,6 +122,120 @@ func (client appendBlobClient) appendBlockResponder(resp pipeline.Response) (pip
return &AppendBlobAppendBlockResponse{rawResponse: resp.Response()}, err
}
// AppendBlockFromURL the Append Block operation commits a new block of data to the end of an existing append blob
// where the contents are read from a source url. The Append Block operation is permitted only if the blob was created
// with x-ms-blob-type set to AppendBlob. Append Block is supported only on version 2015-02-21 version or later.
//
// sourceURL is specify a URL to the copy source. contentLength is the length of the request. sourceRange is bytes of
// source data in the specified range. sourceContentMD5 is specify the md5 calculated for the range of bytes that must
// be read from the copy source. timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. maxSize is optional conditional header. The max length in bytes permitted for
// the append blob. If the Append Block operation would cause the blob to exceed that limit or if the blob size is
// already greater than the value specified in this header, the request will fail with MaxBlobSizeConditionNotMet error
// (HTTP status code 412 - Precondition Failed). appendPosition is optional conditional header, used only for the
// Append Block operation. A number indicating the byte offset to compare. Append Block will succeed only if the append
// position is equal to this number. If it is not, the request will fail with the AppendPositionConditionNotMet error
// (HTTP status code 412 - Precondition Failed). ifModifiedSince is specify this header value to operate only on a blob
// if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate
// only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to
// operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a
// matching value. sourceIfModifiedSince is specify this header value to operate only on a blob if it has been modified
// since the specified date/time. sourceIfUnmodifiedSince is specify this header value to operate only on a blob if it
// has not been modified since the specified date/time. sourceIfMatch is specify an ETag value to operate only on blobs
// with a matching value. sourceIfNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client appendBlobClient) AppendBlockFromURL(ctx context.Context, sourceURL string, contentLength int64, sourceRange *string, sourceContentMD5 []byte, timeout *int32, leaseID *string, maxSize *int64, appendPosition *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (*AppendBlobAppendBlockFromURLResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.appendBlockFromURLPreparer(sourceURL, contentLength, sourceRange, sourceContentMD5, timeout, leaseID, maxSize, appendPosition, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, requestID)
if err != nil {
return nil, err
}
resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.appendBlockFromURLResponder}, req)
if err != nil {
return nil, err
}
return resp.(*AppendBlobAppendBlockFromURLResponse), err
}
// appendBlockFromURLPreparer prepares the AppendBlockFromURL request.
func (client appendBlobClient) appendBlockFromURLPreparer(sourceURL string, contentLength int64, sourceRange *string, sourceContentMD5 []byte, timeout *int32, leaseID *string, maxSize *int64, appendPosition *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
}
params := req.URL.Query()
if timeout != nil {
params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
}
params.Set("comp", "appendblock")
req.URL.RawQuery = params.Encode()
req.Header.Set("x-ms-copy-source", sourceURL)
if sourceRange != nil {
req.Header.Set("x-ms-source-range", *sourceRange)
}
if sourceContentMD5 != nil {
req.Header.Set("x-ms-source-content-md5", base64.StdEncoding.EncodeToString(sourceContentMD5))
}
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
if maxSize != nil {
req.Header.Set("x-ms-blob-condition-maxsize", strconv.FormatInt(*maxSize, 10))
}
if appendPosition != nil {
req.Header.Set("x-ms-blob-condition-appendpos", strconv.FormatInt(*appendPosition, 10))
}
if ifModifiedSince != nil {
req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
}
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
}
if sourceIfModifiedSince != nil {
req.Header.Set("x-ms-source-if-modified-since", (*sourceIfModifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfUnmodifiedSince != nil {
req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfMatch != nil {
req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
}
if sourceIfNoneMatch != nil {
req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
}
req.Header.Set("x-ms-version", ServiceVersion)
if requestID != nil {
req.Header.Set("x-ms-client-request-id", *requestID)
}
return req, nil
}
// appendBlockFromURLResponder handles the response to the AppendBlockFromURL request.
func (client appendBlobClient) appendBlockFromURLResponder(resp pipeline.Response) (pipeline.Response, error) {
err := validateResponse(resp, http.StatusOK, http.StatusCreated)
if resp == nil {
return nil, err
}
io.Copy(ioutil.Discard, resp.Response().Body)
resp.Response().Body.Close()
return &AppendBlobAppendBlockFromURLResponse{rawResponse: resp.Response()}, err
}
// Create the Create Append Blob operation creates a new append blob.
//
// contentLength is the length of the request. timeout is the timeout parameter is expressed in seconds. For more
@@ -135,25 +253,22 @@ func (client appendBlobClient) appendBlockResponder(resp pipeline.Response) (pip
// destination blob. If one or more name-value pairs are specified, the destination blob is created with the specified
// metadata, and metadata is not copied from the source blob or file. Note that beginning with version 2009-09-19,
// metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing Containers, Blobs, and
// Metadata for more information. leaseID is if specified, the operation only succeeds if the container's lease is
// Metadata for more information. leaseID is if specified, the operation only succeeds if the resource's lease is
// active and matches this ID. blobContentDisposition is optional. Sets the blob's Content-Disposition header.
// ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the specified
// date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified
// since the specified date/time. ifMatches is specify an ETag value to operate only on blobs with a matching value.
// since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching value.
// ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. requestID is provides a
// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client appendBlobClient) Create(ctx context.Context, contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*AppendBlobCreateResponse, error) {
func (client appendBlobClient) Create(ctx context.Context, contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*AppendBlobCreateResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.createPreparer(contentLength, timeout, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.createPreparer(contentLength, timeout, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -165,7 +280,7 @@ func (client appendBlobClient) Create(ctx context.Context, contentLength int64,
}
// createPreparer prepares the Create request.
func (client appendBlobClient) createPreparer(contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client appendBlobClient) createPreparer(contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -208,8 +323,8 @@ func (client appendBlobClient) createPreparer(contentLength int64, timeout *int3
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))

View File

@@ -6,14 +6,13 @@ package azblob
import (
"context"
"encoding/base64"
"github.com/Azure/azure-pipeline-go/pipeline"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
"github.com/Azure/azure-pipeline-go/pipeline"
)
// blobClient is the client for the Blob methods of the Azblob service.
@@ -32,7 +31,7 @@ func newBlobClient(url url.URL, p pipeline.Pipeline) blobClient {
// copyID is the copy identifier provided in the x-ms-copy-id header of the original Copy Blob operation. timeout is
// the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character
// limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) AbortCopyFromURL(ctx context.Context, copyID string, timeout *int32, leaseID *string, requestID *string) (*BlobAbortCopyFromURLResponse, error) {
@@ -99,18 +98,18 @@ func (client blobClient) abortCopyFromURLResponder(resp pipeline.Response) (pipe
// service returns 400 (Invalid request) if the proposed lease ID is not in the correct format. See Guid Constructor
// (String) for a list of valid GUID string formats. ifModifiedSince is specify this header value to operate only on a
// blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
// operate only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value
// operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
// recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) AcquireLease(ctx context.Context, timeout *int32, duration *int32, proposedLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobAcquireLeaseResponse, error) {
func (client blobClient) AcquireLease(ctx context.Context, timeout *int32, duration *int32, proposedLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobAcquireLeaseResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.acquireLeasePreparer(timeout, duration, proposedLeaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.acquireLeasePreparer(timeout, duration, proposedLeaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -122,7 +121,7 @@ func (client blobClient) AcquireLease(ctx context.Context, timeout *int32, durat
}
// acquireLeasePreparer prepares the AcquireLease request.
func (client blobClient) acquireLeasePreparer(timeout *int32, duration *int32, proposedLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) acquireLeasePreparer(timeout *int32, duration *int32, proposedLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -145,8 +144,8 @@ func (client blobClient) acquireLeasePreparer(timeout *int32, duration *int32, p
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -182,18 +181,18 @@ func (client blobClient) acquireLeaseResponder(resp pipeline.Response) (pipeline
// not appear with a break operation, a fixed-duration lease breaks after the remaining lease period elapses, and an
// infinite lease breaks immediately. ifModifiedSince is specify this header value to operate only on a blob if it has
// been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate only
// on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching
// value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the
// analytics logs when storage analytics logging is enabled.
func (client blobClient) BreakLease(ctx context.Context, timeout *int32, breakPeriod *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobBreakLeaseResponse, error) {
// blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
// blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client blobClient) BreakLease(ctx context.Context, timeout *int32, breakPeriod *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobBreakLeaseResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.breakLeasePreparer(timeout, breakPeriod, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.breakLeasePreparer(timeout, breakPeriod, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -205,7 +204,7 @@ func (client blobClient) BreakLease(ctx context.Context, timeout *int32, breakPe
}
// breakLeasePreparer prepares the BreakLease request.
func (client blobClient) breakLeasePreparer(timeout *int32, breakPeriod *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) breakLeasePreparer(timeout *int32, breakPeriod *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -225,8 +224,8 @@ func (client blobClient) breakLeasePreparer(timeout *int32, breakPeriod *int32,
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -253,25 +252,25 @@ func (client blobClient) breakLeaseResponder(resp pipeline.Response) (pipeline.R
// ChangeLease [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete
// operations
//
// leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID.
// proposedLeaseID is proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request) if the
// proposed lease ID is not in the correct format. See Guid Constructor (String) for a list of valid GUID string
// formats. timeout is the timeout parameter is expressed in seconds. For more information, see <a
// leaseID is specifies the current lease ID on the resource. proposedLeaseID is proposed lease ID, in a GUID string
// format. The Blob service returns 400 (Invalid request) if the proposed lease ID is not in the correct format. See
// Guid Constructor (String) for a list of valid GUID string formats. timeout is the timeout parameter is expressed in
// seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> ifModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
// on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate
// on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate
// only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a
// matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded
// in the analytics logs when storage analytics logging is enabled.
func (client blobClient) ChangeLease(ctx context.Context, leaseID string, proposedLeaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobChangeLeaseResponse, error) {
func (client blobClient) ChangeLease(ctx context.Context, leaseID string, proposedLeaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobChangeLeaseResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.changeLeasePreparer(leaseID, proposedLeaseID, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.changeLeasePreparer(leaseID, proposedLeaseID, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -283,7 +282,7 @@ func (client blobClient) ChangeLease(ctx context.Context, leaseID string, propos
}
// changeLeasePreparer prepares the ChangeLease request.
func (client blobClient) changeLeasePreparer(leaseID string, proposedLeaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) changeLeasePreparer(leaseID string, proposedLeaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -302,8 +301,8 @@ func (client blobClient) changeLeasePreparer(leaseID string, proposedLeaseID str
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -327,6 +326,111 @@ func (client blobClient) changeLeaseResponder(resp pipeline.Response) (pipeline.
return &BlobChangeLeaseResponse{rawResponse: resp.Response()}, err
}
// CopyFromURL the Copy From URL operation copies a blob or an internet resource to a new blob. It will not return a
// response until the copy is complete.
//
// copySource is specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that
// specifies a page blob snapshot. The value should be URL-encoded as it would appear in a request URI. The source blob
// must either be public or must be authenticated via a shared access signature. timeout is the timeout parameter is
// expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> metadata is optional. Specifies a user-defined name-value pair associated
// with the blob. If no name-value pairs are specified, the operation will copy the metadata from the source blob or
// file to the destination blob. If one or more name-value pairs are specified, the destination blob is created with
// the specified metadata, and metadata is not copied from the source blob or file. Note that beginning with version
// 2009-09-19, metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing
// Containers, Blobs, and Metadata for more information. sourceIfModifiedSince is specify this header value to operate
// only on a blob if it has been modified since the specified date/time. sourceIfUnmodifiedSince is specify this header
// value to operate only on a blob if it has not been modified since the specified date/time. sourceIfMatch is specify
// an ETag value to operate only on blobs with a matching value. sourceIfNoneMatch is specify an ETag value to operate
// only on blobs without a matching value. ifModifiedSince is specify this header value to operate only on a blob if it
// has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
// blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client blobClient) CopyFromURL(ctx context.Context, copySource string, timeout *int32, metadata map[string]string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (*BlobCopyFromURLResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.copyFromURLPreparer(copySource, timeout, metadata, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, leaseID, requestID)
if err != nil {
return nil, err
}
resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.copyFromURLResponder}, req)
if err != nil {
return nil, err
}
return resp.(*BlobCopyFromURLResponse), err
}
// copyFromURLPreparer prepares the CopyFromURL request.
func (client blobClient) copyFromURLPreparer(copySource string, timeout *int32, metadata map[string]string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
}
params := req.URL.Query()
if timeout != nil {
params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
}
req.URL.RawQuery = params.Encode()
if metadata != nil {
for k, v := range metadata {
req.Header.Set("x-ms-meta-"+k, v)
}
}
if sourceIfModifiedSince != nil {
req.Header.Set("x-ms-source-if-modified-since", (*sourceIfModifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfUnmodifiedSince != nil {
req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfMatch != nil {
req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
}
if sourceIfNoneMatch != nil {
req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
}
if ifModifiedSince != nil {
req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
}
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
}
req.Header.Set("x-ms-copy-source", copySource)
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
req.Header.Set("x-ms-version", ServiceVersion)
if requestID != nil {
req.Header.Set("x-ms-client-request-id", *requestID)
}
req.Header.Set("x-ms-requires-sync", "true")
return req, nil
}
// copyFromURLResponder handles the response to the CopyFromURL request.
func (client blobClient) copyFromURLResponder(resp pipeline.Response) (pipeline.Response, error) {
err := validateResponse(resp, http.StatusOK, http.StatusAccepted)
if resp == nil {
return nil, err
}
io.Copy(ioutil.Discard, resp.Response().Body)
resp.Response().Body.Close()
return &BlobCopyFromURLResponse{rawResponse: resp.Response()}, err
}
// CreateSnapshot the Create Snapshot operation creates a read-only snapshot of a blob
//
// timeout is the timeout parameter is expressed in seconds. For more information, see <a
@@ -338,22 +442,19 @@ func (client blobClient) changeLeaseResponder(resp pipeline.Response) (pipeline.
// 2009-09-19, metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing
// Containers, Blobs, and Metadata for more information. ifModifiedSince is specify this header value to operate only
// on a blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
// operate only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value
// operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. leaseID is if specified, the operation only succeeds if the container's lease is active
// and matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
// recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) CreateSnapshot(ctx context.Context, timeout *int32, metadata map[string]string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (*BlobCreateSnapshotResponse, error) {
// without a matching value. leaseID is if specified, the operation only succeeds if the resource's lease is active and
// matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded
// in the analytics logs when storage analytics logging is enabled.
func (client blobClient) CreateSnapshot(ctx context.Context, timeout *int32, metadata map[string]string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (*BlobCreateSnapshotResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.createSnapshotPreparer(timeout, metadata, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, leaseID, requestID)
req, err := client.createSnapshotPreparer(timeout, metadata, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, leaseID, requestID)
if err != nil {
return nil, err
}
@@ -365,7 +466,7 @@ func (client blobClient) CreateSnapshot(ctx context.Context, timeout *int32, met
}
// createSnapshotPreparer prepares the CreateSnapshot request.
func (client blobClient) createSnapshotPreparer(timeout *int32, metadata map[string]string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (pipeline.Request, error) {
func (client blobClient) createSnapshotPreparer(timeout *int32, metadata map[string]string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -387,8 +488,8 @@ func (client blobClient) createSnapshotPreparer(timeout *int32, metadata map[str
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -430,23 +531,23 @@ func (client blobClient) createSnapshotResponder(resp pipeline.Response) (pipeli
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating
// a Snapshot of a Blob.</a> timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. deleteSnapshots is required if the blob has associated snapshots. Specify one
// of the following two options: include: Delete the base blob and all of its snapshots. only: Delete only the blob's
// snapshots and not the blob itself ifModifiedSince is specify this header value to operate only on a blob if it has
// been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate only
// on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching
// value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the
// analytics logs when storage analytics logging is enabled.
func (client blobClient) Delete(ctx context.Context, snapshot *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobDeleteResponse, error) {
// blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
// blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client blobClient) Delete(ctx context.Context, snapshot *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobDeleteResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.deletePreparer(snapshot, timeout, leaseID, deleteSnapshots, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.deletePreparer(snapshot, timeout, leaseID, deleteSnapshots, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -458,7 +559,7 @@ func (client blobClient) Delete(ctx context.Context, snapshot *string, timeout *
}
// deletePreparer prepares the Delete request.
func (client blobClient) deletePreparer(snapshot *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) deletePreparer(snapshot *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("DELETE", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -483,8 +584,8 @@ func (client blobClient) deletePreparer(snapshot *string, timeout *int32, leaseI
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -516,22 +617,22 @@ func (client blobClient) deleteResponder(resp pipeline.Response) (pipeline.Respo
// a Snapshot of a Blob.</a> timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> rangeParameter is return only the bytes of the blob in the specified
// range. leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID.
// range. leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
// rangeGetContentMD5 is when set to true and specified together with the Range, the service returns the MD5 hash for
// the range, as long as the range is less than or equal to 4 MB in size. ifModifiedSince is specify this header value
// to operate only on a blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this
// header value to operate only on a blob if it has not been modified since the specified date/time. ifMatches is
// specify an ETag value to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to
// operate only on blobs without a matching value. requestID is provides a client-generated, opaque value with a 1 KB
// character limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) Download(ctx context.Context, snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, rangeGetContentMD5 *bool, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*downloadResponse, error) {
// header value to operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify
// an ETag value to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only
// on blobs without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character
// limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) Download(ctx context.Context, snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, rangeGetContentMD5 *bool, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*downloadResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.downloadPreparer(snapshot, timeout, rangeParameter, leaseID, rangeGetContentMD5, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.downloadPreparer(snapshot, timeout, rangeParameter, leaseID, rangeGetContentMD5, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -543,7 +644,7 @@ func (client blobClient) Download(ctx context.Context, snapshot *string, timeout
}
// downloadPreparer prepares the Download request.
func (client blobClient) downloadPreparer(snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, rangeGetContentMD5 *bool, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) downloadPreparer(snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, rangeGetContentMD5 *bool, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("GET", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -571,8 +672,8 @@ func (client blobClient) downloadPreparer(snapshot *string, timeout *int32, rang
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -593,6 +694,44 @@ func (client blobClient) downloadResponder(resp pipeline.Response) (pipeline.Res
return &downloadResponse{rawResponse: resp.Response()}, err
}
// GetAccountInfo returns the sku name and account kind
func (client blobClient) GetAccountInfo(ctx context.Context) (*BlobGetAccountInfoResponse, error) {
req, err := client.getAccountInfoPreparer()
if err != nil {
return nil, err
}
resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.getAccountInfoResponder}, req)
if err != nil {
return nil, err
}
return resp.(*BlobGetAccountInfoResponse), err
}
// getAccountInfoPreparer prepares the GetAccountInfo request.
func (client blobClient) getAccountInfoPreparer() (pipeline.Request, error) {
req, err := pipeline.NewRequest("GET", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
}
params := req.URL.Query()
params.Set("restype", "account")
params.Set("comp", "properties")
req.URL.RawQuery = params.Encode()
req.Header.Set("x-ms-version", ServiceVersion)
return req, nil
}
// getAccountInfoResponder handles the response to the GetAccountInfo request.
func (client blobClient) getAccountInfoResponder(resp pipeline.Response) (pipeline.Response, error) {
err := validateResponse(resp, http.StatusOK)
if resp == nil {
return nil, err
}
io.Copy(ioutil.Discard, resp.Response().Body)
resp.Response().Body.Close()
return &BlobGetAccountInfoResponse{rawResponse: resp.Response()}, err
}
// GetProperties the Get Properties operation returns all user-defined metadata, standard HTTP properties, and system
// properties for the blob. It does not return the content of the blob.
//
@@ -601,21 +740,21 @@ func (client blobClient) downloadResponder(resp pipeline.Response) (pipeline.Res
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating
// a Snapshot of a Blob.</a> timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. ifModifiedSince is specify this header value to operate only on a blob if it
// has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate only
// on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching
// value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the
// analytics logs when storage analytics logging is enabled.
func (client blobClient) GetProperties(ctx context.Context, snapshot *string, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobGetPropertiesResponse, error) {
// blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
// blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client blobClient) GetProperties(ctx context.Context, snapshot *string, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobGetPropertiesResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.getPropertiesPreparer(snapshot, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.getPropertiesPreparer(snapshot, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -627,7 +766,7 @@ func (client blobClient) GetProperties(ctx context.Context, snapshot *string, ti
}
// getPropertiesPreparer prepares the GetProperties request.
func (client blobClient) getPropertiesPreparer(snapshot *string, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) getPropertiesPreparer(snapshot *string, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("HEAD", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -649,8 +788,8 @@ func (client blobClient) getPropertiesPreparer(snapshot *string, timeout *int32,
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -676,23 +815,23 @@ func (client blobClient) getPropertiesResponder(resp pipeline.Response) (pipelin
// ReleaseLease [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete
// operations
//
// leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID. timeout
// is the timeout parameter is expressed in seconds. For more information, see <a
// leaseID is specifies the current lease ID on the resource. timeout is the timeout parameter is expressed in seconds.
// For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> ifModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
// on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate
// on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate
// only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a
// matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded
// in the analytics logs when storage analytics logging is enabled.
func (client blobClient) ReleaseLease(ctx context.Context, leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobReleaseLeaseResponse, error) {
func (client blobClient) ReleaseLease(ctx context.Context, leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobReleaseLeaseResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.releaseLeasePreparer(leaseID, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.releaseLeasePreparer(leaseID, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -704,7 +843,7 @@ func (client blobClient) ReleaseLease(ctx context.Context, leaseID string, timeo
}
// releaseLeasePreparer prepares the ReleaseLease request.
func (client blobClient) releaseLeasePreparer(leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) releaseLeasePreparer(leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -722,8 +861,8 @@ func (client blobClient) releaseLeasePreparer(leaseID string, timeout *int32, if
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -750,23 +889,23 @@ func (client blobClient) releaseLeaseResponder(resp pipeline.Response) (pipeline
// RenewLease [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete
// operations
//
// leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID. timeout
// is the timeout parameter is expressed in seconds. For more information, see <a
// leaseID is specifies the current lease ID on the resource. timeout is the timeout parameter is expressed in seconds.
// For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> ifModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
// on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate
// on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate
// only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a
// matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded
// in the analytics logs when storage analytics logging is enabled.
func (client blobClient) RenewLease(ctx context.Context, leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobRenewLeaseResponse, error) {
func (client blobClient) RenewLease(ctx context.Context, leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobRenewLeaseResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.renewLeasePreparer(leaseID, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.renewLeasePreparer(leaseID, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -778,7 +917,7 @@ func (client blobClient) RenewLease(ctx context.Context, leaseID string, timeout
}
// renewLeasePreparer prepares the RenewLease request.
func (client blobClient) renewLeasePreparer(leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) renewLeasePreparer(leaseID string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -796,8 +935,8 @@ func (client blobClient) renewLeasePreparer(leaseID string, timeout *int32, ifMo
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -832,22 +971,21 @@ func (client blobClient) renewLeaseResponder(resp pipeline.Response) (pipeline.R
// blocks were validated when each was uploaded. blobContentEncoding is optional. Sets the blob's content encoding. If
// specified, this property is stored with the blob and returned with a read request. blobContentLanguage is optional.
// Set the blob's content language. If specified, this property is stored with the blob and returned with a read
// request. leaseID is if specified, the operation only succeeds if the container's lease is active and matches this
// ID. ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the
// specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been
// modified since the specified date/time. ifMatches is specify an ETag value to operate only on blobs with a matching
// value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// blobContentDisposition is optional. Sets the blob's Content-Disposition header. requestID is provides a
// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client blobClient) SetHTTPHeaders(ctx context.Context, timeout *int32, blobCacheControl *string, blobContentType *string, blobContentMD5 []byte, blobContentEncoding *string, blobContentLanguage *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, blobContentDisposition *string, requestID *string) (*BlobSetHTTPHeadersResponse, error) {
// request. leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
// ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the specified
// date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified
// since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching value.
// ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. blobContentDisposition is
// optional. Sets the blob's Content-Disposition header. requestID is provides a client-generated, opaque value with a
// 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) SetHTTPHeaders(ctx context.Context, timeout *int32, blobCacheControl *string, blobContentType *string, blobContentMD5 []byte, blobContentEncoding *string, blobContentLanguage *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobContentDisposition *string, requestID *string) (*BlobSetHTTPHeadersResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.setHTTPHeadersPreparer(timeout, blobCacheControl, blobContentType, blobContentMD5, blobContentEncoding, blobContentLanguage, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, blobContentDisposition, requestID)
req, err := client.setHTTPHeadersPreparer(timeout, blobCacheControl, blobContentType, blobContentMD5, blobContentEncoding, blobContentLanguage, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, blobContentDisposition, requestID)
if err != nil {
return nil, err
}
@@ -859,7 +997,7 @@ func (client blobClient) SetHTTPHeaders(ctx context.Context, timeout *int32, blo
}
// setHTTPHeadersPreparer prepares the SetHTTPHeaders request.
func (client blobClient) setHTTPHeadersPreparer(timeout *int32, blobCacheControl *string, blobContentType *string, blobContentMD5 []byte, blobContentEncoding *string, blobContentLanguage *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, blobContentDisposition *string, requestID *string) (pipeline.Request, error) {
func (client blobClient) setHTTPHeadersPreparer(timeout *int32, blobCacheControl *string, blobContentType *string, blobContentMD5 []byte, blobContentEncoding *string, blobContentLanguage *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobContentDisposition *string, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -894,8 +1032,8 @@ func (client blobClient) setHTTPHeadersPreparer(timeout *int32, blobCacheControl
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -932,23 +1070,20 @@ func (client blobClient) setHTTPHeadersResponder(resp pipeline.Response) (pipeli
// the specified metadata, and metadata is not copied from the source blob or file. Note that beginning with version
// 2009-09-19, metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing
// Containers, Blobs, and Metadata for more information. leaseID is if specified, the operation only succeeds if the
// container's lease is active and matches this ID. ifModifiedSince is specify this header value to operate only on a
// resource's lease is active and matches this ID. ifModifiedSince is specify this header value to operate only on a
// blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
// operate only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value
// operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
// recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) SetMetadata(ctx context.Context, timeout *int32, metadata map[string]string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlobSetMetadataResponse, error) {
func (client blobClient) SetMetadata(ctx context.Context, timeout *int32, metadata map[string]string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlobSetMetadataResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.setMetadataPreparer(timeout, metadata, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.setMetadataPreparer(timeout, metadata, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -960,7 +1095,7 @@ func (client blobClient) SetMetadata(ctx context.Context, timeout *int32, metada
}
// setMetadataPreparer prepares the SetMetadata request.
func (client blobClient) setMetadataPreparer(timeout *int32, metadata map[string]string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blobClient) setMetadataPreparer(timeout *int32, metadata map[string]string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -985,8 +1120,8 @@ func (client blobClient) setMetadataPreparer(timeout *int32, metadata map[string
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -1018,15 +1153,16 @@ func (client blobClient) setMetadataResponder(resp pipeline.Response) (pipeline.
// information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> requestID is provides a client-generated, opaque value with a 1 KB
// character limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) SetTier(ctx context.Context, tier AccessTierType, timeout *int32, requestID *string) (*BlobSetTierResponse, error) {
// character limit that is recorded in the analytics logs when storage analytics logging is enabled. leaseID is if
// specified, the operation only succeeds if the resource's lease is active and matches this ID.
func (client blobClient) SetTier(ctx context.Context, tier AccessTierType, timeout *int32, requestID *string, leaseID *string) (*BlobSetTierResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.setTierPreparer(tier, timeout, requestID)
req, err := client.setTierPreparer(tier, timeout, requestID, leaseID)
if err != nil {
return nil, err
}
@@ -1038,7 +1174,7 @@ func (client blobClient) SetTier(ctx context.Context, tier AccessTierType, timeo
}
// setTierPreparer prepares the SetTier request.
func (client blobClient) setTierPreparer(tier AccessTierType, timeout *int32, requestID *string) (pipeline.Request, error) {
func (client blobClient) setTierPreparer(tier AccessTierType, timeout *int32, requestID *string, leaseID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -1054,6 +1190,9 @@ func (client blobClient) setTierPreparer(tier AccessTierType, timeout *int32, re
if requestID != nil {
req.Header.Set("x-ms-client-request-id", *requestID)
}
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
return req, nil
}
@@ -1082,27 +1221,23 @@ func (client blobClient) setTierResponder(resp pipeline.Response) (pipeline.Resp
// 2009-09-19, metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing
// Containers, Blobs, and Metadata for more information. sourceIfModifiedSince is specify this header value to operate
// only on a blob if it has been modified since the specified date/time. sourceIfUnmodifiedSince is specify this header
// value to operate only on a blob if it has not been modified since the specified date/time. sourceIfMatches is
// specify an ETag value to operate only on blobs with a matching value. sourceIfNoneMatch is specify an ETag value to
// operate only on blobs without a matching value. ifModifiedSince is specify this header value to operate only on a
// blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
// operate only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value
// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. leaseID is if specified, the operation only succeeds if the container's lease is active
// and matches this ID. sourceLeaseID is specify this header to perform the operation only if the lease ID given
// matches the active lease ID of the source blob. requestID is provides a client-generated, opaque value with a 1 KB
// character limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blobClient) StartCopyFromURL(ctx context.Context, copySource string, timeout *int32, metadata map[string]string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatches *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, leaseID *string, sourceLeaseID *string, requestID *string) (*BlobStartCopyFromURLResponse, error) {
// value to operate only on a blob if it has not been modified since the specified date/time. sourceIfMatch is specify
// an ETag value to operate only on blobs with a matching value. sourceIfNoneMatch is specify an ETag value to operate
// only on blobs without a matching value. ifModifiedSince is specify this header value to operate only on a blob if it
// has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
// blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client blobClient) StartCopyFromURL(ctx context.Context, copySource string, timeout *int32, metadata map[string]string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (*BlobStartCopyFromURLResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.startCopyFromURLPreparer(copySource, timeout, metadata, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatches, sourceIfNoneMatch, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, leaseID, sourceLeaseID, requestID)
req, err := client.startCopyFromURLPreparer(copySource, timeout, metadata, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, leaseID, requestID)
if err != nil {
return nil, err
}
@@ -1114,7 +1249,7 @@ func (client blobClient) StartCopyFromURL(ctx context.Context, copySource string
}
// startCopyFromURLPreparer prepares the StartCopyFromURL request.
func (client blobClient) startCopyFromURLPreparer(copySource string, timeout *int32, metadata map[string]string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatches *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, leaseID *string, sourceLeaseID *string, requestID *string) (pipeline.Request, error) {
func (client blobClient) startCopyFromURLPreparer(copySource string, timeout *int32, metadata map[string]string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, leaseID *string, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -1135,8 +1270,8 @@ func (client blobClient) startCopyFromURLPreparer(copySource string, timeout *in
if sourceIfUnmodifiedSince != nil {
req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfMatches != nil {
req.Header.Set("x-ms-source-if-match", string(*sourceIfMatches))
if sourceIfMatch != nil {
req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
}
if sourceIfNoneMatch != nil {
req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
@@ -1147,8 +1282,8 @@ func (client blobClient) startCopyFromURLPreparer(copySource string, timeout *in
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -1157,9 +1292,6 @@ func (client blobClient) startCopyFromURLPreparer(copySource string, timeout *in
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
if sourceLeaseID != nil {
req.Header.Set("x-ms-source-lease-id", *sourceLeaseID)
}
req.Header.Set("x-ms-version", ServiceVersion)
if requestID != nil {
req.Header.Set("x-ms-client-request-id", *requestID)

View File

@@ -48,25 +48,22 @@ func newBlockBlobClient(url url.URL, p pipeline.Pipeline) blockBlobClient {
// destination blob. If one or more name-value pairs are specified, the destination blob is created with the specified
// metadata, and metadata is not copied from the source blob or file. Note that beginning with version 2009-09-19,
// metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing Containers, Blobs, and
// Metadata for more information. leaseID is if specified, the operation only succeeds if the container's lease is
// Metadata for more information. leaseID is if specified, the operation only succeeds if the resource's lease is
// active and matches this ID. blobContentDisposition is optional. Sets the blob's Content-Disposition header.
// ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the specified
// date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified
// since the specified date/time. ifMatches is specify an ETag value to operate only on blobs with a matching value.
// since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching value.
// ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. requestID is provides a
// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client blockBlobClient) CommitBlockList(ctx context.Context, blocks BlockLookupList, timeout *int32, blobCacheControl *string, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlockBlobCommitBlockListResponse, error) {
func (client blockBlobClient) CommitBlockList(ctx context.Context, blocks BlockLookupList, timeout *int32, blobCacheControl *string, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlockBlobCommitBlockListResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.commitBlockListPreparer(blocks, timeout, blobCacheControl, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.commitBlockListPreparer(blocks, timeout, blobCacheControl, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -78,7 +75,7 @@ func (client blockBlobClient) CommitBlockList(ctx context.Context, blocks BlockL
}
// commitBlockListPreparer prepares the CommitBlockList request.
func (client blockBlobClient) commitBlockListPreparer(blocks BlockLookupList, timeout *int32, blobCacheControl *string, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blockBlobClient) commitBlockListPreparer(blocks BlockLookupList, timeout *int32, blobCacheControl *string, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -121,8 +118,8 @@ func (client blockBlobClient) commitBlockListPreparer(blocks BlockLookupList, ti
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -163,7 +160,7 @@ func (client blockBlobClient) commitBlockListResponder(resp pipeline.Response) (
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating
// a Snapshot of a Blob.</a> timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character
// limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blockBlobClient) GetBlockList(ctx context.Context, listType BlockListType, snapshot *string, timeout *int32, leaseID *string, requestID *string) (*BlockList, error) {
@@ -223,7 +220,7 @@ func (client blockBlobClient) getBlockListResponder(resp pipeline.Response) (pip
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -240,13 +237,14 @@ func (client blockBlobClient) getBlockListResponder(resp pipeline.Response) (pip
// blockID is a valid Base64 string value that identifies the block. Prior to encoding, the string must be less than or
// equal to 64 bytes in size. For a given blob, the length of the value specified for the blockid parameter must be the
// same size for each block. contentLength is the length of the request. body is initial data body will be closed upon
// successful return. Callers should ensure closure when receiving an error.timeout is the timeout parameter is
// expressed in seconds. For more information, see <a
// successful return. Callers should ensure closure when receiving an error.transactionalContentMD5 is specify the
// transactional md5 for the body, to be validated by the service. timeout is the timeout parameter is expressed in
// seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character
// limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blockBlobClient) StageBlock(ctx context.Context, blockID string, contentLength int64, body io.ReadSeeker, timeout *int32, leaseID *string, requestID *string) (*BlockBlobStageBlockResponse, error) {
func (client blockBlobClient) StageBlock(ctx context.Context, blockID string, contentLength int64, body io.ReadSeeker, transactionalContentMD5 []byte, timeout *int32, leaseID *string, requestID *string) (*BlockBlobStageBlockResponse, error) {
if err := validate([]validation{
{targetValue: body,
constraints: []constraint{{target: "body", name: null, rule: true, chain: nil}}},
@@ -255,7 +253,7 @@ func (client blockBlobClient) StageBlock(ctx context.Context, blockID string, co
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.stageBlockPreparer(blockID, contentLength, body, timeout, leaseID, requestID)
req, err := client.stageBlockPreparer(blockID, contentLength, body, transactionalContentMD5, timeout, leaseID, requestID)
if err != nil {
return nil, err
}
@@ -267,7 +265,7 @@ func (client blockBlobClient) StageBlock(ctx context.Context, blockID string, co
}
// stageBlockPreparer prepares the StageBlock request.
func (client blockBlobClient) stageBlockPreparer(blockID string, contentLength int64, body io.ReadSeeker, timeout *int32, leaseID *string, requestID *string) (pipeline.Request, error) {
func (client blockBlobClient) stageBlockPreparer(blockID string, contentLength int64, body io.ReadSeeker, transactionalContentMD5 []byte, timeout *int32, leaseID *string, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, body)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -280,6 +278,9 @@ func (client blockBlobClient) stageBlockPreparer(blockID string, contentLength i
params.Set("comp", "block")
req.URL.RawQuery = params.Encode()
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
if transactionalContentMD5 != nil {
req.Header.Set("Content-MD5", base64.StdEncoding.EncodeToString(transactionalContentMD5))
}
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
@@ -306,22 +307,26 @@ func (client blockBlobClient) stageBlockResponder(resp pipeline.Response) (pipel
//
// blockID is a valid Base64 string value that identifies the block. Prior to encoding, the string must be less than or
// equal to 64 bytes in size. For a given blob, the length of the value specified for the blockid parameter must be the
// same size for each block. contentLength is the length of the request. sourceURL is specifiy an URL to the copy
// source. sourceRange is bytes of source data in the specified range. sourceContentMD5 is specify the md5 calculated
// for the range of bytes that must be read from the copy source. timeout is the timeout parameter is expressed in
// seconds. For more information, see <a
// same size for each block. contentLength is the length of the request. sourceURL is specify a URL to the copy source.
// sourceRange is bytes of source data in the specified range. sourceContentMD5 is specify the md5 calculated for the
// range of bytes that must be read from the copy source. timeout is the timeout parameter is expressed in seconds. For
// more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// lease is active and matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character
// limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client blockBlobClient) StageBlockFromURL(ctx context.Context, blockID string, contentLength int64, sourceURL *string, sourceRange *string, sourceContentMD5 []byte, timeout *int32, leaseID *string, requestID *string) (*BlockBlobStageBlockFromURLResponse, error) {
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. sourceIfModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. sourceIfUnmodifiedSince is specify this header value to operate
// only on a blob if it has not been modified since the specified date/time. sourceIfMatch is specify an ETag value to
// operate only on blobs with a matching value. sourceIfNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
// recorded in the analytics logs when storage analytics logging is enabled.
func (client blockBlobClient) StageBlockFromURL(ctx context.Context, blockID string, contentLength int64, sourceURL string, sourceRange *string, sourceContentMD5 []byte, timeout *int32, leaseID *string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (*BlockBlobStageBlockFromURLResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.stageBlockFromURLPreparer(blockID, contentLength, sourceURL, sourceRange, sourceContentMD5, timeout, leaseID, requestID)
req, err := client.stageBlockFromURLPreparer(blockID, contentLength, sourceURL, sourceRange, sourceContentMD5, timeout, leaseID, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -333,7 +338,7 @@ func (client blockBlobClient) StageBlockFromURL(ctx context.Context, blockID str
}
// stageBlockFromURLPreparer prepares the StageBlockFromURL request.
func (client blockBlobClient) stageBlockFromURLPreparer(blockID string, contentLength int64, sourceURL *string, sourceRange *string, sourceContentMD5 []byte, timeout *int32, leaseID *string, requestID *string) (pipeline.Request, error) {
func (client blockBlobClient) stageBlockFromURLPreparer(blockID string, contentLength int64, sourceURL string, sourceRange *string, sourceContentMD5 []byte, timeout *int32, leaseID *string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -346,9 +351,7 @@ func (client blockBlobClient) stageBlockFromURLPreparer(blockID string, contentL
params.Set("comp", "block")
req.URL.RawQuery = params.Encode()
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
if sourceURL != nil {
req.Header.Set("x-ms-copy-source", *sourceURL)
}
req.Header.Set("x-ms-copy-source", sourceURL)
if sourceRange != nil {
req.Header.Set("x-ms-source-range", *sourceRange)
}
@@ -358,6 +361,18 @@ func (client blockBlobClient) stageBlockFromURLPreparer(blockID string, contentL
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
if sourceIfModifiedSince != nil {
req.Header.Set("x-ms-source-if-modified-since", (*sourceIfModifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfUnmodifiedSince != nil {
req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfMatch != nil {
req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
}
if sourceIfNoneMatch != nil {
req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
}
req.Header.Set("x-ms-version", ServiceVersion)
if requestID != nil {
req.Header.Set("x-ms-client-request-id", *requestID)
@@ -397,27 +412,24 @@ func (client blockBlobClient) stageBlockFromURLResponder(resp pipeline.Response)
// destination blob. If one or more name-value pairs are specified, the destination blob is created with the specified
// metadata, and metadata is not copied from the source blob or file. Note that beginning with version 2009-09-19,
// metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing Containers, Blobs, and
// Metadata for more information. leaseID is if specified, the operation only succeeds if the container's lease is
// Metadata for more information. leaseID is if specified, the operation only succeeds if the resource's lease is
// active and matches this ID. blobContentDisposition is optional. Sets the blob's Content-Disposition header.
// ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the specified
// date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified
// since the specified date/time. ifMatches is specify an ETag value to operate only on blobs with a matching value.
// since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching value.
// ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. requestID is provides a
// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client blockBlobClient) Upload(ctx context.Context, body io.ReadSeeker, contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*BlockBlobUploadResponse, error) {
func (client blockBlobClient) Upload(ctx context.Context, body io.ReadSeeker, contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*BlockBlobUploadResponse, error) {
if err := validate([]validation{
{targetValue: body,
constraints: []constraint{{target: "body", name: null, rule: true, chain: nil}}},
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.uploadPreparer(body, contentLength, timeout, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.uploadPreparer(body, contentLength, timeout, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -429,7 +441,7 @@ func (client blockBlobClient) Upload(ctx context.Context, body io.ReadSeeker, co
}
// uploadPreparer prepares the Upload request.
func (client blockBlobClient) uploadPreparer(body io.ReadSeeker, contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client blockBlobClient) uploadPreparer(body io.ReadSeeker, contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, body)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -472,8 +484,8 @@ func (client blockBlobClient) uploadPreparer(body io.ReadSeeker, contentLength i
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))

View File

@@ -10,7 +10,7 @@ import (
const (
// ServiceVersion specifies the version of the operations used in this package.
ServiceVersion = "2018-03-28"
ServiceVersion = "2018-11-09"
)
// managementClient is the base client for Azblob.

View File

@@ -7,14 +7,13 @@ import (
"bytes"
"context"
"encoding/xml"
"github.com/Azure/azure-pipeline-go/pipeline"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
"github.com/Azure/azure-pipeline-go/pipeline"
)
// containerClient is the client for the Container methods of the Azblob service.
@@ -179,10 +178,10 @@ func (client containerClient) breakLeaseResponder(resp pipeline.Response) (pipel
// ChangeLease [Update] establishes and manages a lock on a container for delete operations. The lock duration can be
// 15 to 60 seconds, or can be infinite
//
// leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID.
// proposedLeaseID is proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request) if the
// proposed lease ID is not in the correct format. See Guid Constructor (String) for a list of valid GUID string
// formats. timeout is the timeout parameter is expressed in seconds. For more information, see <a
// leaseID is specifies the current lease ID on the resource. proposedLeaseID is proposed lease ID, in a GUID string
// format. The Blob service returns 400 (Invalid request) if the proposed lease ID is not in the correct format. See
// Guid Constructor (String) for a list of valid GUID string formats. timeout is the timeout parameter is expressed in
// seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> ifModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
@@ -264,10 +263,7 @@ func (client containerClient) Create(ctx context.Context, timeout *int32, metada
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.createPreparer(timeout, metadata, access, requestID)
@@ -324,7 +320,7 @@ func (client containerClient) createResponder(resp pipeline.Response) (pipeline.
//
// timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. ifModifiedSince is specify this header value to operate only on a blob if it
// has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. requestID is provides a client-generated, opaque
@@ -391,7 +387,7 @@ func (client containerClient) deleteResponder(resp pipeline.Response) (pipeline.
//
// timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character
// limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client containerClient) GetAccessPolicy(ctx context.Context, timeout *int32, leaseID *string, requestID *string) (*SignedIdentifiers, error) {
@@ -448,7 +444,7 @@ func (client containerClient) getAccessPolicyResponder(resp pipeline.Response) (
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -460,12 +456,50 @@ func (client containerClient) getAccessPolicyResponder(resp pipeline.Response) (
return result, nil
}
// GetAccountInfo returns the sku name and account kind
func (client containerClient) GetAccountInfo(ctx context.Context) (*ContainerGetAccountInfoResponse, error) {
req, err := client.getAccountInfoPreparer()
if err != nil {
return nil, err
}
resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.getAccountInfoResponder}, req)
if err != nil {
return nil, err
}
return resp.(*ContainerGetAccountInfoResponse), err
}
// getAccountInfoPreparer prepares the GetAccountInfo request.
func (client containerClient) getAccountInfoPreparer() (pipeline.Request, error) {
req, err := pipeline.NewRequest("GET", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
}
params := req.URL.Query()
params.Set("restype", "account")
params.Set("comp", "properties")
req.URL.RawQuery = params.Encode()
req.Header.Set("x-ms-version", ServiceVersion)
return req, nil
}
// getAccountInfoResponder handles the response to the GetAccountInfo request.
func (client containerClient) getAccountInfoResponder(resp pipeline.Response) (pipeline.Response, error) {
err := validateResponse(resp, http.StatusOK)
if resp == nil {
return nil, err
}
io.Copy(ioutil.Discard, resp.Response().Body)
resp.Response().Body.Close()
return &ContainerGetAccountInfoResponse{rawResponse: resp.Response()}, err
}
// GetProperties returns all user-defined metadata and system properties for the specified container. The data returned
// does not include the container's list of blobs
//
// timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. requestID is provides a client-generated, opaque value with a 1 KB character
// limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client containerClient) GetProperties(ctx context.Context, timeout *int32, leaseID *string, requestID *string) (*ContainerGetPropertiesResponse, error) {
@@ -601,7 +635,7 @@ func (client containerClient) listBlobFlatSegmentResponder(resp pipeline.Respons
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -699,7 +733,7 @@ func (client containerClient) listBlobHierarchySegmentResponder(resp pipeline.Re
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -714,8 +748,8 @@ func (client containerClient) listBlobHierarchySegmentResponder(resp pipeline.Re
// ReleaseLease [Update] establishes and manages a lock on a container for delete operations. The lock duration can be
// 15 to 60 seconds, or can be infinite
//
// leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID. timeout
// is the timeout parameter is expressed in seconds. For more information, see <a
// leaseID is specifies the current lease ID on the resource. timeout is the timeout parameter is expressed in seconds.
// For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> ifModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
@@ -782,8 +816,8 @@ func (client containerClient) releaseLeaseResponder(resp pipeline.Response) (pip
// RenewLease [Update] establishes and manages a lock on a container for delete operations. The lock duration can be 15
// to 60 seconds, or can be infinite
//
// leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID. timeout
// is the timeout parameter is expressed in seconds. For more information, see <a
// leaseID is specifies the current lease ID on the resource. timeout is the timeout parameter is expressed in seconds.
// For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> ifModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
@@ -853,7 +887,7 @@ func (client containerClient) renewLeaseResponder(resp pipeline.Response) (pipel
// containerACL is the acls for the container timeout is the timeout parameter is expressed in seconds. For more
// information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. access is specifies whether data in the container may be accessed publicly and
// the level of access ifModifiedSince is specify this header value to operate only on a blob if it has been modified
// since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has
@@ -933,7 +967,7 @@ func (client containerClient) setAccessPolicyResponder(resp pipeline.Response) (
//
// timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. metadata is optional. Specifies a user-defined name-value pair associated with
// the blob. If no name-value pairs are specified, the operation will copy the metadata from the source blob or file to
// the destination blob. If one or more name-value pairs are specified, the destination blob is created with the
@@ -946,10 +980,7 @@ func (client containerClient) SetMetadata(ctx context.Context, timeout *int32, l
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.setMetadataPreparer(timeout, leaseID, metadata, ifModifiedSince, requestID)

View File

@@ -32,24 +32,24 @@ func newPageBlobClient(url url.URL, p pipeline.Pipeline) pageBlobClient {
// information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> rangeParameter is return only the bytes of the blob in the specified
// range. leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID.
// range. leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
// ifSequenceNumberLessThanOrEqualTo is specify this header value to operate only on a blob if it has a sequence number
// less than or equal to the specified. ifSequenceNumberLessThan is specify this header value to operate only on a blob
// if it has a sequence number less than the specified. ifSequenceNumberEqualTo is specify this header value to operate
// only on a blob if it has the specified sequence number. ifModifiedSince is specify this header value to operate only
// on a blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
// operate only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value
// operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
// recorded in the analytics logs when storage analytics logging is enabled.
func (client pageBlobClient) ClearPages(ctx context.Context, contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobClearPagesResponse, error) {
func (client pageBlobClient) ClearPages(ctx context.Context, contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobClearPagesResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.clearPagesPreparer(contentLength, timeout, rangeParameter, leaseID, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.clearPagesPreparer(contentLength, timeout, rangeParameter, leaseID, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -61,7 +61,7 @@ func (client pageBlobClient) ClearPages(ctx context.Context, contentLength int64
}
// clearPagesPreparer prepares the ClearPages request.
func (client pageBlobClient) clearPagesPreparer(contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) clearPagesPreparer(contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -94,8 +94,8 @@ func (client pageBlobClient) clearPagesPreparer(contentLength int64, timeout *in
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -129,28 +129,20 @@ func (client pageBlobClient) clearPagesResponder(resp pipeline.Response) (pipeli
// must either be public or must be authenticated via a shared access signature. timeout is the timeout parameter is
// expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> metadata is optional. Specifies a user-defined name-value pair associated
// with the blob. If no name-value pairs are specified, the operation will copy the metadata from the source blob or
// file to the destination blob. If one or more name-value pairs are specified, the destination blob is created with
// the specified metadata, and metadata is not copied from the source blob or file. Note that beginning with version
// 2009-09-19, metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing
// Containers, Blobs, and Metadata for more information. ifModifiedSince is specify this header value to operate only
// on a blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
// operate only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value
// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
// recorded in the analytics logs when storage analytics logging is enabled.
func (client pageBlobClient) CopyIncremental(ctx context.Context, copySource string, timeout *int32, metadata map[string]string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobCopyIncrementalResponse, error) {
// Timeouts for Blob Service Operations.</a> ifModifiedSince is specify this header value to operate only on a blob if
// it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
// on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate
// only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a
// matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded
// in the analytics logs when storage analytics logging is enabled.
func (client pageBlobClient) CopyIncremental(ctx context.Context, copySource string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobCopyIncrementalResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.copyIncrementalPreparer(copySource, timeout, metadata, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.copyIncrementalPreparer(copySource, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -162,7 +154,7 @@ func (client pageBlobClient) CopyIncremental(ctx context.Context, copySource str
}
// copyIncrementalPreparer prepares the CopyIncremental request.
func (client pageBlobClient) copyIncrementalPreparer(copySource string, timeout *int32, metadata map[string]string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) copyIncrementalPreparer(copySource string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -173,19 +165,14 @@ func (client pageBlobClient) copyIncrementalPreparer(copySource string, timeout
}
params.Set("comp", "incrementalcopy")
req.URL.RawQuery = params.Encode()
if metadata != nil {
for k, v := range metadata {
req.Header.Set("x-ms-meta-"+k, v)
}
}
if ifModifiedSince != nil {
req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
}
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -211,8 +198,9 @@ func (client pageBlobClient) copyIncrementalResponder(resp pipeline.Response) (p
// Create the Create operation creates a new page blob.
//
// contentLength is the length of the request. timeout is the timeout parameter is expressed in seconds. For more
// information, see <a
// contentLength is the length of the request. blobContentLength is this header specifies the maximum size for the page
// blob, up to 1 TB. The page blob size must be aligned to a 512-byte boundary. timeout is the timeout parameter is
// expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> blobContentType is optional. Sets the blob's content type. If specified,
// this property is stored with the blob and returned with a read request. blobContentEncoding is optional. Sets the
@@ -226,28 +214,23 @@ func (client pageBlobClient) copyIncrementalResponder(resp pipeline.Response) (p
// destination blob. If one or more name-value pairs are specified, the destination blob is created with the specified
// metadata, and metadata is not copied from the source blob or file. Note that beginning with version 2009-09-19,
// metadata names must adhere to the naming rules for C# identifiers. See Naming and Referencing Containers, Blobs, and
// Metadata for more information. leaseID is if specified, the operation only succeeds if the container's lease is
// Metadata for more information. leaseID is if specified, the operation only succeeds if the resource's lease is
// active and matches this ID. blobContentDisposition is optional. Sets the blob's Content-Disposition header.
// ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the specified
// date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified
// since the specified date/time. ifMatches is specify an ETag value to operate only on blobs with a matching value.
// ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. blobContentLength is this
// header specifies the maximum size for the page blob, up to 1 TB. The page blob size must be aligned to a 512-byte
// boundary. blobSequenceNumber is set for page blobs only. The sequence number is a user-controlled value that you can
// use to track requests. The value of the sequence number must be between 0 and 2^63 - 1. requestID is provides a
// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client pageBlobClient) Create(ctx context.Context, contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, blobContentLength *int64, blobSequenceNumber *int64, requestID *string) (*PageBlobCreateResponse, error) {
// since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching value.
// ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. blobSequenceNumber is set
// for page blobs only. The sequence number is a user-controlled value that you can use to track requests. The value of
// the sequence number must be between 0 and 2^63 - 1. requestID is provides a client-generated, opaque value with a 1
// KB character limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client pageBlobClient) Create(ctx context.Context, contentLength int64, blobContentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (*PageBlobCreateResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}},
{targetValue: metadata,
constraints: []constraint{{target: "metadata", name: null, rule: false,
chain: []constraint{{target: "metadata", name: pattern, rule: `^[a-zA-Z]+$`, chain: nil}}}}}}); err != nil {
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.createPreparer(contentLength, timeout, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, blobContentLength, blobSequenceNumber, requestID)
req, err := client.createPreparer(contentLength, blobContentLength, timeout, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseID, blobContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, blobSequenceNumber, requestID)
if err != nil {
return nil, err
}
@@ -259,7 +242,7 @@ func (client pageBlobClient) Create(ctx context.Context, contentLength int64, ti
}
// createPreparer prepares the Create request.
func (client pageBlobClient) createPreparer(contentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, blobContentLength *int64, blobSequenceNumber *int64, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) createPreparer(contentLength int64, blobContentLength int64, timeout *int32, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -302,15 +285,13 @@ func (client pageBlobClient) createPreparer(contentLength int64, timeout *int32,
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
}
if blobContentLength != nil {
req.Header.Set("x-ms-blob-content-length", strconv.FormatInt(*blobContentLength, 10))
}
req.Header.Set("x-ms-blob-content-length", strconv.FormatInt(blobContentLength, 10))
if blobSequenceNumber != nil {
req.Header.Set("x-ms-blob-sequence-number", strconv.FormatInt(*blobSequenceNumber, 10))
}
@@ -342,21 +323,21 @@ func (client pageBlobClient) createResponder(resp pipeline.Response) (pipeline.R
// a Snapshot of a Blob.</a> timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> rangeParameter is return only the bytes of the blob in the specified
// range. leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID.
// range. leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
// ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the specified
// date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified
// since the specified date/time. ifMatches is specify an ETag value to operate only on blobs with a matching value.
// since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching value.
// ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. requestID is provides a
// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client pageBlobClient) GetPageRanges(ctx context.Context, snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*PageList, error) {
func (client pageBlobClient) GetPageRanges(ctx context.Context, snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageList, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.getPageRangesPreparer(snapshot, timeout, rangeParameter, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.getPageRangesPreparer(snapshot, timeout, rangeParameter, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -368,7 +349,7 @@ func (client pageBlobClient) GetPageRanges(ctx context.Context, snapshot *string
}
// getPageRangesPreparer prepares the GetPageRanges request.
func (client pageBlobClient) getPageRangesPreparer(snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) getPageRangesPreparer(snapshot *string, timeout *int32, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("GET", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -394,8 +375,8 @@ func (client pageBlobClient) getPageRangesPreparer(snapshot *string, timeout *in
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -420,7 +401,7 @@ func (client pageBlobClient) getPageRangesResponder(resp pipeline.Response) (pip
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -432,8 +413,8 @@ func (client pageBlobClient) getPageRangesResponder(resp pipeline.Response) (pip
return result, nil
}
// GetPageRangesDiff [Update] The Get Page Ranges Diff operation returns the list of valid page ranges for a page blob
// that were changed between target blob and previous snapshot.
// GetPageRangesDiff the Get Page Ranges Diff operation returns the list of valid page ranges for a page blob that were
// changed between target blob and previous snapshot.
//
// snapshot is the snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to
// retrieve. For more information on working with blob snapshots, see <a
@@ -445,21 +426,21 @@ func (client pageBlobClient) getPageRangesResponder(resp pipeline.Response) (pip
// target blob and previous snapshot. Changed pages include both updated and cleared pages. The target blob may be a
// snapshot, as long as the snapshot specified by prevsnapshot is the older of the two. Note that incremental snapshots
// are currently supported only for blobs created on or after January 1, 2016. rangeParameter is return only the bytes
// of the blob in the specified range. leaseID is if specified, the operation only succeeds if the container's lease is
// of the blob in the specified range. leaseID is if specified, the operation only succeeds if the resource's lease is
// active and matches this ID. ifModifiedSince is specify this header value to operate only on a blob if it has been
// modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if
// it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate only on blobs
// it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on blobs
// with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client pageBlobClient) GetPageRangesDiff(ctx context.Context, snapshot *string, timeout *int32, prevsnapshot *string, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*PageList, error) {
func (client pageBlobClient) GetPageRangesDiff(ctx context.Context, snapshot *string, timeout *int32, prevsnapshot *string, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageList, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.getPageRangesDiffPreparer(snapshot, timeout, prevsnapshot, rangeParameter, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.getPageRangesDiffPreparer(snapshot, timeout, prevsnapshot, rangeParameter, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -471,7 +452,7 @@ func (client pageBlobClient) GetPageRangesDiff(ctx context.Context, snapshot *st
}
// getPageRangesDiffPreparer prepares the GetPageRangesDiff request.
func (client pageBlobClient) getPageRangesDiffPreparer(snapshot *string, timeout *int32, prevsnapshot *string, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) getPageRangesDiffPreparer(snapshot *string, timeout *int32, prevsnapshot *string, rangeParameter *string, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("GET", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -500,8 +481,8 @@ func (client pageBlobClient) getPageRangesDiffPreparer(snapshot *string, timeout
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -526,7 +507,7 @@ func (client pageBlobClient) getPageRangesDiffResponder(resp pipeline.Response)
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -544,21 +525,21 @@ func (client pageBlobClient) getPageRangesDiffResponder(resp pipeline.Response)
// be aligned to a 512-byte boundary. timeout is the timeout parameter is expressed in seconds. For more information,
// see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. ifModifiedSince is specify this header value to operate only on a blob if it
// has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate only
// on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching
// value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the
// analytics logs when storage analytics logging is enabled.
func (client pageBlobClient) Resize(ctx context.Context, blobContentLength int64, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobResizeResponse, error) {
// blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
// blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
// logs when storage analytics logging is enabled.
func (client pageBlobClient) Resize(ctx context.Context, blobContentLength int64, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobResizeResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.resizePreparer(blobContentLength, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.resizePreparer(blobContentLength, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -570,7 +551,7 @@ func (client pageBlobClient) Resize(ctx context.Context, blobContentLength int64
}
// resizePreparer prepares the Resize request.
func (client pageBlobClient) resizePreparer(blobContentLength int64, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) resizePreparer(blobContentLength int64, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -590,8 +571,8 @@ func (client pageBlobClient) resizePreparer(blobContentLength int64, timeout *in
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -621,23 +602,23 @@ func (client pageBlobClient) resizeResponder(resp pipeline.Response) (pipeline.R
// applies to page blobs only. This property indicates how the service should modify the blob's sequence number timeout
// is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the container's
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. ifModifiedSince is specify this header value to operate only on a blob if it
// has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
// blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value to operate only
// on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching
// value. blobSequenceNumber is set for page blobs only. The sequence number is a user-controlled value that you can
// use to track requests. The value of the sequence number must be between 0 and 2^63 - 1. requestID is provides a
// blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
// blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
// blobSequenceNumber is set for page blobs only. The sequence number is a user-controlled value that you can use to
// track requests. The value of the sequence number must be between 0 and 2^63 - 1. requestID is provides a
// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client pageBlobClient) UpdateSequenceNumber(ctx context.Context, sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (*PageBlobUpdateSequenceNumberResponse, error) {
func (client pageBlobClient) UpdateSequenceNumber(ctx context.Context, sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (*PageBlobUpdateSequenceNumberResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.updateSequenceNumberPreparer(sequenceNumberAction, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, blobSequenceNumber, requestID)
req, err := client.updateSequenceNumberPreparer(sequenceNumberAction, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, blobSequenceNumber, requestID)
if err != nil {
return nil, err
}
@@ -649,7 +630,7 @@ func (client pageBlobClient) UpdateSequenceNumber(ctx context.Context, sequenceN
}
// updateSequenceNumberPreparer prepares the UpdateSequenceNumber request.
func (client pageBlobClient) updateSequenceNumberPreparer(sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) updateSequenceNumberPreparer(sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -669,8 +650,8 @@ func (client pageBlobClient) updateSequenceNumberPreparer(sequenceNumberAction S
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -700,21 +681,22 @@ func (client pageBlobClient) updateSequenceNumberResponder(resp pipeline.Respons
// UploadPages the Upload Pages operation writes a range of pages to a page blob
//
// body is initial data body will be closed upon successful return. Callers should ensure closure when receiving an
// error.contentLength is the length of the request. timeout is the timeout parameter is expressed in seconds. For more
// error.contentLength is the length of the request. transactionalContentMD5 is specify the transactional md5 for the
// body, to be validated by the service. timeout is the timeout parameter is expressed in seconds. For more
// information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> rangeParameter is return only the bytes of the blob in the specified
// range. leaseID is if specified, the operation only succeeds if the container's lease is active and matches this ID.
// range. leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
// ifSequenceNumberLessThanOrEqualTo is specify this header value to operate only on a blob if it has a sequence number
// less than or equal to the specified. ifSequenceNumberLessThan is specify this header value to operate only on a blob
// if it has a sequence number less than the specified. ifSequenceNumberEqualTo is specify this header value to operate
// only on a blob if it has the specified sequence number. ifModifiedSince is specify this header value to operate only
// on a blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
// operate only on a blob if it has not been modified since the specified date/time. ifMatches is specify an ETag value
// operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
// recorded in the analytics logs when storage analytics logging is enabled.
func (client pageBlobClient) UploadPages(ctx context.Context, body io.ReadSeeker, contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobUploadPagesResponse, error) {
func (client pageBlobClient) UploadPages(ctx context.Context, body io.ReadSeeker, contentLength int64, transactionalContentMD5 []byte, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobUploadPagesResponse, error) {
if err := validate([]validation{
{targetValue: body,
constraints: []constraint{{target: "body", name: null, rule: true, chain: nil}}},
@@ -723,7 +705,7 @@ func (client pageBlobClient) UploadPages(ctx context.Context, body io.ReadSeeker
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.uploadPagesPreparer(body, contentLength, timeout, rangeParameter, leaseID, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatches, ifNoneMatch, requestID)
req, err := client.uploadPagesPreparer(body, contentLength, transactionalContentMD5, timeout, rangeParameter, leaseID, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
if err != nil {
return nil, err
}
@@ -735,7 +717,7 @@ func (client pageBlobClient) UploadPages(ctx context.Context, body io.ReadSeeker
}
// uploadPagesPreparer prepares the UploadPages request.
func (client pageBlobClient) uploadPagesPreparer(body io.ReadSeeker, contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatches *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
func (client pageBlobClient) uploadPagesPreparer(body io.ReadSeeker, contentLength int64, transactionalContentMD5 []byte, timeout *int32, rangeParameter *string, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, body)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
@@ -747,6 +729,9 @@ func (client pageBlobClient) uploadPagesPreparer(body io.ReadSeeker, contentLeng
params.Set("comp", "page")
req.URL.RawQuery = params.Encode()
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
if transactionalContentMD5 != nil {
req.Header.Set("Content-MD5", base64.StdEncoding.EncodeToString(transactionalContentMD5))
}
if rangeParameter != nil {
req.Header.Set("x-ms-range", *rangeParameter)
}
@@ -768,8 +753,8 @@ func (client pageBlobClient) uploadPagesPreparer(body io.ReadSeeker, contentLeng
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatches != nil {
req.Header.Set("If-Match", string(*ifMatches))
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
@@ -792,3 +777,120 @@ func (client pageBlobClient) uploadPagesResponder(resp pipeline.Response) (pipel
resp.Response().Body.Close()
return &PageBlobUploadPagesResponse{rawResponse: resp.Response()}, err
}
// UploadPagesFromURL the Upload Pages operation writes a range of pages to a page blob where the contents are read
// from a URL
//
// sourceURL is specify a URL to the copy source. sourceRange is bytes of source data in the specified range. The
// length of this range should match the ContentLength header and x-ms-range/Range destination range header.
// contentLength is the length of the request. rangeParameter is the range of bytes to which the source range would be
// written. The range should be 512 aligned and range-end is required. sourceContentMD5 is specify the md5 calculated
// for the range of bytes that must be read from the copy source. timeout is the timeout parameter is expressed in
// seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
// lease is active and matches this ID. ifSequenceNumberLessThanOrEqualTo is specify this header value to operate only
// on a blob if it has a sequence number less than or equal to the specified. ifSequenceNumberLessThan is specify this
// header value to operate only on a blob if it has a sequence number less than the specified. ifSequenceNumberEqualTo
// is specify this header value to operate only on a blob if it has the specified sequence number. ifModifiedSince is
// specify this header value to operate only on a blob if it has been modified since the specified date/time.
// ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified since the
// specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching value. ifNoneMatch is
// specify an ETag value to operate only on blobs without a matching value. sourceIfModifiedSince is specify this
// header value to operate only on a blob if it has been modified since the specified date/time.
// sourceIfUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified since the
// specified date/time. sourceIfMatch is specify an ETag value to operate only on blobs with a matching value.
// sourceIfNoneMatch is specify an ETag value to operate only on blobs without a matching value. requestID is provides
// a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
// analytics logging is enabled.
func (client pageBlobClient) UploadPagesFromURL(ctx context.Context, sourceURL string, sourceRange string, contentLength int64, rangeParameter string, sourceContentMD5 []byte, timeout *int32, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (*PageBlobUploadPagesFromURLResponse, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.uploadPagesFromURLPreparer(sourceURL, sourceRange, contentLength, rangeParameter, sourceContentMD5, timeout, leaseID, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, requestID)
if err != nil {
return nil, err
}
resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.uploadPagesFromURLResponder}, req)
if err != nil {
return nil, err
}
return resp.(*PageBlobUploadPagesFromURLResponse), err
}
// uploadPagesFromURLPreparer prepares the UploadPagesFromURL request.
func (client pageBlobClient) uploadPagesFromURLPreparer(sourceURL string, sourceRange string, contentLength int64, rangeParameter string, sourceContentMD5 []byte, timeout *int32, leaseID *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("PUT", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
}
params := req.URL.Query()
if timeout != nil {
params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
}
params.Set("comp", "page")
req.URL.RawQuery = params.Encode()
req.Header.Set("x-ms-copy-source", sourceURL)
req.Header.Set("x-ms-source-range", sourceRange)
if sourceContentMD5 != nil {
req.Header.Set("x-ms-source-content-md5", base64.StdEncoding.EncodeToString(sourceContentMD5))
}
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
req.Header.Set("x-ms-range", rangeParameter)
if leaseID != nil {
req.Header.Set("x-ms-lease-id", *leaseID)
}
if ifSequenceNumberLessThanOrEqualTo != nil {
req.Header.Set("x-ms-if-sequence-number-le", strconv.FormatInt(*ifSequenceNumberLessThanOrEqualTo, 10))
}
if ifSequenceNumberLessThan != nil {
req.Header.Set("x-ms-if-sequence-number-lt", strconv.FormatInt(*ifSequenceNumberLessThan, 10))
}
if ifSequenceNumberEqualTo != nil {
req.Header.Set("x-ms-if-sequence-number-eq", strconv.FormatInt(*ifSequenceNumberEqualTo, 10))
}
if ifModifiedSince != nil {
req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
}
if ifUnmodifiedSince != nil {
req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if ifMatch != nil {
req.Header.Set("If-Match", string(*ifMatch))
}
if ifNoneMatch != nil {
req.Header.Set("If-None-Match", string(*ifNoneMatch))
}
if sourceIfModifiedSince != nil {
req.Header.Set("x-ms-source-if-modified-since", (*sourceIfModifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfUnmodifiedSince != nil {
req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
}
if sourceIfMatch != nil {
req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
}
if sourceIfNoneMatch != nil {
req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
}
req.Header.Set("x-ms-version", ServiceVersion)
if requestID != nil {
req.Header.Set("x-ms-client-request-id", *requestID)
}
req.Header.Set("x-ms-page-write", "update")
return req, nil
}
// uploadPagesFromURLResponder handles the response to the UploadPagesFromURL request.
func (client pageBlobClient) uploadPagesFromURLResponder(resp pipeline.Response) (pipeline.Response, error) {
err := validateResponse(resp, http.StatusOK, http.StatusCreated)
if resp == nil {
return nil, err
}
io.Copy(ioutil.Discard, resp.Response().Body)
resp.Response().Body.Close()
return &PageBlobUploadPagesFromURLResponse{rawResponse: resp.Response()}, err
}

View File

@@ -55,7 +55,7 @@ func validateResponse(resp pipeline.Response, successStatusCodes ...int) error {
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return NewResponseError(err, resp.Response(), "failed to read response body")
return err
}
// the service code, description and details will be populated during unmarshalling
responseError := NewResponseError(nil, resp.Response(), resp.Response().Status)

View File

@@ -25,6 +25,44 @@ func newServiceClient(url url.URL, p pipeline.Pipeline) serviceClient {
return serviceClient{newManagementClient(url, p)}
}
// GetAccountInfo returns the sku name and account kind
func (client serviceClient) GetAccountInfo(ctx context.Context) (*ServiceGetAccountInfoResponse, error) {
req, err := client.getAccountInfoPreparer()
if err != nil {
return nil, err
}
resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.getAccountInfoResponder}, req)
if err != nil {
return nil, err
}
return resp.(*ServiceGetAccountInfoResponse), err
}
// getAccountInfoPreparer prepares the GetAccountInfo request.
func (client serviceClient) getAccountInfoPreparer() (pipeline.Request, error) {
req, err := pipeline.NewRequest("GET", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
}
params := req.URL.Query()
params.Set("restype", "account")
params.Set("comp", "properties")
req.URL.RawQuery = params.Encode()
req.Header.Set("x-ms-version", ServiceVersion)
return req, nil
}
// getAccountInfoResponder handles the response to the GetAccountInfo request.
func (client serviceClient) getAccountInfoResponder(resp pipeline.Response) (pipeline.Response, error) {
err := validateResponse(resp, http.StatusOK)
if resp == nil {
return nil, err
}
io.Copy(ioutil.Discard, resp.Response().Body)
resp.Response().Body.Close()
return &ServiceGetAccountInfoResponse{rawResponse: resp.Response()}, err
}
// GetProperties gets the properties of a storage account's Blob service, including properties for Storage Analytics
// and CORS (Cross-Origin Resource Sharing) rules.
//
@@ -83,7 +121,7 @@ func (client serviceClient) getPropertiesResponder(resp pipeline.Response) (pipe
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -153,7 +191,86 @@ func (client serviceClient) getStatisticsResponder(resp pipeline.Response) (pipe
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
err = xml.Unmarshal(b, result)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to unmarshal response body")
}
}
return result, nil
}
// GetUserDelegationKey retrieves a user delgation key for the Blob service. This is only a valid operation when using
// bearer token authentication.
//
// timeout is the timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> requestID is provides a client-generated, opaque value with a 1 KB
// character limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client serviceClient) GetUserDelegationKey(ctx context.Context, keyInfo KeyInfo, timeout *int32, requestID *string) (*UserDelegationKey, error) {
if err := validate([]validation{
{targetValue: timeout,
constraints: []constraint{{target: "timeout", name: null, rule: false,
chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
return nil, err
}
req, err := client.getUserDelegationKeyPreparer(keyInfo, timeout, requestID)
if err != nil {
return nil, err
}
resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.getUserDelegationKeyResponder}, req)
if err != nil {
return nil, err
}
return resp.(*UserDelegationKey), err
}
// getUserDelegationKeyPreparer prepares the GetUserDelegationKey request.
func (client serviceClient) getUserDelegationKeyPreparer(keyInfo KeyInfo, timeout *int32, requestID *string) (pipeline.Request, error) {
req, err := pipeline.NewRequest("POST", client.url, nil)
if err != nil {
return req, pipeline.NewError(err, "failed to create request")
}
params := req.URL.Query()
if timeout != nil {
params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
}
params.Set("restype", "service")
params.Set("comp", "userdelegationkey")
req.URL.RawQuery = params.Encode()
req.Header.Set("x-ms-version", ServiceVersion)
if requestID != nil {
req.Header.Set("x-ms-client-request-id", *requestID)
}
b, err := xml.Marshal(keyInfo)
if err != nil {
return req, pipeline.NewError(err, "failed to marshal request body")
}
req.Header.Set("Content-Type", "application/xml")
err = req.SetBody(bytes.NewReader(b))
if err != nil {
return req, pipeline.NewError(err, "failed to set request body")
}
return req, nil
}
// getUserDelegationKeyResponder handles the response to the GetUserDelegationKey request.
func (client serviceClient) getUserDelegationKeyResponder(resp pipeline.Response) (pipeline.Response, error) {
err := validateResponse(resp, http.StatusOK)
if resp == nil {
return nil, err
}
result := &UserDelegationKey{rawResponse: resp.Response()}
if err != nil {
return result, err
}
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, err
}
if len(b) > 0 {
b = removeBOM(b)
@@ -183,7 +300,7 @@ func (client serviceClient) getStatisticsResponder(resp pipeline.Response) (pipe
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a> requestID is provides a client-generated, opaque value with a 1 KB
// character limit that is recorded in the analytics logs when storage analytics logging is enabled.
func (client serviceClient) ListContainersSegment(ctx context.Context, prefix *string, marker *string, maxresults *int32, include ListContainersIncludeType, timeout *int32, requestID *string) (*ListContainersResponse, error) {
func (client serviceClient) ListContainersSegment(ctx context.Context, prefix *string, marker *string, maxresults *int32, include ListContainersIncludeType, timeout *int32, requestID *string) (*ListContainersSegmentResponse, error) {
if err := validate([]validation{
{targetValue: maxresults,
constraints: []constraint{{target: "maxresults", name: null, rule: false,
@@ -201,7 +318,7 @@ func (client serviceClient) ListContainersSegment(ctx context.Context, prefix *s
if err != nil {
return nil, err
}
return resp.(*ListContainersResponse), err
return resp.(*ListContainersSegmentResponse), err
}
// listContainersSegmentPreparer prepares the ListContainersSegment request.
@@ -241,14 +358,14 @@ func (client serviceClient) listContainersSegmentResponder(resp pipeline.Respons
if resp == nil {
return nil, err
}
result := &ListContainersResponse{rawResponse: resp.Response()}
result := &ListContainersSegmentResponse{rawResponse: resp.Response()}
if err != nil {
return result, err
}
defer resp.Response().Body.Close()
b, err := ioutil.ReadAll(resp.Response().Body)
if err != nil {
return result, NewResponseError(err, resp.Response(), "failed to read response body")
return result, err
}
if len(b) > 0 {
b = removeBOM(b)

View File

@@ -5,7 +5,7 @@ package azblob
// UserAgent returns the UserAgent string to use when sending http.Requests.
func UserAgent() string {
return "Azure-SDK-For-Go/0.0.0 azblob/2018-03-28"
return "Azure-SDK-For-Go/0.0.0 azblob/2018-11-09"
}
// Version returns the semantic version (see http://semver.org) of the client.

View File

@@ -65,7 +65,7 @@ func (r *DownloadResponse) Body(o RetryReaderOptions) io.ReadCloser {
func(ctx context.Context, getInfo HTTPGetterInfo) (*http.Response, error) {
resp, err := r.b.Download(ctx, getInfo.Offset, getInfo.Count,
BlobAccessConditions{
HTTPAccessConditions: HTTPAccessConditions{IfMatch: getInfo.ETag},
ModifiedAccessConditions: ModifiedAccessConditions{IfMatch: getInfo.ETag},
},
false)
if err != nil {

View File

@@ -0,0 +1,29 @@
// +build appengine
package colorable
import (
"io"
"os"
_ "github.com/mattn/go-isatty"
)
// NewColorable return new instance of Writer which handle escape sequence.
func NewColorable(file *os.File) io.Writer {
if file == nil {
panic("nil passed instead of *os.File to NewColorable()")
}
return file
}
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
func NewColorableStdout() io.Writer {
return os.Stdout
}
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
func NewColorableStderr() io.Writer {
return os.Stderr
}

11
vendor/github.com/mattn/go-ieproxy/GetProxyFunc.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
package ieproxy
import (
"net/http"
"net/url"
)
// GetProxyFunc is a forwarder for the OS-Exclusive proxyMiddleman_os.go files
func GetProxyFunc() func(*http.Request) (*url.URL, error) {
return proxyMiddleman()
}

23
vendor/github.com/mattn/go-ieproxy/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2014 mattn
Copyright (c) 2017 oliverpool
Copyright (c) 2019 Adele Reed
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

49
vendor/github.com/mattn/go-ieproxy/README.md generated vendored Normal file
View File

@@ -0,0 +1,49 @@
# ieproxy
Go package to detect the proxy settings on Windows platform.
The settings are initially attempted to be read from the [`WinHttpGetIEProxyConfigForCurrentUser` DLL call](https://docs.microsoft.com/en-us/windows/desktop/api/winhttp/nf-winhttp-winhttpgetieproxyconfigforcurrentuser), but falls back to the registry (`CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings`) in the event the DLL call fails.
For more information, take a look at the [documentation](https://godoc.org/github.com/mattn/go-ieproxy)
## Methods
You can either obtain a `net/http` compatible proxy function using `ieproxy.GetProxyFunc()`, set environment variables using `ieproxy.OverrideEnvWithStaticProxy()` (though no automatic configuration is available this way), or obtain the proxy settings via `ieproxy.GetConf()`.
| Method | Supported configuration options: |
|----------------------------------------|-----------------------------------------------|
| `ieproxy.GetProxyFunc()` | Static, Specified script, and fully automatic |
| `ieproxy.OverrideEnvWithStaticProxy()` | Static |
| `ieproxy.GetConf()` | Depends on how you use it |
## Examples
### Using GetProxyFunc():
```go
func init() {
http.DefaultTransport.(*http.Transport).Proxy = ieproxy.GetProxyFunc()
}
```
GetProxyFunc acts as a middleman between `net/http` and `mattn/go-ieproxy` in order to select the correct proxy configuration based off the details supplied in the config.
### Using OverrideEnvWithStaticProxy():
```go
func init() {
ieproxy.OverrideEnvWithStaticProxy()
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
}
```
OverrideEnvWithStaticProxy overrides the relevant environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`) with the **static, manually configured** proxy details typically found in the registry.
### Using GetConf():
```go
func main() {
conf := ieproxy.GetConf()
//Handle proxies how you want to.
}
```

51
vendor/github.com/mattn/go-ieproxy/ieproxy.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
// Package ieproxy is a utility to retrieve the proxy parameters (especially of Internet Explorer on windows)
//
// On windows, it gathers the parameters from the registry (regedit), while it uses env variable on other platforms
package ieproxy
import "os"
// ProxyConf gathers the configuration for proxy
type ProxyConf struct {
Static StaticProxyConf // static configuration
Automatic ProxyScriptConf // script configuration
}
// StaticProxyConf contains the configuration for static proxy
type StaticProxyConf struct {
// Is the proxy active?
Active bool
// Proxy address for each scheme (http, https)
// "" (empty string) is the fallback proxy
Protocols map[string]string
// Addresses not to be browsed via the proxy (comma-separated, linux-like)
NoProxy string
}
// ProxyScriptConf contains the configuration for automatic proxy
type ProxyScriptConf struct {
// Is the proxy active?
Active bool
// PreConfiguredURL of the .pac file.
// If this is empty and Active is true, auto-configuration should be assumed.
PreConfiguredURL string
}
// GetConf retrieves the proxy configuration from the Windows Regedit
func GetConf() ProxyConf {
return getConf()
}
// OverrideEnvWithStaticProxy writes new values to the
// `http_proxy`, `https_proxy` and `no_proxy` environment variables.
// The values are taken from the Windows Regedit (should be called in `init()` function - see example)
func OverrideEnvWithStaticProxy() {
overrideEnvWithStaticProxy(GetConf(), os.Setenv)
}
// FindProxyForURL computes the proxy for a given URL according to the pac file
func (psc *ProxyScriptConf) FindProxyForURL(URL string) string {
return psc.findProxyForURL(URL)
}
type envSetter func(string, string) error

10
vendor/github.com/mattn/go-ieproxy/ieproxy_unix.go generated vendored Normal file
View File

@@ -0,0 +1,10 @@
// +build !windows
package ieproxy
func getConf() ProxyConf {
return ProxyConf{}
}
func overrideEnvWithStaticProxy(pc ProxyConf, setenv envSetter) {
}

164
vendor/github.com/mattn/go-ieproxy/ieproxy_windows.go generated vendored Normal file
View File

@@ -0,0 +1,164 @@
package ieproxy
import (
"strings"
"sync"
"unsafe"
"golang.org/x/sys/windows/registry"
)
type regeditValues struct {
ProxyServer string
ProxyOverride string
ProxyEnable uint64
AutoConfigURL string
}
var once sync.Once
var windowsProxyConf ProxyConf
// GetConf retrieves the proxy configuration from the Windows Regedit
func getConf() ProxyConf {
once.Do(writeConf)
return windowsProxyConf
}
func writeConf() {
var (
cfg *tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG
err error
)
if cfg, err = getUserConfigFromWindowsSyscall(); err != nil {
regedit, _ := readRegedit() // If the syscall fails, backup to manual detection.
windowsProxyConf = parseRegedit(regedit)
return
}
defer globalFreeWrapper(cfg.lpszProxy)
defer globalFreeWrapper(cfg.lpszProxyBypass)
defer globalFreeWrapper(cfg.lpszAutoConfigUrl)
windowsProxyConf = ProxyConf{
Static: StaticProxyConf{
Active: cfg.lpszProxy != nil,
},
Automatic: ProxyScriptConf{
Active: cfg.lpszAutoConfigUrl != nil || cfg.fAutoDetect,
},
}
if windowsProxyConf.Static.Active {
protocol := make(map[string]string)
for _, s := range strings.Split(StringFromUTF16Ptr(cfg.lpszProxy), ";") {
s = strings.TrimSpace(s)
if s == "" {
continue
}
pair := strings.SplitN(s, "=", 2)
if len(pair) > 1 {
protocol[pair[0]] = pair[1]
} else {
protocol[""] = pair[0]
}
}
windowsProxyConf.Static.Protocols = protocol
if cfg.lpszProxyBypass != nil {
windowsProxyConf.Static.NoProxy = strings.Replace(StringFromUTF16Ptr(cfg.lpszProxyBypass), ";", ",", -1)
}
}
if windowsProxyConf.Automatic.Active {
windowsProxyConf.Automatic.PreConfiguredURL = StringFromUTF16Ptr(cfg.lpszAutoConfigUrl)
}
}
func getUserConfigFromWindowsSyscall() (*tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG, error) {
handle, _, err := winHttpOpen.Call(0, 0, 0, 0, 0)
if handle == 0 {
return &tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG{}, err
}
defer winHttpCloseHandle.Call(handle)
config := new(tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG)
ret, _, err := winHttpGetIEProxyConfigForCurrentUser.Call(uintptr(unsafe.Pointer(config)))
if ret > 0 {
err = nil
}
return config, err
}
// OverrideEnvWithStaticProxy writes new values to the
// http_proxy, https_proxy and no_proxy environment variables.
// The values are taken from the Windows Regedit (should be called in init() function)
func overrideEnvWithStaticProxy(conf ProxyConf, setenv envSetter) {
if conf.Static.Active {
for _, scheme := range []string{"http", "https"} {
url := mapFallback(scheme, "", conf.Static.Protocols)
setenv(scheme+"_proxy", url)
}
if conf.Static.NoProxy != "" {
setenv("no_proxy", conf.Static.NoProxy)
}
}
}
func parseRegedit(regedit regeditValues) ProxyConf {
protocol := make(map[string]string)
for _, s := range strings.Split(regedit.ProxyServer, ";") {
if s == "" {
continue
}
pair := strings.SplitN(s, "=", 2)
if len(pair) > 1 {
protocol[pair[0]] = pair[1]
} else {
protocol[""] = pair[0]
}
}
return ProxyConf{
Static: StaticProxyConf{
Active: regedit.ProxyEnable > 0,
Protocols: protocol,
NoProxy: strings.Replace(regedit.ProxyOverride, ";", ",", -1), // to match linux style
},
Automatic: ProxyScriptConf{
Active: regedit.AutoConfigURL != "",
PreConfiguredURL: regedit.AutoConfigURL,
},
}
}
func readRegedit() (values regeditValues, err error) {
k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE)
if err != nil {
return
}
defer k.Close()
values.ProxyServer, _, err = k.GetStringValue("ProxyServer")
if err != nil && err != registry.ErrNotExist {
return
}
values.ProxyOverride, _, err = k.GetStringValue("ProxyOverride")
if err != nil && err != registry.ErrNotExist {
return
}
values.ProxyEnable, _, err = k.GetIntegerValue("ProxyEnable")
if err != nil && err != registry.ErrNotExist {
return
}
values.AutoConfigURL, _, err = k.GetStringValue("AutoConfigURL")
if err != nil && err != registry.ErrNotExist {
return
}
err = nil
return
}

View File

@@ -0,0 +1,15 @@
package ieproxy
import (
"golang.org/x/sys/windows"
"unsafe"
)
var kernel32 = windows.NewLazySystemDLL("kernel32.dll")
var globalFree = kernel32.NewProc("GlobalFree")
func globalFreeWrapper(ptr *uint16) {
if ptr != nil {
_, _, _ = globalFree.Call(uintptr(unsafe.Pointer(ptr)))
}
}

7
vendor/github.com/mattn/go-ieproxy/pac_unix.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// +build !windows
package ieproxy
func (psc *ProxyScriptConf) findProxyForURL(URL string) string {
return ""
}

72
vendor/github.com/mattn/go-ieproxy/pac_windows.go generated vendored Normal file
View File

@@ -0,0 +1,72 @@
package ieproxy
import (
"strings"
"syscall"
"unsafe"
)
func (psc *ProxyScriptConf) findProxyForURL(URL string) string {
if !psc.Active {
return ""
}
proxy, _ := getProxyForURL(psc.PreConfiguredURL, URL)
i := strings.Index(proxy, ";")
if i >= 0 {
return proxy[:i]
}
return proxy
}
func getProxyForURL(pacfileURL, URL string) (string, error) {
pacfileURLPtr, err := syscall.UTF16PtrFromString(pacfileURL)
if err != nil {
return "", err
}
URLPtr, err := syscall.UTF16PtrFromString(URL)
if err != nil {
return "", err
}
handle, _, err := winHttpOpen.Call(0, 0, 0, 0, 0)
if handle == 0 {
return "", err
}
defer winHttpCloseHandle.Call(handle)
dwFlags := fWINHTTP_AUTOPROXY_CONFIG_URL
dwAutoDetectFlags := autoDetectFlag(0)
pfURLptr := pacfileURLPtr
if pacfileURL == "" {
dwFlags = fWINHTTP_AUTOPROXY_AUTO_DETECT
dwAutoDetectFlags = fWINHTTP_AUTO_DETECT_TYPE_DNS_A | fWINHTTP_AUTO_DETECT_TYPE_DHCP
pfURLptr = nil
}
options := tWINHTTP_AUTOPROXY_OPTIONS{
dwFlags: dwFlags, // adding cache might cause issues: https://github.com/mattn/go-ieproxy/issues/6
dwAutoDetectFlags: dwAutoDetectFlags,
lpszAutoConfigUrl: pfURLptr,
lpvReserved: nil,
dwReserved: 0,
fAutoLogonIfChallenged: true, // may not be optimal https://msdn.microsoft.com/en-us/library/windows/desktop/aa383153(v=vs.85).aspx
} // lpszProxyBypass isn't used as this only executes in cases where there (may) be a pac file (autodetect can fail), where lpszProxyBypass couldn't be returned.
// in the case that autodetect fails and no pre-specified pacfile is present, no proxy is returned.
info := new(tWINHTTP_PROXY_INFO)
ret, _, err := winHttpGetProxyForURL.Call(
handle,
uintptr(unsafe.Pointer(URLPtr)),
uintptr(unsafe.Pointer(&options)),
uintptr(unsafe.Pointer(info)),
)
if ret > 0 {
err = nil
}
defer globalFreeWrapper(info.lpszProxyBypass)
defer globalFreeWrapper(info.lpszProxy)
return StringFromUTF16Ptr(info.lpszProxy), err
}

View File

@@ -0,0 +1,13 @@
// +build !windows
package ieproxy
import (
"net/http"
"net/url"
)
func proxyMiddleman() func(req *http.Request) (i *url.URL, e error) {
// Fallthrough to ProxyFromEnvironment on all other OSes.
return http.ProxyFromEnvironment
}

View File

@@ -0,0 +1,51 @@
package ieproxy
import (
"net/http"
"net/url"
"golang.org/x/net/http/httpproxy"
)
func proxyMiddleman() func(req *http.Request) (i *url.URL, e error) {
// Get the proxy configuration
conf := GetConf()
envcfg := httpproxy.FromEnvironment()
if envcfg.HTTPProxy != "" || envcfg.HTTPSProxy != "" {
// If the user manually specifies environment variables, prefer those over the Windows config.
return http.ProxyFromEnvironment
} else if conf.Automatic.Active {
// If automatic proxy obtaining is specified
return func(req *http.Request) (i *url.URL, e error) {
host := conf.Automatic.FindProxyForURL(req.URL.String())
if host == "" {
return nil, nil
}
return &url.URL{Host: host}, nil
}
} else if conf.Static.Active {
// If static proxy obtaining is specified
prox := httpproxy.Config{
HTTPSProxy: mapFallback("https", "", conf.Static.Protocols),
HTTPProxy: mapFallback("http", "", conf.Static.Protocols),
NoProxy: conf.Static.NoProxy,
}
return func(req *http.Request) (i *url.URL, e error) {
return prox.ProxyFunc()(req.URL)
}
} else {
// Final fallthrough case; use the environment variables.
return http.ProxyFromEnvironment
}
}
// Return oKey or fbKey if oKey doesn't exist in the map.
func mapFallback(oKey, fbKey string, m map[string]string) string {
if v, ok := m[oKey]; ok {
return v
} else {
return m[fbKey]
}
}

23
vendor/github.com/mattn/go-ieproxy/utils.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
package ieproxy
import (
"unicode/utf16"
"unsafe"
)
// StringFromUTF16Ptr converts a *uint16 C string to a Go String
func StringFromUTF16Ptr(s *uint16) string {
if s == nil {
return ""
}
p := (*[1<<30 - 1]uint16)(unsafe.Pointer(s))
// find the string length
sz := 0
for p[sz] != 0 {
sz++
}
return string(utf16.Decode(p[:sz:sz]))
}

View File

@@ -0,0 +1,50 @@
package ieproxy
import "golang.org/x/sys/windows"
var winHttp = windows.NewLazySystemDLL("winhttp.dll")
var winHttpGetProxyForURL = winHttp.NewProc("WinHttpGetProxyForUrl")
var winHttpOpen = winHttp.NewProc("WinHttpOpen")
var winHttpCloseHandle = winHttp.NewProc("WinHttpCloseHandle")
var winHttpGetIEProxyConfigForCurrentUser = winHttp.NewProc("WinHttpGetIEProxyConfigForCurrentUser")
type tWINHTTP_AUTOPROXY_OPTIONS struct {
dwFlags autoProxyFlag
dwAutoDetectFlags autoDetectFlag
lpszAutoConfigUrl *uint16
lpvReserved *uint16
dwReserved uint32
fAutoLogonIfChallenged bool
}
type autoProxyFlag uint32
const (
fWINHTTP_AUTOPROXY_AUTO_DETECT = autoProxyFlag(0x00000001)
fWINHTTP_AUTOPROXY_CONFIG_URL = autoProxyFlag(0x00000002)
fWINHTTP_AUTOPROXY_NO_CACHE_CLIENT = autoProxyFlag(0x00080000)
fWINHTTP_AUTOPROXY_NO_CACHE_SVC = autoProxyFlag(0x00100000)
fWINHTTP_AUTOPROXY_NO_DIRECTACCESS = autoProxyFlag(0x00040000)
fWINHTTP_AUTOPROXY_RUN_INPROCESS = autoProxyFlag(0x00010000)
fWINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY = autoProxyFlag(0x00020000)
fWINHTTP_AUTOPROXY_SORT_RESULTS = autoProxyFlag(0x00400000)
)
type autoDetectFlag uint32
const (
fWINHTTP_AUTO_DETECT_TYPE_DHCP = autoDetectFlag(0x00000001)
fWINHTTP_AUTO_DETECT_TYPE_DNS_A = autoDetectFlag(0x00000002)
)
type tWINHTTP_PROXY_INFO struct {
dwAccessType uint32
lpszProxy *uint16
lpszProxyBypass *uint16
}
type tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG struct {
fAutoDetect bool
lpszAutoConfigUrl *uint16
lpszProxy *uint16
lpszProxyBypass *uint16
}

File diff suppressed because it is too large Load Diff