-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
132 lines (113 loc) · 2.93 KB
/
main.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
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"sync"
"time"
"github.com/paulbellamy/ratecounter"
)
var wg sync.WaitGroup
type Job struct {
url string
}
func isLetter(c rune) bool {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
}
func httpRequest(url string) (bool, error) {
var netClient = &http.Client{
Timeout: time.Second * 5,
}
resp, err := netClient.Get(url)
if err != nil {
return false, err
}
defer resp.Body.Close()
_, err = io.Copy(ioutil.Discard, resp.Body)
if err != nil {
return false, err
}
if resp.StatusCode == http.StatusOK {
return true, nil
}
return false, fmt.Errorf("wrong status")
}
func producer(jobs chan<- *Job, stopChan chan struct{}, baseURL string, maxNumbers int, availableLetters string) {
alphabetLen := len(availableLetters)
//var links []string
for _, firstLetter := range availableLetters {
if !isLetter(firstLetter) {
log.Fatalf("a number or non alphabetic character was entered in letters flag: %v", firstLetter)
}
secondLetter := 'a'
thirdLetter := 'a'
for second := 0; second < alphabetLen; second++ {
secondLetter = rune(availableLetters[second])
for third := 0; third < alphabetLen; third++ {
thirdLetter = rune(availableLetters[third])
for number := 0; number < maxNumbers; number++ {
newURL := fmt.Sprintf(baseURL, string(firstLetter), string(secondLetter), string(thirdLetter), number)
jobs <- &Job{url: newURL}
}
}
}
}
close(jobs)
}
func worker(jobs <-chan *Job, results chan<- *Job, counter *ratecounter.RateCounter) {
defer wg.Done()
for {
j, more := <-jobs
if more {
_, err := httpRequest(j.url)
counter.Incr(1)
if err == nil {
log.Println("found valid url:", j.url)
os.Exit(2)
}
} else {
return
}
}
}
func signalStop(signalCh chan os.Signal, stopChan chan struct{}) {
<-signalCh
close(stopChan)
}
func rateDisplay(counter *ratecounter.RateCounter) {
for {
time.Sleep(time.Second)
fmt.Println(counter.Rate())
}
}
func main() {
defaultBaseUrl := "https://test-assets.s3.amazonaws.com/test/test/20190619/20190619-TEST-test%s%s%s%03d.png"
workerRoutines := flag.Int("threads", 5000, "")
maxNumbers := flag.Int("maxNumbers", 1000, "")
maxJobs := flag.Int("maxjobs", 1000000, "")
availableLetters := flag.String("availableLetters", "abcdefghijklmnopqrstuvwxyz", "")
baseUrl := flag.String("baseURL", defaultBaseUrl, "")
flag.Parse()
var jobs = make(chan *Job, *maxJobs)
var results = make(chan *Job)
var done = make(chan bool, 1)
counter := ratecounter.NewRateCounter(1 * time.Second)
stopChan := make(chan struct{})
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, os.Interrupt)
for w := 1; w <= *workerRoutines; w++ {
wg.Add(1)
go worker(jobs, results, counter)
}
go producer(jobs, stopChan, *baseUrl, *maxNumbers, *availableLetters)
go signalStop(signalCh, stopChan)
go rateDisplay(counter)
wg.Wait()
close(results)
<-done
}