<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	alsa "github.com/Narsil/alsa-go"
	"github.com/youpy/go-wav"
	"gopkg.in/gin-gonic/gin.v1"
)

func aplay(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}

	r := wav.NewReader(file)
	format, err := r.Format()
	if err != nil {
		return err
	}
	if format.AudioFormat != 1 {
		return fmt.Errorf("audio format (%x) is not supported", format.AudioFormat)
	}
	var sampleFormat alsa.SampleFormat
	switch format.BitsPerSample {
	case 8:
		sampleFormat = alsa.SampleFormatU8
	case 16:
		sampleFormat = alsa.SampleFormatS16LE
	default:
		return fmt.Errorf("sample format (%x) should be 8 or 16", format.BitsPerSample)
	}

	handle := alsa.New()
	err = handle.Open("default", alsa.StreamTypePlayback, alsa.ModeBlock)
	if err != nil {
		return err
	}
	defer handle.Close()
	handle.SampleFormat = sampleFormat
	handle.SampleRate = int(format.SampleRate)
	handle.Channels = int(format.NumChannels)
	fmt.Printf("format: %#v\n", handle)
	err = handle.ApplyHwParams()
	if err != nil {
		return err
	}

	buf, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	_, err = handle.Write(buf)
	return err
}

func main() {
	token := os.Getenv("TOKEN")
	fmt.Println("token:", token)

	r := gin.Default()

	r.POST("/play", func(c *gin.Context) {
		if q, ok := c.GetPostForm("token"); !ok || q != token {
			c.JSON(http.StatusOK, gin.H{
				"attachments": []map[string]string{
					map[string]string{
						"title": "error",
						"text":  "token is invalid",
						"color": "#bf271b",
					},
				},
			})
			return
		}
		err := aplay("/usr/local/share/bell.wav")
		if err != nil {
			fmt.Println(err)
			c.JSON(http.StatusOK, gin.H{
				"attachments": []map[string]string{
					map[string]string{
						"title": "error",
						"text":  err.Error(),
						"color": "#bf271b",
					},
				},
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{"text": "呼び出し中です..."})
	})

	r.Run()
}
<commit_msg>Add sleep<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	alsa "github.com/Narsil/alsa-go"
	"github.com/youpy/go-wav"
	"gopkg.in/gin-gonic/gin.v1"
)

func aplay(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}

	r := wav.NewReader(file)
	format, err := r.Format()
	if err != nil {
		return err
	}
	if format.AudioFormat != 1 {
		return fmt.Errorf("audio format (%x) is not supported", format.AudioFormat)
	}
	var sampleFormat alsa.SampleFormat
	switch format.BitsPerSample {
	case 8:
		sampleFormat = alsa.SampleFormatU8
	case 16:
		sampleFormat = alsa.SampleFormatS16LE
	default:
		return fmt.Errorf("sample format (%x) should be 8 or 16", format.BitsPerSample)
	}

	handle := alsa.New()
	err = handle.Open("default", alsa.StreamTypePlayback, alsa.ModeBlock)
	if err != nil {
		return err
	}
	defer handle.Close()
	handle.SampleFormat = sampleFormat
	handle.SampleRate = int(format.SampleRate)
	handle.Channels = int(format.NumChannels)
	err = handle.ApplyHwParams()
	if err != nil {
		return err
	}

	buf, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	ch := make(chan error)
	go func() {
		_, err := handle.Write(buf)
		ch <- err
	}()
	time.Sleep(time.Second)
	return <-ch
}

