Skip to content

Commit 0f21086

Browse files
committed
init commit: simple http client
0 parents  commit 0f21086

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

main.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"flag"
6+
"fmt"
7+
"io/ioutil"
8+
"log"
9+
"net/http"
10+
"net/url"
11+
"strings"
12+
)
13+
14+
func main() {
15+
var targetURL string
16+
var body string
17+
var contentType string
18+
var accessKey string
19+
var secretKey string
20+
var method string
21+
var verbose bool
22+
flag.StringVar(&method, "X", "GET", "http request method")
23+
flag.StringVar(&targetURL, "u", "", "URL")
24+
flag.StringVar(&body, "d", "", "request body")
25+
flag.StringVar(&contentType, "t", "application/json", "content type")
26+
flag.StringVar(&accessKey, "ak", "", "QINIU access key")
27+
flag.StringVar(&secretKey, "sk", "", "QINIU secret key")
28+
flag.BoolVar(&verbose, "v", false, "show verbose (HTTP headers)")
29+
flag.Parse()
30+
31+
client := &http.Client{}
32+
client.Transport = &qiniuMacTransport{
33+
AccessKey: accessKey,
34+
SecretKey: []byte(secretKey),
35+
RoundTripper: http.DefaultTransport,
36+
showHeaders: verbose,
37+
}
38+
39+
reqURL, err := url.Parse(targetURL)
40+
if err != nil {
41+
log.Fatalf("invalid URL %s, parse URL failed: %v", targetURL, err)
42+
}
43+
if reqURL.Scheme == "" {
44+
reqURL.Scheme = "http"
45+
}
46+
req, err := http.NewRequest(method, reqURL.String(), bytes.NewReader([]byte(body)))
47+
if err != nil {
48+
log.Fatalf("failed to create request: %v", err)
49+
}
50+
if verbose {
51+
fmt.Printf("> %s %s\n", req.Method, req.URL.String())
52+
}
53+
if body != "" {
54+
req.Header.Set("Content-Type", contentType)
55+
}
56+
57+
resp, err := client.Do(req)
58+
if err != nil {
59+
fmt.Println()
60+
log.Fatalf("failed to get response: %v", err)
61+
}
62+
if verbose {
63+
fmt.Println()
64+
fmt.Printf("< %s %s %s\n", resp.Request.Method, resp.Request.URL.String(), resp.Status)
65+
for key, value := range resp.Header {
66+
fmt.Printf("< %s=%s\n", key, strings.Join(value, ";"))
67+
}
68+
}
69+
respBobyBuf, _ := ioutil.ReadAll(resp.Body)
70+
fmt.Println(string(respBobyBuf))
71+
72+
}

qiniumac.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"crypto/hmac"
6+
"crypto/sha1"
7+
"io"
8+
"io/ioutil"
9+
"net/http"
10+
"sort"
11+
)
12+
13+
const qiniuHeaderPrefix = "X-Qiniu-"
14+
15+
func includeBody(req *http.Request, ctType string) bool {
16+
return req.ContentLength != 0 && req.Body != nil && ctType != "" && ctType != "application/octet-stream"
17+
}
18+
19+
// ---------------------------------------------------------------------------------------
20+
21+
type sortByHeaderKey []string
22+
23+
func (p sortByHeaderKey) Len() int { return len(p) }
24+
func (p sortByHeaderKey) Less(i, j int) bool { return p[i] < p[j] }
25+
func (p sortByHeaderKey) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
26+
27+
func signQiniuHeaderValues(header http.Header, w io.Writer) {
28+
var keys []string
29+
for key := range header {
30+
if len(key) > len(qiniuHeaderPrefix) && key[:len(qiniuHeaderPrefix)] == qiniuHeaderPrefix {
31+
keys = append(keys, key)
32+
}
33+
}
34+
if len(keys) == 0 {
35+
return
36+
}
37+
38+
if len(keys) > 1 {
39+
sort.Sort(sortByHeaderKey(keys))
40+
}
41+
for _, key := range keys {
42+
io.WriteString(w, "\n"+key+": "+header.Get(key))
43+
}
44+
}
45+
46+
// SignRequest calculate
47+
func SignRequest(sk []byte, req *http.Request) ([]byte, error) {
48+
49+
h := hmac.New(sha1.New, sk)
50+
51+
u := req.URL
52+
data := req.Method + " " + u.Path
53+
if u.RawQuery != "" {
54+
data += "?" + u.RawQuery
55+
}
56+
io.WriteString(h, data+"\nHost: "+req.Host)
57+
58+
ctType := req.Header.Get("Content-Type")
59+
if ctType != "" {
60+
io.WriteString(h, "\nContent-Type: "+ctType)
61+
}
62+
63+
signQiniuHeaderValues(req.Header, h)
64+
65+
io.WriteString(h, "\n\n")
66+
67+
if includeBody(req, ctType) {
68+
bodyBytes, _ := ioutil.ReadAll(req.Body)
69+
req.Body.Close() // must close
70+
req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
71+
h.Write(bodyBytes)
72+
}
73+
74+
return h.Sum(nil), nil
75+
}

transport.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"encoding/base64"
5+
"fmt"
6+
"net/http"
7+
"strings"
8+
)
9+
10+
type qiniuMacTransport struct {
11+
AccessKey string
12+
SecretKey []byte
13+
RoundTripper http.RoundTripper
14+
showHeaders bool
15+
}
16+
17+
func (tr *qiniuMacTransport) RoundTrip(req *http.Request) (*http.Response, error) {
18+
if tr.AccessKey != "" {
19+
sign, err := SignRequest(tr.SecretKey, req)
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
auth := "Qiniu " + tr.AccessKey + ":" + base64.URLEncoding.EncodeToString(sign)
25+
req.Header.Set("Authorization", auth)
26+
}
27+
if tr.showHeaders {
28+
for key, value := range req.Header {
29+
fmt.Printf("> %s=%s\n", key, strings.Join(value, ";"))
30+
}
31+
}
32+
33+
return tr.RoundTripper.RoundTrip(req)
34+
}

0 commit comments

Comments
 (0)