forked from turbobytes/cdnfinder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfullfinder.go
120 lines (108 loc) · 2.93 KB
/
fullfinder.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
package cdnfinder
import (
"log"
"sort"
"sync"
"time"
)
// Hdr holds the name/value pair for http headers in output
// Need this to maintain api compatibility
type Hdr struct {
Name string `json:"name"`
Value string `json:"value"`
}
// FullResource is description of each individual resource
type FullResource struct {
Count int `json:"count"`
Bytes int `json:"bytes"`
IsBase bool `json:"isbase"`
Hostname string `json:"hostname"`
Headers []Hdr `json:"headers"`
CNAMES []string `json:"cnames"`
CDN *string `json:"cdn"`
HeaderGuess *string `json:"headerguess"`
}
// FullOutput is the result of FullFinder
type FullOutput struct {
BaseCDN *string `json:"basecdn"`
AssetCDN *string `json:"assetcdn"`
Resources []FullResource `json:"everything"`
}
// fullSort is intermediary type to make FullOutput.Resources sortable
type fullSort []FullResource
// Len satisfies sort.Sort interface
func (a fullSort) Len() int { return len(a) }
// Swap satisfies sort.Sort interface
func (a fullSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// Less satisfies sort.Sort interface
func (a fullSort) Less(i, j int) bool { return a[i].Count < a[j].Count }
type hostcdn struct {
cdn string
cnames []string
}
// parseraw populates CDN information to raw output from phantomjs
func parseraw(raw *rawDiscovery, server string, verbose bool) *FullOutput {
out := &FullOutput{
Resources: make([]FullResource, 0),
}
hostmap := make(map[string]hostcdn)
var wg sync.WaitGroup
mut := &sync.Mutex{}
for k := range raw.Resources {
wg.Add(1)
go func(k, server string) {
cdn, cnames, _ := HostnametoCDN(k, server)
mut.Lock()
hostmap[k] = hostcdn{cdn, cnames}
mut.Unlock()
wg.Done()
}(k, server)
}
wg.Wait()
//Populate resources
for k, v := range raw.Resources {
hm := hostmap[k]
cdn := hm.cdn
cnames := hm.cnames
res := FullResource{
Count: v.Count,
Bytes: v.Bytes,
IsBase: v.IsBase,
Headers: make([]Hdr, 0),
Hostname: k,
CNAMES: cnames,
}
for key := range *v.Headers {
res.Headers = append(res.Headers, Hdr{key, v.Headers.Get(key)})
}
if cdn != "" {
res.CDN = &cdn
}
//Header Guess
res.HeaderGuess = headerguess(v.Headers)
if res.CDN == nil {
res.CDN = res.HeaderGuess
}
if k == raw.BasePageHost && res.CDN != nil {
out.BaseCDN = res.CDN
}
out.Resources = append(out.Resources, res)
}
sort.Sort(sort.Reverse(fullSort(out.Resources)))
//Most popular hostname by count decides AssetCDN
if len(out.Resources) > 0 {
out.AssetCDN = out.Resources[0].CDN
}
return out
}
// FullFinder detects the CDN(s) used by a url by loading it in browser
func FullFinder(url, server string, timeout time.Duration, verbose bool) (*FullOutput, error) {
raw, err := discoverResources(url, timeout, verbose)
if err != nil {
return nil, err
}
if verbose {
log.Println(raw)
}
return parseraw(raw, server, verbose), nil
}