Last active
November 5, 2019 17:00
-
-
Save gen2brain/5ef30d8dd9e464df27a514106890af74 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"context" | |
"flag" | |
"fmt" | |
"io" | |
"log" | |
"net/http" | |
"os" | |
"runtime" | |
"strings" | |
"sync" | |
"time" | |
) | |
func main() { | |
flag.Usage = func() { | |
fmt.Fprintf(os.Stderr, "Usage: %s [<flags>] [<args> ...]\n", os.Args[0]) | |
flag.PrintDefaults() | |
} | |
conn := flag.Int("conn", runtime.NumCPU()+1, "Number of concurrent connections") | |
flag.Parse() | |
a := NewArgs() | |
args := a.Arguments(flag.Args()) | |
pool := make(chan int, *conn) | |
var wg sync.WaitGroup | |
f, err := os.OpenFile("broken.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) | |
if err != nil { | |
log.Println(err) | |
os.Exit(1) | |
} | |
defer f.Close() | |
client := &http.Client{ | |
CheckRedirect: func(req *http.Request, via []*http.Request) error { | |
if len(via) >= 10 { | |
return fmt.Errorf("%s stopped after 10 redirects", req.Host) | |
} | |
return nil | |
}, | |
} | |
for i, arg := range args { | |
pool <- 1 | |
wg.Add(1) | |
if !strings.HasPrefix(arg, "http://") || !strings.HasPrefix(arg, "https://") { | |
arg = "http://" + arg | |
} | |
go func(uri string, idx int) { | |
defer func() { | |
wg.Done() | |
<-pool | |
}() | |
req, err := http.NewRequest("HEAD", uri, nil) | |
if err != nil { | |
log.Println(uri, err) | |
return | |
} | |
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) | |
defer cancel() | |
req = req.WithContext(ctx) | |
res, err := client.Do(req) | |
if err != nil { | |
if strings.Contains(err.Error(), "10 redirects") { | |
f.WriteString(req.Host + "\n") | |
} | |
log.Println(uri, err) | |
return | |
} | |
res.Body.Close() | |
fmt.Println(i, uri, "DONE") | |
}(arg, i) | |
} | |
wg.Wait() | |
} | |
// Args type. | |
type Args struct{} | |
// NewArgs returns new Args. | |
func NewArgs() *Args { | |
return &Args{} | |
} | |
// Lines returns slice of lines from reader. | |
func (a *Args) Lines(r io.Reader) (l []string) { | |
scanner := bufio.NewScanner(r) | |
for scanner.Scan() { | |
text := scanner.Text() | |
if !strings.HasPrefix(text, "#") { // If not commented | |
l = append(l, text) | |
} | |
} | |
return | |
} | |
// Piped checks if we have a piped stdin. | |
func (a *Args) Piped() bool { | |
f, err := os.Stdin.Stat() | |
if err != nil { | |
return false | |
} | |
if f.Mode()&os.ModeNamedPipe == 0 { | |
return false | |
} else { | |
return true | |
} | |
} | |
// Arguments returns slice of arguments. | |
func (a *Args) Arguments(input []string) []string { | |
args := make([]string, 0) | |
if a.Piped() { // Piped stdin | |
l := a.Lines(os.Stdin) | |
args = append(args, l...) | |
} else { | |
for _, arg := range input { | |
if _, err := os.Stat(arg); err == nil { // If arg is file | |
f, _ := os.Open(arg) | |
l := a.Lines(f) | |
args = append(args, l...) | |
f.Close() | |
} else { | |
args = append(args, arg) | |
} | |
} | |
} | |
return args | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment