Skip to content

Commit 88fcce2

Browse files
committed
cleanup
1 parent 4e1ce06 commit 88fcce2

File tree

4 files changed

+93
-52
lines changed

4 files changed

+93
-52
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2019 Go Lift - Building Strong Go Tools
3+
Copyright (c) 2019-2022 Go Lift - Building Strong Go Tools
44
Copyright (c) 2018 David Newhall II
55

66
Permission is hereby granted, free of charge, to any person obtaining a copy

deluge.go

+83-44
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package deluge
22

33
import (
44
"bytes"
5+
"context"
56
"crypto/tls"
67
"encoding/base64"
78
"encoding/json"
@@ -17,24 +18,45 @@ import (
1718
"golang.org/x/net/publicsuffix"
1819
)
1920

21+
// Custom errors.
22+
var (
23+
ErrInvalidVersion = fmt.Errorf("invalid data returned while checking version")
24+
ErrDelugeError = fmt.Errorf("deluge error")
25+
ErrAuthFailed = fmt.Errorf("authentication failed")
26+
)
27+
28+
type Client struct {
29+
*http.Client
30+
cookie bool
31+
}
32+
2033
// Deluge is what you get for providing a password.
2134
type Deluge struct {
22-
*http.Client
23-
URL string
35+
*Client
36+
*Config
2437
auth string
2538
id int
2639
Version string // Currently unused, for display purposes only.
2740
Backends map[string]Backend // Currently unused, for display purposes only.
2841
DebugLog func(msg string, fmt ...interface{})
2942
}
3043

44+
// NewNoAuth returns a Deluge object without authenticating or trying to connect.
45+
func NewNoAuth(config *Config) (*Deluge, error) {
46+
return newConfig(config, false)
47+
}
48+
3149
// New creates a http.Client with authenticated cookies.
3250
// Used to make additional, authenticated requests to the APIs.
3351
func New(config *Config) (*Deluge, error) {
52+
return newConfig(config, true)
53+
}
54+
55+
func newConfig(config *Config, login bool) (*Deluge, error) {
3456
// The cookie jar is used to auth Deluge.
3557
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
3658
if err != nil {
37-
return nil, fmt.Errorf("cookiejar.New(publicsuffix): %v", err)
59+
return nil, fmt.Errorf("cookiejar.New(publicsuffix): %w", err)
3860
}
3961

4062
if !strings.HasSuffix(config.URL, "/") {
@@ -51,18 +73,24 @@ func New(config *Config) (*Deluge, error) {
5173
}
5274

5375
deluge := &Deluge{
54-
URL: config.URL,
76+
Config: config,
5577
auth: config.HTTPUser,
5678
Backends: make(map[string]Backend),
5779
DebugLog: config.DebugLog,
58-
Client: &http.Client{
59-
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: config.VerifySSL}},
60-
Jar: jar,
61-
Timeout: config.Timeout.Round(time.Millisecond),
80+
Client: &Client{
81+
Client: &http.Client{
82+
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: config.VerifySSL}}, //nolint:gosec
83+
Jar: jar,
84+
Timeout: config.Timeout.Round(time.Millisecond),
85+
},
6286
},
6387
}
6488

