<commit_before>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"log"
	"mime"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
)

type file struct {
	path         string
	absPath      string
	size         int64
	lastModified time.Time
}

// walk a local directory
func walk(basePath string, files chan<- file) {
	filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			// skip hidden directories like .git
			if strings.HasPrefix(info.Name(), ".") {
				return filepath.SkipDir
			}
		} else {
			if info.Name() == ".DS_Store" {
				return nil
			}
			abs, err := filepath.Abs(path)
			rel, err := filepath.Rel(basePath, path)
			if err != nil {
				return err
			}
			files <- file{path: rel, absPath: abs, size: info.Size(), lastModified: info.ModTime()}
		}
		return nil
	})
	close(files)
}

func plan(sourcePath string, destBucket *s3.Bucket, uploadFiles chan<- file) {
	contents, err := destBucket.GetBucketContents()
	if err != nil {
		log.Fatal(err)
	}
	remoteFiles := *contents

	localFiles := make(chan file)
	go walk(sourcePath, localFiles)

	for f := range localFiles {
		up := true
		reason := "not found"

		if key, ok := remoteFiles[f.path]; ok {
			up, reason = shouldOverwrite(f, key)
			// remove from map, whatever is leftover should be deleted:
			delete(remoteFiles, f.path)
		}

		if up {
			fmt.Printf("%s %s, uploading.\n", f.path, reason)
			uploadFiles <- f
		} else {
			fmt.Printf("%s %s, skipping.\n", f.path, reason)
		}
	}
	close(uploadFiles)

	var filesToDelete = make([]string, 0, len(remoteFiles))
	for key := range remoteFiles {
		fmt.Printf("%s not found in source, deleting.\n", key)
		filesToDelete = append(filesToDelete, key)
	}
	cleanup(filesToDelete, destBucket)
}

// can use size and time (or possibly md5) to determine what needs to upload
func shouldOverwrite(source file, dest s3.Key) (bool, string) {
	if source.size != dest.Size {
		return true, "file size mismatch"
	}

	sourceMod := source.lastModified.UTC().Format(time.RFC3339)
	if sourceMod == dest.LastModified {
		// no need to upload if times match, but different times may just be drift
		return false, "last modified match"
	}

	etag, err := calculateETag(source.absPath)
	if err != nil {
		log.Fatalf("Error calculating ETag for %s: %v", source.absPath, err)
	}
	if dest.ETag == etag {
		return false, "etags match"
	}
	return true, "etags mismatch"
}

func calculateETag(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	h := md5.New()
	_, err = io.Copy(h, f)
	if err != nil {
		return "", err
	}
	etag := fmt.Sprintf("\"%x\"", h.Sum(nil))
	return etag, nil
}

func upload(source file, destBucket *s3.Bucket) error {
	f, err := os.Open(source.absPath)
	if err != nil {
		return err
	}
	defer f.Close()

	contentType := mime.TypeByExtension(filepath.Ext(source.path))
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	return destBucket.PutReader(source.path, f, source.size, contentType, "public-read")
}

func cleanup(paths []string, destBucket *s3.Bucket) error {
	// only can delete 1000 at a time, TODO: split if needed
	return destBucket.MultiDel(paths)
}

var wg sync.WaitGroup

// worker uploads files
func worker(filesToUpload <-chan file, destBucket *s3.Bucket) {
	for f := range filesToUpload {
		err := upload(f, destBucket)
		if err != nil {
			fmt.Printf("Error uploading %s: %s\n", f.path, err)
		}
	}

	wg.Done()
}

func main() {
	var accessKey, secretKey, sourcePath, regionName, bucketName string
	var numberOfWorkers int
	var help bool
	flag.StringVar(&accessKey, "key", "", "Access Key ID for AWS")
	flag.StringVar(&secretKey, "secret", "", "Secret Access Key for AWS")
	flag.StringVar(&regionName, "region", "us-east-1", "Name of region for AWS")
	flag.StringVar(&bucketName, "bucket", "", "Destination bucket name on AWS")
	flag.StringVar(&sourcePath, "source", ".", "path of files to upload")
	flag.IntVar(&numberOfWorkers, "workers", 10, "number of workers to upload files")
	flag.BoolVar(&help, "h", false, "help")
	flag.Parse()

	if help {
		flag.Usage()
		return
	}

	if accessKey == "" || secretKey == "" {
		fmt.Println("AWS key and secret are required.")
		flag.Usage()
		os.Exit(1)
	}

	if bucketName == "" {
		fmt.Println("AWS bucket is required.")
		flag.Usage()
		os.Exit(1)
	}

	auth := aws.Auth{AccessKey: accessKey, SecretKey: secretKey}
	region := aws.Regions[regionName]
	s := s3.New(auth, region)
	b := s.Bucket(bucketName)

	filesToUpload := make(chan file)
	for i := 0; i < numberOfWorkers; i++ {
		wg.Add(1)
		go worker(filesToUpload, b)
	}
	plan(sourcePath, b, filesToUpload)
	wg.Wait()
}
<commit_msg>shuffle code around and add some comments<commit_after>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"log"
	"mime"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/mitchellh/goamz/aws" // http://gopkg.in/amz.v2
	"github.com/mitchellh/goamz/s3"
)

type file struct {
	path         string // relative path
	absPath      string // absolute path
	size         int64
	lastModified time.Time
}

var wg sync.WaitGroup

func main() {
	var accessKey, secretKey, sourcePath, regionName, bucketName string
	var numberOfWorkers int
	var help bool

	// Usage example:
	// s3up -source=public/ -bucket=origin.edmontongo.org -key=$AWS_ACCESS_KEY_ID -secret=$AWS_SECRET_ACCESS_KEY

	flag.StringVar(&accessKey, "key", "", "Access Key ID for AWS")
	flag.StringVar(&secretKey, "secret", "", "Secret Access Key for AWS")
	flag.StringVar(&regionName, "region", "us-east-1", "Name of region for AWS")
	flag.StringVar(&bucketName, "bucket", "", "Destination bucket name on AWS")
	flag.StringVar(&sourcePath, "source", ".", "path of files to upload")
	flag.IntVar(&numberOfWorkers, "workers", 10, "number of workers to upload files")
	flag.BoolVar(&help, "h", false, "help")

	flag.Parse()

	if help {
		flag.Usage()
		return
	}

	if accessKey == "" || secretKey == "" {
		fmt.Println("AWS key and secret are required.")
		flag.Usage()
		os.Exit(1)
	}

	if bucketName == "" {
		fmt.Println("AWS bucket is required.")
		flag.Usage()
		os.Exit(1)
	}

	// Get bucket with bucketName
	auth := aws.Auth{AccessKey: accessKey, SecretKey: secretKey}
	region := aws.Regions[regionName]
	s := s3.New(auth, region)
	b := s.Bucket(bucketName)

	filesToUpload := make(chan file)
	for i := 0; i < numberOfWorkers; i++ {
		wg.Add(1)
		go worker(filesToUpload, b)
	}

	plan(sourcePath, b, filesToUpload)
	wg.Wait()
}

// plan figures out which files need to be uploaded.
func plan(sourcePath string, destBucket *s3.Bucket, uploadFiles chan<- file) {
	// List all files in the remote bucket
	contents, err := destBucket.GetBucketContents()
	if err != nil {
		log.Fatal(err)
	}
	remoteFiles := *contents

	// All local files at sourcePath
	localFiles := make(chan file)
	go walk(sourcePath, localFiles)

	for f := range localFiles {
		// default: upload because local file not found on remote.
		up := true
		reason := "not found"

		if key, ok := remoteFiles[f.path]; ok {
			up, reason = shouldOverwrite(f, key)
			// remove from map, whatever is leftover should be deleted:
			delete(remoteFiles, f.path)
		}

		if up {
			fmt.Printf("%s %s, uploading.\n", f.path, reason)
			uploadFiles <- f
		} else {
			fmt.Printf("%s %s, skipping.\n", f.path, reason)
		}
	}
	close(uploadFiles)

	// any remote files not found locally should be removed:
	var filesToDelete = make([]string, 0, len(remoteFiles))
	for key := range remoteFiles {
		fmt.Printf("%s not found in source, deleting.\n", key)
		filesToDelete = append(filesToDelete, key)
	}
	cleanup(filesToDelete, destBucket)
}

// walk a local directory
func walk(basePath string, files chan<- file) {
	filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			// skip hidden directories like .git
			if strings.HasPrefix(info.Name(), ".") {
				return filepath.SkipDir
			}
		} else {
			if info.Name() == ".DS_Store" {
				return nil
			}
			abs, err := filepath.Abs(path)
			if err != nil {
				return err
			}
			rel, err := filepath.Rel(basePath, path)
			if err != nil {
				return err
			}
			files <- file{path: rel, absPath: abs, size: info.Size(), lastModified: info.ModTime()}
		}
		return nil
	})
	close(files)
}

// shouldOverwrite uses size or md5 to determine what needs to be uploaded
func shouldOverwrite(source file, dest s3.Key) (up bool, reason string) {
	if source.size != dest.Size {
		return true, "file size mismatch"
	}

	sourceMod := source.lastModified.UTC().Format(time.RFC3339)
	if sourceMod == dest.LastModified {
		// no need to upload if times match, but different times may just be drift
		return false, "last modified match"
	}

	etag, err := calculateETag(source.absPath)
	if err != nil {
		log.Fatalf("Error calculating ETag for %s: %v", source.absPath, err)
	}
	if dest.ETag == etag {
		return false, "etags match"
	}
	return true, "etags mismatch"
}

func calculateETag(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	h := md5.New()
	_, err = io.Copy(h, f)
	if err != nil {
		return "", err
	}
	etag := fmt.Sprintf("\"%x\"", h.Sum(nil))
	return etag, nil
}

func cleanup(paths []string, destBucket *s3.Bucket) error {
	// only can delete 1000 at a time, TODO: split if needed
	return destBucket.MultiDel(paths)
}

// worker uploads files
func worker(filesToUpload <-chan file, destBucket *s3.Bucket) {
	for f := range filesToUpload {
		err := upload(f, destBucket)
		if err != nil {
			fmt.Printf("Error uploading %s: %s\n", f.path, err)
		}
	}

	wg.Done()
}

func upload(source file, destBucket *s3.Bucket) error {
	f, err := os.Open(source.absPath)
	if err != nil {
		return err
	}
	defer f.Close()

	contentType := mime.TypeByExtension(filepath.Ext(source.path))
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	return destBucket.PutReader(source.path, f, source.size, contentType, "public-read")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
)

var options struct {
	dir string
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage:  %s [options] command\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.StringVar(&options.dir, "dir", ".", "directory to watch")
	flag.Parse()

	cmd := flag.Args()
	if len(cmd) == 0 {
		flag.Usage()
		os.Exit(1)
	}

	watcher, err := NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(options.dir)
	if err != nil {
		log.Fatal(err)
	}

	var nowRunning *Process
	nowRunning, err = StartProcess(cmd)
	if err != nil {
		log.Fatal(err)
	}

	for {
		select {
		case event := <-watcher.Events:
			log.Println(event)
			err = nowRunning.Restart()
			if err != nil {
				log.Println("error:", err)
			}
		case err := <-watcher.Errors:
			log.Println("error:", err)
		}
	}
}
<commit_msg>Watch multiple directories<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
)

var options struct {
	dir string
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage:  %s [options] command\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.StringVar(&options.dir, "dir", ".", "directories to watch (separate multiple directories with commas)")
	flag.Parse()

	cmd := flag.Args()
	if len(cmd) == 0 {
		flag.Usage()
		os.Exit(1)
	}

	watcher, err := NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	dirs := strings.Split(options.dir, ",")
	for _, d := range dirs {
		err = watcher.Add(d)
		if err != nil {
			log.Fatal(err)
		}
	}

	var nowRunning *Process
	nowRunning, err = StartProcess(cmd)
	if err != nil {
		log.Fatal(err)
	}

	for {
		select {
		case event := <-watcher.Events:
			log.Println(event)
			err = nowRunning.Restart()
			if err != nil {
				log.Println("error:", err)
			}
		case err := <-watcher.Errors:
			log.Println("error:", err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"encoding/base64"
	"encoding/json"

	"github.com/abbot/go-http-auth"
	"github.com/dchest/uniuri"
	"github.com/zenazn/goji/bind"
	"goji.io"
	"goji.io/pat"
)

const cookieName string = "_htpasswd_auth"
const sessionFormat string = time.RFC3339
const slack time.Duration = time.Duration(10) * time.Second

var sessionDir string
var domain string
var htpasswd string
var realm string
var cookieLifetime int

var validChars map[rune]bool

func init() {
	validChars = map[rune]bool{}
	for _, c := range string(uniuri.StdChars) {
		validChars[c] = true
	}
}

type session struct {
	CreatedRaw string `json:"created"`
	Domain     string

	Name    string    `json:"-"`
	Created time.Time `json:"-"`
}

func (s *session) fill(name string) error {
	t, err := time.Parse(sessionFormat, s.CreatedRaw)
	if err != nil {
		return err
	}
	s.Created = t
	s.Name = name
	return nil
}

func (s *session) Valid() bool {
	sessionExpiry := s.Created.Add(time.Duration(cookieLifetime) * time.Second)
	return sessionExpiry.After(time.Now())
}
func validateCookie(cookie string) (*session, error) {
	for _, c := range cookie {
		if _, ok := validChars[c]; !ok {
			return nil, errors.New("Cookie value contains invalid characters.")
		}
	}
	sessionPath := filepath.Join(sessionDir, cookie)
	base, err := filepath.Rel(sessionDir, sessionPath)
	if err != nil {
		return nil, err
	}
	if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "/") {
		return nil, errors.New("Cookie value would traverse directories")
	}

	// From here on, the session pathname is assumed innocent & handle-able.
	data, err := ioutil.ReadFile(sessionPath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Printf("Request for a session that doesn't exist: %q", sessionPath)
			return nil, err
		} else {
			log.Printf("Checking session %q failed: %s", sessionPath, err)
			return nil, err
		}
	}
	session := session{}
	json.Unmarshal(data, &session)
	err = session.fill(cookie)
	if err != nil {
		os.Remove(sessionPath) // clean up the session
		return nil, err
	}
	if !session.Valid() {
		return nil, errors.New("Session is no longer valid.")
	}
	return &session, nil
}

func invalidateCookie() *http.Cookie {
	return &http.Cookie{Name: cookieName, Value: "nope", Domain: domain, Path: "/", MaxAge: -1, Secure: true, HttpOnly: true}
}

func checkSession(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie(cookieName)
	newCookie := invalidateCookie()
	success := false

	// Cleanup and ensure we always send a decent response:
	defer func() {
		if !success {
			http.SetCookie(w, newCookie)
			http.Error(w, "Session is not/no longer valid", http.StatusUnauthorized)
			return
		}
		w.WriteHeader(200)
		w.Write([]byte{})
	}()
	if err != nil {
		if err == http.ErrNoCookie {
			return
		}
	}
	session, err := validateCookie(cookie.Value)
	if err != nil {
		log.Printf("Error validating session: %s", err)
		return
	}

	if session.Domain != r.Host {
		log.Printf("Session %q was made for another domain: %s; wanted %q", cookie.Value, session.Domain, r.Host)
		return
	}

	// We have a valid session that hasn't yet expired. Let's call it a success.
	success = true
	return
}

func newSession(domain string) (string, error) {
	name := uniuri.NewLen(90)
	sessionPath := filepath.Join(sessionDir, name)
	data, err := json.Marshal(session{CreatedRaw: time.Now().Add(slack).Format(sessionFormat), Domain: domain})
	if err != nil {
		return "", err
	}
	err = ioutil.WriteFile(sessionPath, data, 0400)
	if err != nil {
		return "", err
	}
	return name, nil
}

func login(w http.ResponseWriter, r *http.Request) {
	newCookie := invalidateCookie()
	success := false
	defer func() {
		http.SetCookie(w, newCookie)
		if !success {
			http.Error(w, "Nope", http.StatusUnauthorized)
			return
		}
	}()
	user := r.PostFormValue("login")
	if len(user) == 0 {
		return
	}

	password := r.PostFormValue("password")
	if len(password) == 0 {
		return
	}

	log.Printf("Validating login request by user %q on %q", user, r.Host)

	// This is very awful, but sadly go-http-auth has no better interface:
	authenticator := auth.NewBasicAuthenticator(realm, auth.HtpasswdFileProvider(htpasswd))
	r.Header["Authorization"] = []string{
		fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", user, password)))),
	}
	if user != authenticator.CheckAuth(r) {
		log.Printf("User %q couldn't be authenticated", user)
		return
	}

	// OK, we're authenticated. Let's get a session started:
	session, err := newSession(r.Host)
	if err != nil {
		return
	}
	newCookie.Value = session
	newCookie.MaxAge = cookieLifetime
	if r.PostFormValue("ephemeral") == "" {
		newCookie.Expires = time.Now().Add(time.Duration(cookieLifetime) * time.Second)
	}
	success = true
}

func runCleanup() {
	deleted := 0
	err := filepath.Walk(sessionDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}
		if info.IsDir() {
			if path != sessionDir {
				return filepath.SkipDir
			} else {
				return nil
			}
		}
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		var sess session
		err = json.Unmarshal(data, &sess)
		if err != nil {
			fmt.Printf("Session %q was invalid JSON (%s), leaving it", path, err)
			return nil
		}
		if err = sess.fill(filepath.Base(path)); err != nil {
			fmt.Printf("Session %q has an invalid date (%s), deleting it", path, err)
			return os.Remove(path)
		}
		if !sess.Valid() {
			deleted += 1
			return os.Remove(path)
		}
		return nil
	})
	if err != nil {
		log.Printf("Couldn't delete sessions: %s", err)
		os.Exit(2)
	}
	if deleted > 0 {
		log.Printf("Cleaned out %d sessions from %q.", deleted, sessionDir)
	}
}

func main() {
	var cleanup bool

	bind.DefaultBind = "127.0.0.1:8000"
	bind.WithFlag()
	flag.StringVar(&sessionDir, "sessions", "/var/db/http-auth/cookies", "Directory in which htpasswd-login-form places sessions")
	flag.StringVar(&domain, "domain", "", "Domain to set on all cookies")
	flag.StringVar(&htpasswd, "htpasswd", "/etc/nginx/.htpasswd", "htpasswd file to use for authentication")
	flag.StringVar(&realm, "realm", "example.com", "HTTP Basic auth realm to pretend we run in")
	flag.IntVar(&cookieLifetime, "lifetime", 86400, "Maximum cookie lifetime in seconds")
	flag.BoolVar(&cleanup, "cleanup", false, "Perform once-in-a-while cleanup actions")
	flag.Parse()

	if cleanup {
		runCleanup()
		return
	}

	mux := goji.NewMux()
	mux.HandleFunc(pat.Get("/auth"), checkSession)
	mux.HandleFunc(pat.Post("/login"), login)

	http.Serve(bind.Default(), mux)
}
<commit_msg>Add -secure flag (default to true)<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"encoding/base64"
	"encoding/json"

	"github.com/abbot/go-http-auth"
	"github.com/dchest/uniuri"
	"github.com/zenazn/goji/bind"
	"goji.io"
	"goji.io/pat"
)

const cookieName string = "_htpasswd_auth"
const sessionFormat string = time.RFC3339
const slack time.Duration = time.Duration(10) * time.Second

var sessionDir string
var domain string
var htpasswd string
var realm string
var cookieLifetime int
var secure bool

var validChars map[rune]bool

func init() {
	validChars = map[rune]bool{}
	for _, c := range string(uniuri.StdChars) {
		validChars[c] = true
	}
}

type session struct {
	CreatedRaw string `json:"created"`
	Domain     string

	Name    string    `json:"-"`
	Created time.Time `json:"-"`
}

func (s *session) fill(name string) error {
	t, err := time.Parse(sessionFormat, s.CreatedRaw)
	if err != nil {
		return err
	}
	s.Created = t
	s.Name = name
	return nil
}

func (s *session) Valid() bool {
	sessionExpiry := s.Created.Add(time.Duration(cookieLifetime) * time.Second)
	return sessionExpiry.After(time.Now())
}
func validateCookie(cookie string) (*session, error) {
	for _, c := range cookie {
		if _, ok := validChars[c]; !ok {
			return nil, errors.New("Cookie value contains invalid characters.")
		}
	}
	sessionPath := filepath.Join(sessionDir, cookie)
	base, err := filepath.Rel(sessionDir, sessionPath)
	if err != nil {
		return nil, err
	}
	if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "/") {
		return nil, errors.New("Cookie value would traverse directories")
	}

	// From here on, the session pathname is assumed innocent & handle-able.
	data, err := ioutil.ReadFile(sessionPath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Printf("Request for a session that doesn't exist: %q", sessionPath)
			return nil, err
		} else {
			log.Printf("Checking session %q failed: %s", sessionPath, err)
			return nil, err
		}
	}
	session := session{}
	json.Unmarshal(data, &session)
	err = session.fill(cookie)
	if err != nil {
		os.Remove(sessionPath) // clean up the session
		return nil, err
	}
	if !session.Valid() {
		return nil, errors.New("Session is no longer valid.")
	}
	return &session, nil
}

func invalidateCookie() *http.Cookie {
	return &http.Cookie{Name: cookieName, Value: "nope", Domain: domain, Path: "/", MaxAge: -1, Secure: secure, HttpOnly: true}
}

func checkSession(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie(cookieName)
	newCookie := invalidateCookie()
	success := false

	// Cleanup and ensure we always send a decent response:
	defer func() {
		if !success {
			http.SetCookie(w, newCookie)
			http.Error(w, "Session is not/no longer valid", http.StatusUnauthorized)
			return
		}
		w.WriteHeader(200)
		w.Write([]byte{})
	}()
	if err != nil {
		if err == http.ErrNoCookie {
			return
		}
	}
	session, err := validateCookie(cookie.Value)
	if err != nil {
		log.Printf("Error validating session: %s", err)
		return
	}

	if session.Domain != r.Host {
		log.Printf("Session %q was made for another domain: %s; wanted %q", cookie.Value, session.Domain, r.Host)
		return
	}

	// We have a valid session that hasn't yet expired. Let's call it a success.
	success = true
	return
}

func newSession(domain string) (string, error) {
	name := uniuri.NewLen(90)
	sessionPath := filepath.Join(sessionDir, name)
	data, err := json.Marshal(session{CreatedRaw: time.Now().Add(slack).Format(sessionFormat), Domain: domain})
	if err != nil {
		return "", err
	}
	err = ioutil.WriteFile(sessionPath, data, 0400)
	if err != nil {
		return "", err
	}
	return name, nil
}

func login(w http.ResponseWriter, r *http.Request) {
	newCookie := invalidateCookie()
	success := false
	defer func() {
		http.SetCookie(w, newCookie)
		if !success {
			http.Error(w, "Nope", http.StatusUnauthorized)
			return
		}
	}()
	user := r.PostFormValue("login")
	if len(user) == 0 {
		return
	}

	password := r.PostFormValue("password")
	if len(password) == 0 {
		return
	}

	log.Printf("Validating login request by user %q on %q", user, r.Host)

	// This is very awful, but sadly go-http-auth has no better interface:
	authenticator := auth.NewBasicAuthenticator(realm, auth.HtpasswdFileProvider(htpasswd))
	r.Header["Authorization"] = []string{
		fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", user, password)))),
	}
	if user != authenticator.CheckAuth(r) {
		log.Printf("User %q couldn't be authenticated", user)
		return
	}

	// OK, we're authenticated. Let's get a session started:
	session, err := newSession(r.Host)
	if err != nil {
		return
	}
	newCookie.Value = session
	newCookie.MaxAge = cookieLifetime
	if r.PostFormValue("ephemeral") == "" {
		newCookie.Expires = time.Now().Add(time.Duration(cookieLifetime) * time.Second)
	}
	success = true
}

func runCleanup() {
	deleted := 0
	err := filepath.Walk(sessionDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}
		if info.IsDir() {
			if path != sessionDir {
				return filepath.SkipDir
			} else {
				return nil
			}
		}
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		var sess session
		err = json.Unmarshal(data, &sess)
		if err != nil {
			fmt.Printf("Session %q was invalid JSON (%s), leaving it", path, err)
			return nil
		}
		if err = sess.fill(filepath.Base(path)); err != nil {
			fmt.Printf("Session %q has an invalid date (%s), deleting it", path, err)
			return os.Remove(path)
		}
		if !sess.Valid() {
			deleted += 1
			return os.Remove(path)
		}
		return nil
	})
	if err != nil {
		log.Printf("Couldn't delete sessions: %s", err)
		os.Exit(2)
	}
	if deleted > 0 {
		log.Printf("Cleaned out %d sessions from %q.", deleted, sessionDir)
	}
}