func main() {
	token := os.Getenv("TOKEN")
	fmt.Println("token:", token)

	r := gin.Default()

	r.POST("/play", func(c *gin.Context) {
		if q, ok := c.GetPostForm("token"); !ok || q != token {
			c.JSON(http.StatusOK, gin.H{
				"attachments": []map[string]string{
					map[string]string{
						"title": "error",
						"text":  "token is invalid",
						"color": "#bf271b",
					},
				},
			})
			return
		}
		err := aplay("/usr/local/share/bell.wav")
		if err != nil {
			fmt.Println(err)
			c.JSON(http.StatusOK, gin.H{
				"attachments": []map[string]string{
					map[string]string{
						"title": "error",
						"text":  err.Error(),
						"color": "#bf271b",
					},
				},
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{"text": "呼び出し中です..."})
	})

	r.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/mux"
	"github.com/teambrookie/showrss/betaseries"
	"github.com/teambrookie/showrss/dao"
	"github.com/teambrookie/showrss/handlers"
	"github.com/teambrookie/showrss/torrent"
	"golang.org/x/oauth2"

	"flag"

	"syscall"

	"strconv"
)

const version = "1.0.0"

func handleNewAuth(newAuth <-chan string, users map[string]bool, refreshLimiter chan<- time.Time) {
	for token := range newAuth {
		if exists := users[token]; !exists {
			users[token] = true
			log.Printf("New user token : %s\n", token)
			refreshLimiter <- time.Now()
		}
	}
}

func searchWorker(jobs <-chan dao.Episode, store dao.EpisodeStore, quality string) {
	for episode := range jobs {
		time.Sleep(2 * time.Second)
		log.Println("Processing : " + episode.Name)
		torrentLink, err := torrent.Search(strconv.Itoa(episode.ShowID), episode.Code, quality)
		log.Println("Result : " + torrentLink)
		if err != nil {
			log.Printf("Error processing %s : %s ...\n", episode.Name, err)
			continue
		}
		if torrentLink == "" {
			continue
		}
		episode.MagnetLink = torrentLink
		episode.LastModified = time.Now()
		err = store.UpdateEpisode(episode)
		if err != nil {
			log.Printf("Error saving %s to DB ...\n", episode.Name)
		}

	}
}

func refresh(limiter <-chan time.Time, users map[string]bool, db dao.EpisodeStore, betaseries betaseries.EpisodeProvider, episodeToSearch chan<- dao.Episode) {
	for {
		<-limiter
		log.Println("Refresh started")
		for user := range users {
			log.Printf("Refresing for user %s\n", user)
			episodes, err := betaseries.Episodes(user)
			if err != nil {
				log.Printf("Error retriving episodes for user %s : %s\n", user, err)
			}
			for _, ep := range episodes {
				err := db.AddEpisode(ep)
				if err != nil {
					log.Printf("Error adding episodes to database: %s", err)
				}
			}
		}
		log.Println("Passing not found episodes to the search worker")
		notFounds, err := db.GetAllNotFoundEpisode()
		if err != nil {
			log.Printf("Error retriving unfound episodes from db : %s\n", err)
		}
		for _, episode := range notFounds {
			episodeToSearch <- episode
		}

	}
}

func main() {

	//Opitional flag for passing the http server address and the db name
	var dbAddr = flag.String("db", "showrss.db", "DB address")
	flag.Parse()

	//API key and secret for Betaseries are retrieve from the environnement variables
	apiKey := os.Getenv("BETASERIES_KEY")
	if apiKey == "" {
		log.Fatalln("BETASERIES_KEY must be set in env")
	}

	apiSecret := os.Getenv("BETASERIES_SECRET")
	if apiSecret == "" {
		log.Fatalln("BETASERIES_SECRET must be set in env")
	}

	// The quality can be specified using an environnement variable
	quality := os.Getenv("SHOWRSS_QUALITY")
	if quality == "" {
		quality = "720p"
	}

	port := os.Getenv("PORT")
	if port == "" {
		port = "7777"
	}

	//workaround for Heroku
	// must enable runtime-dyno-metadata
	//with heroku labs:enable runtime-dyno-metadata -a <app name>
	hostname := os.Getenv("HEROKU_APP_NAME")
	host := fmt.Sprintf("https://%s.herokuapp.com", hostname)

	if hostname == "" {
		hostname, _ = os.Hostname()
		host = fmt.Sprintf("http://%s:%s", hostname, port)
	}

	redirectURL, err := url.Parse(fmt.Sprintf("%s/auth_callback", host))
	if err != nil {
		log.Fatalf("Error parsing redirectURL : %s", err)
	}
	redirectURLString := redirectURL.String()

	// Configuration for the Oauth authentification with Betaseries
	conf := oauth2.Config{
		ClientID:     apiKey,
		ClientSecret: apiSecret,
		Endpoint: oauth2.Endpoint{
			AuthURL:  "https://www.betaseries.com/authorize",
			TokenURL: "https://api.betaseries.com/oauth/access_token",
		},
		RedirectURL: redirectURLString,
	}

	episodeProvider := betaseries.Betaseries{APIKey: apiKey}

	log.Println("Starting server ...")
	log.Println("Connecting to db ...")

	//DB stuff
	store, err := dao.InitDB(*dbAddr)
	if err != nil {
		log.Fatalln("Error connecting to DB")
	}

	err = store.CreateBucket("episodes")
	if err != nil {
		log.Fatalln("Error when creating bucket")
	}

	// Worker stuff
	// A channel is used to pass the episode that we need to search
	episodeToSearch := make(chan dao.Episode, 1000)
	//searchWorker read the episode to search from the channel and if it found them save them in the db
	go searchWorker(episodeToSearch, store, quality)

	refreshLimiter := make(chan time.Time, 10)
	go func() {
		for t := range time.Tick(time.Hour * 1) {
			refreshLimiter <- t
		}
	}()

	// we use a map to store the users because why not (we only store the token for each user si that we can refresh the unseen episodes from Betaseries)
	users := make(map[string]bool)
	newAuthChan := make(chan string, 10)
	go handleNewAuth(newAuthChan, users, refreshLimiter)

	go refresh(refreshLimiter, users, store, episodeProvider, episodeToSearch)

	errChan := make(chan error, 10)

	mux := mux.NewRouter()
	mux.HandleFunc("/", handlers.HelloHandler)
	mux.Handle("/auth", handlers.OauthHandler(conf))
	mux.Handle("/auth_callback", handlers.AuthCallbackHandler(conf, newAuthChan))
	mux.Handle("/episodes", handlers.EpisodeHandler(store))
	mux.Handle("/rss/{user}", handlers.RSSHandler(store, episodeProvider))

	httpServer := http.Server{}
	httpServer.Addr = ":" + port
	httpServer.Handler = handlers.LoggingHandler(mux)

	log.Printf("HTTP service listening on %s", host)

	go func() {
		errChan <- httpServer.ListenAndServe()
	}()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	for {
		select {
		case err := <-errChan:
			if err != nil {
				log.Fatal(err)
			}
		case s := <-signalChan:
			log.Println(fmt.Sprintf("Captured %v. Exiting...", s))
			httpServer.Shutdown(context.Background())
			os.Exit(0)
		}
	}

}
<commit_msg>remove the s<commit_after>package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/mux"
	"github.com/teambrookie/showrss/betaseries"
	"github.com/teambrookie/showrss/dao"
	"github.com/teambrookie/showrss/handlers"
	"github.com/teambrookie/showrss/torrent"
	"golang.org/x/oauth2"

	"flag"

	"syscall"

	"strconv"
)

const version = "1.0.0"

func handleNewAuth(newAuth <-chan string, users map[string]bool, refreshLimiter chan<- time.Time) {
	for token := range newAuth {
		if exists := users[token]; !exists {
			users[token] = true
			log.Printf("New user token : %s\n", token)
			refreshLimiter <- time.Now()
		}
	}
}

func searchWorker(jobs <-chan dao.Episode, store dao.EpisodeStore, quality string) {
	for episode := range jobs {
		time.Sleep(2 * time.Second)
		log.Println("Processing : " + episode.Name)
		torrentLink, err := torrent.Search(strconv.Itoa(episode.ShowID), episode.Code, quality)
		log.Println("Result : " + torrentLink)
		if err != nil {
			log.Printf("Error processing %s : %s ...\n", episode.Name, err)
			continue
		}
		if torrentLink == "" {
			continue
		}
		episode.MagnetLink = torrentLink
		episode.LastModified = time.Now()
		err = store.UpdateEpisode(episode)
		if err != nil {
			log.Printf("Error saving %s to DB ...\n", episode.Name)
		}

	}
}

func refresh(limiter <-chan time.Time, users map[string]bool, db dao.EpisodeStore, betaseries betaseries.EpisodeProvider, episodeToSearch chan<- dao.Episode) {
	for {
		<-limiter
		log.Println("Refresh started")
		for user := range users {
			log.Printf("Refresing for user %s\n", user)
			episodes, err := betaseries.Episodes(user)
			if err != nil {
				log.Printf("Error retriving episodes for user %s : %s\n", user, err)
			}
			for _, ep := range episodes {
				err := db.AddEpisode(ep)
				if err != nil {
					log.Printf("Error adding episodes to database: %s", err)
				}
			}
		}
		log.Println("Passing not found episodes to the search worker")
		notFounds, err := db.GetAllNotFoundEpisode()
		if err != nil {
			log.Printf("Error retriving unfound episodes from db : %s\n", err)
		}
		for _, episode := range notFounds {
			episodeToSearch <- episode
		}

	}
}

func main() {

	//Opitional flag for passing the http server address and the db name
	var dbAddr = flag.String("db", "showrss.db", "DB address")
	flag.Parse()

	//API key and secret for Betaseries are retrieve from the environnement variables
	apiKey := os.Getenv("BETASERIES_KEY")
	if apiKey == "" {
		log.Fatalln("BETASERIES_KEY must be set in env")
	}

	apiSecret := os.Getenv("BETASERIES_SECRET")
	if apiSecret == "" {
		log.Fatalln("BETASERIES_SECRET must be set in env")
	}

	// The quality can be specified using an environnement variable
	quality := os.Getenv("SHOWRSS_QUALITY")
	if quality == "" {
		quality = "720p"
	}

	port := os.Getenv("PORT")
	if port == "" {
		port = "7777"
	}

	//workaround for Heroku
	// must enable runtime-dyno-metadata
	//with heroku labs:enable runtime-dyno-metadata -a <app name>
	hostname := os.Getenv("HEROKU_APP_NAME")
	host := fmt.Sprintf("http://%s.herokuapp.com", hostname)

	if hostname == "" {
		hostname, _ = os.Hostname()
		host = fmt.Sprintf("http://%s:%s", hostname, port)
	}

	redirectURL, err := url.Parse(fmt.Sprintf("%s/auth_callback", host))
	if err != nil {
		log.Fatalf("Error parsing redirectURL : %s", err)
	}
	redirectURLString := redirectURL.String()

	// Configuration for the Oauth authentification with Betaseries
	conf := oauth2.Config{
		ClientID:     apiKey,
		ClientSecret: apiSecret,
		Endpoint: oauth2.Endpoint{
			AuthURL:  "https://www.betaseries.com/authorize",
			TokenURL: "https://api.betaseries.com/oauth/access_token",
		},
		RedirectURL: redirectURLString,
	}

	episodeProvider := betaseries.Betaseries{APIKey: apiKey}

	log.Println("Starting server ...")
	log.Println("Connecting to db ...")

	//DB stuff
	store, err := dao.InitDB(*dbAddr)
	if err != nil {
		log.Fatalln("Error connecting to DB")
	}

	err = store.CreateBucket("episodes")
	if err != nil {
		log.Fatalln("Error when creating bucket")
	}

	// Worker stuff
	// A channel is used to pass the episode that we need to search
	episodeToSearch := make(chan dao.Episode, 1000)
	//searchWorker read the episode to search from the channel and if it found them save them in the db
	go searchWorker(episodeToSearch, store, quality)

	refreshLimiter := make(chan time.Time, 10)
	go func() {
		for t := range time.Tick(time.Hour * 1) {
			refreshLimiter <- t
		}
	}()

	// we use a map to store the users because why not (we only store the token for each user si that we can refresh the unseen episodes from Betaseries)
	users := make(map[string]bool)
	newAuthChan := make(chan string, 10)
	go handleNewAuth(newAuthChan, users, refreshLimiter)

	go refresh(refreshLimiter, users, store, episodeProvider, episodeToSearch)

	errChan := make(chan error, 10)

	mux := mux.NewRouter()
	mux.HandleFunc("/", handlers.HelloHandler)
	mux.Handle("/auth", handlers.OauthHandler(conf))
	mux.Handle("/auth_callback", handlers.AuthCallbackHandler(conf, newAuthChan))
	mux.Handle("/episodes", handlers.EpisodeHandler(store))
	mux.Handle("/rss/{user}", handlers.RSSHandler(store, episodeProvider))

	httpServer := http.Server{}
	httpServer.Addr = ":" + port
	httpServer.Handler = handlers.LoggingHandler(mux)

	log.Printf("HTTP service listening on %s", host)

	go func() {
		errChan <- httpServer.ListenAndServe()
	}()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	for {
		select {
		case err := <-errChan:
			if err != nil {
				log.Fatal(err)
			}
		case s := <-signalChan:
			log.Println(fmt.Sprintf("Captured %v. Exiting...", s))
			httpServer.Shutdown(context.Background())
			os.Exit(0)
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"./slack"
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"
)

func main() {
	config := loadDefaultConfig("./config.json")

	args := make(map[string]*string)
	args["channel"] = flag.String("c", config.Channel, "cannel name")
	args["botname"] = flag.String("n", config.Name, "bot name")
	args["icon"] = flag.String("i", config.Icon, "bot icon. emoji or URL ")
	args["incomingURL"] = flag.String("url", config.Url, "incomingURL")
	args["attachmentsFile"] = flag.String("a", "", "attachment filepath")
	args["param"] = flag.String("p", "", "parameters")
	noStdin := flag.Bool("e", false, "no stdin (for attachments post)")
	flag.Parse()

	output := ""
	if !*noStdin {
		output = getStdin()
	}

	if *args["attachmentsFile"] != "" {
		parameters := str2map(*args["param"], output)
		postAttachments(*args["incomingURL"], *args["attachmentsFile"], parameters)
	} else {
		simplePost(args, output)
	}
}

func postAttachments(incomingURL string, attachmentsFile string, parameters map[string]string) {
	var doc bytes.Buffer
	slackMessage := slack.SlackMessage{}
	tpl := template.Must(template.ParseFiles(attachmentsFile))
	tpl.Execute(&doc, parameters)
	json.Unmarshal(doc.Bytes(), &slackMessage)
	slack.PostSlack(incomingURL, slackMessage)
}

func simplePost(args map[string]*string, text string) {
	slack.PostSlack(*args["incomingURL"], slack.SlackMessage{
		text,
		*args["botname"],
		*args["channel"],
		*args["icon"],
		nil,
	})
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func str2map(param string, str string) map[string]string {
	paramlist := strings.Split(param, "&")
	result := make(map[string]string)

	for _, val := range paramlist {
		a := strings.Split(val, "=")
		if a[1] == "__stdin" {
			result[a[0]] = strings.Replace(str, "\n", "\\n", -1)
		} else {
			result[a[0]] = a[1]
		}
	}
	return result
}

func getStdin() (stdin string) {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		stdin += scanner.Text() + "\n"
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading stdin:", err)
	}
	return stdin
}

func loadDefaultConfig(configFilePath string) slack.Config {
	config := slack.Config{
		os.Getenv("SLACK_URL"),
		os.Getenv("SLACK_CHANNEL"),
		os.Getenv("SLACK_ICON"),
		os.Getenv("SLACK_NAME"),
	}

	if exists(configFilePath) {
		file, err := ioutil.ReadFile(configFilePath)
		if err != nil {
			panic(err)
		}
		json.Unmarshal(file, &config)
	}
	return config
}
<commit_msg>Fix the slack package path<commit_after>package main

import (
	"github.com/komukomo/postslack/slack"
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"
)

func main() {
	config := loadDefaultConfig("./config.json")

	args := make(map[string]*string)
	args["channel"] = flag.String("c", config.Channel, "cannel name")
	args["botname"] = flag.String("n", config.Name, "bot name")
	args["icon"] = flag.String("i", config.Icon, "bot icon. emoji or URL ")
	args["incomingURL"] = flag.String("url", config.Url, "incomingURL")
	args["attachmentsFile"] = flag.String("a", "", "attachment filepath")
	args["param"] = flag.String("p", "", "parameters")
	noStdin := flag.Bool("e", false, "no stdin (for attachments post)")
	flag.Parse()

	output := ""
	if !*noStdin {
		output = getStdin()
	}

	if *args["attachmentsFile"] != "" {
		parameters := str2map(*args["param"], output)
		postAttachments(*args["incomingURL"], *args["attachmentsFile"], parameters)
	} else {
		simplePost(args, output)
	}
}

func postAttachments(incomingURL string, attachmentsFile string, parameters map[string]string) {
	var doc bytes.Buffer
	slackMessage := slack.SlackMessage{}
	tpl := template.Must(template.ParseFiles(attachmentsFile))
	tpl.Execute(&doc, parameters)
	json.Unmarshal(doc.Bytes(), &slackMessage)
	slack.PostSlack(incomingURL, slackMessage)
}

func simplePost(args map[string]*string, text string) {
	slack.PostSlack(*args["incomingURL"], slack.SlackMessage{
		text,
		*args["botname"],
		*args["channel"],
		*args["icon"],
		nil,
	})
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func str2map(param string, str string) map[string]string {
	paramlist := strings.Split(param, "&")
	result := make(map[string]string)

	for _, val := range paramlist {
		a := strings.Split(val, "=")
		if a[1] == "__stdin" {
			result[a[0]] = strings.Replace(str, "\n", "\\n", -1)
		} else {
			result[a[0]] = a[1]
		}
	}
	return result
}

func getStdin() (stdin string) {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		stdin += scanner.Text() + "\n"
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading stdin:", err)
	}
	return stdin
}

func loadDefaultConfig(configFilePath string) slack.Config {
	config := slack.Config{
		os.Getenv("SLACK_URL"),
		os.Getenv("SLACK_CHANNEL"),
		os.Getenv("SLACK_ICON"),
		os.Getenv("SLACK_NAME"),
	}

	if exists(configFilePath) {
		file, err := ioutil.ReadFile(configFilePath)
		if err != nil {
			panic(err)
		}
		json.Unmarshal(file, &config)
	}
	return config
}
<|endoftext|>


<commit_before>/* httpstress-go is a CLI utility for stress testing of HTTP servers with many concurrent connections.

Usage: httpstress-go [options] <URL list>

Options:
 * `URL list` – URLs to fetch (required)
 * `-c NUM` – concurrent connections number (defaults to 1)
 * `-n NUM` – total connections number (optional)
 * `-v` – print version to stdout and exit

Example: httpstress-go -c 1000 http://localhost http://google.com

Returns 0 if no errors, 1 if some failed (see stdout), 2 on kill and 3 in case of invalid options.

Prints error count for each URL to stdout (does not count successful attempts).
Errors and debugging information go to stderr.

Error output is YAML-formatted. Example:
 errors:
   - location: http://localhost
     count:    334
   - location: http://127.0.0.1
     count:    333

Please note that this utility uses GOMAXPROCS environment variable if it's present.
If not, this defaults to CPU count + 1. */
package main

/* Copyright 2014 Chai Chillum

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. */

import (
	"flag"
	"fmt"
	"github.com/chillum/httpstress"
	"os"
	"runtime"
)

// Application version
const Version = "2.0"

func main() {
	var conn, max int
	flag.IntVar(&conn, "c", 1, "concurrent connections count")
	flag.IntVar(&max, "n", 0, "total connections (optional)")
	version := flag.Bool("v", false, "print version to stdout and exit")
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[options] <URL list>")
		fmt.Fprintln(os.Stderr, "  <URL list>: URLs to fetch (required)")
		flag.PrintDefaults()
		fmt.Fprintln(os.Stderr, "Docs:\n  https://godoc.org/github.com/chillum/httpstress-go")
		fmt.Fprintln(os.Stderr, "  godoc github.com/chillum/httpstress-go")
		fmt.Fprintln(os.Stderr, "Example:")
		fmt.Fprintln(os.Stderr, "  httpstress-go -c 1000 http://localhost http://google.com")
		os.Exit(3)
	}
	flag.Parse()

	if *version {
		fmt.Println("httpstress-go", Version)
		fmt.Println("httpstress", httpstress.Version)
		fmt.Println(runtime.Version(), runtime.GOOS, runtime.GOARCH)
		os.Exit(0)
	}

	urls := flag.Args()
	if len(urls) < 1 {
		flag.Usage()
	}

	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU() + 1)
	}

	setlimits(&conn) // Platform-specific code: see unix.go and windows.go for details.

	out, err := httpstress.Test(conn, max, urls)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", err)
		flag.Usage()
	}

	if len(out) > 0 {
		fmt.Println("errors:")
		for url, num := range out {
			fmt.Print("  - location: ", url, "\n    count:    ", num, "\n")
		}
		os.Exit(1)
	} else {
		fmt.Fprintln(os.Stderr, "Test finished. No failed requests.")
	}
}
<commit_msg>v2.0.1: GOOS and GOARCH in -v<commit_after>/* httpstress-go is a CLI utility for stress testing of HTTP servers with many concurrent connections.

Usage: httpstress-go [options] <URL list>

Options:
 * `URL list` – URLs to fetch (required)
 * `-c NUM` – concurrent connections number (defaults to 1)
 * `-n NUM` – total connections number (optional)
 * `-v` – print version to stdout and exit

Example: httpstress-go -c 1000 http://localhost http://google.com

Returns 0 if no errors, 1 if some failed (see stdout), 2 on kill and 3 in case of invalid options.

Prints error count for each URL to stdout (does not count successful attempts).
Errors and debugging information go to stderr.

Error output is YAML-formatted. Example:
 errors:
   - location: http://localhost
     count:    334
   - location: http://127.0.0.1
     count:    333

Please note that this utility uses GOMAXPROCS environment variable if it's present.
If not, this defaults to CPU count + 1. */
package main

