Skip to content

Commit 1e97c81

Browse files
committed
Splits request's url property into prefix and suffix (#17)
1 parent 2240cad commit 1e97c81

File tree

5 files changed

+78
-51
lines changed

5 files changed

+78
-51
lines changed

http.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ var httpClient = &http.Client{
2525

2626
func doRequest(r request) response {
2727

28-
req, err := http.NewRequest(r.method, r.url.String(), nil)
28+
req, err := http.NewRequest(r.method, r.URL(), nil)
2929
if err != nil {
3030
return response{request: r, err: err}
3131
}
3232
req.Close = true
3333

3434
// add the host header to the request manually so it shows up in the output
35-
r.headers = append(r.headers, fmt.Sprintf("Host: %s", r.url.Hostname()))
35+
r.headers = append(r.headers, fmt.Sprintf("Host: %s", r.Hostname()))
3636

3737
for _, h := range r.headers {
3838
parts := strings.SplitN(h, ":", 2)

main.go

+9-31
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,12 @@ package main
33
import (
44
"bufio"
55
"fmt"
6-
"net/url"
76
"os"
87
"path/filepath"
98
"sync"
109
"time"
1110
)
1211

13-
// a request is a wrapper for a URL that we want to request
14-
type request struct {
15-
method string
16-
url *url.URL
17-
headers []string
18-
}
19-
20-
// a response is a wrapper around an HTTP response;
21-
// it contains the request value for context.
22-
type response struct {
23-
request request
24-
25-
status string
26-
statusCode int
27-
headers []string
28-
body []byte
29-
err error
30-
}
31-
3212
// a requester is a function that makes HTTP requests
3313
type requester func(request) response
3414

@@ -74,10 +54,7 @@ func main() {
7454
}
7555

7656
// set up a rate limiter
77-
rl := &rateLimiter{
78-
delay: time.Duration(c.delay * 1000000),
79-
reqs: make(map[string]time.Time),
80-
}
57+
rl := newRateLimiter(time.Duration(c.delay * 1000000))
8158

8259
// the request and response channels for
8360
// the worker pool
@@ -91,7 +68,7 @@ func main() {
9168

9269
go func() {
9370
for req := range requests {
94-
rl.Block(req.url)
71+
rl.Block(req.Hostname())
9572
responses <- doRequest(req)
9673
}
9774
wg.Done()
@@ -113,7 +90,7 @@ func main() {
11390
fmt.Fprintf(os.Stderr, "failed to save file: %s\n", err)
11491
}
11592

116-
line := fmt.Sprintf("%s %s (%s)\n", path, res.request.url, res.status)
93+
line := fmt.Sprintf("%s %s (%s)\n", path, res.request.URL(), res.status)
11794
fmt.Fprintf(index, "%s", line)
11895
if c.verbose {
11996
fmt.Printf("%s", line)
@@ -125,12 +102,13 @@ func main() {
125102
// send requests for each suffix for every prefix
126103
for _, suffix := range suffixes {
127104
for _, prefix := range prefixes {
128-
u, err := url.Parse(prefix + suffix)
129-
if err != nil {
130-
fmt.Printf("failed to parse url: %s\n", err)
131-
continue
105+
106+
requests <- request{
107+
method: c.method,
108+
prefix: prefix,
109+
suffix: suffix,
110+
headers: c.headers,
132111
}
133-
requests <- request{method: c.method, url: u, headers: c.headers}
134112
}
135113
}
136114

ratelimit.go

+22-10
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,56 @@
11
package main
22

33
import (
4-
"net/url"
54
"sync"
65
"time"
76
)
87

8+
// a rateLimiter allows you to delay operations
9+
// on a per-key basis. I.e. only one operation for
10+
// a given key can be done within the delay time
911
type rateLimiter struct {
1012
sync.Mutex
1113
delay time.Duration
12-
reqs map[string]time.Time
14+
ops map[string]time.Time
1315
}
1416

15-
func (r *rateLimiter) Block(u *url.URL) {
17+
// newRateLimiter returns a new *rateLimiter for the
18+
// provided delay
19+
func newRateLimiter(delay time.Duration) *rateLimiter {
20+
return &rateLimiter{
21+
delay: delay,
22+
ops: make(map[string]time.Time),
23+
}
24+
}
25+
26+
// Block blocks until an operation for key is
27+
// allowed to proceed
28+
func (r *rateLimiter) Block(key string) {
1629
now := time.Now()
17-
key := u.Hostname()
1830

1931
r.Lock()
2032

2133
// if there's nothing in the map we can
2234
// return straight away
23-
if _, ok := r.reqs[key]; !ok {
24-
r.reqs[key] = now
35+
if _, ok := r.ops[key]; !ok {
36+
r.ops[key] = now
2537
r.Unlock()
2638
return
2739
}
2840

2941
// if time is up we can return straight away
30-
t := r.reqs[key]
42+
t := r.ops[key]
3143
deadline := t.Add(r.delay)
3244
if now.After(deadline) {
33-
r.reqs[key] = now
45+
r.ops[key] = now
3446
r.Unlock()
3547
return
3648
}
3749

3850
remaining := deadline.Sub(now)
3951

40-
// Set the time of the request
41-
r.reqs[key] = now.Add(remaining)
52+
// Set the time of the operation
53+
r.ops[key] = now.Add(remaining)
4254
r.Unlock()
4355

4456
// Block for the remaining time

request.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package main
2+
3+
import "net/url"
4+
5+
// a request is a wrapper for a URL that we want to request
6+
type request struct {
7+
method string
8+
prefix string
9+
suffix string
10+
headers []string
11+
}
12+
13+
// Hostname returns the hostname part of the request
14+
func (r request) Hostname() string {
15+
u, err := url.Parse(r.prefix)
16+
17+
// the hostname part is used only for the rate
18+
// limiting and the
19+
if err != nil {
20+
return "unknown"
21+
}
22+
return u.Hostname()
23+
}
24+
25+
// URL returns the full URL to request
26+
func (r request) URL() string {
27+
return r.prefix + r.suffix
28+
}

response.go

+17-8
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,26 @@ import (
99
"path"
1010
)
1111

12+
// a response is a wrapper around an HTTP response;
13+
// it contains the request value for context.
14+
type response struct {
15+
request request
16+
17+
status string
18+
statusCode int
19+
headers []string
20+
body []byte
21+
err error
22+
}
23+
24+
// String returns a string representation of the request and response
1225
func (r response) String() string {
1326
b := &bytes.Buffer{}
1427

15-
b.WriteString(r.request.url.String())
28+
b.WriteString(r.request.URL())
1629
b.WriteString("\n\n")
1730

18-
qs := ""
19-
if len(r.request.url.Query()) > 0 {
20-
qs = "?" + r.request.url.Query().Encode()
21-
}
22-
23-
b.WriteString(fmt.Sprintf("> %s %s%s HTTP/1.1\n", r.request.method, r.request.url.EscapedPath(), qs))
31+
b.WriteString(fmt.Sprintf("> %s %s HTTP/1.1\n", r.request.method, r.request.suffix))
2432

2533
// request headers
2634
for _, h := range r.request.headers {
@@ -43,13 +51,14 @@ func (r response) String() string {
4351
return b.String()
4452
}
4553

54+
// save write a request and response output to disk
4655
func (r response) save(pathPrefix string) (string, error) {
4756

4857
content := []byte(r.String())
4958
checksum := sha1.Sum(content)
5059
parts := []string{pathPrefix}
5160

52-
parts = append(parts, r.request.url.Hostname())
61+
parts = append(parts, r.request.Hostname())
5362
parts = append(parts, fmt.Sprintf("%x", checksum))
5463

5564
p := path.Join(parts...)

0 commit comments

Comments
 (0)