func main() {
	var cleanup bool

	bind.DefaultBind = "127.0.0.1:8000"
	bind.WithFlag()
	flag.StringVar(&sessionDir, "sessions", "/var/db/http-auth/cookies", "Directory in which htpasswd-login-form places sessions")
	flag.StringVar(&domain, "domain", "", "Domain to set on all cookies")
	flag.StringVar(&htpasswd, "htpasswd", "/etc/nginx/.htpasswd", "htpasswd file to use for authentication")
	flag.StringVar(&realm, "realm", "example.com", "HTTP Basic auth realm to pretend we run in")
	flag.IntVar(&cookieLifetime, "lifetime", 86400, "Maximum cookie lifetime in seconds")
	flag.BoolVar(&secure, "secure", true, "Whether to set cookies to secure (false is useful for dev)")

	flag.BoolVar(&cleanup, "cleanup", false, "Perform once-in-a-while cleanup actions")
	flag.Parse()

	if cleanup {
		runCleanup()
		return
	}

	mux := goji.NewMux()
	mux.HandleFunc(pat.Get("/auth"), checkSession)
	mux.HandleFunc(pat.Post("/login"), login)

	http.Serve(bind.Default(), mux)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
)

const (
	HTTPS_TEMPLATE = `` +
		`  DNS Lookup   TCP Connection   TLS Handshake   Server Processing   Content Transfer` + "\n" +
		`[%s  |     %s  |    %s  |        %s  |       %s  ]` + "\n" +
		`            |                |               |                   |                  |` + "\n" +
		`   namelookup:%s      |               |                   |                  |` + "\n" +
		`                       connect:%s     |                   |                  |` + "\n" +
		`                                   pretransfer:%s         |                  |` + "\n" +
		`                                                     starttransfer:%s        |` + "\n" +
		`                                                                                total:%s` + "\n"

	HTTP_TEMPLATE = `` +
		`   DNS Lookup   TCP Connection   Server Processing   Content Transfer` + "\n" +
		`[ %s  |     %s  |        %s  |       %s  ]` + "\n" +
		`             |                |                   |                  |` + "\n" +
		`    namelookup:%s      |                   |                  |` + "\n" +
		`                        connect:%s         |                  |` + "\n" +
		`                                      starttransfer:%s        |` + "\n" +
		`                                                                 total:%s` + "\n"
)

var (
	requestBody io.Reader

	grayscale = func(code int) func(string) string {
		if color.NoColor {
			return func(s string) string { return s }
		}
		return func(s string) string {
			return fmt.Sprintf("\x1b[;38;5;%dm%s\x1b[0m", code+232, s)
		}
	}

	// Command line flags.
	httpMethod      string
	postBody        string
	followRedirects bool
	onlyHeader      bool
	insecure        bool
	httpHeaders     headers
	saveOutput      bool
	outputFile      string

	// number of redirects followed
	redirectsFollowed int

	usage = fmt.Sprintf("usage: %s URL", os.Args[0])
)

const maxRedirects = 10

func init() {
	flag.StringVar(&httpMethod, "X", "GET", "HTTP method to use")
	flag.StringVar(&postBody, "d", "", "the body of a POST or PUT request")
	flag.BoolVar(&followRedirects, "L", false, "follow 30x redirects")
	flag.BoolVar(&onlyHeader, "I", false, "don't read body of request")
	flag.BoolVar(&insecure, "k", false, "allow insecure SSL connections")
	flag.Var(&httpHeaders, "H", "HTTP Header(s) to set. Can be used multiple times. -H 'Accept:...' -H 'Range:....'")
	flag.BoolVar(&saveOutput, "O", false, "Save body as remote filename")
	flag.StringVar(&outputFile, "o", "", "output file for body")

	flag.Usage = func() {
		os.Stderr.WriteString(usage + "\n")
		flag.PrintDefaults()
		os.Exit(2)
	}
}

func printf(format string, a ...interface{}) (n int, err error) {
	if color.Output == os.Stdout {
		return fmt.Printf(format, a...)
	}
	return fmt.Fprintf(color.Output, format, a...)
}

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) != 1 {
		flag.Usage()
	}

	if (httpMethod == "POST" || httpMethod == "PUT") && postBody == "" {
		log.Fatal("must supply post body using -d when POST or PUT is used")
	}

	url, err := url.Parse(args[0])
	if err != nil {
		log.Fatalf("could not parse url %q: %v", args[0], err)
	}
	visit(url)
}

func headerKeyValue(h string) (string, string) {
	i := strings.Index(h, ":")
	if i == -1 {
		log.Fatalf("Header '%s' has invalid format, missing ':'", h)
	}
	return strings.TrimRight(h[:i], " "), strings.TrimLeft(h[i:], " :")
}

// visit visits a url and times the interaction.
// If the response is a 30x, visit follows the redirect.
func visit(url *url.URL) {
	scheme := url.Scheme
	hostport := url.Host
	host, port := func() (string, string) {
		host, port, err := net.SplitHostPort(hostport)
		if err != nil {
			host = hostport
		}
		switch scheme {
		case "https":
			if port == "" {
				port = "443"
			}
		case "http":
			if port == "" {
				port = "80"
			}
		default:
			log.Fatalf("unsupported url scheme %q", scheme)
		}
		return host, port
	}()

	t0 := time.Now() // before dns resolution
	raddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%s", host, port))
	if err != nil {
		log.Fatalf("unable to resolve host: %v", err)
	}

	var conn net.Conn
	t1 := time.Now() // after dns resolution, before connect
	conn, err = net.DialTCP("tcp", nil, raddr)
	if err != nil {
		log.Fatalf("unable to connect to host %vv %v", raddr, err)
	}
	printf("\n%s%s\n", color.GreenString("Connected to "), color.CyanString(raddr.String()))

	var t2 time.Time // after connect, before TLS handshake
	if scheme == "https" {
		t2 = time.Now()
		c := tls.Client(conn, &tls.Config{
			ServerName:         host,
			InsecureSkipVerify: insecure,
		})
		if err := c.Handshake(); err != nil {
			log.Fatalf("unable to negotiate TLS handshake: %v", err)
		}
		conn = c
	}

	t3 := time.Now() // after connect, before request
	if onlyHeader {
		httpMethod = "HEAD"
	}
	req, err := http.NewRequest(httpMethod, url.String(), createBody(postBody))
	if err != nil {
		log.Fatalf("unable to create request: %v", err)
	}
	for _, h := range httpHeaders {
		req.Header.Add(headerKeyValue(h))
	}

	if err := req.Write(conn); err != nil {
		log.Fatalf("failed to write request: %v", err)
	}

	t4 := time.Now() // after request, before read response
	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		log.Fatalf("failed to read response: %v", err)
	}

	t5 := time.Now() // after read response
	bodyMsg := readResponseBody(req, resp)
	resp.Body.Close()

	t6 := time.Now() // after read body

	// print status line and headers
	printf("\n%s%s%s\n", color.GreenString("HTTP"), grayscale(14)("/"), color.CyanString("%d.%d %s", resp.ProtoMajor, resp.ProtoMinor, resp.Status))

	names := make([]string, 0, len(resp.Header))
	for k := range resp.Header {
		names = append(names, k)
	}
	sort.Sort(headers(names))
	for _, k := range names {
		printf("%s %s\n", grayscale(14)(k+":"), color.CyanString(strings.Join(resp.Header[k], ",")))
	}

	if bodyMsg != "" {
		printf("\n%s\n", bodyMsg)
	}

	fmta := func(d time.Duration) string {
		return color.CyanString("%7dms", int(d/time.Millisecond))
	}

	fmtb := func(d time.Duration) string {
		return color.CyanString("%-9s", strconv.Itoa(int(d/time.Millisecond))+"ms")
	}

	colorize := func(s string) string {
		v := strings.Split(s, "\n")
		v[0] = grayscale(16)(v[0])
		return strings.Join(v, "\n")
	}

	fmt.Println()

	switch scheme {
	case "https":
		printf(colorize(HTTPS_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t2.Sub(t1)), // tcp connection
			fmta(t3.Sub(t2)), // tls handshake
			fmta(t5.Sub(t4)), // server processing
			fmta(t6.Sub(t5)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t2.Sub(t0)), // connect
			fmtb(t3.Sub(t0)), // pretransfer
			fmtb(t5.Sub(t0)), // starttransfer
			fmtb(t6.Sub(t0)), // total
		)
	case "http":
		printf(colorize(HTTP_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t3.Sub(t1)), // tcp connection
			fmta(t5.Sub(t3)), // server processing
			fmta(t6.Sub(t5)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t3.Sub(t0)), // connect
			fmtb(t5.Sub(t0)), // starttransfer
			fmtb(t6.Sub(t0)), // total
		)
	}

	if followRedirects && isRedirect(resp) {
		loc, err := resp.Location()
		if err != nil {
			if err == http.ErrNoLocation {
				// 30x but no Location to follow, give up.
				return
			}
			log.Fatalf("unable to follow redirect: %v", err)
		}

		redirectsFollowed++
		if redirectsFollowed > maxRedirects {
			log.Fatalf("maximum number of redirects (%d) followed\n", maxRedirects)
		}

		visit(loc)
	}
}

func isRedirect(resp *http.Response) bool {
	return resp.StatusCode > 299 && resp.StatusCode < 400
}

func createBody(body string) io.Reader {
	if strings.HasPrefix(body, "@") {
		filename := body[1:]
		f, err := os.Open(filename)
		if err != nil {
			log.Fatalf("failed to open data file %s: %v", filename, err)
		}
		return f
	}
	return strings.NewReader(body)
}

// readResponseBody consumes the body of the response.
// readResponseBody returns an informational message about the
// disposition of the response body's contents.
func readResponseBody(req *http.Request, resp *http.Response) string {
	if isRedirect(resp) || req.Method == http.MethodHead {
		return ""
	}

	w := ioutil.Discard
	msg := color.CyanString("Body discarded")

	if saveOutput == true || outputFile != "" {
		filename := outputFile

		if saveOutput == true {
			// TODO(dfc) handle Content-Disposition: attachment
			filename = path.Base(req.URL.RequestURI())

			if filename == "/" {
				log.Fatalf("No remote filename; specify output filename with -o to save response body")
			}
		}

		f, err := os.Create(filename)
		if err != nil {
			log.Fatalf("unable to create file %s", outputFile)
		}
		defer f.Close()
		w = f
		msg = color.CyanString("Body read")
	}

	if _, err := io.Copy(w, resp.Body); err != nil {
		log.Fatalf("failed to read response body: %v", err)
	}

	return msg
}

type headers []string

func (h headers) String() string {
	var o []string
	for _, v := range h {
		o = append(o, "-H "+v)
	}
	return strings.Join(o, " ")
}

func (h *headers) Set(v string) error {
	*h = append(*h, v)
	return nil
}

func (h headers) Len() int      { return len(h) }
func (h headers) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h headers) Less(i, j int) bool {
	a, b := h[i], h[j]

	// server always sorts at the top
	if a == "Server" {
		return true
	}
	if b == "Server" {
		return false
	}

	endtoend := func(n string) bool {
		// https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
		switch n {
		case "Connection",
			"Keep-Alive",
			"Proxy-Authenticate",
			"Proxy-Authorization",
			"TE",
			"Trailers",
			"Transfer-Encoding",
			"Upgrade":
			return false
		default:
			return true
		}
	}

	x, y := endtoend(a), endtoend(b)
	if x == y {
		// both are of the same class
		return a < b
	}
	return x
}
<commit_msg>removed unused package var<commit_after>package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
)

const (
	HTTPS_TEMPLATE = `` +
		`  DNS Lookup   TCP Connection   TLS Handshake   Server Processing   Content Transfer` + "\n" +
		`[%s  |     %s  |    %s  |        %s  |       %s  ]` + "\n" +
		`            |                |               |                   |                  |` + "\n" +
		`   namelookup:%s      |               |                   |                  |` + "\n" +
		`                       connect:%s     |                   |                  |` + "\n" +
		`                                   pretransfer:%s         |                  |` + "\n" +
		`                                                     starttransfer:%s        |` + "\n" +
		`                                                                                total:%s` + "\n"

	HTTP_TEMPLATE = `` +
		`   DNS Lookup   TCP Connection   Server Processing   Content Transfer` + "\n" +
		`[ %s  |     %s  |        %s  |       %s  ]` + "\n" +
		`             |                |                   |                  |` + "\n" +
		`    namelookup:%s      |                   |                  |` + "\n" +
		`                        connect:%s         |                  |` + "\n" +
		`                                      starttransfer:%s        |` + "\n" +
		`                                                                 total:%s` + "\n"
)

var (
	grayscale = func(code int) func(string) string {
		if color.NoColor {
			return func(s string) string { return s }
		}
		return func(s string) string {
			return fmt.Sprintf("\x1b[;38;5;%dm%s\x1b[0m", code+232, s)
		}
	}

	// Command line flags.
	httpMethod      string
	postBody        string
	followRedirects bool
	onlyHeader      bool
	insecure        bool
	httpHeaders     headers
	saveOutput      bool
	outputFile      string

	// number of redirects followed
	redirectsFollowed int

	usage = fmt.Sprintf("usage: %s URL", os.Args[0])
)

const maxRedirects = 10

func init() {
	flag.StringVar(&httpMethod, "X", "GET", "HTTP method to use")
	flag.StringVar(&postBody, "d", "", "the body of a POST or PUT request")
	flag.BoolVar(&followRedirects, "L", false, "follow 30x redirects")
	flag.BoolVar(&onlyHeader, "I", false, "don't read body of request")
	flag.BoolVar(&insecure, "k", false, "allow insecure SSL connections")
	flag.Var(&httpHeaders, "H", "HTTP Header(s) to set. Can be used multiple times. -H 'Accept:...' -H 'Range:....'")
	flag.BoolVar(&saveOutput, "O", false, "Save body as remote filename")
	flag.StringVar(&outputFile, "o", "", "output file for body")

	flag.Usage = func() {
		os.Stderr.WriteString(usage + "\n")
		flag.PrintDefaults()
		os.Exit(2)
	}
}

func printf(format string, a ...interface{}) (n int, err error) {
	if color.Output == os.Stdout {
		return fmt.Printf(format, a...)
	}
	return fmt.Fprintf(color.Output, format, a...)
}

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) != 1 {
		flag.Usage()
	}

	if (httpMethod == "POST" || httpMethod == "PUT") && postBody == "" {
		log.Fatal("must supply post body using -d when POST or PUT is used")
	}

	url, err := url.Parse(args[0])
	if err != nil {
		log.Fatalf("could not parse url %q: %v", args[0], err)
	}
	visit(url)
}

func headerKeyValue(h string) (string, string) {
	i := strings.Index(h, ":")
	if i == -1 {
		log.Fatalf("Header '%s' has invalid format, missing ':'", h)
	}
	return strings.TrimRight(h[:i], " "), strings.TrimLeft(h[i:], " :")
}

// visit visits a url and times the interaction.
// If the response is a 30x, visit follows the redirect.
func visit(url *url.URL) {
	scheme := url.Scheme
	hostport := url.Host
	host, port := func() (string, string) {
		host, port, err := net.SplitHostPort(hostport)
		if err != nil {
			host = hostport
		}
		switch scheme {
		case "https":
			if port == "" {
				port = "443"
			}
		case "http":
			if port == "" {
				port = "80"
			}
		default:
			log.Fatalf("unsupported url scheme %q", scheme)
		}
		return host, port
	}()

	t0 := time.Now() // before dns resolution
	raddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%s", host, port))
	if err != nil {
		log.Fatalf("unable to resolve host: %v", err)
	}

	var conn net.Conn
	t1 := time.Now() // after dns resolution, before connect
	conn, err = net.DialTCP("tcp", nil, raddr)
	if err != nil {
		log.Fatalf("unable to connect to host %vv %v", raddr, err)
	}
	printf("\n%s%s\n", color.GreenString("Connected to "), color.CyanString(raddr.String()))

	var t2 time.Time // after connect, before TLS handshake
	if scheme == "https" {
		t2 = time.Now()
		c := tls.Client(conn, &tls.Config{
			ServerName:         host,
			InsecureSkipVerify: insecure,
		})
		if err := c.Handshake(); err != nil {
			log.Fatalf("unable to negotiate TLS handshake: %v", err)
		}
		conn = c
	}

	t3 := time.Now() // after connect, before request
	if onlyHeader {
		httpMethod = "HEAD"
	}
	req, err := http.NewRequest(httpMethod, url.String(), createBody(postBody))
	if err != nil {
		log.Fatalf("unable to create request: %v", err)
	}
	for _, h := range httpHeaders {
		req.Header.Add(headerKeyValue(h))
	}

	if err := req.Write(conn); err != nil {
		log.Fatalf("failed to write request: %v", err)
	}

	t4 := time.Now() // after request, before read response
	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		log.Fatalf("failed to read response: %v", err)
	}

	t5 := time.Now() // after read response
	bodyMsg := readResponseBody(req, resp)
	resp.Body.Close()

	t6 := time.Now() // after read body

	// print status line and headers
	printf("\n%s%s%s\n", color.GreenString("HTTP"), grayscale(14)("/"), color.CyanString("%d.%d %s", resp.ProtoMajor, resp.ProtoMinor, resp.Status))

	names := make([]string, 0, len(resp.Header))
	for k := range resp.Header {
		names = append(names, k)
	}
	sort.Sort(headers(names))
	for _, k := range names {
		printf("%s %s\n", grayscale(14)(k+":"), color.CyanString(strings.Join(resp.Header[k], ",")))
	}

	if bodyMsg != "" {
		printf("\n%s\n", bodyMsg)
	}

	fmta := func(d time.Duration) string {
		return color.CyanString("%7dms", int(d/time.Millisecond))
	}

	fmtb := func(d time.Duration) string {
		return color.CyanString("%-9s", strconv.Itoa(int(d/time.Millisecond))+"ms")
	}

	colorize := func(s string) string {
		v := strings.Split(s, "\n")
		v[0] = grayscale(16)(v[0])
		return strings.Join(v, "\n")
	}

	fmt.Println()

	switch scheme {
	case "https":
		printf(colorize(HTTPS_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t2.Sub(t1)), // tcp connection
			fmta(t3.Sub(t2)), // tls handshake
			fmta(t5.Sub(t4)), // server processing
			fmta(t6.Sub(t5)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t2.Sub(t0)), // connect
			fmtb(t3.Sub(t0)), // pretransfer
			fmtb(t5.Sub(t0)), // starttransfer
			fmtb(t6.Sub(t0)), // total
		)
	case "http":
		printf(colorize(HTTP_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t3.Sub(t1)), // tcp connection
			fmta(t5.Sub(t3)), // server processing
			fmta(t6.Sub(t5)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t3.Sub(t0)), // connect
			fmtb(t5.Sub(t0)), // starttransfer
			fmtb(t6.Sub(t0)), // total
		)
	}

	if followRedirects && isRedirect(resp) {
		loc, err := resp.Location()
		if err != nil {
			if err == http.ErrNoLocation {
				// 30x but no Location to follow, give up.
				return
			}
			log.Fatalf("unable to follow redirect: %v", err)
		}

		redirectsFollowed++
		if redirectsFollowed > maxRedirects {
			log.Fatalf("maximum number of redirects (%d) followed\n", maxRedirects)
		}

		visit(loc)
	}
}

func isRedirect(resp *http.Response) bool {
	return resp.StatusCode > 299 && resp.StatusCode < 400
}

func createBody(body string) io.Reader {
	if strings.HasPrefix(body, "@") {
		filename := body[1:]
		f, err := os.Open(filename)
		if err != nil {
			log.Fatalf("failed to open data file %s: %v", filename, err)
		}
		return f
	}
	return strings.NewReader(body)
}

// readResponseBody consumes the body of the response.
// readResponseBody returns an informational message about the
// disposition of the response body's contents.
func readResponseBody(req *http.Request, resp *http.Response) string {
	if isRedirect(resp) || req.Method == http.MethodHead {
		return ""
	}

	w := ioutil.Discard
	msg := color.CyanString("Body discarded")

	if saveOutput == true || outputFile != "" {
		filename := outputFile

		if saveOutput == true {
			// TODO(dfc) handle Content-Disposition: attachment
			filename = path.Base(req.URL.RequestURI())

			if filename == "/" {
				log.Fatalf("No remote filename; specify output filename with -o to save response body")
			}
		}

		f, err := os.Create(filename)
		if err != nil {
			log.Fatalf("unable to create file %s", outputFile)
		}
		defer f.Close()
		w = f
		msg = color.CyanString("Body read")
	}

	if _, err := io.Copy(w, resp.Body); err != nil {
		log.Fatalf("failed to read response body: %v", err)
	}

	return msg
}

type headers []string

func (h headers) String() string {
	var o []string
	for _, v := range h {
		o = append(o, "-H "+v)
	}
	return strings.Join(o, " ")
}

func (h *headers) Set(v string) error {
	*h = append(*h, v)
	return nil
}

func (h headers) Len() int      { return len(h) }
func (h headers) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h headers) Less(i, j int) bool {
	a, b := h[i], h[j]

	// server always sorts at the top
	if a == "Server" {
		return true
	}
	if b == "Server" {
		return false
	}

	endtoend := func(n string) bool {
		// https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
		switch n {
		case "Connection",
			"Keep-Alive",
			"Proxy-Authenticate",
			"Proxy-Authorization",
			"TE",
			"Trailers",
			"Transfer-Encoding",
			"Upgrade":
			return false
		default:
			return true
		}
	}

	x, y := endtoend(a), endtoend(b)
	if x == y {
		// both are of the same class
		return a < b
	}
	return x
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"net/http"
	"os"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/skibish/ddns/conf"
	"github.com/skibish/ddns/do"
	"github.com/skibish/ddns/ipprovider"
	"github.com/skibish/ddns/notifier"
)

var (
	digio   do.DigitalOceanInterface
	cf      *conf.Configuration
	periodC <-chan time.Time
)

var (
	reqTimeouts = flag.Duration("req-timeout", 10*time.Second, "Request timeout to external resources")
	checkPeriod = flag.Duration("check-period", 5*time.Minute, "Check if IP has been changed period")
	confFile    = flag.String("conf-file", "$HOME/.ddns.yml", "Location of the configuration file")
)

// current remembered IP
var currentIP string

func init() {
	log.SetFormatter(&log.TextFormatter{
		DisableColors: true,
	})
	log.SetOutput(os.Stdout)
}

func main() {
	flag.Parse()

	// read configuration
	var errConf error
	cf, errConf = conf.NewConfiguration(*confFile)
	if errConf != nil {
		log.Fatal(errConf.Error())
	}

	// try to register all provided hooks
	for k, v := range cf.Notify {
		hook, errGet := notifier.GetHook(k, v)
		if errGet != nil {
			log.Println(errGet)
			continue
		}
		log.AddHook(hook)
	}

	// setup http client
	hc := &http.Client{
		Timeout: *reqTimeouts,
	}

	// initialte digital ocean client
	digio = do.NewDigitalOcean(cf.Domain, cf.Token, hc)

	// register providers
	ipprovider.Register(hc)

	// get current IP
	currentIP = ipprovider.GetIP()
	if currentIP == "" {
		log.Fatal("IP can't be empty in the beginning... Do you have internet connection?")
	}
	log.Infof("Current IP is %q", currentIP)

	// do request to the digital ocean API for list of records
	allRecords, errGetDR := digio.GetDomainRecords()
	if errGetDR != nil {
		log.Fatal(errGetDR.Error())
	}

	// do initial sync of records
	var errSync error
	errSync = syncRecords(cf, allRecords)
	if errSync != nil {
		log.Fatal(errSync.Error())
	}

	periodC = time.NewTicker(*checkPeriod).C

	// start main proceess
	go func(cf *conf.Configuration) {
		// for defined period of time, perform IP check
		for {
			select {
			case <-periodC:
				errCheck := checkAndUpdate(cf, ipprovider.GetIP)
				if errCheck != nil {
					log.Errorf("Failed to update: %s", errCheck.Error())
				}
			}
		}

	}(cf)

	select {}
}

// syncRecords perform initial sync between what we provided
// in configuration and what already exist in DNS records
func syncRecords(cf *conf.Configuration, allRecords []do.Record) error {
	cRec := len(cf.Records)
	cAllRec := len(allRecords)
	for i := 0; i < cRec; i++ {
		for j := 0; j < cAllRec; j++ {

			// we are only interested in those who have full match
			// by `type AND name`
			if cf.Records[i].Type == allRecords[j].Type &&
				cf.Records[i].Name == allRecords[j].Name {
				cf.Records[i] = allRecords[j]
				break
			}
		}

		// if there was no match, we should create new DNS record
		// and updatee current configuration
		if cf.Records[i].ID == 0 {
			cf.Records[i].Data = currentIP

			newR, errCreate := digio.CreateRecord(cf.Records[i])
			if errCreate != nil {
				return errCreate
			}

			cf.Records[i] = *newR
		}

		// if IPs are different, update record
		if cf.Records[i].Data != currentIP {
			cf.Records[i].Data = currentIP

			newR, errUpdate := digio.UpdateRecord(cf.Records[i])
			if errUpdate != nil {
				return errUpdate
			}

			cf.Records[i] = *newR
		}
	}

	return nil
}

// checkAndUpdate check for new IP and if it has been changed,
// trigger the update of the DNS records
func checkAndUpdate(cf *conf.Configuration, getIP ipprovider.FGetIP) error {
	log.Debug("IP check")
	newIP := getIP()

	if currentIP != newIP {
		log.Infof("IP has changed from %q to %q", currentIP, newIP)
		currentIP = newIP

		cRec := len(cf.Records)
		for i := 0; i < cRec; i++ {
			cf.Records[i].Data = currentIP

			newR, errUpdate := digio.UpdateRecord(cf.Records[i])
			if errUpdate != nil {
				return errUpdate
			}

			cf.Records[i] = *newR
		}
	}

	return nil
}
<commit_msg>Change log.Println to log.Debug<commit_after>package main

import (
	"flag"
	"net/http"
	"os"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/skibish/ddns/conf"
	"github.com/skibish/ddns/do"
	"github.com/skibish/ddns/ipprovider"
	"github.com/skibish/ddns/notifier"
)

var (
	digio   do.DigitalOceanInterface
	cf      *conf.Configuration
	periodC <-chan time.Time
)

var (
	reqTimeouts = flag.Duration("req-timeout", 10*time.Second, "Request timeout to external resources")
	checkPeriod = flag.Duration("check-period", 5*time.Minute, "Check if IP has been changed period")
	confFile    = flag.String("conf-file", "$HOME/.ddns.yml", "Location of the configuration file")
)

// current remembered IP
var currentIP string

func init() {
	log.SetFormatter(&log.TextFormatter{
		DisableColors: true,
	})
	log.SetOutput(os.Stdout)
}

func main() {
	flag.Parse()

	// read configuration
	var errConf error
	cf, errConf = conf.NewConfiguration(*confFile)
	if errConf != nil {
		log.Fatal(errConf.Error())
	}

	// try to register all provided hooks
	for k, v := range cf.Notify {
		hook, errGet := notifier.GetHook(k, v)
		if errGet != nil {
			log.Debug(errGet)
			continue
		}
		log.AddHook(hook)
	}

	// setup http client
	hc := &http.Client{
		Timeout: *reqTimeouts,
	}

	// initialte digital ocean client
	digio = do.NewDigitalOcean(cf.Domain, cf.Token, hc)

	// register providers
	ipprovider.Register(hc)

	// get current IP
	currentIP = ipprovider.GetIP()
	if currentIP == "" {
		log.Fatal("IP can't be empty in the beginning... Do you have internet connection?")
	}
	log.Infof("Current IP is %q", currentIP)

	// do request to the digital ocean API for list of records
	allRecords, errGetDR := digio.GetDomainRecords()
	if errGetDR != nil {
		log.Fatal(errGetDR.Error())
	}

	// do initial sync of records
	var errSync error
	errSync = syncRecords(cf, allRecords)
	if errSync != nil {
		log.Fatal(errSync.Error())
	}

	periodC = time.NewTicker(*checkPeriod).C

	// start main proceess
	go func(cf *conf.Configuration) {
		// for defined period of time, perform IP check
		for {
			select {
			case <-periodC:
				errCheck := checkAndUpdate(cf, ipprovider.GetIP)
				if errCheck != nil {
					log.Errorf("Failed to update: %s", errCheck.Error())
				}
			}
		}

	}(cf)

	select {}
}

// syncRecords perform initial sync between what we provided
// in configuration and what already exist in DNS records
func syncRecords(cf *conf.Configuration, allRecords []do.Record) error {
	cRec := len(cf.Records)
	cAllRec := len(allRecords)
	for i := 0; i < cRec; i++ {
		for j := 0; j < cAllRec; j++ {

			// we are only interested in those who have full match
			// by `type AND name`
			if cf.Records[i].Type == allRecords[j].Type &&
				cf.Records[i].Name == allRecords[j].Name {
				cf.Records[i] = allRecords[j]
				break
			}
		}

		// if there was no match, we should create new DNS record
		// and updatee current configuration
		if cf.Records[i].ID == 0 {
			cf.Records[i].Data = currentIP

			newR, errCreate := digio.CreateRecord(cf.Records[i])
			if errCreate != nil {
				return errCreate
			}

			cf.Records[i] = *newR
		}

		// if IPs are different, update record
		if cf.Records[i].Data != currentIP {
			cf.Records[i].Data = currentIP

			newR, errUpdate := digio.UpdateRecord(cf.Records[i])
			if errUpdate != nil {
				return errUpdate
			}

			cf.Records[i] = *newR
		}
	}

	return nil
}

// checkAndUpdate check for new IP and if it has been changed,
// trigger the update of the DNS records
func checkAndUpdate(cf *conf.Configuration, getIP ipprovider.FGetIP) error {
	log.Debug("IP check")
	newIP := getIP()

	if currentIP != newIP {
		log.Infof("IP has changed from %q to %q", currentIP, newIP)
		currentIP = newIP

		cRec := len(cf.Records)
		for i := 0; i < cRec; i++ {
			cf.Records[i].Data = currentIP

			newR, errUpdate := digio.UpdateRecord(cf.Records[i])
			if errUpdate != nil {
				return errUpdate
			}

			cf.Records[i] = *newR
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"

	"github.com/ogier/pflag"
	"github.com/yuya-takeyama/argf"
)

var (
	name    = "csvp"
	version = "0.8.1"
)

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s [OPTION]... [FILE]...
Print selected parts of CSV from each FILE to standard output.

Options:
  -i, --indexes=LIST
                 select only these indexes
  -h, --headers=LIST
                 select only these headers
  -t, --tsv
                 equivalent to -d'\t'
  -d, --delimiter=DELIM
                 use DELIM instead of comma for field delimiter
  -D, --output-delimiter=STRING
                 use STRING as the output delimiter (default: \t)
  --help
                 display this help text and exit
  --version
                 output version information and exit
`[1:], name)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func printErr(err error) {
	fmt.Fprintf(os.Stderr, "%s: %s\n", name, err)
}

type Option struct {
	IndexesList     string
	HeadersList     string
	IsTSV           bool
	Delimiter       string
	OutputDelimiter string
	IsHelp          bool
	IsVersion       bool
	Files           []string
}

func parseOption(args []string) (opt *Option, err error) {
	flag := pflag.NewFlagSet(name, pflag.ContinueOnError)
	flag.SetOutput(ioutil.Discard)

	opt = &Option{}
	flag.StringVarP(&opt.IndexesList, "indexes", "i", "", "")
	flag.StringVarP(&opt.HeadersList, "headers", "h", "", "")
	flag.BoolVarP(&opt.IsTSV, "tsv", "t", false, "")
	flag.StringVarP(&opt.Delimiter, "delimiter", "d", ",", "")
	flag.StringVarP(&opt.OutputDelimiter, "output-delimiter", "D", "\t", "")
	flag.BoolVarP(&opt.IsHelp, "help", "", false, "")
	flag.BoolVarP(&opt.IsVersion, "version", "", false, "")

	if err = flag.Parse(args); err != nil {
		return nil, err
	}

	opt.Files = flag.Args()
	return opt, nil
}

func toDelimiter(s string) (r rune, err error) {
	s, err = strconv.Unquote(`"` + s + `"`)
	if err != nil {
		return 0, err
	}

	runes := []rune(s)
	if len(runes) != 1 {
		return 0, fmt.Errorf("the delimiter must be a single character")
	}
	return runes[0], nil
}

func newCSVScannerFromOption(opt *Option) (c *CSVScanner, err error) {
	var selector Selector
	switch {
	case opt.IndexesList != "" && opt.HeadersList != "":
		return nil, fmt.Errorf("only one type of list may be specified")
	case opt.IndexesList != "":
		selector = NewIndexes(opt.IndexesList)
	case opt.HeadersList != "":
		selector = NewHeaders(opt.HeadersList)
	default:
		selector = NewAll()
	}

	reader, err := argf.From(opt.Files)
	if err != nil {
		return nil, err
	}

	c = NewCSVScanner(selector, reader)
	c.SetOutputDelimiter(opt.OutputDelimiter)
	switch {
	case opt.IsTSV:
		c.SetDelimiter('\t')
	default:
		delimiter, err := toDelimiter(opt.Delimiter)
		if err != nil {
			return nil, err
		}
		c.SetDelimiter(delimiter)
	}
	return c, nil
}

func do(c *CSVScanner) error {
	for c.Scan() {
		fmt.Println(c.Text())
	}
	return c.Err()
}

func guideToHelp() {
	fmt.Fprintf(os.Stderr, "Try '%s --help' for more information.\n", name)
}

func _main() int {
	opt, err := parseOption(os.Args[1:])
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}
	switch {
	case opt.IsHelp:
		printUsage()
		return 0
	case opt.IsVersion:
		printVersion()
		return 0
	}

	c, err := newCSVScannerFromOption(opt)
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}
	if err = do(c); err != nil {
		printErr(err)
		return 1
	}
	return 0
}

func main() {
	e := _main()
	os.Exit(e)
}
<commit_msg>Declare guideToHelp before Option<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"

	"github.com/ogier/pflag"
	"github.com/yuya-takeyama/argf"
)

var (
	name    = "csvp"
	version = "0.8.1"
)

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s [OPTION]... [FILE]...
Print selected parts of CSV from each FILE to standard output.

Options:
  -i, --indexes=LIST
                 select only these indexes
  -h, --headers=LIST
                 select only these headers
  -t, --tsv
                 equivalent to -d'\t'
  -d, --delimiter=DELIM
                 use DELIM instead of comma for field delimiter
  -D, --output-delimiter=STRING
                 use STRING as the output delimiter (default: \t)
  --help
                 display this help text and exit
  --version
                 output version information and exit
`[1:], name)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func printErr(err error) {
	fmt.Fprintf(os.Stderr, "%s: %s\n", name, err)
}

func guideToHelp() {
	fmt.Fprintf(os.Stderr, "Try '%s --help' for more information.\n", name)
}

type Option struct {
	IndexesList     string
	HeadersList     string
	IsTSV           bool
	Delimiter       string
	OutputDelimiter string
	IsHelp          bool
	IsVersion       bool
	Files           []string
}

func parseOption(args []string) (opt *Option, err error) {
	flag := pflag.NewFlagSet(name, pflag.ContinueOnError)
	flag.SetOutput(ioutil.Discard)

	opt = &Option{}
	flag.StringVarP(&opt.IndexesList, "indexes", "i", "", "")
	flag.StringVarP(&opt.HeadersList, "headers", "h", "", "")
	flag.BoolVarP(&opt.IsTSV, "tsv", "t", false, "")
	flag.StringVarP(&opt.Delimiter, "delimiter", "d", ",", "")
	flag.StringVarP(&opt.OutputDelimiter, "output-delimiter", "D", "\t", "")
	flag.BoolVarP(&opt.IsHelp, "help", "", false, "")
	flag.BoolVarP(&opt.IsVersion, "version", "", false, "")

	if err = flag.Parse(args); err != nil {
		return nil, err
	}

	opt.Files = flag.Args()
	return opt, nil
}

func toDelimiter(s string) (r rune, err error) {
	s, err = strconv.Unquote(`"` + s + `"`)
	if err != nil {
		return 0, err
	}

	runes := []rune(s)
	if len(runes) != 1 {
		return 0, fmt.Errorf("the delimiter must be a single character")
	}
	return runes[0], nil
}

func newCSVScannerFromOption(opt *Option) (c *CSVScanner, err error) {
	var selector Selector
	switch {
	case opt.IndexesList != "" && opt.HeadersList != "":
		return nil, fmt.Errorf("only one type of list may be specified")
	case opt.IndexesList != "":
		selector = NewIndexes(opt.IndexesList)
	case opt.HeadersList != "":
		selector = NewHeaders(opt.HeadersList)
	default:
		selector = NewAll()
	}

	reader, err := argf.From(opt.Files)
	if err != nil {
		return nil, err
	}

	c = NewCSVScanner(selector, reader)
	c.SetOutputDelimiter(opt.OutputDelimiter)
	switch {
	case opt.IsTSV:
		c.SetDelimiter('\t')
	default:
		delimiter, err := toDelimiter(opt.Delimiter)
		if err != nil {
			return nil, err
		}
		c.SetDelimiter(delimiter)
	}
	return c, nil
}

func do(c *CSVScanner) error {
	for c.Scan() {
		fmt.Println(c.Text())
	}
	return c.Err()
}

func _main() int {
	opt, err := parseOption(os.Args[1:])
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}
	switch {
	case opt.IsHelp:
		printUsage()
		return 0
	case opt.IsVersion:
		printVersion()
		return 0
	}

	c, err := newCSVScannerFromOption(opt)
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}
	if err = do(c); err != nil {
		printErr(err)
		return 1
	}
	return 0
}

