aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/qna/qna.go151
1 files changed, 104 insertions, 47 deletions
diff --git a/internal/qna/qna.go b/internal/qna/qna.go
index d883016..3ad942c 100644
--- a/internal/qna/qna.go
+++ b/internal/qna/qna.go
@@ -2,10 +2,13 @@ package qna
import (
"context"
+ "crypto/tls"
"errors"
"fmt"
"net"
"net/url"
+ "os"
+ "strings"
)
type Question struct {
@@ -35,93 +38,147 @@ func (a Answer) String() string {
}
type Progress struct {
- NumReqErr int // error before sending requests
- NumDoErr int // error while sending request
- NumOK int // got response
- NumOnion int // found onion in response
-
- // More details about deadline errors while sending request
- NumDeadline int
-
- // More details about DNS errors while sending request
- NumDNSNotFound int
- NumDNSTimeout int
- NumDNSOther int
+ NumOK int
+ NumOnion int
+
+ NumMakeReqErr int
+ NumDNSNotFoundErr int
+ NumDNSTimeoutErr int
+ NumDNSOtherErr int
+ NumConnErr int
+ NumTLSCertErr int
+ NumTLSOtherErr int
+ Num3xxErr int
+ NumDeadlineErr int
+ NumOtherErr int
}
func (p Progress) String() string {
- str := fmt.Sprintf(" Processed: %d\n", p.NumAnswer())
+ str := fmt.Sprintf(" Processed: %d\n", p.NumProcess())
str += fmt.Sprintf(" Success: %d (Onion-Location:%d)\n", p.NumOK, p.NumOnion)
- str += fmt.Sprintf(" Failure: %d (Req:%d Do:%d)\n", p.NumError(), p.NumReqErr, p.NumDoErr)
- str += fmt.Sprintf(" CTX: %d (Deadline exceeded)\n", p.NumDeadline)
- str += fmt.Sprintf(" DNS: %d (NotFound:%d Timeout:%d Other:%d)",
- p.NumDNSErr(), p.NumDNSNotFound, p.NumDNSTimeout, p.NumDNSOther)
+ str += fmt.Sprintf(" Failure: %d (See breakdown below)\n", p.NumError())
+ str += fmt.Sprintf(" Req: %d (Before sending request)\n", p.NumMakeReqErr)
+ str += fmt.Sprintf(" DNS: %d (NotFound:%d Timeout:%d Other:%d)\n",
+ p.NumDNSErr(), p.NumDNSNotFoundErr, p.NumDNSTimeoutErr, p.NumDNSOtherErr)
+ str += fmt.Sprintf(" TCP: %d (Connection error)\n", p.NumConnErr)
+ str += fmt.Sprintf(" TLS: %d (Cert:%d Other:%d)\n", p.NumTLSErr(), p.NumTLSCertErr, p.NumTLSOtherErr)
+ str += fmt.Sprintf(" 3xx: %d (Too many redirects)\n", p.Num3xxErr)
+ str += fmt.Sprintf(" CTX: %d (Deadline exceeded)\n", p.NumDeadlineErr)
+ str += fmt.Sprintf(" ???: %d (Other errors)", p.NumOtherErr)
return str
}
-func (p *Progress) NumAnswer() int {
- return p.NumReqErr + p.NumDoErr + p.NumOK
+func (p *Progress) NumDNSErr() int {
+ return p.NumDNSNotFoundErr + p.NumDNSTimeoutErr + p.NumDNSOtherErr
+}
+
+func (p *Progress) NumTLSErr() int {
+ return p.NumTLSCertErr + p.NumTLSOtherErr
}
func (p *Progress) NumError() int {
- return p.NumReqErr + p.NumDoErr
+ return p.NumMakeReqErr + p.NumDNSErr() + p.NumConnErr + p.NumTLSErr() + p.Num3xxErr + p.NumDeadlineErr + p.NumOtherErr
}
-func (p *Progress) NumDNSErr() int {
- return p.NumDNSNotFound + p.NumDNSTimeout + p.NumDNSOther
+func (p *Progress) NumProcess() int {
+ return p.NumOK + p.NumError()
}
func (p *Progress) AddAnswer(a Answer) {
- if a.ReqErr != nil {
- p.NumReqErr += 1
+ if err := a.ReqErr; err != nil {
+ p.NumMakeReqErr++
return
}
- if a.DoErr != nil {
- p.NumDoErr += 1
- if isDeadlineError(a.DoErr) {
- p.NumDeadline += 1
- }
- if err := dnsError(a.DoErr); err != nil {
- if err.IsTimeout {
- p.NumDNSTimeout += 1
- } else if err.IsNotFound {
- p.NumDNSNotFound += 1
+ if err := a.DoErr; err != nil {
+ if e := dnsError(err); e != nil {
+ if e.IsTimeout {
+ p.NumDNSTimeoutErr++
+ } else if e.IsNotFound {
+ p.NumDNSNotFoundErr++
} else {
- p.NumDNSOther += 1
+ p.NumDNSOtherErr++
}
+ } else if isConnError(err) {
+ p.NumConnErr++
+ } else if isTLSError(err) {
+ if isTLSCertError(err) {
+ p.NumTLSCertErr++
+ } else {
+ p.NumTLSOtherErr++
+ }
+ } else if is3xxErr(err) {
+ p.Num3xxErr++
+ } else if isDeadlineError(err) {
+ p.NumDeadlineErr++
+ } else {
+ p.NumOtherErr++
}
return
}
-
- p.NumOK += 1
+ p.NumOK++
if a.HTTP != "" || a.HTML != "" {
- p.NumOnion += 1
+ p.NumOnion++
}
}
func dnsError(err error) *net.DNSError {
- if err == nil {
+ urlErr, ok := err.(*url.Error)
+ if !ok {
+ return nil
+ }
+ opErr, ok := urlErr.Err.(*net.OpError)
+ if !ok {
+ return nil
+ }
+ dnsErr, ok := opErr.Err.(*net.DNSError)
+ if !ok {
return nil
}
+ return dnsErr
+}
+func isConnError(err error) bool {
urlErr, ok := err.(*url.Error)
if !ok {
- return nil
+ return false
+ }
+ if urlErr.Err.Error() == "EOF" {
+ return true
}
+ opErr, ok := urlErr.Err.(*net.OpError)
+ if !ok {
+ return false
+ }
+ syscallErr, ok := opErr.Err.(*os.SyscallError)
+ if !ok {
+ return false
+ }
+ return syscallErr.Syscall == "connect" || syscallErr.Syscall == "read"
+}
- err = urlErr.Err
- opErr, ok := err.(*net.OpError)
+func isTLSError(err error) bool {
+ urlErr, ok := err.(*url.Error)
if !ok {
- return nil
+ return false
+ }
+ if urlErr.Error() == "http: server gave HTTP response to HTTPS client" {
+ return true
}
+ return strings.Contains(urlErr.Error(), "tls: ")
+}
- err = opErr.Err
- dnsErr, ok := err.(*net.DNSError)
+func isTLSCertError(err error) bool {
+ urlErr, ok := err.(*url.Error)
if !ok {
- return nil
+ return false
}
+ _, ok = urlErr.Err.(*tls.CertificateVerificationError)
+ return ok
+}
- return dnsErr
+func is3xxErr(err error) bool {
+ urlErr, ok := err.(*url.Error)
+ return ok && urlErr.Err.Error() == "stopped after 10 redirects"
}
func isDeadlineError(err error) bool {