Skip to content

Instantly share code, notes, and snippets.

@gen2brain
Last active November 5, 2019 17:00
Show Gist options
  • Save gen2brain/5ef30d8dd9e464df27a514106890af74 to your computer and use it in GitHub Desktop.
Save gen2brain/5ef30d8dd9e464df27a514106890af74 to your computer and use it in GitHub Desktop.
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