request.go raw
1 /*
2 * Copyright 2017 Baidu, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 * either express or implied. See the License for the specific language governing permissions
12 * and limitations under the License.
13 */
14
15 // request.go - defines the BCE servies request
16
17 package bce
18
19 import (
20 "bytes"
21 "fmt"
22 "hash"
23 "io"
24 "io/ioutil"
25 "os"
26
27 "github.com/baidubce/bce-sdk-go/http"
28 "github.com/baidubce/bce-sdk-go/util"
29 )
30
31 // Body defines the data structure used in BCE request.
32 // Every BCE request that sets the body field must set its content-length and content-md5 headers
33 // to ensure the correctness of the body content forcely, and users can also set the content-sha256
34 // header to strengthen the correctness with the "SetHeader" method.
35 type Body struct {
36 stream io.ReadCloser
37 writer io.Writer
38 size int64
39 contentMD5 string
40 }
41
42 func (b *Body) Stream() io.ReadCloser { return b.stream }
43
44 func (b *Body) SetStream(stream io.ReadCloser) { b.stream = stream }
45
46 func (b *Body) Writer() io.Writer { return b.writer }
47
48 func (b *Body) SetWriter(w io.Writer) { b.writer = w }
49
50 func (b *Body) Size() int64 { return b.size }
51
52 func (b *Body) SetSize(size int64) { b.size = size }
53
54 func (b *Body) ContentMD5() string { return b.contentMD5 }
55
56 func (b *Body) SetContentMD5(md5Str string) { b.contentMD5 = md5Str }
57
58 func (b *Body) Read(p []byte) (int, error) {
59 n, err := b.stream.Read(p)
60 if n > 0 && b.writer != nil {
61 if n, err := b.writer.Write(p[:n]); err != nil {
62 return n, err
63 }
64 }
65 return n, err
66 }
67
68 func (b *Body) Close() error {
69 if rclose, ok := b.stream.(io.ReadCloser); ok {
70 rclose.Close()
71 }
72 if b.writer != nil {
73 if wclose, ok := b.writer.(io.WriteCloser); ok {
74 wclose.Close()
75 }
76 }
77 return nil
78 }
79
80 func (b *Body) Crc32() uint32 {
81 if b.writer != nil {
82 if hc32, ok := b.writer.(hash.Hash32); ok {
83 return hc32.Sum32()
84 }
85 }
86 return 0
87 }
88
89 // NewBodyFromBytes - build a Body object from the byte stream to be used in the http request, it
90 // calculates the content-md5 of the byte stream and store the size as well as the stream.
91 //
92 // PARAMS:
93 // - stream: byte stream
94 //
95 // RETURNS:
96 // - *Body: the return Body object
97 // - error: error if any specific error occurs
98 func NewBodyFromBytes(stream []byte) (*Body, error) {
99 buf := bytes.NewBuffer(stream)
100 size := int64(buf.Len())
101 contentMD5, err := util.CalculateContentMD5(buf, size)
102 if err != nil {
103 return nil, err
104 }
105 buf = bytes.NewBuffer(stream)
106 return &Body{stream: ioutil.NopCloser(buf), size: size, contentMD5: contentMD5}, nil
107 }
108
109 // NewBodyFromString - build a Body object from the string to be used in the http request, it
110 // calculates the content-md5 of the byte stream and store the size as well as the stream.
111 //
112 // PARAMS:
113 // - str: the input string
114 //
115 // RETURNS:
116 // - *Body: the return Body object
117 // - error: error if any specific error occurs
118 func NewBodyFromString(str string) (*Body, error) {
119 buf := bytes.NewBufferString(str)
120 size := int64(len(str))
121 contentMD5, err := util.CalculateContentMD5(buf, size)
122 if err != nil {
123 return nil, err
124 }
125 buf = bytes.NewBufferString(str)
126 return &Body{stream: ioutil.NopCloser(buf), size: size, contentMD5: contentMD5}, nil
127 }
128
129 // NewBodyFromFile - build a Body object from the given file name to be used in the http request,
130 // it calculates the content-md5 of the byte stream and store the size as well as the stream.
131 //
132 // PARAMS:
133 // - fname: the given file name
134 //
135 // RETURNS:
136 // - *Body: the return Body object
137 // - error: error if any specific error occurs
138 func NewBodyFromFile(fname string) (*Body, error) {
139 file, err := os.Open(fname)
140 if err != nil {
141 return nil, err
142 }
143 fileInfo, infoErr := file.Stat()
144 if infoErr != nil {
145 return nil, infoErr
146 }
147 contentMD5, md5Err := util.CalculateContentMD5(file, fileInfo.Size())
148 if md5Err != nil {
149 return nil, md5Err
150 }
151 if _, err = file.Seek(0, 0); err != nil {
152 return nil, err
153 }
154 return &Body{stream: file, size: fileInfo.Size(), contentMD5: contentMD5}, nil
155 }
156
157 // NewBodyFromSectionFile - build a Body object from the given file pointer with offset and size.
158 // It calculates the content-md5 of the given content and store the size as well as the stream.
159 //
160 // PARAMS:
161 // - file: the input file pointer
162 // - off: offset of current section body
163 // - size: current section body size
164 //
165 // RETURNS:
166 // - *Body: the return Body object
167 // - error: error if any specific error occurs
168 func NewBodyFromSectionFile(file *os.File, off, size int64) (*Body, error) {
169 if _, err := file.Seek(off, 0); err != nil {
170 return nil, err
171 }
172 contentMD5, md5Err := util.CalculateContentMD5(file, size)
173 if md5Err != nil {
174 return nil, md5Err
175 }
176 if _, err := file.Seek(0, 0); err != nil {
177 return nil, err
178 }
179 section := io.NewSectionReader(file, off, size)
180 return &Body{stream: ioutil.NopCloser(section), size: size, contentMD5: contentMD5}, nil
181 }
182
183 // NewBodyFromSizedReader - build a Body object from the given reader with size.
184 // It calculates the content-md5 of the given content and store the size as well as the stream.
185 //
186 // PARAMS:
187 // - r: the input reader
188 // - size: the size to be read, -1 is read all
189 //
190 // RETURNS:
191 // - *Body: the return Body object
192 // - error: error if any specific error occurs
193 func NewBodyFromSizedReader(r io.Reader, size int64) (*Body, error) {
194 var buffer bytes.Buffer
195 var rlen int64
196 var err error
197 if size >= 0 {
198 rlen, err = io.CopyN(&buffer, r, size)
199 } else {
200 rlen, err = io.Copy(&buffer, r)
201 }
202 if err != nil {
203 return nil, err
204 }
205 if rlen != int64(buffer.Len()) { // must be equal
206 return nil, NewBceClientError("unexpected reader")
207 }
208 if size >= 0 {
209 if rlen < size {
210 return nil, NewBceClientError("size is great than reader actual size")
211 }
212 }
213 contentMD5, err := util.CalculateContentMD5(bytes.NewBuffer(buffer.Bytes()), rlen)
214 if err != nil {
215 return nil, err
216 }
217 body := &Body{
218 stream: ioutil.NopCloser(&buffer),
219 size: rlen,
220 contentMD5: contentMD5,
221 }
222 return body, nil
223 }
224
225 // BceRequest defines the request structure for accessing BCE services
226 type BceRequest struct {
227 http.Request
228 requestId string
229 clientError *BceClientError
230 }
231
232 func (b *BceRequest) RequestId() string { return b.requestId }
233
234 func (b *BceRequest) SetRequestId(val string) { b.requestId = val }
235
236 func (b *BceRequest) ClientError() *BceClientError { return b.clientError }
237
238 func (b *BceRequest) SetClientError(err *BceClientError) { b.clientError = err }
239
240 func (b *BceRequest) SetBody(body *Body) { // override SetBody derived from http.Request
241 b.Request.SetBody(body)
242 b.SetLength(body.Size()) // set field of "net/http.Request.ContentLength"
243 if body.ContentMD5() != "" {
244 b.SetHeader(http.CONTENT_MD5, body.ContentMD5())
245 }
246 if body.Size() > 0 {
247 b.SetHeader(http.CONTENT_LENGTH, fmt.Sprintf("%d", body.Size()))
248 }
249 }
250
251 func (b *BceRequest) BuildHttpRequest() {
252 // Only need to build the specific `requestId` field for BCE, other fields are same as the
253 // `http.Request` as well as its methods.
254 if len(b.requestId) == 0 {
255 // Construct the request ID with UUID
256 b.requestId = util.NewRequestId()
257 }
258 b.SetHeader(http.BCE_REQUEST_ID, b.requestId)
259 }
260
261 func (b *BceRequest) String() string {
262 requestIdStr := "requestId=" + b.requestId
263 if b.clientError != nil {
264 return requestIdStr + ", client error: " + b.ClientError().Error()
265 }
266 return requestIdStr + "\n" + b.Request.String()
267 }
268