func main() {
	e := _main()
	os.Exit(e)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"os/signal"
	"path/filepath"
	"time"

	pb "github.com/cheggaaa/pb"
	"github.com/dustin/go-humanize"
	core "github.com/ipfs/go-ipfs/core"
	path "github.com/ipfs/go-ipfs/path"
	uio "github.com/ipfs/go-ipfs/unixfs/io"
	"github.com/jawher/mow.cli"
	context "golang.org/x/net/context"
)

func main() {
	cmd := cli.App("ipget", "Retrieve and save IPFS objects.")
	cmd.Spec = "IPFS_PATH [-o]"
	hash := cmd.String(cli.StringArg{
		Name:  "IPFS_PATH",
		Value: "",
		Desc:  "the IPFS object path",
	})
	outFile := cmd.StringOpt("o output", "", "output file path")
	cmd.Action = func() {
		if *outFile == "" {
			*outFile = filepath.Base(*hash)
		}

		if err := get(*hash, *outFile); err != nil {
			fmt.Fprintf(os.Stderr, "ipget failed: %s", err)
			os.Remove(*outFile)
			os.Exit(1)
		}
	}
	cmd.Run(os.Args)
}

func get(path, outFile string) error {
	start := time.Now()
	ctx, cancel := context.WithCancel(context.Background())
	node, err := core.NewNode(ctx, &core.BuildCfg{
		Online: true,
	})
	if err != nil {
		return fmt.Errorf("ipfs NewNode() failed: %s", err)
	}

	err = node.Bootstrap(core.DefaultBootstrapConfig)
	if err != nil {
		return fmt.Errorf("node Bootstrap() failed: %s", err)
	}

	fmt.Fprintf(os.Stderr, "IPFS Node bootstrapping (took %v)\n", time.Since(start))

	// Cancel the ipfs node context if the process gets interrupted or killed.
	go func() {
		interrupts := make(chan os.Signal, 1)
		signal.Notify(interrupts, os.Interrupt, os.Kill)
		<-interrupts
		cancel()
	}()

	reader, length, err := cat(node.Context(), node, path)
	if err != nil {
		return fmt.Errorf("cat failed: %s", err)
	}

	file, err := os.Create(outFile)
	if err != nil {
		return fmt.Errorf("Creating output file %q failed: %s", outFile, err)
	}

	bar := pb.New(int(length)).SetUnits(pb.U_BYTES)
	bar.Output = os.Stderr
	bar.ShowSpeed = false
	bar.Start()
	writer := io.MultiWriter(file, bar)

	if _, err := io.Copy(writer, reader); err != nil {
		return fmt.Errorf("copy failed: %s", err)
	}

	bar.Finish()

	fmt.Fprintf(os.Stderr, "Wrote %q to %q (%s) (took %v)\n", path, outFile,
		humanize.Bytes(length), time.Since(start))
	return nil
}

func cat(ctx context.Context, node *core.IpfsNode, fpath string) (io.Reader, uint64, error) {
	dagnode, err := core.Resolve(ctx, node, path.Path(fpath))
	if err != nil {
		return nil, 0, err
	}

	reader, err := uio.NewDagReader(ctx, dagnode, node.DAG)
	if err != nil {
		return nil, 0, err
	}

	return reader, reader.Size(), nil
}
<commit_msg>Defaults output name to last segment of IPFS path.<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/signal"
	"time"

	pb "github.com/cheggaaa/pb"
	"github.com/dustin/go-humanize"
	core "github.com/ipfs/go-ipfs/core"
	path "github.com/ipfs/go-ipfs/path"
	uio "github.com/ipfs/go-ipfs/unixfs/io"
	"github.com/jawher/mow.cli"
	context "golang.org/x/net/context"
)

func main() {
	cmd := cli.App("ipget", "Retrieve and save IPFS objects.")
	cmd.Spec = "IPFS_PATH [-o]"
	hash := cmd.String(cli.StringArg{
		Name:  "IPFS_PATH",
		Value: "",
		Desc:  "the IPFS object path",
	})
	outFile := cmd.StringOpt("o output", "", "output file path")
	cmd.Action = func() {
		if *outFile == "" {
			ipfsPath, err := path.ParsePath(*hash)
			if err != nil {
				fmt.Fprintf(os.Stderr, "ParsePath failure: %s", err)
				os.Exit(1)
			}
			segments := ipfsPath.Segments()
			*outFile = segments[len(segments)-1]
		}

		if err := get(*hash, *outFile); err != nil {
			fmt.Fprintf(os.Stderr, "ipget failed: %s", err)
			os.Remove(*outFile)
			os.Exit(1)
		}
	}
	cmd.Run(os.Args)
}

func get(path, outFile string) error {
	start := time.Now()
	ctx, cancel := context.WithCancel(context.Background())
	node, err := core.NewNode(ctx, &core.BuildCfg{
		Online: true,
	})
	if err != nil {
		return fmt.Errorf("ipfs NewNode() failed: %s", err)
	}

	err = node.Bootstrap(core.DefaultBootstrapConfig)
	if err != nil {
		return fmt.Errorf("node Bootstrap() failed: %s", err)
	}

	fmt.Fprintf(os.Stderr, "IPFS Node bootstrapping (took %v)\n", time.Since(start))

	// Cancel the ipfs node context if the process gets interrupted or killed.
	go func() {
		interrupts := make(chan os.Signal, 1)
		signal.Notify(interrupts, os.Interrupt, os.Kill)
		<-interrupts
		cancel()
	}()

	reader, length, err := cat(node.Context(), node, path)
	if err != nil {
		return fmt.Errorf("cat failed: %s", err)
	}

	file, err := os.Create(outFile)
	if err != nil {
		return fmt.Errorf("Creating output file %q failed: %s", outFile, err)
	}

	bar := pb.New(int(length)).SetUnits(pb.U_BYTES)
	bar.Output = os.Stderr
	bar.ShowSpeed = false
	bar.Start()
	writer := io.MultiWriter(file, bar)

	if _, err := io.Copy(writer, reader); err != nil {
		return fmt.Errorf("copy failed: %s", err)
	}

	bar.Finish()

	fmt.Fprintf(os.Stderr, "Wrote %q to %q (%s) (took %v)\n", path, outFile,
		humanize.Bytes(length), time.Since(start))
	return nil
}

func cat(ctx context.Context, node *core.IpfsNode, fpath string) (io.Reader, uint64, error) {
	dagnode, err := core.Resolve(ctx, node, path.Path(fpath))
	if err != nil {
		return nil, 0, err
	}

	reader, err := uio.NewDagReader(ctx, dagnode, node.DAG)
	if err != nil {
		return nil, 0, err
	}

	return reader, reader.Size(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
)

var filename string

func init() {
	flag.StringVar(&filename, "f", "", "Specify the CrdLog.log file to read")
}

// helper function to exit application with a message to Stderr.
func exitOnError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(-1)
	}
}

// parse the line and return only the response of the transaction.
func fetchRawData(v string) string {
	slice := strings.Split(v, "2 D 0 0 6")
	return slice[len(slice)-1]
}

// convert the hex value of the response into it's string representation
func convertHexToASCII(v string) (string, error) {
	hex := bytes.NewBufferString(v)
	ascii := bytes.Buffer{}
	var d byte
	for {
		_, err := fmt.Fscanf(hex, " %X", &d)

		if err == io.EOF {
			break
		} else if err != nil {
			return "", err
		}

		err = ascii.WriteByte(d)
		if err != nil {
			return "", err
		}
	}
	return string(ascii.Bytes()), nil
}

// decode take a line from the CrdtLog.Log file and parse the reponse
// into a human readable format
func decode(v string) ([]string, error) {

	data := fetchRawData(v)

	if len(data) <= 2 {
		return nil, errors.New("Empty line")
	}

	data, err := convertHexToASCII(data)
	if err != nil {
		return nil, errors.New("Bad format")
	}

	//remove the two hidden characters at the end of the line
	if byte(data[len(data)-1]) == 0x0 {
		data = data[:len(data)-1]
	}

	if byte(data[len(data)-1]) == 0x4E {
		data = data[:len(data)-1]
	}

	delim := 0x1C
	field := strings.Split(data, string(delim))
	return field, nil
}

// readLine break the file stream into line of
func readLine(reader *bufio.Reader) (string, error) {
	line, err := reader.ReadString('\n')
	lLen := len(line)

	if lLen == 0 {
		return "", err
	}

	if line[lLen-2] == '\r' {
		return line[:lLen-2], err
	}

	return line[:lLen-1], err
}

func main() {
	flag.Parse()

	if len(filename) == 0 {
		fmt.Fprintln(os.Stderr, "Missing CrdLog file")
		os.Exit(-1)
	}

	stream, err := os.Open(filename)
	exitOnError(err)
	defer stream.Close()

	reader := bufio.NewReader(stream)
	i := 0
	for {
		line, err := readLine(reader)
		if err != nil {
			break
		}
		if strings.Contains(line, ":2 D 0 0") {
			i++
			fields, err := decode(line)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}

			fmt.Println(i, ": ------")
			fmt.Println(fields[0])
			for _, v := range fields[1:] {
				fmt.Println("[FS]", v)
			}
			fmt.Println(i, ": ------")
		}
	}
}
<commit_msg>refactor: rename readLine to readNextLine<commit_after>package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
)

var filename string

func init() {
	flag.StringVar(&filename, "f", "", "Specify the CrdLog.log file to read")
}

// helper function to exit application with a message to Stderr.
func exitOnError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(-1)
	}
}

// parse the line and return only the response of the transaction.
func fetchRawData(v string) string {
	slice := strings.Split(v, "2 D 0 0 6")
	return slice[len(slice)-1]
}

// convert the hex value of the response into it's string representation
func convertHexToASCII(v string) (string, error) {
	hex := bytes.NewBufferString(v)
	ascii := bytes.Buffer{}
	var d byte
	for {
		_, err := fmt.Fscanf(hex, " %X", &d)

		if err == io.EOF {
			break
		} else if err != nil {
			return "", err
		}

		err = ascii.WriteByte(d)
		if err != nil {
			return "", err
		}
	}
	return string(ascii.Bytes()), nil
}

// decode take a line from the CrdtLog.Log file and parse the reponse
// into a human readable format
func decode(v string) ([]string, error) {

	data := fetchRawData(v)

	if len(data) <= 2 {
		return nil, errors.New("Empty line")
	}

	data, err := convertHexToASCII(data)
	if err != nil {
		return nil, errors.New("Bad format")
	}

	//remove the two hidden characters at the end of the line
	if byte(data[len(data)-1]) == 0x0 {
		data = data[:len(data)-1]
	}

	if byte(data[len(data)-1]) == 0x4E {
		data = data[:len(data)-1]
	}

	delim := 0x1C
	field := strings.Split(data, string(delim))
	return field, nil
}

// readNextLine break the file stream into line of
func readNextLine(reader *bufio.Reader) (string, error) {
	line, err := reader.ReadString('\n')
	lLen := len(line)

	if lLen == 0 {
		return "", err
	}

	if line[lLen-2] == '\r' {
		return line[:lLen-2], err
	}

	return line[:lLen-1], err
}

func main() {
	flag.Parse()

	if len(filename) == 0 {
		fmt.Fprintln(os.Stderr, "Missing CrdLog file")
		os.Exit(-1)
	}

	stream, err := os.Open(filename)
	exitOnError(err)
	defer stream.Close()

	reader := bufio.NewReader(stream)
	i := 0
	for {
		line, err := readNextLine(reader)
		if err != nil {
			break
		}
		if strings.Contains(line, ":2 D 0 0") {
			i++
			fields, err := decode(line)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}

			fmt.Println(i, ": ------")
			fmt.Println(fields[0])
			for _, v := range fields[1:] {
				fmt.Println("[FS]", v)
			}
			fmt.Println(i, ": ------")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"os"

	restful "github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	flags "github.com/jessevdk/go-flags"
)

var logger *log.Logger = log.New(os.Stdout, "", 0)

const prefix = "/api"

type Options struct {
	KubernetesMaster string `short:"k" long:"kubernetes-master" description:"The URL to the Kubernetes master" default:"http://localhost:8080"`
}

var options Options

var parser = flags.NewParser(&options, flags.Default)

func main() {
	if _, err := parser.Parse(); err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			parser.WriteHelp(os.Stderr)
			os.Exit(1)
		}
		os.Exit(0)
	}

	restful.DefaultContainer.Filter(NCSACommonLogFormatLogger())

	config := swagger.Config{
		WebServices: restful.RegisteredWebServices(),
		ApiPath:     prefix + "/apidocs.json",
	}
	swagger.InstallSwaggerService(config)

	resources := []SelfRegisteringResource{
		PingResource{},
	}
	for _, resource := range resources {
		resource.Register(prefix)
	}

	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>Make listen port configurable<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	k8sclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	restful "github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	flags "github.com/jessevdk/go-flags"
)