/* Copyright 2014 Chai Chillum

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. */

import (
	"flag"
	"fmt"
	"github.com/chillum/httpstress"
	"os"
	"runtime"
)

// Application version
const Version = "2.0.1"

func main() {
	var conn, max int
	flag.IntVar(&conn, "c", 1, "concurrent connections count")
	flag.IntVar(&max, "n", 0, "total connections (optional)")
	version := flag.Bool("v", false, "print version to stdout and exit")
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[options] <URL list>")
		fmt.Fprintln(os.Stderr, "  <URL list>: URLs to fetch (required)")
		flag.PrintDefaults()
		fmt.Fprintln(os.Stderr, "Docs:\n  https://godoc.org/github.com/chillum/httpstress-go")
		fmt.Fprintln(os.Stderr, "  godoc github.com/chillum/httpstress-go")
		fmt.Fprintln(os.Stderr, "Example:")
		fmt.Fprintln(os.Stderr, "  httpstress-go -c 1000 http://localhost http://google.com")
		os.Exit(3)
	}
	flag.Parse()

	if *version {
		fmt.Println("httpstress-go", Version)
		fmt.Println("httpstress", httpstress.Version)
		fmt.Println(runtime.Version(), runtime.GOOS, runtime.GOARCH)
		os.Exit(0)
	}

	urls := flag.Args()
	if len(urls) < 1 {
		flag.Usage()
	}

	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU() + 1)
	}

	setlimits(&conn) // Platform-specific code: see unix.go and windows.go for details.

	out, err := httpstress.Test(conn, max, urls)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", err)
		flag.Usage()
	}

	if len(out) > 0 {
		fmt.Println("errors:")
		for url, num := range out {
			fmt.Print("  - location: ", url, "\n    count:    ", num, "\n")
		}
		os.Exit(1)
	} else {
		fmt.Fprintln(os.Stderr, "Test finished. No failed requests.")
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Foursquare Labs Inc.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"time"

	_ "expvar"
	_ "net/http/pprof"

	"github.com/apache/thrift/lib/go/thrift"
	"github.com/foursquare/fsgo/adminz"
	"github.com/foursquare/fsgo/report"
	"github.com/foursquare/quiver/hfile"
)

var version string = "HEAD?"
var buildTime string = "unknown?"

type SettingDefs struct {
	port    int
	rpcPort int

	downloadOnly bool

	debug bool

	bloom int

	mlock bool

	configJsonUrl string

	cachePath string

	zk            string
	discoveryPath string
}

var Settings SettingDefs

func readSettings() []string {
	s := SettingDefs{}
	flag.IntVar(&s.port, "port", 9999, "listen port")
	flag.IntVar(&s.rpcPort, "rpc-port", 0, "listen port for raw thrift rpc (framed tbinary)")

	flag.BoolVar(&s.debug, "debug", false, "print more output")

	flag.IntVar(&s.bloom, "bloom", 0, "bloom filter wrong-positive % (or 0 to disable): lower numbers use more RAM but filter more queries.")

	flag.BoolVar(&s.downloadOnly, "download-only", false, "exit after downloading remote files to local cache.")

	flag.BoolVar(&s.mlock, "mlock", false, "mlock mapped files in memory rather than copy to heap.")

	flag.StringVar(&s.configJsonUrl, "config-json", "", "URL of collection configuration json")

	flag.StringVar(&s.cachePath, "cache", os.TempDir(), "local path to write files fetched (*not* cleaned up automatically)")

	flag.StringVar(&s.zk, "zookeeper", "", "zookeeper")
	flag.StringVar(&s.discoveryPath, "discovery", "", "service discovery base path")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			`
Usage: %s [options] col1=path1 col2=path2 ...
`, os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	Settings = s

	if (len(flag.Args()) > 0) == (Settings.configJsonUrl != "") {
		log.Println("Collections must be specified OR URL to configuration json.")
		flag.Usage()
		os.Exit(-1)
	}

	return flag.Args()
}

func main() {
	log.Printf("Quiver version %s (built %s, %s).\n\n", version, buildTime, runtime.Version())
	t := time.Now()

	graphite := report.Flag()
	args := readSettings()

	stats := report.NewRecorder().
		EnableGCInfoCollection().
		MaybeReportTo(graphite).
		RegisterHttp().
		SetAsDefault()

	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}

	registrations := new(Registrations)

	if Settings.discoveryPath != "" && !Settings.downloadOnly {
		registrations.Connect()
		defer registrations.Close()
	}

	configs := getCollectionConfig(args)

	log.Println("Loading collections...")

	cs, err := hfile.LoadCollections(configs, Settings.cachePath, Settings.downloadOnly, stats)

	if err != nil {
		log.Fatal(err)
	}

	if Settings.downloadOnly {
		stats.FlushNow()
		return
	}

	if Settings.bloom > 0 {
		beforeBloom := time.Now()
		for _, c := range cs.Collections {
			log.Println("Calculating bloom filter for", c.Name)
			c.CalculateBloom(float64(Settings.bloom) / 100)
		}
		stats.TimeSince("startup.bloom", beforeBloom)
	}

	log.Printf("Serving on http://%s:%d/ \n", hostname, Settings.port)

	http.Handle("/rpc/HFileService", WrapHttpRpcHandler(cs, stats))

	admin := adminz.New()
	admin.KillfilePaths(adminz.Killfiles(Settings.port))

	admin.Servicez(func() interface{} {
		return struct {
			Collections map[string]*hfile.Reader `json:"collections"`
			Impl        string                   `json:"implementation"`
		}{
			cs.Collections,
			"quiver",
		}
	})

	admin.OnPause(registrations.Leave)
	admin.OnResume(func() {
		if Settings.discoveryPath != "" {
			registrations.Join(hostname, Settings.discoveryPath, configs, 0)
		}
	})

	http.HandleFunc("/hfilez", admin.ServicezHandler)
	http.HandleFunc("/", admin.ServicezHandler)

	http.HandleFunc("/debug/bloom/enable", func(w http.ResponseWriter, r *http.Request) {
		for _, c := range cs.Collections {
			c.EnableBloom()
		}
	})

	http.HandleFunc("/debug/bloom/disable", func(w http.ResponseWriter, r *http.Request) {
		for _, c := range cs.Collections {
			c.DisableBloom()
		}
	})

	http.HandleFunc("/debug/bloom/calc", func(w http.ResponseWriter, r *http.Request) {
		if falsePos, err := strconv.Atoi(r.URL.Query().Get("err")); err != nil {
			http.Error(w, err.Error(), 400)
		} else if falsePos > 99 || falsePos < 1 {
			http.Error(w, "`err` param must be a false pos rate between 0 and 100", 400)
		} else {
			admin.Pause()
			defer admin.Resume()
			for _, c := range cs.Collections {
				fmt.Fprintln(w, "Recalculating bloom for", c.Name)
				c.CalculateBloom(float64(falsePos) / 100)
			}
		}
	})

	runtime.GC()
	stats.FlushNow()

	admin.Start()
	stats.TimeSince("startup.total", t)

	if Settings.rpcPort > 0 {
		s, err := NewTRpcServer(fmt.Sprintf(":%d", Settings.rpcPort), WrapProcessor(cs, stats), thrift.NewTBinaryProtocolFactory(true, true))
		if err != nil {
			log.Fatalln("Could not open RPC port", Settings.rpcPort, err)
		} else {
			if err := s.Listen(); err != nil {
				log.Fatalln("Failed to listen on RPC port", err)
			}
			go func() {
				log.Fatalln(s.Serve())
			}()
			log.Println("Listening for raw RPC on", Settings.rpcPort)
		}

	}

	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", Settings.port), nil))
}
<commit_msg>add quiver_version to servicez<commit_after>// Copyright (C) 2015 Foursquare Labs Inc.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"time"

	_ "expvar"
	_ "net/http/pprof"

	"github.com/apache/thrift/lib/go/thrift"
	"github.com/foursquare/fsgo/adminz"
	"github.com/foursquare/fsgo/report"
	"github.com/foursquare/quiver/hfile"
)

var version string = "HEAD?"
var buildTime string = "unknown?"

type SettingDefs struct {
	port    int
	rpcPort int

	downloadOnly bool

	debug bool

	bloom int

	mlock bool

	configJsonUrl string

	cachePath string

	zk            string
	discoveryPath string
}

var Settings SettingDefs

