-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathexample.Goroutine_Http.go
More file actions
105 lines (89 loc) · 3.06 KB
/
example.Goroutine_Http.go
File metadata and controls
105 lines (89 loc) · 3.06 KB
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
// example.Goroutine_Http — DeathByCaptcha Go client, concurrent HTTP usage.
//
// Demonstrates the recommended pattern for concurrent use of HttpClient:
// each goroutine creates its own client instance, which avoids any shared
// connection state. Go's net/http.Client is also safe for shared use, but
// one-instance-per-goroutine gives the clearest ownership and is idiomatic
// in high-throughput producer/consumer pipelines.
//
// Usage:
//
// DBC_USERNAME=user DBC_PASSWORD=pass go run example.Goroutine_Http.go
// DBC_USERNAME=user DBC_PASSWORD=pass DBC_THREADS=4 DBC_IMAGE_PATH=images/normal.jpg \
// go run example.Goroutine_Http.go
package main
import (
"errors"
"fmt"
"log"
"os"
"strconv"
"sync"
dbc "github.com/deathbycaptcha/deathbycaptcha-api-client-go/v4/deathbycaptcha"
)
// httpWorker runs in its own goroutine and owns its own HttpClient instance.
func httpWorker(username, password, imagePath string, decodeMode bool, workerID int, wg *sync.WaitGroup) {
defer wg.Done()
// Each goroutine creates its own client — no shared connection state.
client := dbc.NewHttpClient(username, password)
defer client.Close()
if !decodeMode {
bal, err := client.GetBalance()
if err != nil {
fmt.Fprintf(os.Stderr, "[HTTP goroutine %d] error: %v\n", workerID, err)
return
}
fmt.Printf("[HTTP goroutine %d] balance: %.4f US cents\n", workerID, bal)
return
}
captcha, err := client.Decode(imagePath, dbc.DefaultTimeout, map[string]string{})
if err != nil {
var ade *dbc.AccessDeniedException
if errors.As(err, &ade) {
fmt.Fprintf(os.Stderr, "[HTTP goroutine %d] access denied: %v\n", workerID, err)
} else {
fmt.Fprintf(os.Stderr, "[HTTP goroutine %d] error: %v\n", workerID, err)
}
return
}
if captcha == nil {
fmt.Printf("[HTTP goroutine %d] timeout — no solution received\n", workerID)
return
}
fmt.Printf("[HTTP goroutine %d] solved CAPTCHA %d: %s\n", workerID, captcha.CaptchaID, *captcha.Text)
// Uncomment to report an incorrect solution:
// client.Report(captcha.CaptchaID)
}
func main() {
username := os.Getenv("DBC_USERNAME")
password := os.Getenv("DBC_PASSWORD")
imagePath := os.Getenv("DBC_IMAGE_PATH")
threadsEnv := os.Getenv("DBC_THREADS")
if username == "" || password == "" {
log.Fatal("Set DBC_USERNAME and DBC_PASSWORD environment variables before running.")
}
goroutineCount := 2
if threadsEnv != "" {
n, err := strconv.Atoi(threadsEnv)
if err != nil || n <= 0 {
log.Fatal("DBC_THREADS must be a positive integer.")
}
goroutineCount = n
}
decodeMode := imagePath != ""
// Sanity-check credentials on the main goroutine before spawning workers.
sanity := dbc.NewHttpClient(username, password)
bal, err := sanity.GetBalance()
sanity.Close()
if err != nil {
log.Fatalf("[HTTP main] credential check failed: %v", err)
}
fmt.Printf("[HTTP main] initial balance: %.4f US cents\n", bal)
var wg sync.WaitGroup
wg.Add(goroutineCount)
for i := 1; i <= goroutineCount; i++ {
go httpWorker(username, password, imagePath, decodeMode, i, &wg)
}
wg.Wait()
fmt.Println("[HTTP main] all goroutines finished.")
}