65-
if err := deluge.Login(config.Password); err != nil {
89+
if !login {
90+
return deluge, nil
91+
}
92+
93+
if err := deluge.Login(); err != nil {
6694
return deluge, err
6795
}
6896

@@ -76,33 +104,35 @@ func New(config *Config) (*Deluge, error) {
76104
}
77105

78106
// Login sets the cookie jar with authentication information.
79-
func (d *Deluge) Login(password string) error {
107+
func (d *Deluge) Login() error {
108+
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
109+
defer cancel()
110+
80111
// This []string{config.Password} line is how you send auth creds. It's weird.
81-
req, err := d.DelReq(AuthLogin, []string{password})
112+
req, err := d.DelReq(ctx, AuthLogin, []string{d.Password})
82113
if err != nil {
83-
return fmt.Errorf("DelReq(AuthLogin, json): %v", err)
114+
return fmt.Errorf("DelReq(AuthLogin, json): %w", err)
84115
}
85116

86117
resp, err := d.Do(req)
87118
if err != nil {
88-
return fmt.Errorf("d.Do(req): %v", err)
119+
return fmt.Errorf("d.Do(req): %w", err)
89120
}
121+
defer resp.Body.Close()
90122

91-
defer func() {
92-
_, _ = io.Copy(ioutil.Discard, resp.Body)
93-
_ = resp.Body.Close()
94-
}()
123+
_, _ = io.Copy(ioutil.Discard, resp.Body) // must read body to avoid memory leak.
95124

96125
if resp.StatusCode != http.StatusOK {
97-
return fmt.Errorf("authentication failed: %v[%v] (status: %v/%v)",
98-
req.URL.String(), AuthLogin, resp.StatusCode, resp.Status)
126+
return fmt.Errorf("%w: %v[%v] (status: %v/%v)",
127+
ErrAuthFailed, req.URL.String(), AuthLogin, resp.StatusCode, resp.Status)
99128
}
100129

130+
d.Client.cookie = true
131+
101132
return nil
102133
}
103134

104135
// setVersion digs into the first server in the web UI to find the version.
105-
// This is currently unused in this libyrar, and provided for display only.
106136
func (d *Deluge) setVersion() error {
107137
response, err := d.Get(GeHosts, []string{})
108138
if err != nil {
@@ -114,17 +144,17 @@ func (d *Deluge) setVersion() error {
114144
servers := make([][]interface{}, 0)
115145
if err := json.Unmarshal(response.Result, &servers); err != nil {
116146
d.logPayload(response.Result)
117-
return fmt.Errorf("json.Unmarshal(rawResult1): %v", err)
147+
return fmt.Errorf("json.Unmarshal(rawResult1): %w", err)
118148
}
119149

120150
serverID := ""
121151

122152
// Store each server info (so consumers can access them easily).
123153
for _, server := range servers {
124-
serverID = server[0].(string)
154+
serverID, _ = server[0].(string)
125155
d.Backends[serverID] = Backend{
126156
ID: serverID,
127-
Addr: server[1].(string) + ":" + strconv.FormatFloat(server[2].(float64), 'f', 0, 64),
157+
Addr: server[1].(string) + ":" + strconv.FormatFloat(server[2].(float64), 'f', 0, 64), //nolint:gomnd
128158
Prot: server[3].(string),
129159
}
130160
}
@@ -138,30 +168,33 @@ func (d *Deluge) setVersion() error {
138168
server := make([]interface{}, 0)
139169
if err = json.Unmarshal(response.Result, &server); err != nil {
140170
d.logPayload(response.Result)
141-
return fmt.Errorf("json.Unmarshal(rawResult2): %v", err)
171+
return fmt.Errorf("json.Unmarshal(rawResult2): %w", err)
142172
}
143173

144174
const payloadSegments = 3
145175

146176
if len(server) < payloadSegments {
147177
d.logPayload(response.Result)
148-
return fmt.Errorf("invalid data returned while checking version")
178+
return ErrInvalidVersion
149179
}
150180

151181
// Version comes last in the mixed list.
152-
d.Version = server[len(server)-1].(string)
182+
var ok bool
183+
if d.Version, ok = server[len(server)-1].(string); !ok {
184+
return ErrInvalidVersion
185+
}
153186

154187
return nil
155188
}
156189

157190
// DelReq is a small helper function that adds headers and marshals the json.
158-
func (d Deluge) DelReq(method string, params interface{}) (req *http.Request, err error) {
191+
func (d Deluge) DelReq(ctx context.Context, method string, params interface{}) (req *http.Request, err error) {
159192
d.id++
160193

161194
paramMap := map[string]interface{}{"method": method, "id": d.id, "params": params}
162195
if data, errr := json.Marshal(paramMap); errr != nil {
163-
return req, fmt.Errorf("json.Marshal(params): %v", err)
164-
} else if req, err = http.NewRequest("POST", d.URL, bytes.NewBuffer(data)); err == nil {
196+
return req, fmt.Errorf("json.Marshal(params): %w", err)
197+
} else if req, err = http.NewRequestWithContext(ctx, "POST", d.URL, bytes.NewBuffer(data)); err == nil {
165198
if d.auth != "" {
166199
// In case Deluge is also behind HTTP auth.
167200
req.Header.Add("Authorization", d.auth)
@@ -180,12 +213,12 @@ func (d Deluge) GetXfers() (map[string]*XferStatus, error) {
180213

181214
response, err := d.Get(GetAllTorrents, []string{"", ""})
182215
if err != nil {
183-
return xfers, fmt.Errorf("get(GetAllTorrents): %v", err)
216+
return xfers, fmt.Errorf("get(GetAllTorrents): %w", err)
184217
}
185218

186219
if err := json.Unmarshal(response.Result, &xfers); err != nil {
187220
d.logPayload(response.Result)
188-
return xfers, fmt.Errorf("json.Unmarshal(xfers): %v", err)
221+
return xfers, fmt.Errorf("json.Unmarshal(xfers): %w", err)
189222
}
190223

191224
return xfers, nil
@@ -200,47 +233,53 @@ func (d Deluge) GetXfersCompat() (map[string]*XferStatusCompat, error) {
200233

201234
response, err := d.Get(GetAllTorrents, []string{"", ""})
202235
if err != nil {
203-
return xfers, fmt.Errorf("get(GetAllTorrents): %v", err)
236+
return xfers, fmt.Errorf("get(GetAllTorrents): %w", err)
204237
}
205238

206239
if err := json.Unmarshal(response.Result, &xfers); err != nil {
207240
d.logPayload(response.Result)
208-
return xfers, fmt.Errorf("json.Unmarshal(xfers): %v", err)
241+
return xfers, fmt.Errorf("json.Unmarshal(xfers): %w", err)
209242
}
210243

211244
return xfers, nil
212245
}
213246

214-
// Get a response from Deluge
247+
// Get a response from Deluge.
215248
func (d Deluge) Get(method string, params interface{}) (*Response, error) {
216249
response := new(Response)
217250

218-
req, err := d.DelReq(method, params)
251+
if !d.cookie {
252+
if err := d.Login(); err != nil {
253+
return response, err
254+
}
255+
}
256+
257+
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
258+
defer cancel()
259+
260+
req, err := d.DelReq(ctx, method, params)
219261
if err != nil {
220-
return response, fmt.Errorf("d.DelReq: %v", err)
262+
return response, fmt.Errorf("d.DelReq: %w", err)
221263
}
222264

223265
resp, err := d.Do(req)
224266
if err != nil {
225-
return response, fmt.Errorf("d.Do: %v", err)
267+
return response, fmt.Errorf("d.Do: %w", err)
226268
}
227-
228-
defer func() {
229-
_ = resp.Body.Close()
230-
}()
269+
defer resp.Body.Close()
231270

232271
body, err := ioutil.ReadAll(resp.Body)
233272
if err != nil {
234-
return response, fmt.Errorf("ioutil.ReadAll: %v", err)
273+
return response, fmt.Errorf("ioutil.ReadAll: %w", err)
235274
}
236275

237276
if err = json.Unmarshal(body, &response); err != nil {
238277
d.logPayload(response.Result)
239-
return response, fmt.Errorf("json.Unmarshal(response): %v", err)
278+
return response, fmt.Errorf("json.Unmarshal(response): %w", err)
240279
}
241280

242281
if response.Error.Code != 0 {
243-
return response, fmt.Errorf("deluge error: %v", response.Error.Message)
282+
return response, fmt.Errorf("%w: %s", ErrDelugeError, response.Error.Message)
244283
}
245284

246285
return response, nil

go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module golift.io/deluge
22

3-
go 1.13
3+
go 1.17
44

5-
require golang.org/x/net v0.0.0-20200202094626-16171245cfb2
5+
require golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4

go.sum

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
2-
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
3-
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
4-
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
5-
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1+
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0=
2+
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
3+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
6+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
7+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

0 commit comments

Comments
 (0)