func readSettings() []string {
	s := SettingDefs{}
	flag.IntVar(&s.port, "port", 9999, "listen port")
	flag.IntVar(&s.rpcPort, "rpc-port", 0, "listen port for raw thrift rpc (framed tbinary)")

	flag.BoolVar(&s.debug, "debug", false, "print more output")

	flag.IntVar(&s.bloom, "bloom", 0, "bloom filter wrong-positive % (or 0 to disable): lower numbers use more RAM but filter more queries.")

	flag.BoolVar(&s.downloadOnly, "download-only", false, "exit after downloading remote files to local cache.")

	flag.BoolVar(&s.mlock, "mlock", false, "mlock mapped files in memory rather than copy to heap.")

	flag.StringVar(&s.configJsonUrl, "config-json", "", "URL of collection configuration json")

	flag.StringVar(&s.cachePath, "cache", os.TempDir(), "local path to write files fetched (*not* cleaned up automatically)")

	flag.StringVar(&s.zk, "zookeeper", "", "zookeeper")
	flag.StringVar(&s.discoveryPath, "discovery", "", "service discovery base path")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			`
Usage: %s [options] col1=path1 col2=path2 ...
`, os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	Settings = s

	if (len(flag.Args()) > 0) == (Settings.configJsonUrl != "") {
		log.Println("Collections must be specified OR URL to configuration json.")
		flag.Usage()
		os.Exit(-1)
	}

	return flag.Args()
}

func main() {
	log.Printf("Quiver version %s (built %s, %s).\n\n", version, buildTime, runtime.Version())
	t := time.Now()

	graphite := report.Flag()
	args := readSettings()

	stats := report.NewRecorder().
		EnableGCInfoCollection().
		MaybeReportTo(graphite).
		RegisterHttp().
		SetAsDefault()

	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}

	registrations := new(Registrations)

	if Settings.discoveryPath != "" && !Settings.downloadOnly {
		registrations.Connect()
		defer registrations.Close()
	}

	configs := getCollectionConfig(args)

	log.Println("Loading collections...")

	cs, err := hfile.LoadCollections(configs, Settings.cachePath, Settings.downloadOnly, stats)

	if err != nil {
		log.Fatal(err)
	}

	if Settings.downloadOnly {
		stats.FlushNow()
		return
	}

	if Settings.bloom > 0 {
		beforeBloom := time.Now()
		for _, c := range cs.Collections {
			log.Println("Calculating bloom filter for", c.Name)
			c.CalculateBloom(float64(Settings.bloom) / 100)
		}
		stats.TimeSince("startup.bloom", beforeBloom)
	}

	log.Printf("Serving on http://%s:%d/ \n", hostname, Settings.port)

	http.Handle("/rpc/HFileService", WrapHttpRpcHandler(cs, stats))

	admin := adminz.New()
	admin.KillfilePaths(adminz.Killfiles(Settings.port))

	admin.Servicez(func() interface{} {
		return struct {
			Collections map[string]*hfile.Reader `json:"collections"`
			Impl        string                   `json:"implementation"`
                        QuiverVersion string                 `json:"quiver_version"`
		}{
			cs.Collections,
			"quiver",
                        version,
		}
	})

	admin.OnPause(registrations.Leave)
	admin.OnResume(func() {
		if Settings.discoveryPath != "" {
			registrations.Join(hostname, Settings.discoveryPath, configs, 0)
		}
	})

	http.HandleFunc("/hfilez", admin.ServicezHandler)
	http.HandleFunc("/", admin.ServicezHandler)

	http.HandleFunc("/debug/bloom/enable", func(w http.ResponseWriter, r *http.Request) {
		for _, c := range cs.Collections {
			c.EnableBloom()
		}
	})

	http.HandleFunc("/debug/bloom/disable", func(w http.ResponseWriter, r *http.Request) {
		for _, c := range cs.Collections {
			c.DisableBloom()
		}
	})

	http.HandleFunc("/debug/bloom/calc", func(w http.ResponseWriter, r *http.Request) {
		if falsePos, err := strconv.Atoi(r.URL.Query().Get("err")); err != nil {
			http.Error(w, err.Error(), 400)
		} else if falsePos > 99 || falsePos < 1 {
			http.Error(w, "`err` param must be a false pos rate between 0 and 100", 400)
		} else {
			admin.Pause()
			defer admin.Resume()
			for _, c := range cs.Collections {
				fmt.Fprintln(w, "Recalculating bloom for", c.Name)
				c.CalculateBloom(float64(falsePos) / 100)
			}
		}
	})

	runtime.GC()
	stats.FlushNow()

	admin.Start()
	stats.TimeSince("startup.total", t)

	if Settings.rpcPort > 0 {
		s, err := NewTRpcServer(fmt.Sprintf(":%d", Settings.rpcPort), WrapProcessor(cs, stats), thrift.NewTBinaryProtocolFactory(true, true))
		if err != nil {
			log.Fatalln("Could not open RPC port", Settings.rpcPort, err)
		} else {
			if err := s.Listen(); err != nil {
				log.Fatalln("Failed to listen on RPC port", err)
			}
			go func() {
				log.Fatalln(s.Serve())
			}()
			log.Println("Listening for raw RPC on", Settings.rpcPort)
		}

	}

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


<commit_before>package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"

	"github.com/gorilla/sessions"
	"github.com/joeshaw/envdecode"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

type configuration struct {
	ClientID              string   `env:"CLIENT_ID,required"`                                       // Google Client ID
	ClientSecret          string   `env:"CLIENT_SECRET,required"`                                   // Google Client Secret
	SessionSecret         string   `env:"SESSION_SECRET,required"`                                  // Random session auth key
	SessionEncrypttionKey string   `env:"SESSION_ENCRYPTION_KEY,required"`                          // Random session encryption key
	DNSName               string   `env:"DNS_NAME,required"`                                        // Public facing DNS Hostname
	CookieMaxAge          int      `env:"COOKIE_MAX_AGE,default=1440"`                              // Cookie MaxAge, Defaults to 1 day
	CookieName            string   `env:"COOKIE_NAME,default=sproxy_session"`                       // The name of the cookie
	ProxyURL              *url.URL `env:"PROXY_URL,default=http://localhost:8000/"`                 // URL to Proxy to
	CallbackPath          string   `env:"CALLBACK_PATH,default=/auth/callback/google"`              // Callback URL
	HealthCheckPath       string   `env:"HEALTH_CHECK_PATH,default=/en-US/static/html/credit.html"` // Health Check path in splunk, this path is proxied w/o auth. The default is a static file served by the splunk web server
	EmailSuffix           string   `env:"EMAIL_SUFFIX,default=@heroku.com"`                         // Required email suffix. Emails w/o this suffix will not be let in
	StateToken            string   `env:"STATE_TOKEN,required"`                                     // Token used when communicating with Google Oauth2 provider
}

var config configuration

func newOauth2Config() *oauth2.Config {
	return &oauth2.Config{
		ClientID:     config.ClientID,
		ClientSecret: config.ClientSecret,
		RedirectURL:  "https://" + config.DNSName + config.CallbackPath,
		Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
		Endpoint:     google.Endpoint,
	}
}

// Authorize the user based on the email stored in the named session and matching the suffix. If the email doesn't exist
// in the session or if the 'OpenIDUser' isn't set in the session, then redirect, otherwise set the X-Openid-User
// header to what was stored in the session.
func authorize(s sessions.Store, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logPrefix := fmt.Sprintf("app=sproxy fn=authorize method=%s path=%s\n",
			r.Method, r.URL.Path)

		session, err := s.Get(r, config.CookieName)
		if err != nil {
			log.Printf("%s auth=failed error=%q\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		o2c := newOauth2Config()

		redirect := o2c.AuthCodeURL(config.StateToken, oauth2.AccessTypeOnline)

		session.Values["return_to"] = r.URL.RequestURI()
		session.Save(r, w)

		email, ok := session.Values["email"]
		if !ok || email == nil || !strings.HasSuffix(email.(string), config.EmailSuffix) {
			if email == nil {
				email = ""
			}
			log.Printf("%s auth=failed missing=Email email=%s redirect=%s\n", logPrefix, email.(string), redirect)
			http.Redirect(w, r, redirect, http.StatusTemporaryRedirect)
			return
		}

		openIDUser, ok := session.Values["OpenIDUser"]
		if !ok || openIDUser == nil {
			if openIDUser == nil {
				openIDUser = ""
			}
			log.Printf("%s auth=failed missing=OpenIDUser user=%s redirect=%s\n", logPrefix, openIDUser.(string), redirect)
			http.Redirect(w, r, redirect, http.StatusFound)
			return
		}

		r.Header.Set("X-Openid-User", openIDUser.(string))
		h.ServeHTTP(w, r)
	})
}

// enforceXForwardedProto header is set before processing the handler.
// If it's not then redirect to the https version of the URL.
func enforceXForwardedProto(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		xff := r.Header.Get("X-Forwarded-Proto")
		if xff == "https" {
			h.ServeHTTP(w, r)
			return
		}

		u := new(url.URL)
		*u = *r.URL
		u.Scheme = "https"
		if u.Host == "" {
			u.Host = r.Host
		}

		http.Redirect(w, r, u.String(), http.StatusFound)
	})
}

// Set the OpenIDUser and other session values based on the data from Google
func handleGoogleCallback(s sessions.Store) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logPrefix := fmt.Sprintf("app=sproxy fn=callback method=%s path=%s\n",
			r.Method, r.URL.Path)

		o2c := newOauth2Config()

		if v := r.FormValue("state"); v != config.StateToken {
			log.Printf("%s callback=failed error=%s\n", logPrefix, fmt.Sprintf("Bad state token: %s", v))
			http.Error(w, "Bad Request", http.StatusBadRequest)
			return
		}

		ctx := context.Background()
		t, err := o2c.Exchange(r.Context(), r.FormValue("code"))
		if err != nil {
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		gp, err := fetchGoogleProfile(ctx, t, o2c)
		if err != nil {
			log.Printf("%s %s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if gp.Email == "" || !strings.HasSuffix(gp.Email, config.EmailSuffix) {
			err := fmt.Errorf("Invalid Google Profile Email: %q", gp.Email)
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusForbidden)
			return
		}

		session, err := s.Get(r, config.CookieName)
		if err != nil {
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		session.Values["email"] = gp.Email
		session.Values["GoogleID"] = gp.ID

		parts := strings.SplitN(gp.Email, "@", 2)
		if len(parts) < 2 {
			err := fmt.Errorf("Unable to determine OpenIDUser from email %q", gp.Email)
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		session.Values["OpenIDUser"] = strings.ToLower(parts[0])
		target, ok := session.Values["return_to"].(string)
		if !ok {
			target = "/"
		}

		if err := session.Save(r, w); err != nil {
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		log.Printf("%s callback=successful\n", logPrefix)
		http.Redirect(w, r, target, http.StatusFound)
	})
}

func handleAuthLogout(s sessions.Store) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logPrefix := fmt.Sprintf("app=sproxy fn=logout method=%s path=%s\n",
			r.Method, r.URL.Path)

		config.CookieMaxAge = -1
		session, err := s.Get(r, config.CookieName)
		if err != nil || session == nil {
			log.Printf("%s logout=failed error=%s\n", logPrefix, err.Error())
			return
		}

		// clear out session values
		//session.Values = map[interface{}]interface{}{}
		store.Options.MaxAge = -1
		session.Save(r, w)

		log.Printf("%s logout=successful\n", logPrefix)
		http.Redirect(w, r, "/", http.StatusFound)
	})
}

