-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathCheckInternetAccess.go
197 lines (176 loc) · 4.82 KB
/
CheckInternetAccess.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package gonetcheck
/*********************************************************************
* gonetcheck - Go package to check general network health
*
* func: CheckInternetAccess
*
* Copyright 2013 Bradley Dean
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http:www.gnu.org/licenses/>.
*/
import (
"time"
"fmt"
)
// The final result to be put on the finalResultChan
type finalResult struct {
NetworkIsUp bool
Errors []error
}
// Determine if it looks like this server has access
// to the internet (ie remote servers)
func CheckInternetAccess(timeout time.Duration, testUrls, testTcpAddrs []string) (bool, []error) {
// This entire function has a timeout starting
// when the function is called
timeoutChan := time.After(timeout)
// All checking goroutines:
// 1. Register thei existence (ie number of checks) by dropping an int
// onto checkCountChan channel
// 2. Drop either a result or an error onto the resultChan or
// errorChan channels
// resultChan and errorChan channels are buffered to allow the check
// goroutines to be cleaned up
checkCountChan := make(chan int, 100)
resultChan := make(chan bool, 100)
errorChan := make(chan error, 100)
// Finally the end-result will be placed on these channels
finalResultChan := make(chan finalResult)
// The finalResultCheck channel acculates and finally
// calculates the final result
go finalResultCheck(
timeoutChan,
checkCountChan,
resultChan,
errorChan,
finalResultChan)
// Run checking goroutines
go runUrlChecks(testUrls, checkCountChan, resultChan, errorChan)
go runTcpChecks(testTcpAddrs, checkCountChan, resultChan, errorChan)
// Block until the finalResultChan receives a value
finalResult := <-finalResultChan
return finalResult.NetworkIsUp, finalResult.Errors
}
// Run all checkUrl checks and transpose UrlStat responses into
// the result/error channels
func runUrlChecks(
testUrls []string,
checkCountChan chan int,
resultChan chan bool,
errorChan chan error) {
checkUrlChan := make(chan UrlStat, 100)
// Launch checks
for _, url := range testUrls {
go checkUrl(url, checkUrlChan)
checkCountChan <- 1
}
// Process results into the resultChan
for {
stat := <-checkUrlChan
switch stat.Error {
case nil:
if stat.ResponseCode < 400 {
resultChan <- true
}
default:
errorChan <- stat.Error
}
}
}
func runTcpChecks(
testTcpAddrs []string,
checkCountChan chan int,
resultChan chan bool,
errorChan chan error) {
checkTcpChan := make(chan error, 100)
// Launch checks
for _, addr := range testTcpAddrs {
go checkTcp(addr, checkTcpChan)
checkCountChan <- 1
}
// Process results into the resultChan
for {
err := <-checkTcpChan
switch err {
case nil:
resultChan <- true
default:
errorChan <- err
}
}
}
// Collate errors and results from the resultChan and errorChan until the
// timeoutChan fires. Once this occurs calculate the final result and
// place it on the finalResultChan
func finalResultCheck(
timeoutChan <-chan time.Time,
checkCountChan chan int,
resultChan chan bool,
errorChan chan error,
finalResultChan chan finalResult) {
// Accumulators
var checkCount int
var successCount int
var failCount int
var errors []error
AccumulatorLoop:
for {
select {
case count := <-checkCountChan:
checkCount += count
case result := <-resultChan:
switch result {
case true:
successCount += 1
default:
failCount += 1
}
// If all checks are in, break
if successCount+failCount+len(errors) >= checkCount {
break AccumulatorLoop
}
case err := <-errorChan:
errors = append(errors, err)
// If all checks are in, break
if successCount+failCount+len(errors) >= checkCount {
break AccumulatorLoop
}
case <-timeoutChan:
break AccumulatorLoop
}
debugLog(
DBG_VERBOSE,
"CheckCount =", checkCount, ";",
"SuccessCount =", successCount, ";",
"FailCount =", failCount, ";",
"Errors =", errors, ";",
)
}
// Calculate the final result
var result finalResult
switch errors {
case nil:
upFraction := float32(successCount) / float32(checkCount)
result.NetworkIsUp = upFraction >= 0.5
result.Errors = nil
default:
result.NetworkIsUp = false
result.Errors = errors
}
debugLog(
DBG_VERBOSE,
"FinalResult =",
fmt.Sprintf("%+v", result),
)
finalResultChan <- result
}