Skip to content

Commit eae5ca6

Browse files
zqyizhengqianyi
andauthored
add PutObjectResponse (#365)
* add PutObjectResponse * fix PutObject to use emptyStringSha256 * fix example * update requestOption --------- Co-authored-by: zhengqianyi <zhengqianyi.zqy@alibaba-inc.com>
1 parent b49990b commit eae5ca6

9 files changed

Lines changed: 95 additions & 29 deletions

client_interface.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ type ClientInterface interface {
345345

346346
// #################### Object Operations #####################
347347
// PutObject put an object to the specified logstore
348-
PutObject(project, logstore, objectName string, content []byte, headers map[string]string) error
348+
PutObject(project, logstore, objectName string, content []byte, headers map[string]string) (*PutObjectResponse, error)
349349
// GetObject get an object from the specified logstore
350350
GetObject(project, logstore, objectName string) (*GetObjectResponse, error)
351351

client_object.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
package sls
22

3+
import "net/http"
4+
5+
// PutObjectResponse defines response from PutObject call
6+
type PutObjectResponse struct {
7+
Headers map[string]string
8+
}
9+
10+
func (resp *PutObjectResponse) GetETag() string {
11+
if resp.Headers == nil {
12+
return ""
13+
}
14+
return resp.Headers[http.CanonicalHeaderKey("Etag")]
15+
}
16+
17+
// GetObjectResponse defines response from GetObject call
318
type GetObjectResponse struct {
419
Body []byte
520
Headers map[string]string
@@ -9,17 +24,17 @@ func (resp *GetObjectResponse) GetETag() string {
924
if resp.Headers == nil {
1025
return ""
1126
}
12-
return resp.Headers["Etag"]
27+
return resp.Headers[http.CanonicalHeaderKey("Etag")]
1328
}
1429

1530
func (resp *GetObjectResponse) GetContentType() string {
1631
if resp.Headers == nil {
1732
return ""
1833
}
19-
return resp.Headers["Content-Type"]
34+
return resp.Headers[http.CanonicalHeaderKey("Content-Type")]
2035
}
2136

22-
func (c *Client) PutObject(project, logstore, objectName string, content []byte, headers map[string]string) error {
37+
func (c *Client) PutObject(project, logstore, objectName string, content []byte, headers map[string]string) (*PutObjectResponse, error) {
2338
ls := convertLogstore(c, project, logstore)
2439
return ls.PutObject(objectName, content, headers)
2540
}

example/multimodal/multimodal_example.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func main() {
2020
client.SetAuthVersion(sls.AuthV4)
2121
client.SetRegion("cn-hangzhou")
2222

23-
fmt.Println("=== Multimodal Configuration Example ===\n")
23+
fmt.Println("=== Multimodal Configuration Example ===")
2424

2525
// Example 1: Get current multimodal configuration
2626
fmt.Println("=== Example 1: Get multimodal configuration ===")

example/object/object_example.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ func main() {
2727
objectName := "test_object_1"
2828
content := []byte("Hello, this is test content")
2929

30-
err := client.PutObject(project, logstore, objectName, content, nil)
30+
putResp, err := client.PutObject(project, logstore, objectName, content, nil)
3131
if err != nil {
3232
fmt.Printf("Put object failed: %v\n", err)
3333
return
3434
}
3535
fmt.Println("Put object success!")
36+
fmt.Printf(" Response ETag: %s\n", putResp.GetETag())
37+
fmt.Printf(" All Response Headers: %v\n", putResp.Headers)
3638

3739
// Get the object back
3840
resp, err := client.GetObject(project, logstore, objectName)
@@ -54,12 +56,14 @@ func main() {
5456
"x-log-meta-version": "1.0",
5557
}
5658

57-
err = client.PutObject(project, logstore, objectName2, content2, headers)
59+
putResp2, err := client.PutObject(project, logstore, objectName2, content2, headers)
5860
if err != nil {
5961
fmt.Printf("Put object with headers failed: %v\n", err)
6062
return
6163
}
6264
fmt.Println("Put object with headers success!")
65+
fmt.Printf(" Response ETag: %s\n", putResp2.GetETag())
66+
fmt.Printf(" All Response Headers: %v\n", putResp2.Headers)
6367

6468
// Get the object back
6569
resp2, err := client.GetObject(project, logstore, objectName2)
@@ -70,8 +74,8 @@ func main() {
7074
fmt.Printf("Get object success! Body: %s\n", string(resp2.Body))
7175
fmt.Printf("ETag: %s\n", resp2.GetETag())
7276
fmt.Printf("Content-Type: %s\n", resp2.GetContentType())
73-
fmt.Printf("x-log-meta-author: %s\n", resp2.Headers["x-log-meta-author"])
74-
fmt.Printf("x-log-meta-version: %s\n", resp2.Headers["x-log-meta-version"])
77+
fmt.Printf("x-log-meta-author: %s\n", resp2.Headers["X-Log-Meta-Author"])
78+
fmt.Printf("x-log-meta-version: %s\n", resp2.Headers["X-Log-Meta-Version"])
7579

7680
// Example 3: Put an object with Content-MD5
7781
fmt.Println("\n=== Example 3: Put an object with Content-MD5 ===")
@@ -87,12 +91,14 @@ func main() {
8791
"Content-Type": "application/octet-stream",
8892
}
8993

90-
err = client.PutObject(project, logstore, objectName3, content3, headers3)
94+
putResp3, err := client.PutObject(project, logstore, objectName3, content3, headers3)
9195
if err != nil {
9296
fmt.Printf("Put object with MD5 failed: %v\n", err)
9397
return
9498
}
9599
fmt.Println("Put object with MD5 success!")
100+
fmt.Printf(" Response ETag: %s\n", putResp3.GetETag())
101+
fmt.Printf(" All Response Headers: %v\n", putResp3.Headers)
96102

97103
// Get the object back
98104
resp3, err := client.GetObject(project, logstore, objectName3)

log_project.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func (p *LogProject) WithRetryTimeout(timeout time.Duration) *LogProject {
129129
// @note you should call http.Response.Body.Close() to close body stream
130130
func (p *LogProject) RawRequest(method, uri string, headers map[string]string, body []byte) (*http.Response, error) {
131131
ctx := context.Background()
132-
return realRequest(ctx, p, method, uri, headers, body)
132+
return realRequest(ctx, p, method, uri, headers, body, defaultRequestOption())
133133
}
134134

135135
// ListLogStore returns all logstore names of project p.

log_store_object.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import (
88
)
99

1010
// PutObject put an object to the specified logstore
11-
func (s *LogStore) PutObject(objectName string, content []byte, headers map[string]string) error {
11+
func (s *LogStore) PutObject(objectName string, content []byte, headers map[string]string) (*PutObjectResponse, error) {
1212
if objectName == "" {
13-
return fmt.Errorf("object name cannot be empty")
13+
return nil, fmt.Errorf("object name cannot be empty")
1414
}
1515

1616
encodedObjectName := objectNameEncode(objectName)
@@ -26,14 +26,29 @@ func (s *LogStore) PutObject(objectName string, content []byte, headers map[stri
2626
h["Content-Type"] = "application/octet-stream"
2727
}
2828

29+
option := &requestOption{
30+
computeContentHash: false,
31+
}
32+
2933
// Send request
30-
r, err := request(s.project, "PUT", uri, h, content)
34+
r, err := requestWithOption(s.project, "PUT", uri, h, content, option)
3135
if err != nil {
32-
return NewClientError(err)
36+
return nil, NewClientError(err)
3337
}
3438
defer r.Body.Close()
3539

36-
return nil
40+
// Extract response headers
41+
respHeaders := make(map[string]string)
42+
for k, v := range r.Header {
43+
if len(v) > 0 {
44+
canonicalKey := http.CanonicalHeaderKey(k)
45+
respHeaders[canonicalKey] = v[0]
46+
}
47+
}
48+
49+
return &PutObjectResponse{
50+
Headers: respHeaders,
51+
}, nil
3752
}
3853

3954
// GetObject get an object from the specified logstore

request.go

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ import (
1515
"golang.org/x/net/context"
1616
)
1717

18+
type requestOption struct {
19+
computeContentHash bool
20+
}
21+
22+
func defaultRequestOption() *requestOption {
23+
return &requestOption{
24+
computeContentHash: true,
25+
}
26+
}
27+
1828
// timeout configs
1929
var (
2030
defaultRequestTimeout = 60 * time.Second
@@ -92,12 +102,21 @@ func retryWriteErrorCheck(ctx context.Context, err error) (bool, error) {
92102
// mock param only for test, default is []
93103
func request(project *LogProject, method, uri string, headers map[string]string,
94104
body []byte, mock ...interface{}) (*http.Response, error) {
105+
return requestWithOption(project, method, uri, headers, body, nil, mock...)
106+
}
107+
108+
func requestWithOption(project *LogProject, method, uri string, headers map[string]string,
109+
body []byte, option *requestOption, mock ...interface{}) (*http.Response, error) {
95110

96111
var r *http.Response
97112
var slsErr error
98113
var err error
99114
var mockErr *mockErrorRetry
100115

116+
if option == nil {
117+
option = defaultRequestOption()
118+
}
119+
101120
project.init()
102121
ctx, cancel := context.WithTimeout(context.Background(), project.retryTimeout)
103122
defer cancel()
@@ -108,7 +127,7 @@ func request(project *LogProject, method, uri string, headers map[string]string,
108127
err = RetryWithCondition(ctx, backoff.NewExponentialBackOff(), func() (bool, error) {
109128
if len(mock) == 0 {
110129
//fmt.Println("real request", project, method, uri, headers, body)
111-
r, slsErr = realRequest(ctx, project, method, uri, headers, body)
130+
r, slsErr = realRequest(ctx, project, method, uri, headers, body, option)
112131
//fmt.Println("real request done")
113132
} else {
114133
r, mockErr = nil, mock[0].(*mockErrorRetry)
@@ -125,7 +144,7 @@ func request(project *LogProject, method, uri string, headers map[string]string,
125144
} else {
126145
err = RetryWithCondition(ctx, backoff.NewExponentialBackOff(), func() (bool, error) {
127146
if len(mock) == 0 {
128-
r, slsErr = realRequest(ctx, project, method, uri, headers, body)
147+
r, slsErr = realRequest(ctx, project, method, uri, headers, body, option)
129148
} else {
130149
r, mockErr = nil, mock[0].(*mockErrorRetry)
131150
mockErr.RetryCnt--
@@ -149,7 +168,7 @@ func request(project *LogProject, method, uri string, headers map[string]string,
149168
// request sends a request to alibaba cloud Log Service.
150169
// @note if error is nil, you must call http.Response.Body.Close() to finalize reader
151170
func realRequest(ctx context.Context, project *LogProject, method, uri string, headers map[string]string,
152-
body []byte) (*http.Response, error) {
171+
body []byte, option *requestOption) (*http.Response, error) {
153172

154173
// The caller should provide 'x-log-bodyrawsize' header
155174
if _, ok := headers[HTTPHeaderBodyRawSize]; !ok {
@@ -194,17 +213,21 @@ func realRequest(ctx context.Context, project *LogProject, method, uri string, h
194213
for k, v := range project.innerHeaders {
195214
headers[k] = v
196215
}
197-
var signer Signer
198-
if project.AuthVersion == AuthV4 {
216+
var err error
217+
switch project.AuthVersion {
218+
case AuthV4:
199219
headers[HTTPHeaderLogDate] = dateTimeISO8601()
200-
signer = NewSignerV4(accessKeyID, accessKeySecret, project.Region)
201-
} else if project.AuthVersion == AuthV0 {
202-
signer = NewSignerV0()
203-
} else {
220+
signer := NewSignerV4(accessKeyID, accessKeySecret, project.Region)
221+
err = signer.SignWithOption(method, uri, headers, body, option.computeContentHash)
222+
case AuthV0:
223+
signer := NewSignerV0()
224+
err = signer.Sign(method, uri, headers, body)
225+
default:
204226
headers[HTTPHeaderDate] = nowRFC1123()
205-
signer = NewSignerV1(accessKeyID, accessKeySecret)
227+
signer := NewSignerV1(accessKeyID, accessKeySecret)
228+
err = signer.Sign(method, uri, headers, body)
206229
}
207-
if err := signer.Sign(method, uri, headers, body); err != nil {
230+
if err != nil {
208231
return nil, err
209232
}
210233

signature_v4.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ func NewSignerV4(accessKeyID, accessKeySecret, region string) *SignerV4 {
4242
}
4343
}
4444

45+
func (s *SignerV4) SignWithOption(method, uri string, headers map[string]string, body []byte, computeContentHash bool) error {
46+
if !computeContentHash {
47+
headers[HTTPHeaderLogContentSha256] = emptyStringSha256
48+
}
49+
return s.Sign(method, uri, headers, body)
50+
}
51+
4552
func (s *SignerV4) isSignedHeader(key string) bool {
4653
if strings.HasPrefix(strings.ToLower(key), "x-log-meta-") {
4754
return false

token_auto_update_client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2171,9 +2171,9 @@ func (c *TokenAutoUpdateClient) ListProjectV3(req *ListProjectRequest) (projects
21712171
return
21722172
}
21732173

2174-
func (c *TokenAutoUpdateClient) PutObject(project, logstore, objectName string, content []byte, headers map[string]string) (err error) {
2174+
func (c *TokenAutoUpdateClient) PutObject(project, logstore, objectName string, content []byte, headers map[string]string) (resp *PutObjectResponse, err error) {
21752175
for i := 0; i < c.maxTryTimes; i++ {
2176-
err = c.logClient.PutObject(project, logstore, objectName, content, headers)
2176+
resp, err = c.logClient.PutObject(project, logstore, objectName, content, headers)
21772177
if !c.processError(err) {
21782178
return
21792179
}

0 commit comments

Comments
 (0)