func main() {
	if err := envdecode.Decode(&config); err != nil {
		log.Fatal(err)
	}

	switch len(config.SessionEncrypttionKey) {
	case 16, 24, 32:
	default:
		log.Fatal("Length of SESSION_ENCRYPTION_KEY is not 16, 24 or 32")
	}

	store := sessions.NewCookieStore([]byte(config.SessionSecret), []byte(config.SessionEncrypttionKey))
	store.Options.MaxAge = config.CookieMaxAge
	store.Options.Secure = true

	proxy := httputil.NewSingleHostReverseProxy(config.ProxyURL)

	// Handle Google Callback
	http.Handle(config.CallbackPath, handleGoogleCallback(store))

	// Health Check
	http.Handle(config.HealthCheckPath, proxy)

	// Base HTTP Request handler
	http.Handle("/", enforceXForwardedProto(authorize(store, proxy)))

	http.Handle("/auth/logout", handleAuthLogout(store))
	http.Handle("/logout", handleAuthLogout(store))

	host := os.Getenv("HOST")

	port := os.Getenv("PORT")
	if port == "" {
		port = "5000"
	}

	listen := host + ":" + port
	log.Println("Listening on", listen)

	log.Fatal(http.ListenAndServe(listen, nil))
}
<commit_msg>correcting logout func<commit_after>package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"

	"github.com/gorilla/sessions"
	"github.com/joeshaw/envdecode"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

type configuration struct {
	ClientID              string   `env:"CLIENT_ID,required"`                                       // Google Client ID
	ClientSecret          string   `env:"CLIENT_SECRET,required"`                                   // Google Client Secret
	SessionSecret         string   `env:"SESSION_SECRET,required"`                                  // Random session auth key
	SessionEncrypttionKey string   `env:"SESSION_ENCRYPTION_KEY,required"`                          // Random session encryption key
	DNSName               string   `env:"DNS_NAME,required"`                                        // Public facing DNS Hostname
	CookieMaxAge          int      `env:"COOKIE_MAX_AGE,default=1440"`                              // Cookie MaxAge, Defaults to 1 day
	CookieName            string   `env:"COOKIE_NAME,default=sproxy_session"`                       // The name of the cookie
	ProxyURL              *url.URL `env:"PROXY_URL,default=http://localhost:8000/"`                 // URL to Proxy to
	CallbackPath          string   `env:"CALLBACK_PATH,default=/auth/callback/google"`              // Callback URL
	HealthCheckPath       string   `env:"HEALTH_CHECK_PATH,default=/en-US/static/html/credit.html"` // Health Check path in splunk, this path is proxied w/o auth. The default is a static file served by the splunk web server
	EmailSuffix           string   `env:"EMAIL_SUFFIX,default=@heroku.com"`                         // Required email suffix. Emails w/o this suffix will not be let in
	StateToken            string   `env:"STATE_TOKEN,required"`                                     // Token used when communicating with Google Oauth2 provider
}

var config configuration

func newOauth2Config() *oauth2.Config {
	return &oauth2.Config{
		ClientID:     config.ClientID,
		ClientSecret: config.ClientSecret,
		RedirectURL:  "https://" + config.DNSName + config.CallbackPath,
		Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
		Endpoint:     google.Endpoint,
	}
}

// Authorize the user based on the email stored in the named session and matching the suffix. If the email doesn't exist
// in the session or if the 'OpenIDUser' isn't set in the session, then redirect, otherwise set the X-Openid-User
// header to what was stored in the session.
func authorize(s sessions.Store, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logPrefix := fmt.Sprintf("app=sproxy fn=authorize method=%s path=%s\n",
			r.Method, r.URL.Path)

		session, err := s.Get(r, config.CookieName)
		if err != nil {
			log.Printf("%s auth=failed error=%q\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		o2c := newOauth2Config()

		redirect := o2c.AuthCodeURL(config.StateToken, oauth2.AccessTypeOnline)

		session.Values["return_to"] = r.URL.RequestURI()
		session.Save(r, w)

		email, ok := session.Values["email"]
		if !ok || email == nil || !strings.HasSuffix(email.(string), config.EmailSuffix) {
			if email == nil {
				email = ""
			}
			log.Printf("%s auth=failed missing=Email email=%s redirect=%s\n", logPrefix, email.(string), redirect)
			http.Redirect(w, r, redirect, http.StatusTemporaryRedirect)
			return
		}

		openIDUser, ok := session.Values["OpenIDUser"]
		if !ok || openIDUser == nil {
			if openIDUser == nil {
				openIDUser = ""
			}
			log.Printf("%s auth=failed missing=OpenIDUser user=%s redirect=%s\n", logPrefix, openIDUser.(string), redirect)
			http.Redirect(w, r, redirect, http.StatusFound)
			return
		}

		r.Header.Set("X-Openid-User", openIDUser.(string))
		h.ServeHTTP(w, r)
	})
}

// enforceXForwardedProto header is set before processing the handler.
// If it's not then redirect to the https version of the URL.
func enforceXForwardedProto(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		xff := r.Header.Get("X-Forwarded-Proto")
		if xff == "https" {
			h.ServeHTTP(w, r)
			return
		}

		u := new(url.URL)
		*u = *r.URL
		u.Scheme = "https"
		if u.Host == "" {
			u.Host = r.Host
		}

		http.Redirect(w, r, u.String(), http.StatusFound)
	})
}

// Set the OpenIDUser and other session values based on the data from Google
func handleGoogleCallback(s sessions.Store) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logPrefix := fmt.Sprintf("app=sproxy fn=callback method=%s path=%s\n",
			r.Method, r.URL.Path)

		o2c := newOauth2Config()

		if v := r.FormValue("state"); v != config.StateToken {
			log.Printf("%s callback=failed error=%s\n", logPrefix, fmt.Sprintf("Bad state token: %s", v))
			http.Error(w, "Bad Request", http.StatusBadRequest)
			return
		}

		ctx := context.Background()
		t, err := o2c.Exchange(r.Context(), r.FormValue("code"))
		if err != nil {
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		gp, err := fetchGoogleProfile(ctx, t, o2c)
		if err != nil {
			log.Printf("%s %s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if gp.Email == "" || !strings.HasSuffix(gp.Email, config.EmailSuffix) {
			err := fmt.Errorf("Invalid Google Profile Email: %q", gp.Email)
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusForbidden)
			return
		}

		session, err := s.Get(r, config.CookieName)
		if err != nil {
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		session.Values["email"] = gp.Email
		session.Values["GoogleID"] = gp.ID

		parts := strings.SplitN(gp.Email, "@", 2)
		if len(parts) < 2 {
			err := fmt.Errorf("Unable to determine OpenIDUser from email %q", gp.Email)
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		session.Values["OpenIDUser"] = strings.ToLower(parts[0])
		target, ok := session.Values["return_to"].(string)
		if !ok {
			target = "/"
		}

		if err := session.Save(r, w); err != nil {
			log.Printf("%s callback=failed error=%s\n", logPrefix, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		log.Printf("%s callback=successful\n", logPrefix)
		http.Redirect(w, r, target, http.StatusFound)
	})
}

func handleAuthLogout(s sessions.Store) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logPrefix := fmt.Sprintf("app=sproxy fn=logout method=%s path=%s\n",
			r.Method, r.URL.Path)

		config.CookieMaxAge = -1
		session, err := s.Get(r, config.CookieName)
		if err != nil || session == nil {
			log.Printf("%s logout=failed error=%s\n", logPrefix, err.Error())
			return
		}

		// clear out session values
		//session.Values = map[interface{}]interface{}{}
		//store.Options.MaxAge = -1
		session.Save(r, w)

		log.Printf("%s logout=successful\n", logPrefix)
		http.Redirect(w, r, "/", http.StatusFound)
	})
}

func main() {
	if err := envdecode.Decode(&config); err != nil {
		log.Fatal(err)
	}

	switch len(config.SessionEncrypttionKey) {
	case 16, 24, 32:
	default:
		log.Fatal("Length of SESSION_ENCRYPTION_KEY is not 16, 24 or 32")
	}

	store := sessions.NewCookieStore([]byte(config.SessionSecret), []byte(config.SessionEncrypttionKey))
	store.Options.MaxAge = config.CookieMaxAge
	store.Options.Secure = true

	proxy := httputil.NewSingleHostReverseProxy(config.ProxyURL)

	// Handle Google Callback
	http.Handle(config.CallbackPath, handleGoogleCallback(store))

	// Health Check
	http.Handle(config.HealthCheckPath, proxy)

	// Base HTTP Request handler
	http.Handle("/", enforceXForwardedProto(authorize(store, proxy)))

	http.Handle("/auth/logout", handleAuthLogout(store))
	http.Handle("/logout", handleAuthLogout(store))

	host := os.Getenv("HOST")

	port := os.Getenv("PORT")
	if port == "" {
		port = "5000"
	}

	listen := host + ":" + port
	log.Println("Listening on", listen)

	log.Fatal(http.ListenAndServe(listen, nil))
}
<|endoftext|>


<commit_before>package main

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

	"github.com/go-martini/martini"
	"github.com/golang/glog"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	"github.com/martini-contrib/render"
	"github.com/zakuni/foonwiki/models"
)

var db gorm.DB

func initDb() gorm.DB {
	db, err := gorm.Open("postgres", os.Getenv("DATABASE_URL"))
	checkErr(err, "sql.Open failed")
	db.DB()

	db.AutoMigrate(models.Wiki{})
	db.AutoMigrate(models.Page{})

	return db
}

func checkErr(err error, msg string) {
	if err != nil {
		glog.Fatalln(msg, err)
	}
}