var logger *log.Logger = log.New(os.Stdout, "", 0)

const prefix = "/api"

type Options struct {
	Port                 uint16 `short:"p" long:"port" description:"The port to listen on" default:"9090"`
	KubernetesMaster     string `short:"k" long:"kubernetes-master" description:"The URL to the Kubernetes master"`
	KubernetesApiVersion string `short:"v" long:"kubernetes-api-version" description:"The version of the Kubernetes API to use" default:"v1beta2"`
}

func main() {
	var options Options
	var parser = flags.NewParser(&options, flags.Default)

	if _, err := parser.Parse(); err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			parser.WriteHelp(os.Stderr)
			os.Exit(1)
		}
		os.Exit(0)
	}

	k8sConfig := &k8sclient.Config{
		Host:    options.KubernetesMaster,
		Version: options.KubernetesApiVersion,
	}

	k8sClient, err := k8sclient.New(k8sConfig)
	if err != nil {
		log.Panic(err)
	}

	if serverVersion, err := k8sClient.ServerVersion(); err != nil {
		log.Panic("Couldn't retrieve Kubernetes server version - incorrect URL possibly? ", err)
	} else {
		log.Printf("Connecting to Kubernetes server at version %v", serverVersion.String())
	}

	restful.DefaultContainer.Filter(NCSACommonLogFormatLogger())

	config := swagger.Config{
		WebServices: restful.RegisteredWebServices(),
		ApiPath:     prefix + "/apidocs.json",
	}
	swagger.InstallSwaggerService(config)

	resources := []SelfRegisteringResource{
		PingResource{},
	}
	for _, resource := range resources {
		resource.Register(prefix)
	}

	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", options.Port), nil))
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Command template is a trivial web server that uses the text/template (and
// html/template) package's "block" feature to implement a kind of template
// inheritance.
//
// It should be executed from the directory in which the source resides,
// as it will look for its template files in the current directory.
package main

import (
    "html/template"
    "log"
    "net/http"
    "strings"
    "path/filepath"
    "os"
    "fmt"
    "sort"
    "bufio"
    "strconv"
)

func main() {
    songs_root := "./songs_master" // 1st argument is the directory location
    filepath.Walk(songs_root, walkpath)

    fmt.Printf("%d Songs loaded.\n", len(filenames))

    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/song/", songHandler)
    http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
    log.Fatal(http.ListenAndServe("localhost:8090", nil))
}

// indexTemplate is the main site template.
// The default template includes two template blocks ("sidebar" and "content")
// that may be replaced in templates derived from this one.
var indexTemplate = template.Must(template.ParseFiles("templates/index.tmpl"))

// Index is a data structure used to populate an indexTemplate.
type Index struct {
    Title string
    Songs []string
}

type SongPage struct {
    Title string
    Songs []string
    Song Song
    HasSong bool
}

// indexHandler is an HTTP handler that serves the index page.
func indexHandler(w http.ResponseWriter, r *http.Request) {
    data := &SongPage{
        Title: "Indigo Song Book",
        HasSong: false,
        Songs: filenames}

    if err := songTemplate.Execute(w, data); err != nil {
        log.Println(err)
    }
}

// imageTemplate is a clone of indexTemplate that provides
// alternate "sidebar" and "content" templates.
var songTemplate = template.Must(template.Must(indexTemplate.Clone()).ParseFiles("templates/song.tmpl"))

// Image is a data structure used to populate an imageTemplate.
type SongFile struct {
    Title string
    Lines  []string
}

func loadSongFile(title string, transpose int) (*Song, error) {
    filename := "songs_master/" + title + ".song"

    song,err := ParseSongFile(filename, transpose);

    if err != nil {
        return nil, err
    }

    return song, nil
}

// imageHandler is an HTTP handler that serves the image pages.
func songHandler(w http.ResponseWriter, r *http.Request) {
    target := strings.TrimPrefix(r.URL.Path, "/song/")

    ind := sort.SearchStrings(filenames, target)
    if ind > len(filenames) && filenames[ind] != target {
        http.NotFound(w, r)
        return
    }

    var transpose = r.FormValue("transpose")
    if len(transpose) == 0 {
        transpose = "0";
    }
    t, err := strconv.Atoi(transpose)

    data, err := loadSongFile(target, t)
    if err != nil {
        log.Println(err)
        return
    }

    page_data := &SongPage{
        Title: "Indigo Song Book",
        Song: *data,
        HasSong: true,
        Songs: filenames}

    if err := songTemplate.Execute(w, page_data); err != nil {
        log.Println(err)
    }
}

var filenames = make([]string, 0)

func walkpath(path string, f os.FileInfo, err error) error {
    if (f.IsDir() && f.Name() != "songs_master") {
        return filepath.SkipDir
    } else if (f.IsDir()) {
        return nil
    }

    file := f.Name()[0:len(f.Name())-5]
    filenames = append(filenames, file)

    fmt.Printf("%s\n", file)
    return nil
}

// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
  file, err := os.Open(path)
  if err != nil {
    return nil, err
  }
  defer file.Close()

  var lines []string
  scanner := bufio.NewScanner(file)
  for scanner.Scan() {
    lines = append(lines, scanner.Text())
  }
  return lines, scanner.Err()
}<commit_msg>GoImported main.go<commit_after>/*
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Command template is a trivial web server that uses the text/template (and
// html/template) package's "block" feature to implement a kind of template
// inheritance.
//
// It should be executed from the directory in which the source resides,
// as it will look for its template files in the current directory.
package main

import (
	"bufio"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

func main() {
	songs_root := "./songs_master" // 1st argument is the directory location
	filepath.Walk(songs_root, walkpath)

	fmt.Printf("%d Songs loaded.\n", len(filenames))

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/song/", songHandler)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
	log.Fatal(http.ListenAndServe("localhost:8090", nil))
}

// indexTemplate is the main site template.
// The default template includes two template blocks ("sidebar" and "content")
// that may be replaced in templates derived from this one.
var indexTemplate = template.Must(template.ParseFiles("templates/index.tmpl"))

// Index is a data structure used to populate an indexTemplate.
type Index struct {
	Title string
	Songs []string
}

type SongPage struct {
	Title   string
	Songs   []string
	Song    Song
	HasSong bool
}

// indexHandler is an HTTP handler that serves the index page.
func indexHandler(w http.ResponseWriter, r *http.Request) {
	data := &SongPage{
		Title:   "Indigo Song Book",
		HasSong: false,
		Songs:   filenames}

	if err := songTemplate.Execute(w, data); err != nil {
		log.Println(err)
	}
}

// imageTemplate is a clone of indexTemplate that provides
// alternate "sidebar" and "content" templates.
var songTemplate = template.Must(template.Must(indexTemplate.Clone()).ParseFiles("templates/song.tmpl"))

// Image is a data structure used to populate an imageTemplate.
type SongFile struct {
	Title string
	Lines []string
}

func loadSongFile(title string, transpose int) (*Song, error) {
	filename := "songs_master/" + title + ".song"

	song, err := ParseSongFile(filename, transpose)

	if err != nil {
		return nil, err
	}

	return song, nil
}

// imageHandler is an HTTP handler that serves the image pages.
func songHandler(w http.ResponseWriter, r *http.Request) {
	target := strings.TrimPrefix(r.URL.Path, "/song/")

	ind := sort.SearchStrings(filenames, target)
	if ind > len(filenames) && filenames[ind] != target {
		http.NotFound(w, r)
		return
	}

	var transpose = r.FormValue("transpose")
	if len(transpose) == 0 {
		transpose = "0"
	}
	t, err := strconv.Atoi(transpose)

	data, err := loadSongFile(target, t)
	if err != nil {
		log.Println(err)
		return
	}

	page_data := &SongPage{
		Title:   "Indigo Song Book",
		Song:    *data,
		HasSong: true,
		Songs:   filenames}

	if err := songTemplate.Execute(w, page_data); err != nil {
		log.Println(err)
	}
}

var filenames = make([]string, 0)

func walkpath(path string, f os.FileInfo, err error) error {
	if f.IsDir() && f.Name() != "songs_master" {
		return filepath.SkipDir
	} else if f.IsDir() {
		return nil
	}

	file := f.Name()[0 : len(f.Name())-5]
	filenames = append(filenames, file)

	fmt.Printf("%s\n", file)
	return nil
}

// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/dweinand/muxt/pkg/config"
	"github.com/dweinand/muxt/pkg/tmux"
	"os"
	"path/filepath"
	"strings"
)

func loadConfig(name string) (*config.Session, error) {
	configDir := filepath.Join(os.Getenv("HOME"), ".muxt")
	confiFile := strings.Join([]string{name, ".toml"}, "")
	configPath := filepath.Join(configDir, confiFile)
	return config.Load(configPath)
}

func start(name string) {
	session, err := loadConfig(name)
	if err != nil {
		panic(err)
	}
	err = tmux.StartSession(session)
	if err != nil {
		panic(err)
	}
}

func usage() {
	fmt.Print("muxt start [NAME]\n")
	os.Exit(1)
}

func main() {
	args := os.Args
	numArgs := len(args)
	if numArgs < 2 {
		usage()
	}
	command := args[1]
	switch command {
	case "start":
		if numArgs < 3 {
			usage()
		}
		start(args[2])
	default:
		usage()
	}
}
<commit_msg>No need for commands<commit_after>package main

import (
	"fmt"
	"github.com/dweinand/muxt/pkg/config"
	"github.com/dweinand/muxt/pkg/tmux"
	"os"
	"path/filepath"
	"strings"
)

func loadConfig(name string) (*config.Session, error) {
	configDir := filepath.Join(os.Getenv("HOME"), ".muxt")
	confiFile := strings.Join([]string{name, ".toml"}, "")
	configPath := filepath.Join(configDir, confiFile)
	return config.Load(configPath)
}

func start(name string) {
	session, err := loadConfig(name)
	if err != nil {
		panic(err)
	}
	err = tmux.StartSession(session)
	if err != nil {
		panic(err)
	}
}

func usage() {
	fmt.Print("muxt [NAME]\n")
	os.Exit(1)
}

func main() {
	args := os.Args
	numArgs := len(args)
	if numArgs < 1 {
		usage()
	}

	start(args[1])
}
<|endoftext|>


<commit_before>package main

import (
	"embed"
	"flag"
	"fmt"
	"io/fs"
	"log"
	"net/http"
	"os"
	"regexp"
	"time"

	"github.com/dgraph-io/badger/v2"
	"github.com/gojp/goreportcard/handlers"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	addr = flag.String("http", ":8000", "HTTP listen address")

	//go:embed assets/*
	embedFS embed.FS
)

func makeHandler(db *badger.DB, name string, fn func(http.ResponseWriter, *http.Request, *badger.DB, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		validPath := regexp.MustCompile(fmt.Sprintf(`^/%s/([a-zA-Z0-9\-_\/\.]+)$`, name))

		m := validPath.FindStringSubmatch(r.URL.Path)

		if m == nil {
			http.NotFound(w, r)
			return
		}
		if len(m) < 1 || m[1] == "" {
			http.Error(w, "Please enter a repository", http.StatusBadRequest)
			return
		}

		repo := m[1]

		// for backwards-compatibility, we must support URLs formatted as
		//   /report/[org]/[repo]
		// and they will be assumed to be github.com URLs. This is because
		// at first Go Report Card only supported github.com URLs, and
		// took only the org name and repo name as parameters. This is no longer the
		// case, but we do not want external links to break.
		oldFormat := regexp.MustCompile(fmt.Sprintf(`^/%s/([a-zA-Z0-9\-_]+)/([a-zA-Z0-9\-_]+)$`, name))
		m2 := oldFormat.FindStringSubmatch(r.URL.Path)
		if m2 != nil {
			// old format is being used
			repo = "github.com/" + repo
			log.Printf("Assuming intended repo is %q, redirecting", repo)
			http.Redirect(w, r, fmt.Sprintf("/%s/%s", name, repo), http.StatusMovedPermanently)
			return
		}

		fn(w, r, db, repo)
	}
}

func injectBadgerHandler(db *badger.DB, fn func(http.ResponseWriter, *http.Request, *badger.DB)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fn(w, r, db)
	}
}

// metrics provides functionality for monitoring the application status
type metrics struct {
	responseTimes *prometheus.SummaryVec
}

// setupMetrics creates custom Prometheus metrics for monitoring
// application statistics.
func setupMetrics() *metrics {
	m := &metrics{}
	m.responseTimes = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Name: "response_time_ms",
			Help: "Time to serve requests",
		},
		[]string{"endpoint"},
	)

	prometheus.MustRegister(m.responseTimes)
	return m
}

// recordDuration records the length of a request from start to now
func (m metrics) recordDuration(start time.Time, name string) {
	elapsed := time.Since(start)
	m.responseTimes.WithLabelValues(name).Observe(float64(elapsed.Milliseconds()))
	log.Printf("Served %s in %s", name, elapsed)
}

// instrument adds metric instrumentation to the handler passed in as argument
func (m metrics) instrument(path string, h http.HandlerFunc) (string, http.HandlerFunc) {
	return path, func(w http.ResponseWriter, r *http.Request) {
		defer m.recordDuration(time.Now(), r.URL.Path)
		h.ServeHTTP(w, r)
	}
}

func main() {
	flag.Parse()
	if err := os.MkdirAll("_repos/src/github.com", 0755); err != nil && !os.IsExist(err) {
		log.Fatal("ERROR: could not create repos dir: ", err)
	}

	db, err := badger.Open(badger.DefaultOptions("/usr/local/badger"))
	if err != nil {
		log.Fatal("ERROR: could not open badger db: ", err)
	}

	assetsFS, err := fs.Sub(embedFS, "assets")
	if err != nil {
		log.Fatal(err)
	}

	gh := handlers.GRCHandler{AssetsFS: http.FS(assetsFS)}

	defer db.Close()

	m := setupMetrics()

	http.HandleFunc(m.instrument("/assets/", http.StripPrefix("/assets/", http.FileServer(http.FS(assetsFS))).ServeHTTP))
	http.HandleFunc(m.instrument("/checks", injectBadgerHandler(db, handlers.CheckHandler)))
	http.HandleFunc(m.instrument("/report/", makeHandler(db, "report", gh.ReportHandler)))
	http.HandleFunc(m.instrument("/badge/", makeHandler(db, "badge", handlers.BadgeHandler)))
	http.HandleFunc(m.instrument("/high_scores/", injectBadgerHandler(db, gh.HighScoresHandler)))
	http.HandleFunc(m.instrument("/supporters/", gh.SupportersHandler))
	http.HandleFunc(m.instrument("/about/", gh.AboutHandler))
	http.HandleFunc(m.instrument("/", injectBadgerHandler(db, gh.HomeHandler)))

	http.Handle("/metrics", promhttp.Handler())

	log.Printf("Running on %s ...", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}
<commit_msg>use truncate option for badger<commit_after>package main

import (
	"embed"
	"flag"
	"fmt"
	"io/fs"
	"log"
	"net/http"
	"os"
	"regexp"
	"time"

	"github.com/dgraph-io/badger/v2"
	"github.com/gojp/goreportcard/handlers"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	addr = flag.String("http", ":8000", "HTTP listen address")

	//go:embed assets/*
	embedFS embed.FS
)

func makeHandler(db *badger.DB, name string, fn func(http.ResponseWriter, *http.Request, *badger.DB, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		validPath := regexp.MustCompile(fmt.Sprintf(`^/%s/([a-zA-Z0-9\-_\/\.]+)$`, name))

		m := validPath.FindStringSubmatch(r.URL.Path)

		if m == nil {
			http.NotFound(w, r)
			return
		}
		if len(m) < 1 || m[1] == "" {
			http.Error(w, "Please enter a repository", http.StatusBadRequest)
			return
		}

		repo := m[1]

		// for backwards-compatibility, we must support URLs formatted as
		//   /report/[org]/[repo]
		// and they will be assumed to be github.com URLs. This is because
		// at first Go Report Card only supported github.com URLs, and
		// took only the org name and repo name as parameters. This is no longer the
		// case, but we do not want external links to break.
		oldFormat := regexp.MustCompile(fmt.Sprintf(`^/%s/([a-zA-Z0-9\-_]+)/([a-zA-Z0-9\-_]+)$`, name))
		m2 := oldFormat.FindStringSubmatch(r.URL.Path)
		if m2 != nil {
			// old format is being used
			repo = "github.com/" + repo
			log.Printf("Assuming intended repo is %q, redirecting", repo)
			http.Redirect(w, r, fmt.Sprintf("/%s/%s", name, repo), http.StatusMovedPermanently)
			return
		}

		fn(w, r, db, repo)
	}
}

func injectBadgerHandler(db *badger.DB, fn func(http.ResponseWriter, *http.Request, *badger.DB)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fn(w, r, db)
	}
}

// metrics provides functionality for monitoring the application status
type metrics struct {
	responseTimes *prometheus.SummaryVec
}

// setupMetrics creates custom Prometheus metrics for monitoring
// application statistics.
func setupMetrics() *metrics {
	m := &metrics{}
	m.responseTimes = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Name: "response_time_ms",
			Help: "Time to serve requests",
		},
		[]string{"endpoint"},
	)

	prometheus.MustRegister(m.responseTimes)
	return m
}

// recordDuration records the length of a request from start to now
func (m metrics) recordDuration(start time.Time, name string) {
	elapsed := time.Since(start)
	m.responseTimes.WithLabelValues(name).Observe(float64(elapsed.Milliseconds()))
	log.Printf("Served %s in %s", name, elapsed)
}

// instrument adds metric instrumentation to the handler passed in as argument
func (m metrics) instrument(path string, h http.HandlerFunc) (string, http.HandlerFunc) {
	return path, func(w http.ResponseWriter, r *http.Request) {
		defer m.recordDuration(time.Now(), r.URL.Path)
		h.ServeHTTP(w, r)
	}
}

func main() {
	flag.Parse()
	if err := os.MkdirAll("_repos/src/github.com", 0755); err != nil && !os.IsExist(err) {
		log.Fatal("ERROR: could not create repos dir: ", err)
	}

	db, err := badger.Open(badger.DefaultOptions("/usr/local/badger").WithTruncate(true))
	if err != nil {
		log.Fatal("ERROR: could not open badger db: ", err)
	}

	assetsFS, err := fs.Sub(embedFS, "assets")
	if err != nil {
		log.Fatal(err)
	}

	gh := handlers.GRCHandler{AssetsFS: http.FS(assetsFS)}

	defer db.Close()

	m := setupMetrics()

	http.HandleFunc(m.instrument("/assets/", http.StripPrefix("/assets/", http.FileServer(http.FS(assetsFS))).ServeHTTP))
	http.HandleFunc(m.instrument("/checks", injectBadgerHandler(db, handlers.CheckHandler)))
	http.HandleFunc(m.instrument("/report/", makeHandler(db, "report", gh.ReportHandler)))
	http.HandleFunc(m.instrument("/badge/", makeHandler(db, "badge", handlers.BadgeHandler)))
	http.HandleFunc(m.instrument("/high_scores/", injectBadgerHandler(db, gh.HighScoresHandler)))
	http.HandleFunc(m.instrument("/supporters/", gh.SupportersHandler))
	http.HandleFunc(m.instrument("/about/", gh.AboutHandler))
	http.HandleFunc(m.instrument("/", injectBadgerHandler(db, gh.HomeHandler)))

	http.Handle("/metrics", promhttp.Handler())

	log.Printf("Running on %s ...", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	// "github.com/samuel/go-zookeeper/zk"
	"encoding/json"
	"net/http"
	"os"
	"strings"
	"time"
)

const (
	VERSION             string = "0.1.0"
	INFRA_SVC_EXHIBITOR string = "/exhibitor/v1/config/get-state"
)

var (
	version bool
	// the endpoint to use
	endpoint string
)

type ExhibitorState struct {
	ExhibitorConfig ExhibitorConfig `json:"config"`
}

type ExhibitorConfig struct {
	LogDir       string `json:"logIndexDirectory"`
	SnapshotsDir string `json:"zookeeperDataDirectory"`
}

func init() {
	flag.BoolVar(&version, "version", false, "Display version information")
	flag.StringVar(&endpoint, "endpoint", "", fmt.Sprintf("The endpoint to use. This depends on the infra service you want to back up. Example: localhost:8181 for Exhibitor"))

	flag.Usage = func() {
		fmt.Printf("Usage: %s [args]\n\n", os.Args[0])
		fmt.Println("Arguments:")
		flag.PrintDefaults()
	}
	flag.Parse()
}

func about() {
	fmt.Printf("This is burry in version %s\n", VERSION)
}

func get(url string, payload interface{}) error {
	c := &http.Client{Timeout: 2 * time.Second}
	r, err := c.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	fmt.Printf("Payload %#v\n", r.Body)
	return json.NewDecoder(r.Body).Decode(payload)
}

func datadirs() {
	stateurl := strings.Join([]string{"http://", endpoint, INFRA_SVC_EXHIBITOR}, "")
	econfig := &ExhibitorState{}
	if err := get(stateurl, econfig); err != nil {
		fmt.Printf("Can't parse response from endpoint due to %s\n", err)
	} else {
		fmt.Printf("Config %#v\n", econfig)
	}
}

func main() {
	if version {
		about()
		os.Exit(0)
	}
	if endpoint == "" {
		flag.Usage()
		os.Exit(1)
	}
	datadirs()
}
<commit_msg>inits ZK walker<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/samuel/go-zookeeper/zk"
	"net/http"
	"os"
	"strings"
	"time"
)

const (
	VERSION             string = "0.1.0"
	INFRA_SVC_EXHIBITOR string = "/exhibitor/v1/config/get-state"
)

var (
	version bool
	// the endpoint to use
	endpoint string
)

type ExhibitorState struct {
	ExhibitorConfig ExhibitorConfig `json:"config"`
}

type ExhibitorConfig struct {
	LogDir       string `json:"logIndexDirectory"`
	SnapshotsDir string `json:"zookeeperDataDirectory"`
}

func init() {
	flag.BoolVar(&version, "version", false, "Display version information")
	flag.StringVar(&endpoint, "endpoint", "", fmt.Sprintf("The endpoint to use. This depends on the infra service you want to back up. Example: localhost:8181 for Exhibitor"))

	flag.Usage = func() {
		fmt.Printf("Usage: %s [args]\n\n", os.Args[0])
		fmt.Println("Arguments:")
		flag.PrintDefaults()
	}
	flag.Parse()
}

func about() {
	fmt.Printf("This is burry in version %s\n", VERSION)
}

func get(url string, payload interface{}) error {
	c := &http.Client{Timeout: 2 * time.Second}
	r, err := c.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	return json.NewDecoder(r.Body).Decode(payload)
}

func datadirs() {
	stateurl := strings.Join([]string{"http://", endpoint, INFRA_SVC_EXHIBITOR}, "")
	econfig := &ExhibitorState{}
	if err := get(stateurl, econfig); err != nil {
		fmt.Printf("Can't parse response from endpoint due to %s\n", err)
	} else {
		fmt.Printf("Config %#v\n", econfig)
	}
}

func walkZK() {
	zks := []string{endpoint}
	conn, _, _ := zk.Connect(zks, time.Second)
	if children, stat, err := conn.Children("/"); err != nil {
		fmt.Println(fmt.Sprintf("Can't find znodes due to %s", err))
	} else {
		fmt.Println(fmt.Sprintf("%+v - %+v", children, stat))
		// for _, c := range children {
		// 	if _, _, err := conn.Get(c); err != nil {
		// 	}
		// }
	}
}

func main() {
	if version {
		about()
		os.Exit(0)
	}
	if endpoint == "" {
		flag.Usage()
		os.Exit(1)
	}
	walkZK()
}
<|endoftext|>


<commit_before>package main

import(
	"fmt"
	"net/http"
)

func mainHandler() http.HandlerFunc{
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
		fmt.Fprintf(w, "Hello World!! I'm working!")
	})
}

func main(){
	http.HandleFunc("/", mainHandler())
	http.ListenAndServe(":8080", nil)
}
<commit_msg>test again<commit_after>package main

import(
	"fmt"
	"net/http"
)

func mainHandler() http.HandlerFunc{
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
		fmt.Fprintf(w, "Hello World!! I'm working! Again now!")
	})
}

func main(){
	http.HandleFunc("/", mainHandler())
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>