Update Request Header Prefixes and Upgrade API Version to 0.6.0 (#5772)

* Update Request Header Prefixes and Upgrade API Version to 0.6.0

- Changed all HTTP request header prefixes from `x-sls-` to `x-log-` to comply with the latest API specifications.
- Adjusted header prefix validation in `signature.go` to support `x-log-` and `x-acs-`.
- Replaced `x-sls-bodyrawsize` with `x-log-bodyrawsize` in multiple files (`log_project.go`, `request.go`, etc.).
- Updated header fields for compression type and signing method (e.g., `x-sls-compresstype` to `x-log-compresstype`).
- Modified authorization logic to replace `SLS` with `LOG`.

* Refactor signature generation logic for improved readability and maintainability

Split the signature generation logic into several independent functions, including header extraction, standardized header construction, and resource string generation. This modular design enhances code clarity, reduces redundant logic, and improves error handling mechanisms. Additionally, comments and code structure have been optimized to ensure easier extension and debugging.

---------

Co-authored-by: 优胜 <zhushaofei.zsf@alibaba-inc.com>
This commit is contained in:
css521 2025-04-11 16:19:46 +08:00 committed by GitHub
parent 8b65fc75c4
commit 3000824ac5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 217 additions and 569 deletions

View File

@ -1,7 +1,7 @@
package alils
const (
version = "0.5.0" // SDK version
version = "0.6.0" // SDK version
signatureMethod = "hmac-sha1" // Signature method
// OffsetNewest is the log head offset, i.e. the offset that will be

View File

@ -39,33 +39,62 @@ func NewLogProject(name, endpoint, AccessKeyID, accessKeySecret string) (p *LogP
return p, nil
}
// ListLogStore returns all logstore names of project p.
func (p *LogProject) ListLogStore() (storeNames []string, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
uri := "/logstores"
r, err := request(p, "GET", uri, h, nil)
// handleResponse is a helper function to process HTTP response and handle common error cases
// Returns response body as []byte and error if any
func handleResponse(r *http.Response, actionDesc string) ([]byte, error) {
body, err := io.ReadAll(r.Body)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
return nil, err
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to list logstore")
err = fmt.Errorf("failed to %s", actionDesc)
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
return nil, err
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return nil, fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
}
return body, nil
}
// sendRequest is a helper function to send HTTP request with specified method, uri, headers and body
// Returns response body as []byte and error if any
func (p *LogProject) sendRequest(method, uri string, headers map[string]string, body []byte) ([]byte, error) {
r, err := request(p, method, uri, headers, body)
if err != nil {
return nil, err
}
return handleResponse(r, "process request "+method+" "+uri)
}
// createStandardHeaders creates a map with standard headers for requests
func createStandardHeaders(bodyLen int) map[string]string {
if bodyLen <= 0 {
return map[string]string{
"x-log-bodyrawsize": "0",
}
}
return map[string]string{
"x-log-bodyrawsize": fmt.Sprintf("%v", bodyLen),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
}
// ListLogStore returns all logstore names of project p.
func (p *LogProject) ListLogStore() (storeNames []string, err error) {
h := createStandardHeaders(0)
uri := "/logstores"
buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil {
return
}
@ -81,39 +110,18 @@ func (p *LogProject) ListLogStore() (storeNames []string, err error) {
}
storeNames = body.LogStores
return
}
// GetLogStore returns logstore according by logstore name.
func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
r, err := request(p, "GET", "/logstores/"+name, h, nil)
buf, err := p.sendRequest("GET", "/logstores/"+name, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to get logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
s = &LogStore{}
err = json.Unmarshal(buf, s)
if err != nil {
@ -145,66 +153,15 @@ func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error)
return
}
h := map[string]string{
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "POST", "/logstores", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to create logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(len(body))
_, err = p.sendRequest("POST", "/logstores", h, body)
return
}
// DeleteLogStore deletes a logstore according by logstore name.
func (p *LogProject) DeleteLogStore(name string) (err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
r, err := request(p, "DELETE", "/logstores/"+name, h, nil)
if err != nil {
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(0)
_, err = p.sendRequest("DELETE", "/logstores/"+name, h, nil)
return
}
@ -228,73 +185,26 @@ func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error)
return
}
h := map[string]string{
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "PUT", "/logstores", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(len(body))
_, err = p.sendRequest("PUT", "/logstores", h, body)
return
}
// ListMachineGroup returns machine group name list and the total number of machine groups.
// The offset starts from 0 and the size is the max number of machine groups could be returned.
func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
if size <= 0 {
size = 500
}
uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size)
r, err := request(p, "GET", uri, h, nil)
buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to list machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Body struct {
MachineGroups []string
Count int
@ -309,39 +219,18 @@ func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int,
m = body.MachineGroups
total = body.Total
return
}
// GetMachineGroup retruns machine group according by machine group name.
func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
r, err := request(p, "GET", "/machinegroups/"+name, h, nil)
buf, err := p.sendRequest("GET", "/machinegroups/"+name, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to get machine group:%v", name)
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
m = &MachineGroup{}
err = json.Unmarshal(buf, m)
if err != nil {
@ -358,35 +247,8 @@ func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) {
return
}
h := map[string]string{
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "POST", "/machinegroups", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to create machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(len(body))
_, err = p.sendRequest("POST", "/machinegroups", h, body)
return
}
@ -397,104 +259,33 @@ func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) {
return
}
h := map[string]string{
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(len(body))
_, err = p.sendRequest("PUT", "/machinegroups/"+m.Name, h, body)
return
}
// DeleteMachineGroup deletes machine group according machine group name.
func (p *LogProject) DeleteMachineGroup(name string) (err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil)
if err != nil {
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(0)
_, err = p.sendRequest("DELETE", "/machinegroups/"+name, h, nil)
return
}
// ListConfig returns config names list and the total number of configs.
// The offset starts from 0 and the size is the max number of configs could be returned.
func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
if size <= 0 {
size = 100
}
uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size)
r, err := request(p, "GET", uri, h, nil)
buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Body struct {
Total int
Configs []string
@ -513,33 +304,13 @@ func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int,
// GetConfig returns config according by config name.
func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
r, err := request(p, "GET", "/configs/"+name, h, nil)
buf, err := p.sendRequest("GET", "/configs/"+name, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
c = &LogConfig{}
err = json.Unmarshal(buf, c)
if err != nil {
@ -556,35 +327,8 @@ func (p *LogProject) UpdateConfig(c *LogConfig) (err error) {
return
}
h := map[string]string{
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "PUT", "/configs/"+c.Name, h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(len(body))
_, err = p.sendRequest("PUT", "/configs/"+c.Name, h, body)
return
}
@ -595,99 +339,28 @@ func (p *LogProject) CreateConfig(c *LogConfig) (err error) {
return
}
h := map[string]string{
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "POST", "/configs", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(len(body))
_, err = p.sendRequest("POST", "/configs", h, body)
return
}
// DeleteConfig deletes a config according by config name.
func (p *LogProject) DeleteConfig(name string) (err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
r, err := request(p, "DELETE", "/configs/"+name, h, nil)
if err != nil {
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
h := createStandardHeaders(0)
_, err = p.sendRequest("DELETE", "/configs/"+name, h, nil)
return
}
// GetAppliedMachineGroups returns applied machine group names list according config name.
func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
uri := fmt.Sprintf("/configs/%v/machinegroups", confName)
r, err := request(p, "GET", uri, h, nil)
buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to get applied machine groups")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Body struct {
Count int
Machinegroups []string
@ -705,34 +378,14 @@ func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []stri
// GetAppliedConfigs returns applied config names list according machine group name groupName.
func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
uri := fmt.Sprintf("/machinegroups/%v/configs", groupName)
r, err := request(p, "GET", uri, h, nil)
buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to applied configs")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Cfg struct {
Count int `json:"count"`
Configs []string `json:"configs"`
@ -750,64 +403,18 @@ func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, er
// ApplyConfigToMachineGroup applies config to machine group.
func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
r, err := request(p, "PUT", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to apply config to machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
_, err = p.sendRequest("PUT", uri, h, nil)
return
}
// RemoveConfigFromMachineGroup removes config from machine group.
func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
}
h := createStandardHeaders(0)
uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
r, err := request(p, "DELETE", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to remove config from machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
_, err = p.sendRequest("DELETE", uri, h, nil)
return
}

View File

@ -32,7 +32,7 @@ type Shard struct {
// ListShards returns shard id list of this logstore.
func (s *LogStore) ListShards() (shardIDs []int, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
"x-log-bodyrawsize": "0",
}
uri := fmt.Sprintf("/logstores/%v/shards", s.Name)
@ -87,8 +87,8 @@ func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
}
h := map[string]string{
"x-sls-compresstype": "lz4",
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
"x-log-compresstype": "lz4",
"x-log-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/x-protobuf",
}
@ -123,7 +123,7 @@ func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
// For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore
func (s *LogStore) GetCursor(shardID int, from string) (cursor string, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
"x-log-bodyrawsize": "0",
}
uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v",
@ -172,7 +172,7 @@ func (s *LogStore) GetLogsBytes(shardID int, cursor string,
logGroupMaxCount int) (out []byte, nextCursor string, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
"x-log-bodyrawsize": "0",
"Accept": "application/x-protobuf",
"Accept-Encoding": "lz4",
}
@ -203,9 +203,9 @@ func (s *LogStore) GetLogsBytes(shardID int, cursor string,
return
}
v, ok := r.Header["X-Sls-Compresstype"]
v, ok := r.Header["X-Log-Compresstype"]
if !ok || len(v) == 0 {
err = fmt.Errorf("can't find 'x-sls-compresstype' header")
err = fmt.Errorf("can't find 'x-log-compresstype' header")
return
}
if v[0] != "lz4" {
@ -213,16 +213,16 @@ func (s *LogStore) GetLogsBytes(shardID int, cursor string,
return
}
v, ok = r.Header["X-Sls-Cursor"]
v, ok = r.Header["X-Log-Cursor"]
if !ok || len(v) == 0 {
err = fmt.Errorf("can't find 'x-sls-cursor' header")
err = fmt.Errorf("can't find 'x-log-cursor' header")
return
}
nextCursor = v[0]
v, ok = r.Header["X-Sls-Bodyrawsize"]
v, ok = r.Header["X-Log-Bodyrawsize"]
if !ok || len(v) == 0 {
err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header")
err = fmt.Errorf("can't find 'x-log-bodyrawsize' header")
return
}
bodyRawSize, err := strconv.Atoi(v[0])

View File

@ -45,7 +45,7 @@ type MachineList struct {
// ListMachines returns the machine list of this machine group.
func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) {
h := map[string]string{
"x-sls-bodyrawsize": "0",
"x-log-bodyrawsize": "0",
}
uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name)

View File

@ -11,17 +11,17 @@ import (
func request(project *LogProject, method, uri string, headers map[string]string,
body []byte) (resp *http.Response, err error) {
// The caller should provide 'x-sls-bodyrawsize' header
if _, ok := headers["x-sls-bodyrawsize"]; !ok {
err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header")
// The caller should provide 'x-log-bodyrawsize' header
if _, ok := headers["x-log-bodyrawsize"]; !ok {
err = fmt.Errorf("can't find 'x-log-bodyrawsize' header")
return
}
// SLS public request headers
headers["Host"] = project.Name + "." + project.Endpoint
headers["Date"] = nowRFC1123()
headers["x-sls-apiversion"] = version
headers["x-sls-signaturemethod"] = signatureMethod
headers["x-log-apiversion"] = version
headers["x-log-signaturemethod"] = signatureMethod
if body != nil {
bodyMD5 := fmt.Sprintf("%X", md5.Sum(body))
headers["Content-MD5"] = bodyMD5
@ -33,12 +33,12 @@ func request(project *LogProject, method, uri string, headers map[string]string,
}
// Calc Authorization
// Authorization = "SLS <AccessKeyID>:<Signature>"
// Authorization = "LOG <AccessKeyID>:<Signature>"
digest, err := signature(project, method, uri, headers)
if err != nil {
return
}
auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyID, digest)
auth := fmt.Sprintf("LOG %v:%v", project.AccessKeyID, digest)
headers["Authorization"] = auth
// Initialize http request

View File

@ -20,92 +20,133 @@ func nowRFC1123() string {
return time.Now().In(gmtLoc).Format(time.RFC1123)
}
// signature calculates a request's signature digest.
// signature calculates a request's signature digest
func signature(project *LogProject, method, uri string,
headers map[string]string) (digest string, err error) {
var contentMD5, contentType, date, canoHeaders, canoResource string
// 1. Extract basic header information
contentMD5 := getHeaderSafe(headers, "Content-MD5")
contentType := getHeaderSafe(headers, "Content-Type")
date, ok := headers["Date"]
if !ok {
return "", fmt.Errorf("Can't find 'Date' header")
}
// 2. Calculate CanonicalizedSLSHeaders
canoHeaders := buildCanonicalHeaders(headers)
// 3. Calculate CanonicalizedResource
canoResource, err := buildCanonicalResource(uri)
if err != nil {
return "", err
}
// 4. Build the signature string
signStr := buildSignString(method, contentMD5, contentType, date, canoHeaders, canoResource)
// 5. Calculate HMAC-SHA1 signature and encode with Base64
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString)AccessKeySecret))
return calculateHmacSha1(signStr, project.AccessKeySecret)
}
// getHeaderSafe safely retrieves a header value, returns empty string if not exists
func getHeaderSafe(headers map[string]string, key string) string {
if val, ok := headers[key]; ok {
return val
}
return ""
}
// buildCanonicalHeaders constructs normalized SLS headers
func buildCanonicalHeaders(headers map[string]string) string {
slsHeaders := make(map[string]string)
var slsHeaderKeys sort.StringSlice
// Extract headers prefixed with x-log- and x-acs-
for k, v := range headers {
l := strings.TrimSpace(strings.ToLower(k))
if strings.HasPrefix(l, "x-log-") || strings.HasPrefix(l, "x-acs-") {
slsHeaders[l] = strings.TrimSpace(v)
slsHeaderKeys = append(slsHeaderKeys, l)
}
}
// Sort headers alphabetically
sort.Sort(slsHeaderKeys)
// Build the canonical header string
var result strings.Builder
for i, k := range slsHeaderKeys {
result.WriteString(k)
result.WriteString(":")
result.WriteString(slsHeaders[k])
if i+1 < len(slsHeaderKeys) {
result.WriteString("\n")
}
}
return result.String()
}
// buildCanonicalResource constructs a normalized resource string
func buildCanonicalResource(uri string) (string, error) {
u, err := url.Parse(uri)
if err != nil {
return "", err
}
result := u.Path
// Process query parameters
if u.RawQuery != "" {
var keys sort.StringSlice
vals := u.Query()
for k := range vals {
keys = append(keys, k)
}
sort.Sort(keys)
result += "?"
for i, k := range keys {
if i > 0 {
result += "&"
}
for _, v := range vals[k] {
result += k + "=" + v
}
}
}
return result, nil
}
// buildSignString constructs the complete signature string
func buildSignString(method, contentMD5, contentType, date, canoHeaders, canoResource string) string {
// SignString = VERB + "\n"
// + CONTENT-MD5 + "\n"
// + CONTENT-TYPE + "\n"
// + DATE + "\n"
// + CanonicalizedSLSHeaders + "\n"
// + CanonicalizedResource
if val, ok := headers["Content-MD5"]; ok {
contentMD5 = val
}
if val, ok := headers["Content-Type"]; ok {
contentType = val
}
date, ok := headers["Date"]
if !ok {
err = fmt.Errorf("Can't find 'Date' header")
return
}
// Calc CanonicalizedSLSHeaders
slsHeaders := make(map[string]string, len(headers))
for k, v := range headers {
l := strings.TrimSpace(strings.ToLower(k))
if strings.HasPrefix(l, "x-sls-") {
slsHeaders[l] = strings.TrimSpace(v)
slsHeaderKeys = append(slsHeaderKeys, l)
}
}
sort.Sort(slsHeaderKeys)
for i, k := range slsHeaderKeys {
canoHeaders += k + ":" + slsHeaders[k]
if i+1 < len(slsHeaderKeys) {
canoHeaders += "\n"
}
}
// Calc CanonicalizedResource
u, err := url.Parse(uri)
if err != nil {
return
}
canoResource += url.QueryEscape(u.Path)
if u.RawQuery != "" {
var keys sort.StringSlice
vals := u.Query()
for k := range vals {
keys = append(keys, k)
}
sort.Sort(keys)
canoResource += "?"
for i, k := range keys {
if i > 0 {
canoResource += "&"
}
for _, v := range vals[k] {
canoResource += k + "=" + v
}
}
}
signStr := method + "\n" +
return method + "\n" +
contentMD5 + "\n" +
contentType + "\n" +
date + "\n" +
canoHeaders + "\n" +
canoResource
}
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString)AccessKeySecret))
mac := hmac.New(sha1.New, []byte(project.AccessKeySecret))
_, err = mac.Write([]byte(signStr))
// calculateHmacSha1 calculates the HMAC-SHA1 signature and encodes it with Base64
func calculateHmacSha1(signStr, secret string) (string, error) {
mac := hmac.New(sha1.New, []byte(secret))
_, err := mac.Write([]byte(signStr))
if err != nil {
return
return "", err
}
digest = base64.StdEncoding.EncodeToString(mac.Sum(nil))
return
return base64.StdEncoding.EncodeToString(mac.Sum(nil)), nil
}