func main() {
	// necessary for glog
	flag.Parse()

	db = initDb()
	defer db.Close()

	m := martini.Classic()
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
	}))

	m.Get("/", func(req *http.Request, r render.Render) {
		qid := req.URL.Query().Get("id")
		if qid != "" {
			id, _ := strconv.ParseInt(qid, 10, 64)
			var page models.Page
			db.First(&page, id)

			r.HTML(200, "page", struct {
				Title string
				Page  models.Page
			}{
				page.Name,
				page,
			})
		} else {
			var newPages []models.Page
			var updatedPages []models.Page
			db.Limit(5).Order("created_at desc").Find(&newPages)
			db.Limit(5).Order("updated_at desc").Find(&updatedPages)

			r.HTML(200, "index", struct {
				Title        string
				NewPages     []models.Page
				UpdatedPages []models.Page
			}{
				"",
				newPages,
				updatedPages,
			})
		}
	})

	m.Get("/pages", func(req *http.Request, r render.Render) {
		qid := req.URL.Query().Get("sortedby")
		var allPages []models.Page

		if qid == "created" {
			db.Order("created_at desc").Find(&allPages)
		} else if qid == "updated" {
			db.Order("updated_at desc").Find(&allPages)
		}

		r.HTML(200, "pages", struct {
			Title string
			Pages []models.Page
		}{
			"AllPages",
			allPages,
		})
	})

	m.Get("/pages/new", func(r render.Render) {
		r.HTML(200, "page", nil)
	})

	m.Post("/pages/", func(req *http.Request, params martini.Params, r render.Render) {
		var page models.Page
		page.Name = req.FormValue("pagename")
		if page.Name == "" {
			page.Name = time.Now().Format("2006/01/02 15:04:05")
		}
		page.Content = req.FormValue("content")
		db.Save(&page)
		r.Redirect("/?id=" + strconv.FormatInt(page.Id, 10))
	})

	m.Get("/pages/:id", func(params martini.Params, r render.Render) {
		qid := params["id"]

		id, _ := strconv.ParseInt(qid, 10, 64)
		var page models.Page
		db.First(&page, id)

		r.HTML(200, "page", struct {
			Title string
			Page  models.Page
		}{
			page.Name,
			page,
		})
	})

	m.Post("/pages/:id", func(req *http.Request, params martini.Params, r render.Render) {
		var page models.Page
		id := params["id"]

		db.First(&page, id)
		page.Name = req.FormValue("pagename")
		if page.Name == "" {
			page.Name = time.Now().Format("2006/01/02 15:04:05")
		}
		page.Content = req.FormValue("content")
		db.Save(&page)

		r.HTML(200, "page", struct {
			Title string
			Page  models.Page
		}{
			page.Name,
			page,
		})
	})

	m.Get("/:wiki", func(params martini.Params, r render.Render) {
		var wiki models.Wiki
		var pages []models.Page
		wikiName := params["wiki"]

		db.Where(models.Wiki{Name: wikiName}).FirstOrInit(&wiki)
		db.Model(&wiki).Related(&pages)
		wiki.Pages = pages

		r.HTML(200, "wiki", struct {
			Title string
			Wiki  models.Wiki
		}{
			wikiName,
			wiki,
		})
	})

	m.Get("/:wiki/:page", func(params martini.Params, r render.Render) {
		var wiki models.Wiki
		var page models.Page
		wikiName := params["wiki"]
		pageName := params["page"]

		db.Where(models.Wiki{Name: wikiName}).FirstOrInit(&wiki)
		if !db.NewRecord(wiki) {
			db.Where(models.Page{Name: pageName, WikiId: wiki.Id}).FirstOrInit(&page)
		} else {
			page.Name = pageName
		}

		r.HTML(200, "page", struct {
			Title    string
			WikiName string
			Page     models.Page
		}{
			wikiName + "/" + pageName,
			wikiName,
			page,
		})
	})

	m.Post("/:wiki/:page", func(req *http.Request, params martini.Params, r render.Render) {
		var wiki models.Wiki
		var page models.Page
		wikiName := params["wiki"]
		pageName := params["page"]

		db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
		db.FirstOrCreate(&page, models.Page{
			Name:   pageName,
			WikiId: wiki.Id,
		})
		page.Content = req.FormValue("content")
		page.Wiki = wiki
		db.Save(&page)

		r.HTML(200, "page", struct {
			Title    string
			WikiName string
			Page     models.Page
		}{
			wikiName + "/" + pageName,
			wikiName,
			page,
		})
	})

	m.Run()
}
<commit_msg>delete go file<commit_after><|endoftext|>


<commit_before>package main
import "os"
import "bufio"

func main() {
	inreader := bufio.NewReader(os.Stdin)
	outwriter := bufio.NewWriter(os.Stdout)

	Decrypt(inreader, outwriter)
}

func Decrypt(inreader *bufio.Reader, outwriter *bufio.Writer) {
	buff := make([]byte, 1024)
	key := NewKeyState("xxx")

	for {
		read, _ := inreader.Read(buff)
		if read == 0 {
			break
		}

		for i := 0; i < read; i++ {
			buff[i] = buff[i] ^ key.NextByte()
		}

		_, err := outwriter.Write(buff[:read])
		if err != nil {
			panic(err)
			break
		}
	}

	outwriter.Flush()
}
<commit_msg>Initial approach of zcryp feature set.<commit_after>package main
import "os"
import "bufio"
import "fmt"
import "flag"

func main() {
	input, output, mode, key, keylength := ReadFlags()
	PrintFlags(input, output, mode, key, keylength)

	if key == nil {
		fmt.Fprintln(os.Stderr, "A key must be supplied.")
		os.Exit(-1)
	}

	buffsize := 1024
	keystate := NewKeyState(*key)

	var inreader *bufio.Reader
	var outwriter *bufio.Writer

	if input != nil && output != nil {
		infile, inerr := os.Open(*input)
		if inerr != nil {
			fmt.Fprintln(os.Stderr, "can't get the stat of file %s", input)
			os.Exit(-1)
		}

		inreader = bufio.NewReader(infile)
		defer infile.Close()

		outfile, outerr := os.Create(*output)
		if outerr != nil {
			fmt.Fprintln(os.Stderr, "Can not creat temp file '%s'", output)
			os.Exit(-1)
		}

		outwriter = bufio.NewWriter(outfile)
		defer outfile.Close()

		fmt.Fprintln(os.Stderr, fmt.Sprintf("file mode, ZCRYP_BUFSIZ:%d len:%d", buffsize, keystate.keylen))
	} else {
		inreader = bufio.NewReader(os.Stdin)
		outwriter = bufio.NewWriter(os.Stdout)

		fmt.Fprintln(os.Stderr, fmt.Sprintf("pipe mode, ZCRYP_BUFSIZ:%d len:%d", buffsize, keystate.keylen))
	}

	err := Decrypt(inreader, outwriter, keystate, buffsize)
	if err != nil {
		panic(err)
	}

	fmt.Fprintln(os.Stderr, "Result: OK")
	os.Exit(0)
}

func ReadFlags() (*string, *string, *string, *string, *string) {
	var input, output, mode, key, length *string

	flag.String("i", "", "If using file mode, the input file to read.")
	flag.String("o", "", "If using file mode, the output file to create.")
	flag.String("m", "", "The encryption mode (unused, for compatibility).")
	flag.String("k", "", "The encryption key.")
	flag.String("l", "", "The encryption key length (unused, for compatibility).")

	// We're doing this wacky visitor bit here to ensure we have nil values for unset flags, which
	// allows us to detect unset flags and display (null) in the output.
	visitor := func(a *flag.Flag) {
		switch a.Name {
			case "i":
				temp := a.Value.String()
				input = &temp
				break
			case "o":
				temp := a.Value.String()
				output = &temp
				break
			case "m":
				temp := a.Value.String()
				mode = &temp
				break
			case "k":
				temp := a.Value.String()
				key = &temp
				break
			case "l":
				temp := a.Value.String()
				length = &temp
				break
		}
	}

	flag.Parse()
	flag.Visit(visitor)

	return input, output, mode, key, length
}

func PrintFlags(input *string, output *string, mode *string, key *string, length *string) {
	// Preserve the (null) display behavior of stock zcryp.
	printableinput := "(null)"
	printableoutput := "(null)"
	printablemode := "(null)"
	printablekey := "(null)"
	printablelength := "(null)"

	if input != nil {
		printableinput = *input
	}

	if output != nil {
		printableoutput = *output
	}

	if mode != nil {
		printablemode = *mode
	}

	if key != nil {
		printablekey = *key
	}

	if length != nil {
		printablelength = *length
	}

	fmt.Fprintln(os.Stderr, fmt.Sprintf("input->%s , output->%s , mode->%s , key->%s, len->%s", printableinput, printableoutput, printablemode, printablekey, printablelength))
}

func Decrypt(inreader *bufio.Reader, outwriter *bufio.Writer, key *KeyState, buffersize int) (error) {
	buff := make([]byte, buffersize)

	for {
		read, _ := inreader.Read(buff)
		if read == 0 {
			break
		}

		for i := 0; i < read; i++ {
			buff[i] = buff[i] ^ key.NextByte()
		}

		_, err := outwriter.Write(buff[:read])
		if err != nil {
			return err
		}
	}

	outwriter.Flush()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

func main() {
	fmt.Println("program start today")
	fmt.Println("yidane come here")
	fmt.Println("Frank-Ding:wq")
	fmt.Println("jiangyd conne here")
	fmt.Println("liupeng come here!!!")
	fmt.Println("test merger")
}
<commit_msg>develop on mydev branch<commit_after>package main

import (
	"fmt"
)

func main() {
	fmt.Println("program start today")
	fmt.Println("yidane come here")
	fmt.Println("Frank-Ding:wq")
	fmt.Println("jiangyd conne here")
	fmt.Println("liupeng come here!!!")
	fmt.Println("test merger")
	fmt.Println("develop on mydev branch")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/keybase/go-libcmdline"
	"github.com/keybase/go-libkb"
	"github.com/keybase/protocol/go"
	fmprpc "github.com/maxtaco/go-framed-msgpack-rpc"
	"github.com/ugorji/go/codec"
	"net"
	"net/rpc"
	"os"
)

// Keep this around to simplify things
var G = &libkb.G

type Daemon struct {
}

func RegisterProtocols(server *rpc.Server, c net.Conn) {
	keybase_1.RegisterSignup(server, SignupHandler{c})
	keybase_1.RegisterConfig(server, ConfigHandler{c})
	keybase_1.RegisterLogin(server, LoginHandler{c})
}

func (d *Daemon) Handle(c net.Conn) {
	server := rpc.NewServer()
	RegisterProtocols(server, c)
	var mh codec.MsgpackHandle
	rpcCodec := fmprpc.MsgpackSpecRpc.ServerCodec(c, &mh, true)
	server.ServeCodec(rpcCodec)
}

func (d *Daemon) Run() (err error) {
	if err = d.ConfigRpcServer(); err != nil {
		return
	}
	if err = d.ListenLoop(); err != nil {
		return
	}
	return
}

func (d *Daemon) ConfigRpcServer() (err error) {
	return nil
}

func (d *Daemon) ListenLoop() (err error) {

	var l net.Listener
	if l, err = G.BindToSocket(); err != nil {
		return
	}
	G.PushShutdownHook(func() error {
		G.Log.Info("Closing socket")
		return l.Close()
	})
	for {
		var c net.Conn
		if c, err = l.Accept(); err != nil {
			return
		}
		go d.Handle(c)

	}
	return nil
}

func (v *Daemon) ParseArgv(ctx *cli.Context) error {
	return nil
}

func (d *Daemon) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:     true,
		KbKeyring:  true,
		GpgKeyring: true,
		API:        true,
		Socket:     true,
	}
}

func parseArgs() (libkb.CommandLine, libcmdline.Command, error) {

	cl := libcmdline.NewCommandLine(false)
	cl.SetDefaultCommand("daemon", &Daemon{})

	cmd, err := cl.Parse(os.Args)
	if err != nil {
		err = fmt.Errorf("Error parsing command line arguments: %s\n", err.Error())
		return nil, nil, err
	}
	return cl, cmd, nil
}

func main() {
	libcmdline.Main(parseArgs, nil)
}
<commit_msg>run/runclient() split<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/keybase/go-libcmdline"
	"github.com/keybase/go-libkb"
	"github.com/keybase/protocol/go"
	fmprpc "github.com/maxtaco/go-framed-msgpack-rpc"
	"github.com/ugorji/go/codec"
	"net"
	"net/rpc"
	"os"
)

// Keep this around to simplify things
var G = &libkb.G

type Daemon struct {
}

func RegisterProtocols(server *rpc.Server, c net.Conn) {
	keybase_1.RegisterSignup(server, SignupHandler{c})
	keybase_1.RegisterConfig(server, ConfigHandler{c})
	keybase_1.RegisterLogin(server, LoginHandler{c})
}

func (d *Daemon) Handle(c net.Conn) {
	server := rpc.NewServer()
	RegisterProtocols(server, c)
	var mh codec.MsgpackHandle
	rpcCodec := fmprpc.MsgpackSpecRpc.ServerCodec(c, &mh, true)
	server.ServeCodec(rpcCodec)
}

func (d *Daemon) RunClient() (err error) {
	return fmt.Errorf("can't run daemon in client mode")
}

func (d *Daemon) Run() (err error) {
	if err = d.ConfigRpcServer(); err != nil {
		return
	}
	if err = d.ListenLoop(); err != nil {
		return
	}
	return
}

func (d *Daemon) ConfigRpcServer() (err error) {
	return nil
}

