aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-streaming-unsigned-trailer.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-streaming-unsigned-trailer.go')
-rw-r--r--vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-streaming-unsigned-trailer.go224
1 files changed, 224 insertions, 0 deletions
diff --git a/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-streaming-unsigned-trailer.go b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-streaming-unsigned-trailer.go
new file mode 100644
index 0000000..77540e2
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-streaming-unsigned-trailer.go
@@ -0,0 +1,224 @@
1/*
2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage
3 * Copyright 2022 MinIO, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package signer
19
20import (
21 "bytes"
22 "fmt"
23 "io"
24 "net/http"
25 "strconv"
26 "strings"
27 "time"
28)
29
30// getUnsignedChunkLength - calculates the length of chunk metadata
31func getUnsignedChunkLength(chunkDataSize int64) int64 {
32 return int64(len(fmt.Sprintf("%x", chunkDataSize))) +
33 crlfLen +
34 chunkDataSize +
35 crlfLen
36}
37
38// getUSStreamLength - calculates the length of the overall stream (data + metadata)
39func getUSStreamLength(dataLen, chunkSize int64, trailers http.Header) int64 {
40 if dataLen <= 0 {
41 return 0
42 }
43
44 chunksCount := int64(dataLen / chunkSize)
45 remainingBytes := int64(dataLen % chunkSize)
46 streamLen := int64(0)
47 streamLen += chunksCount * getUnsignedChunkLength(chunkSize)
48 if remainingBytes > 0 {
49 streamLen += getUnsignedChunkLength(remainingBytes)
50 }
51 streamLen += getUnsignedChunkLength(0)
52 if len(trailers) > 0 {
53 for name, placeholder := range trailers {
54 if len(placeholder) > 0 {
55 streamLen += int64(len(name) + len(trailerKVSeparator) + len(placeholder[0]) + 1)
56 }
57 }
58 streamLen += crlfLen
59 }
60
61 return streamLen
62}
63
64// prepareStreamingRequest - prepares a request with appropriate
65// headers before computing the seed signature.
66func prepareUSStreamingRequest(req *http.Request, sessionToken string, dataLen int64, timestamp time.Time) {
67 req.TransferEncoding = []string{"aws-chunked"}
68 if sessionToken != "" {
69 req.Header.Set("X-Amz-Security-Token", sessionToken)
70 }
71
72 req.Header.Set("X-Amz-Date", timestamp.Format(iso8601DateFormat))
73 // Set content length with streaming signature for each chunk included.
74 req.ContentLength = getUSStreamLength(dataLen, int64(payloadChunkSize), req.Trailer)
75}
76
77// StreamingUSReader implements chunked upload signature as a reader on
78// top of req.Body's ReaderCloser chunk header;data;... repeat
79type StreamingUSReader struct {
80 contentLen int64 // Content-Length from req header
81 baseReadCloser io.ReadCloser // underlying io.Reader
82 bytesRead int64 // bytes read from underlying io.Reader
83 buf bytes.Buffer // holds signed chunk
84 chunkBuf []byte // holds raw data read from req Body
85 chunkBufLen int // no. of bytes read so far into chunkBuf
86 done bool // done reading the underlying reader to EOF
87 chunkNum int
88 totalChunks int
89 lastChunkSize int
90 trailer http.Header
91}
92
93// writeChunk - signs a chunk read from s.baseReader of chunkLen size.
94func (s *StreamingUSReader) writeChunk(chunkLen int, addCrLf bool) {
95 s.buf.WriteString(strconv.FormatInt(int64(chunkLen), 16) + "\r\n")
96
97 // Write chunk data into streaming buffer
98 s.buf.Write(s.chunkBuf[:chunkLen])
99
100 // Write the chunk trailer.
101 if addCrLf {
102 s.buf.Write([]byte("\r\n"))
103 }
104
105 // Reset chunkBufLen for next chunk read.
106 s.chunkBufLen = 0
107 s.chunkNum++
108}
109
110// addSignedTrailer - adds a trailer with the provided headers,
111// then signs a chunk and adds it to output.
112func (s *StreamingUSReader) addTrailer(h http.Header) {
113 olen := len(s.chunkBuf)
114 s.chunkBuf = s.chunkBuf[:0]
115 for k, v := range h {
116 s.chunkBuf = append(s.chunkBuf, []byte(strings.ToLower(k)+trailerKVSeparator+v[0]+"\n")...)
117 }
118
119 s.buf.Write(s.chunkBuf)
120 s.buf.WriteString("\r\n\r\n")
121
122 // Reset chunkBufLen for next chunk read.
123 s.chunkBuf = s.chunkBuf[:olen]
124 s.chunkBufLen = 0
125 s.chunkNum++
126}
127
128// StreamingUnsignedV4 - provides chunked upload
129func StreamingUnsignedV4(req *http.Request, sessionToken string, dataLen int64, reqTime time.Time) *http.Request {
130 // Set headers needed for streaming signature.
131 prepareUSStreamingRequest(req, sessionToken, dataLen, reqTime)
132
133 if req.Body == nil {
134 req.Body = io.NopCloser(bytes.NewReader([]byte("")))
135 }
136
137 stReader := &StreamingUSReader{
138 baseReadCloser: req.Body,
139 chunkBuf: make([]byte, payloadChunkSize),
140 contentLen: dataLen,
141 chunkNum: 1,
142 totalChunks: int((dataLen+payloadChunkSize-1)/payloadChunkSize) + 1,
143 lastChunkSize: int(dataLen % payloadChunkSize),
144 }
145 if len(req.Trailer) > 0 {
146 stReader.trailer = req.Trailer
147 // Remove...
148 req.Trailer = nil
149 }
150
151 req.Body = stReader
152
153 return req
154}
155
156// Read - this method performs chunk upload signature providing a
157// io.Reader interface.
158func (s *StreamingUSReader) Read(buf []byte) (int, error) {
159 switch {
160 // After the last chunk is read from underlying reader, we
161 // never re-fill s.buf.
162 case s.done:
163
164 // s.buf will be (re-)filled with next chunk when has lesser
165 // bytes than asked for.
166 case s.buf.Len() < len(buf):
167 s.chunkBufLen = 0
168 for {
169 n1, err := s.baseReadCloser.Read(s.chunkBuf[s.chunkBufLen:])
170 // Usually we validate `err` first, but in this case
171 // we are validating n > 0 for the following reasons.
172 //
173 // 1. n > 0, err is one of io.EOF, nil (near end of stream)
174 // A Reader returning a non-zero number of bytes at the end
175 // of the input stream may return either err == EOF or err == nil
176 //
177 // 2. n == 0, err is io.EOF (actual end of stream)
178 //
179 // Callers should always process the n > 0 bytes returned
180 // before considering the error err.
181 if n1 > 0 {
182 s.chunkBufLen += n1
183 s.bytesRead += int64(n1)
184
185 if s.chunkBufLen == payloadChunkSize ||
186 (s.chunkNum == s.totalChunks-1 &&
187 s.chunkBufLen == s.lastChunkSize) {
188 // Sign the chunk and write it to s.buf.
189 s.writeChunk(s.chunkBufLen, true)
190 break
191 }
192 }
193 if err != nil {
194 if err == io.EOF {
195 // No more data left in baseReader - last chunk.
196 // Done reading the last chunk from baseReader.
197 s.done = true
198
199 // bytes read from baseReader different than
200 // content length provided.
201 if s.bytesRead != s.contentLen {
202 return 0, fmt.Errorf("http: ContentLength=%d with Body length %d", s.contentLen, s.bytesRead)
203 }
204
205 // Sign the chunk and write it to s.buf.
206 s.writeChunk(0, len(s.trailer) == 0)
207 if len(s.trailer) > 0 {
208 // Trailer must be set now.
209 s.addTrailer(s.trailer)
210 }
211 break
212 }
213 return 0, err
214 }
215
216 }
217 }
218 return s.buf.Read(buf)
219}
220
221// Close - this method makes underlying io.ReadCloser's Close method available.
222func (s *StreamingUSReader) Close() error {
223 return s.baseReadCloser.Close()
224}