forked from xinsnake/go-http-digest-auth-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
digest_auth_client.go
149 lines (120 loc) · 3.33 KB
/
digest_auth_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package digest_auth_client
import (
"bytes"
"fmt"
"net/http"
"time"
)
type DigestRequest struct {
Body string
Method string
Password string
Uri string
Username string
Auth *authorization
Wa *wwwAuthenticate
}
type DigestTransport struct {
Password string
Username string
}
// NewRequest creates a new DigestRequest object
func NewRequest(username, password, method, uri, body string) DigestRequest {
dr := DigestRequest{}
dr.UpdateRequest(username, password, method, uri, body)
return dr
}
// NewTransport creates a new DigestTransport object
func NewTransport(username, password string) DigestTransport {
dt := DigestTransport{}
dt.Password = password
dt.Username = username
return dt
}
// UpdateRequest is called when you want to reuse an existing
// DigestRequest connection with new request information
func (dr *DigestRequest) UpdateRequest(username, password, method, uri, body string) *DigestRequest {
dr.Body = body
dr.Method = method
dr.Password = password
dr.Uri = uri
dr.Username = username
return dr
}
// RoundTrip implements the http.RoundTripper interface
func (dt *DigestTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
username := dt.Username
password := dt.Password
method := req.Method
uri := req.URL.String()
var body string
if req.Body != nil {
buf := new(bytes.Buffer)
buf.ReadFrom(req.Body)
body = buf.String()
}
dr := NewRequest(username, password, method, uri, body)
return dr.Execute()
}
// Execute initialise the request and get a response
func (dr *DigestRequest) Execute() (resp *http.Response, err error) {
if dr.Auth != nil {
return dr.executeExistingDigest()
}
var req *http.Request
if req, err = http.NewRequest(dr.Method, dr.Uri, bytes.NewReader([]byte(dr.Body))); err != nil {
return nil, err
}
client := &http.Client{
Timeout: 30 * time.Second,
}
if resp, err = client.Do(req); err != nil {
return nil, err
}
if resp.StatusCode == 401 {
return dr.executeNewDigest(resp)
}
// return the resp to user to handle resp.body.Close()
return resp, nil
}
func (dr *DigestRequest) executeNewDigest(resp *http.Response) (resp2 *http.Response, err error) {
var (
auth *authorization
wa *wwwAuthenticate
waString string
)
// body not required for authentication, closing
resp.Body.Close()
if waString = resp.Header.Get("WWW-Authenticate"); waString == "" {
return nil, fmt.Errorf("failed to get WWW-Authenticate header, please check your server configuration")
}
wa = newWwwAuthenticate(waString)
dr.Wa = wa
if auth, err = newAuthorization(dr); err != nil {
return nil, err
}
if resp2, err = dr.executeRequest(auth.toString()); err != nil {
return nil, err
}
dr.Auth = auth
return resp2, nil
}
func (dr *DigestRequest) executeExistingDigest() (resp *http.Response, err error) {
var auth *authorization
if auth, err = dr.Auth.refreshAuthorization(dr); err != nil {
return nil, err
}
dr.Auth = auth
return dr.executeRequest(dr.Auth.toString())
}
func (dr *DigestRequest) executeRequest(authString string) (resp *http.Response, err error) {
var req *http.Request
if req, err = http.NewRequest(dr.Method, dr.Uri, bytes.NewReader([]byte(dr.Body))); err != nil {
return nil, err
}
req.Header.Add("Authorization", authString)
client := &http.Client{
Timeout: 30 * time.Second,
}
return client.Do(req)
}