func (d *Daemon) ListenLoop() (err error) {

	var l net.Listener
	if l, err = G.BindToSocket(); err != nil {
		return
	}
	G.PushShutdownHook(func() error {
		G.Log.Info("Closing socket")
		return l.Close()
	})
	for {
		var c net.Conn
		if c, err = l.Accept(); err != nil {
			return
		}
		go d.Handle(c)

	}
	return nil
}

func (v *Daemon) ParseArgv(ctx *cli.Context) error {
	return nil
}

func (d *Daemon) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:     true,
		KbKeyring:  true,
		GpgKeyring: true,
		API:        true,
		Socket:     true,
	}
}

func parseArgs() (libkb.CommandLine, libcmdline.Command, error) {

	cl := libcmdline.NewCommandLine(false)
	cl.SetDefaultCommand("daemon", &Daemon{})

	cmd, err := cl.Parse(os.Args)
	if err != nil {
		err = fmt.Errorf("Error parsing command line arguments: %s\n", err.Error())
		return nil, nil, err
	}
	return cl, cmd, nil
}

func main() {
	libcmdline.Main(parseArgs, nil, false)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"cfd"
	"github.com/urfave/cli"
)

func listAllSupports() ([]string, error) {
	supports := []string{}
	for _, f := range cfd.HardwareFunctionsSet {
		supported, err := testFunctionSupported(f)
		if err != nil {
			return nil, err
		}
		if supported {
			supports = append(supports, f)
		}
	}
	return supports, nil
}

func showFunctionDetail(name string) (string, error) {
	var sensor cfd.Sensor
	switch name {
	case "gpu":
		sensor = cfd.NewGpuSensor()
	case "nvram":
		sensor = cfd.NewNVRAMSensor()
	case "qat":
		sensor = cfd.NewQATSensor()
	case "nic_bandwidth":
		sensor = cfd.NewNICBandwidthSensor()
	default:
		return "", errors.New("Not a valid function!")
	}

	return sensor.Detail()
}

func testFunctionSupported(name string) (bool, error) {
	var sensor cfd.Sensor
	switch name {
	case "gpu":
		sensor = cfd.NewGpuSensor()
	case "nvram":
		sensor = cfd.NewNVRAMSensor()
	case "qat":
		sensor = cfd.NewQATSensor()
	case "nic_bandwidth":
		sensor = cfd.NewNICBandwidthSensor()
	default:
		return false, errors.New("Not a valid function!")
	}

	return sensor.IsSupported()
}

func main() {
	app := cli.NewApp()
	app.EnableBashCompletion = true
	app.Version = "v1.0.0"
	app.Name = "Composable Function Discover"
	app.Usage = "A toolkit for discovering hardware functons in system"

	app.Commands = []cli.Command{
		{
			Name:    "list",
			Aliases: []string{"ls"},
			Usage:   "List the harware functions supported",
			Action: func(c *cli.Context) error {
				supports, _ := listAllSupports()
				fmt.Println(strings.Join(supports, ","))
				return nil
			},
		},
		{
			Name:    "show",
			Aliases: []string{"s"},
			Usage:   "Show the detal of one hardware function",
			Action: func(c *cli.Context) error {
				detail, _ := showFunctionDetail(c.String("function"))
				fmt.Println(detail)
				return nil
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "function, f",
					Usage: "Specify a hardware function to show detail",
				},
			},
		},
		{
			Name:    "test",
			Aliases: []string{"t"},
			Usage:   "Test if a hardware function supported",
			Action: func(c *cli.Context) error {
				isSupport, _ := testFunctionSupported(c.String("function"))
				fmt.Println(isSupport)
				return nil
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "function, f",
					Usage: "Specify a hardware function to test",
				},
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>Fix the typo of the app name.<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"cfd"
	"github.com/urfave/cli"
)

func listAllSupports() ([]string, error) {
	supports := []string{}
	for _, f := range cfd.HardwareFunctionsSet {
		supported, err := testFunctionSupported(f)
		if err != nil {
			return nil, err
		}
		if supported {
			supports = append(supports, f)
		}
	}
	return supports, nil
}

func showFunctionDetail(name string) (string, error) {
	var sensor cfd.Sensor
	switch name {
	case "gpu":
		sensor = cfd.NewGpuSensor()
	case "nvram":
		sensor = cfd.NewNVRAMSensor()
	case "qat":
		sensor = cfd.NewQATSensor()
	case "nic_bandwidth":
		sensor = cfd.NewNICBandwidthSensor()
	default:
		return "", errors.New("Not a valid function!")
	}

	return sensor.Detail()
}

func testFunctionSupported(name string) (bool, error) {
	var sensor cfd.Sensor
	switch name {
	case "gpu":
		sensor = cfd.NewGpuSensor()
	case "nvram":
		sensor = cfd.NewNVRAMSensor()
	case "qat":
		sensor = cfd.NewQATSensor()
	case "nic_bandwidth":
		sensor = cfd.NewNICBandwidthSensor()
	default:
		return false, errors.New("Not a valid function!")
	}

	return sensor.IsSupported()
}

func main() {
	app := cli.NewApp()
	app.EnableBashCompletion = true
	app.Version = "v1.0.0"
	app.Name = "Composable Function Discoverer"
	app.Usage = "A toolkit for discovering hardware functons in system"

	app.Commands = []cli.Command{
		{
			Name:    "list",
			Aliases: []string{"ls"},
			Usage:   "List the harware functions supported",
			Action: func(c *cli.Context) error {
				supports, _ := listAllSupports()
				fmt.Println(strings.Join(supports, ","))
				return nil
			},
		},
		{
			Name:    "show",
			Aliases: []string{"s"},
			Usage:   "Show the detal of one hardware function",
			Action: func(c *cli.Context) error {
				detail, _ := showFunctionDetail(c.String("function"))
				fmt.Println(detail)
				return nil
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "function, f",
					Usage: "Specify a hardware function to show detail",
				},
			},
		},
		{
			Name:    "test",
			Aliases: []string{"t"},
			Usage:   "Test if a hardware function supported",
			Action: func(c *cli.Context) error {
				isSupport, _ := testFunctionSupported(c.String("function"))
				fmt.Println(isSupport)
				return nil
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "function, f",
					Usage: "Specify a hardware function to test",
				},
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"

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

func usage() {
	os.Stderr.WriteString(`
Usage: alita [OPTION]... [FILE]...
Align FILE(s), or standard input.

Delimiter control:
  -d, --delimiter=DELIM    delimit line by DELIM
  -r, --regexp             DELIM is a regular expression
  -c, --count=COUNT        delimit line COUNT times

Output control:
  -m, --margin=FORMAT      join cells by FORMAT
  -j, --justify=SEQUENCE   justify cells by SEQUENCE

Miscellaneous:
  -h, --help               show this help message
      --version            print the version
`[1:])
}

func printVersion() {
	os.Stderr.WriteString(`
0.7.1
`[1:])
}

func printErr(err error) {
	fmt.Fprintln(os.Stderr, "alita:", err)
}

func guideToHelp() {
	os.Stderr.WriteString(`
Try 'alita --help' for more information.
`[1:])
}

func do(a *Aligner, r io.Reader) error {
	if err := a.ReadAll(r); err != nil {
		return err
	}
	return a.Flush(os.Stdout)
}

func _main() int {
	opt, err := ParseOption(os.Args[1:])
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}

	switch {
	case opt.IsHelp:
		usage()
		return 0
	case opt.IsVersion:
		printVersion()
		return 0
	}

	a, err := NewAligner(opt)
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}
	r, err := argf.From(opt.Files)
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}

	if err = do(a, r); err != nil {
		printErr(err)
		return 1
	}
	return 0
}

func main() {
	e := _main()
	os.Exit(e)
}
<commit_msg>s/usage/printUsage/<commit_after>package main

import (
	"fmt"
	"io"
	"os"

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

func printUsage() {
	os.Stderr.WriteString(`
Usage: alita [OPTION]... [FILE]...
Align FILE(s), or standard input.

Delimiter control:
  -d, --delimiter=DELIM    delimit line by DELIM
  -r, --regexp             DELIM is a regular expression
  -c, --count=COUNT        delimit line COUNT times

Output control:
  -m, --margin=FORMAT      join cells by FORMAT
  -j, --justify=SEQUENCE   justify cells by SEQUENCE

Miscellaneous:
  -h, --help               show this help message
      --version            print the version
`[1:])
}

func printVersion() {
	os.Stderr.WriteString(`
0.7.1
`[1:])
}

func printErr(err error) {
	fmt.Fprintln(os.Stderr, "alita:", err)
}

func guideToHelp() {
	os.Stderr.WriteString(`
Try 'alita --help' for more information.
`[1:])
}

func do(a *Aligner, r io.Reader) error {
	if err := a.ReadAll(r); err != nil {
		return err
	}
	return a.Flush(os.Stdout)
}

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
	}

	a, err := NewAligner(opt)
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}
	r, err := argf.From(opt.Files)
	if err != nil {
		printErr(err)
		guideToHelp()
		return 2
	}

	if err = do(a, r); err != nil {
		printErr(err)
		return 1
	}
	return 0
}

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


<commit_before>/*
七牛本地上传客户端
$ qn_cli --help
*/
package main

import (
	"crypto/md5"
	"encoding/hex"
	"flag"
	"fmt"
	"log"
	url2 "net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"golang.org/x/net/context"
	"qiniupkg.com/api.v7/kodo"
	"qiniupkg.com/api.v7/kodocli"
)

const Version string = "0.4.0"

var ignorePaths = []string{
	".git", ".hg", ".svn", ".module-cache", ".bin",
}

type stringSlice []string

func (s *stringSlice) String() string {
	return fmt.Sprintf("%s", *s)
}
func (s *stringSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

// 生成上传 token
func genUpToken(a *args, c *kodo.Client, key string) string {
	policy := kodo.PutPolicy{
		Scope: a.bucketName,
		// ReturnBody: `{"bucket": $(bucket),"key": $(key)}`,
		DetectMime: 1,
	}
	if key != "" {
		policy.SaveKey = key
		if a.overwrite {
			policy.Scope = policy.Scope + ":" + key
			policy.InsertOnly = 0
		}
	}
	return c.MakeUptoken(&policy)
}

// 上传本地文件
func uploadFile(
	uploader kodocli.Uploader, ctx context.Context, localFile, key, uptoken string) (ret *kodocli.PutRet, err error) {
	ret = &kodocli.PutRet{}
	if key == "" {
		err = uploader.PutFileWithoutKey(ctx, ret, uptoken, localFile, nil)
	} else {
		err = uploader.PutFile(ctx, ret, uptoken, key, localFile, nil)
	}
	return
}

// 自动生成文件名
func autoFileName(p string) (string, string, string) {
	dirname, name := path.Split(p)
	ext := path.Ext(name)
	return dirname, name, ext
}

func autoMD5FileName(p string) string {
	dirname, oldName, ext := autoFileName(p)
	now := int(time.Now().Nanosecond())
	hash := md5.Sum([]byte(
		strconv.Itoa(now),
	))
	newName := dirname + oldName + "_" + hex.EncodeToString(hash[:]) + ext
	return newName
}

func walkFiles(files []string, ignorePaths []string) (fileSlice []string) {
	for _, file := range files {
		matches, err := filepath.Glob(file)
		if err == nil {

			for _, path := range matches {
				// 遍历目录
				err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
					if err != nil {
						log.Print(err)
						return nil
					}

					// ignore ignorePaths
					for _, i := range ignorePaths {
						p := filepath.Base(path)
						if m, _ := filepath.Match(i, p); m {
							if info.IsDir() {
								return filepath.SkipDir
							}
							return nil
						}
					}
					if info.IsDir() {
						return nil
					}

					fileSlice = append(fileSlice, path)
					return nil
				})
				if err != nil {
					log.Print(err)
				}
			}
		}
	}

	return
}

func finalURL(bucketURL, key string) (url string) {
	u, _ := url2.Parse(bucketURL + key)
	url = u.String()
	return
}

type args struct {
	bucketName  string
	bucketURL   string
	fileSlice   []string
	key         string
	autoName    bool
	autoMD5Name bool
	overwrite   bool
	saveDir     string
	verbose     bool
	maxTasks    int
}

func parseArgs() *args {
	// 保存名称
	saveName := flag.String("n", "", "Save name")
	saveDir := flag.String("d", "", "Save dirname")
	autoName := flag.Bool("a", true, "Auto named saved files")
	autoMD5Name := flag.Bool("md5", false, "Auto named saved files use MD5 value")
	overwrite := flag.Bool("w", true, "Overwrite exists files")
	verbose := flag.Bool("v", false, "Verbose mode")
	version := flag.Bool("V", false, "Version info")
	maxTasks := flag.Int("max-tasks", 5, "Max upload tasks")
	var ignores stringSlice
	flag.Var(&ignores, "i", "ignores")

	flag.Parse()
	if *version {
		fmt.Println("qn_cli " + Version)
		os.Exit(0)
	}

	files := flag.Args()

	bucketName := os.Getenv("QINIU_BUCKET_NAME")
	bucketURL := os.Getenv("QINIU_BUCKET_URL")
	accessKey := os.Getenv("QINIU_ACCESS_KEY")
	secretKey := os.Getenv("QINIU_SECRET_KEY")
	if *verbose {
		fmt.Printf("bucketName: %s\n", bucketName)
		fmt.Printf("bucketURL: %s\n", bucketURL)
		fmt.Printf("accessKey: %s\n", accessKey)
		fmt.Printf("secretKey: %s\n", secretKey)
	}

	key := *saveName
	// 支持通配符
	fileSlice := walkFiles(files, ignorePaths)

	if len(fileSlice) == 0 {
		flag.PrintDefaults()
		fmt.Println("need files: qn_cli FILE [FILE ...]")
		os.Exit(1)
	}

	// 配置 accessKey, secretKey
	kodo.SetMac(accessKey, secretKey)
	if len(ignores) != 0 {
		ignorePaths = append(ignorePaths, ignores...)
	}

	return &args{
		bucketName:  bucketName,
		bucketURL:   bucketURL,
		fileSlice:   fileSlice,
		key:         key,
		autoName:    *autoName,
		autoMD5Name: *autoMD5Name,
		overwrite:   *overwrite,
		saveDir:     *saveDir,
		verbose:     *verbose,
		maxTasks:    *maxTasks,
	}
}

func main() {
	a := parseArgs()
	if a.verbose {
		fmt.Println(a)
	}

	// 定义任务组
	var wg sync.WaitGroup
	cts := 1

	// 上传文件
	for _, file := range a.fileSlice {
		// 正在上传的任务数超出了限制，等待上传完成
		if cts > a.maxTasks {
			wg.Wait()
		}

		// 增加一个任务
		wg.Add(1)
		cts++

		// 使用 goroutine 异步执行上传任务
		go func(file string) {
			defer wg.Done()          // 标记任务完成
			defer func() { cts-- }() // 正在进行的任务数减一

			key := a.key
			zone := 0
			c := kodo.New(zone, nil)
			uploader := kodocli.NewUploader(zone, nil)
			ctx := context.Background()

			if a.autoMD5Name && key == "" {
				key = autoMD5FileName(file)
			} else if a.autoName && key == "" {
				key = file
			}
			if a.saveDir != "" {
				key = path.Join(a.saveDir, key)
			}
			token := genUpToken(a, c, key)

			// 上传文件
			ret, err := uploadFile(uploader, ctx, file, key, token)
			if err != nil {
				if a.verbose {
					fmt.Printf("%s: %s ✕\n", file, err)
				} else {
					fmt.Printf("%s ✕\n", file)
				}
				log.Fatal(err)
			} else {
				url := finalURL(a.bucketURL, ret.Key)
				if a.verbose {
					fmt.Printf("%s: %s ✓\n", file, url)
				} else {
					fmt.Printf("%s\n", url)
				}
			}
		}(file)
	}

	// 等待所有任务完成
	wg.Wait()
}
<commit_msg>const Version -> func Version() string<commit_after>/*
七牛本地上传客户端
$ qn_cli --help
*/
package main

import (
	"crypto/md5"
	"encoding/hex"
	"flag"
	"fmt"
	"log"
	url2 "net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"golang.org/x/net/context"
	"qiniupkg.com/api.v7/kodo"
	"qiniupkg.com/api.v7/kodocli"
)

func Version() (v string) {
	return "0.4.1"
}

var ignorePaths = []string{
	".git", ".hg", ".svn", ".module-cache", ".bin",
}

type stringSlice []string

func (s *stringSlice) String() string {
	return fmt.Sprintf("%s", *s)
}
func (s *stringSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

// 生成上传 token
func genUpToken(a *args, c *kodo.Client, key string) string {
	policy := kodo.PutPolicy{
		Scope: a.bucketName,
		// ReturnBody: `{"bucket": $(bucket),"key": $(key)}`,
		DetectMime: 1,
	}
	if key != "" {
		policy.SaveKey = key
		if a.overwrite {
			policy.Scope = policy.Scope + ":" + key
			policy.InsertOnly = 0
		}
	}
	return c.MakeUptoken(&policy)
}

// 上传本地文件
func uploadFile(
	uploader kodocli.Uploader, ctx context.Context, localFile, key, uptoken string) (ret *kodocli.PutRet, err error) {
	ret = &kodocli.PutRet{}
	if key == "" {
		err = uploader.PutFileWithoutKey(ctx, ret, uptoken, localFile, nil)
	} else {
		err = uploader.PutFile(ctx, ret, uptoken, key, localFile, nil)
	}
	return
}

// 自动生成文件名
func autoFileName(p string) (string, string, string) {
	dirname, name := path.Split(p)
	ext := path.Ext(name)
	return dirname, name, ext
}

func autoMD5FileName(p string) string {
	dirname, oldName, ext := autoFileName(p)
	now := int(time.Now().Nanosecond())
	hash := md5.Sum([]byte(
		strconv.Itoa(now),
	))
	newName := dirname + oldName + "_" + hex.EncodeToString(hash[:]) + ext
	return newName
}

func walkFiles(files []string, ignorePaths []string) (fileSlice []string) {
	for _, file := range files {
		matches, err := filepath.Glob(file)
		if err == nil {

			for _, path := range matches {
				// 遍历目录
				err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
					if err != nil {
						log.Print(err)
						return nil
					}

					// ignore ignorePaths
					for _, i := range ignorePaths {
						p := filepath.Base(path)
						if m, _ := filepath.Match(i, p); m {
							if info.IsDir() {
								return filepath.SkipDir
							}
							return nil
						}
					}
					if info.IsDir() {
						return nil
					}

					fileSlice = append(fileSlice, path)
					return nil
				})
				if err != nil {
					log.Print(err)
				}
			}
		}
	}

	return
}

func finalURL(bucketURL, key string) (url string) {
	u, _ := url2.Parse(bucketURL + key)
	url = u.String()
	return
}

type args struct {
	bucketName  string
	bucketURL   string
	fileSlice   []string
	key         string
	autoName    bool
	autoMD5Name bool
	overwrite   bool
	saveDir     string
	verbose     bool
	maxTasks    int
}

func parseArgs() *args {
	// 保存名称
	saveName := flag.String("n", "", "Save name")
	saveDir := flag.String("d", "", "Save dirname")
	autoName := flag.Bool("a", true, "Auto named saved files")
	autoMD5Name := flag.Bool("md5", false, "Auto named saved files use MD5 value")
	overwrite := flag.Bool("w", true, "Overwrite exists files")
	verbose := flag.Bool("v", false, "Verbose mode")
	version := flag.Bool("V", false, "Version info")
	maxTasks := flag.Int("max-tasks", 5, "Max upload tasks")
	var ignores stringSlice
	flag.Var(&ignores, "i", "ignores")

	flag.Parse()
	if *version {
		fmt.Println("qn_cli " + Version())
		os.Exit(0)
	}

	files := flag.Args()

	bucketName := os.Getenv("QINIU_BUCKET_NAME")
	bucketURL := os.Getenv("QINIU_BUCKET_URL")
	accessKey := os.Getenv("QINIU_ACCESS_KEY")
	secretKey := os.Getenv("QINIU_SECRET_KEY")
	if *verbose {
		fmt.Printf("bucketName: %s\n", bucketName)
		fmt.Printf("bucketURL: %s\n", bucketURL)
		fmt.Printf("accessKey: %s\n", accessKey)
		fmt.Printf("secretKey: %s\n", secretKey)
	}

	key := *saveName
	// 支持通配符
	fileSlice := walkFiles(files, ignorePaths)

	if len(fileSlice) == 0 {
		flag.PrintDefaults()
		fmt.Println("need files: qn_cli FILE [FILE ...]")
		os.Exit(1)
	}

	// 配置 accessKey, secretKey
	kodo.SetMac(accessKey, secretKey)
	if len(ignores) != 0 {
		ignorePaths = append(ignorePaths, ignores...)
	}

	return &args{
		bucketName:  bucketName,
		bucketURL:   bucketURL,
		fileSlice:   fileSlice,
		key:         key,
		autoName:    *autoName,
		autoMD5Name: *autoMD5Name,
		overwrite:   *overwrite,
		saveDir:     *saveDir,
		verbose:     *verbose,
		maxTasks:    *maxTasks,
	}
}

func main() {
	a := parseArgs()
	if a.verbose {
		fmt.Println(a)
	}

	// 定义任务组
	var wg sync.WaitGroup
	cts := 1

	// 上传文件
	for _, file := range a.fileSlice {
		// 正在上传的任务数超出了限制，等待上传完成
		if cts > a.maxTasks {
			wg.Wait()
		}

		// 增加一个任务
		wg.Add(1)
		cts++

		// 使用 goroutine 异步执行上传任务
		go func(file string) {
			defer wg.Done()          // 标记任务完成
			defer func() { cts-- }() // 正在进行的任务数减一

			key := a.key
			zone := 0
			c := kodo.New(zone, nil)
			uploader := kodocli.NewUploader(zone, nil)
			ctx := context.Background()

			if a.autoMD5Name && key == "" {
				key = autoMD5FileName(file)
			} else if a.autoName && key == "" {
				key = file
			}
			if a.saveDir != "" {
				key = path.Join(a.saveDir, key)
			}
			token := genUpToken(a, c, key)

			// 上传文件
			ret, err := uploadFile(uploader, ctx, file, key, token)
			if err != nil {
				if a.verbose {
					fmt.Printf("%s: %s ✕\n", file, err)
				} else {
					fmt.Printf("%s ✕\n", file)
				}
				log.Fatal(err)
			} else {
				url := finalURL(a.bucketURL, ret.Key)
				if a.verbose {
					fmt.Printf("%s: %s ✓\n", file, url)
				} else {
					fmt.Printf("%s\n", url)
				}
			}
		}(file)
	}

	// 等待所有任务完成
	wg.Wait()
}
<|endoftext|>