<commit_before>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./lua" ./lua

import (
	"./modules/luaconf"
	"./modules/middleware"
	"./modules/scheduler"
	"./modules/static"
	"bufio"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/gin-gonic/contrib/gzip"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"log"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File sytem functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func serve(srv *gin.Engine, en_http bool, en_https bool, bind string, binds string, cert string, key string) {
	end := make(chan bool)
	if en_http {
		go serveHTTP(srv, bind)
	}
	if en_https {
		go serveHTTPS(srv, binds, cert, key)
	}
	<-end
}
func serveHTTP(srv *gin.Engine, bind string) {
	err := http.ListenAndServe(bind, srv)
	if err != nil {
		panic(err)
	}
}
func serveHTTPS(srv *gin.Engine, bind string, cert string, key string) {
	err := http.ListenAndServeTLS(bind, cert, key, srv)
	if err != nil {
		panic(err)
	}
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache

	// Use config
	flag.String("config", "", "Parse Config File")
	var script = flag.String("script", "", "Parse Lua Script as initialization")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 8080, "Port to run Webserver on (HTTP)")
	var ports = flag.Int("ports", 8443, "Port to run Webserver on (HTTPS)")
	var cert = flag.String("cert", "", "Certificate File for HTTPS")
	var key = flag.String("key", "", "Key File for HTTPS")
	var en_http = flag.Bool("http", true, "Listen HTTP")
	var en_https = flag.Bool("https", false, "Listen HTTPS")

	jobs = flag.Int("states", runtime.NumCPU(), "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", runtime.NumCPU(), "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Requests and Cache information")
	useGzip := flag.Bool("gzip", false, "Use GZIP")

	flag.Parse()

	if *en_https {
		if *key == "" || *cert == "" {
			panic("Need to have a Key and a Cert defined.")
		}
	}

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	filesystem = initPhysFS(root)
	defer physfs.Deinit()
	go scheduler.Run()        // Run the scheduler.
	go middleware.Preloader() // Run the Preloader.
	middleware.Init(*jobs)    // Run init sequence.
	if *script == "" {
		srv := new_server()
		if *useLogger {
			doLog = true
			srv.Use(gin.Logger())
		}
		if *useRecovery {
			srv.Use(gin.Recovery())
		}
		if *useGzip {
			srv.Use(gzip.Gzip(gzip.DefaultCompression))
		}
		bootstrap(srv, "", cfe)
		serve(srv, *en_http, *en_https, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	} else {
		srv, err := luaconf.Configure(*script, cfe, *webroot)
		if err != nil {
			panic(err)
		}
		serve(srv, *en_http, *en_https, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	}
}
<commit_msg>HTTP/2 support<commit_after>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./lua" ./lua

import (
	"./modules/luaconf"
	"./modules/middleware"
	"./modules/scheduler"
	"./modules/static"
	"bufio"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	http2 "github.com/bradfitz/http2"
	"github.com/gin-gonic/contrib/gzip"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"log"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File sytem functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func serve(srv *gin.Engine, en_http bool, en_https bool, en_http2 bool, bind string, binds string, cert string, key string) {
	end := make(chan bool)
	if en_http {
		go serveHTTP(srv, bind, en_http2)
	}
	if en_https {
		go serveHTTPS(srv, binds, en_http2, cert, key)
	}
	<-end
}
func serveHTTP(srv *gin.Engine, bind string, en_http2 bool) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServe()
	if err != nil {
		panic(err)
	}
}
func serveHTTPS(srv *gin.Engine, bind string, en_http2 bool, cert string, key string) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServeTLS(cert, key)
	if err != nil {
		panic(err)
	}
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache

	// Use config
	flag.String("config", "", "Parse Config File")
	var script = flag.String("script", "", "Parse Lua Script as initialization")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 8080, "Port to run Webserver on (HTTP)")
	var ports = flag.Int("ports", 8443, "Port to run Webserver on (HTTPS)")
	var cert = flag.String("cert", "", "Certificate File for HTTPS")
	var key = flag.String("key", "", "Key File for HTTPS")
	var en_http = flag.Bool("http", true, "Listen HTTP")
	var en_https = flag.Bool("https", false, "Listen HTTPS")
	var en_http2 = flag.Bool("http2", false, "Enable HTTP/2")

	jobs = flag.Int("states", runtime.NumCPU(), "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", runtime.NumCPU(), "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Requests and Cache information")
	useGzip := flag.Bool("gzip", false, "Use GZIP")

	flag.Parse()

	if *en_https {
		if *key == "" || *cert == "" {
			panic("Need to have a Key and a Cert defined.")
		}
	}

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	filesystem = initPhysFS(root)
	defer physfs.Deinit()
	go scheduler.Run()        // Run the scheduler.
	go middleware.Preloader() // Run the Preloader.
	middleware.Init(*jobs)    // Run init sequence.
	if *script == "" {
		srv := new_server()
		if *useLogger {
			doLog = true
			srv.Use(gin.Logger())
		}
		if *useRecovery {
			srv.Use(gin.Recovery())
		}
		if *useGzip {
			srv.Use(gzip.Gzip(gzip.DefaultCompression))
		}
		bootstrap(srv, "", cfe)
		serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	} else {
		srv, err := luaconf.Configure(*script, cfe, *webroot)
		if err != nil {
			panic(err)
		}
		serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	}
}
<|endoftext|>


<commit_before>package participant

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"

	"github.com/jinzhu/gorm"
)

func List(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	channelId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		fmt.Println(err)
		return helpers.NewBadRequestResponse(err)
	}

	req := models.NewChannelParticipant()
	req.ChannelId = channelId
	participants, err := req.List()
	if err != nil {
		if err == gorm.RecordNotFound {
			return helpers.NewNotFoundResponse()
		}
		return helpers.NewBadRequestResponse(err)
	}

	return helpers.NewOKResponse(participants)
}

func Add(u *url.URL, h http.Header, req *models.ChannelParticipant) (int, http.Header, interface{}, error) {
	channelId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	accountId, err := helpers.GetURIInt64(u, "accountId")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	if err := checkChannelPrerequisites(channelId, accountId); err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	req.AccountId = accountId
	req.ChannelId = channelId
	req.StatusConstant = models.ChannelParticipant_STATUS_ACTIVE

	if err := req.Create(); err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	return helpers.NewOKResponse(req)
}

func checkChannelPrerequisites(channelId, accountId int64) error {
	c := models.NewChannel()
	c.Id = channelId
	if err := c.Fetch(); err != nil {
		return err
	}
	if c.TypeConstant == models.Channel_TYPE_PINNED_ACTIVITY {
		return errors.New("You can not add a new participant into pinned activity channel")
	}
	return nil
}

func Delete(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	channelId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	accountId, err := helpers.GetURIInt64(u, "accountId")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	req := models.NewChannelParticipant()
	req.AccountId = accountId
	req.ChannelId = channelId

	if err := req.Delete(); err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	// yes it is deleted but not removed completely from our system
	return helpers.NewDeletedResponse()
}
<commit_msg>Social: add checks for business logic<commit_after>package participant

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"

	"github.com/jinzhu/gorm"
)

func List(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	channelId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		fmt.Println(err)
		return helpers.NewBadRequestResponse(err)
	}

	req := models.NewChannelParticipant()
	req.ChannelId = channelId
	participants, err := req.List()
	if err != nil {
		if err == gorm.RecordNotFound {
			return helpers.NewNotFoundResponse()
		}
		return helpers.NewBadRequestResponse(err)
	}

	return helpers.NewOKResponse(participants)
}

// todo fix duplicate code block with Delete handler
func Add(u *url.URL, h http.Header, req *models.ChannelParticipant) (int, http.Header, interface{}, error) {
	// we are getting requester from body for now, but it will be gotten
	// from the token
	requesterId := req.AccountId
	if requesterId == 0 {
		return helpers.NewBadRequestResponse(errors.New("Requester AccountId is not set"))
	}

	channelId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	accountId, err := helpers.GetURIInt64(u, "accountId")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	if err := checkChannelPrerequisites(channelId, requesterId, accountId); err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	// do not forget to override account id
	req.AccountId = accountId
	req.ChannelId = channelId
	req.StatusConstant = models.ChannelParticipant_STATUS_ACTIVE

	if err := req.Create(); err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	return helpers.NewOKResponse(req)
}

func checkChannelPrerequisites(channelId, requesterId, accountId int64) error {
	c := models.NewChannel()
	c.Id = channelId
	if err := c.Fetch(); err != nil {
		return err
	}
	if c.TypeConstant == models.Channel_TYPE_PINNED_ACTIVITY {
		return errors.New("You can not add a new participant into pinned activity channel")
	}
	return nil
}

func Delete(u *url.URL, h http.Header, req *models.ChannelParticipant) (int, http.Header, interface{}, error) {
	// we are getting requester from body for now, but it will be gotten
	// from the token
	requesterId := req.AccountId
	if requesterId == 0 {
		return helpers.NewBadRequestResponse(errors.New("Requester AccountId is not set"))
	}

	channelId, err := helpers.GetURIInt64(u, "id")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	accountId, err := helpers.GetURIInt64(u, "accountId")
	if err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	if err := checkChannelPrerequisites(channelId, requesterId, accountId); err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	// do not forget to override account id
	req.AccountId = accountId
	req.ChannelId = channelId

	if err := req.Delete(); err != nil {
		return helpers.NewBadRequestResponse(err)
	}

	// yes it is deleted but not removed completely from our system
	return helpers.NewDeletedResponse()
}
<|endoftext|>


<commit_before>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/helper"
	"time"

	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/koding/worker"
	"github.com/streadway/amqp"
)

var (
	PopularPostKey = "popularpost"
)

type Action func(*PopularPostController, []byte) error

type PopularPostController struct {
	routes map[string]Action
	log    logging.Logger
	redis  *redis.RedisSession
}

func (t *PopularPostController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func NewPopularPostController(log logging.Logger, redis *redis.RedisSession) *PopularPostController {
	ppc := &PopularPostController{
		log:   log,
		redis: redis,
	}

	routes := map[string]Action{
		// "api.channel_message_deleted": (*PopularPostController).MessageDeleted,

		"api.interaction_created": (*PopularPostController).InteractionSaved,
		"api.interaction_deleted": (*PopularPostController).InteractionDeleted,
	}

	ppc.routes = routes
	return ppc
}

func (f *PopularPostController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(f, data)
}

func (f *PopularPostController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent(1, data)
}

func (f *PopularPostController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent(-1, data)
}

func (f *PopularPostController) handleInteractionEvent(incrementCount int, data []byte) error {
	i, err := helper.MapToInteraction(data)
	if err != nil {
		return err
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(i.MessageId); err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if !f.isEligible(c, cm) {
		f.log.Error("Not eligible Interaction Id:%d", i.Id)
		return nil
	}

	_, err = f.redis.SortedSetIncrBy(GetDailyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetWeeklyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetMonthlyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	return nil

}

func (f *PopularPostController) isEligible(c *models.Channel, cm *models.ChannelMessage) bool {
	// to-do do not forget to remove return true
	return true

	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return false
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return false
	}

	return true
}

func PreparePopularPostKey(group, channelName, statisticName string, year, dateNumber int) string {
	return fmt.Sprintf(
		"%s:%s:%s:%s:%d:%s:%d",
		config.Get().Environment,
		group,
		PopularPostKey,
		channelName,
		year,
		statisticName,
		dateNumber,
	)
}

func GetDailyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	day := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		day = i.CreatedAt.UTC().YearDay()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now().UTC()
		day = now.YearDay()
		year, _, _ = now.Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "daily", year, day)
}

func GetWeeklyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	weekNumber := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		_, weekNumber = i.CreatedAt.ISOWeek()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now()
		_, weekNumber = now.ISOWeek()
		year, _, _ = now.UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "weekly", year, weekNumber)
}

func GetMonthlyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	var month time.Month
	year := 2014

	if !i.CreatedAt.IsZero() {
		year, month, _ = i.CreatedAt.UTC().Date()
	} else {
		year, month, _ = time.Now().UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "monthly", year, int(month))
}
<commit_msg>Social: fix forgotten serious bug<commit_after>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/helper"
	"time"

	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/koding/worker"
	"github.com/streadway/amqp"
)

var (
	PopularPostKey = "popularpost"
)

type Action func(*PopularPostController, []byte) error

type PopularPostController struct {
	routes map[string]Action
	log    logging.Logger
	redis  *redis.RedisSession
}

func (t *PopularPostController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func NewPopularPostController(log logging.Logger, redis *redis.RedisSession) *PopularPostController {
	ppc := &PopularPostController{
		log:   log,
		redis: redis,
	}

	routes := map[string]Action{
		// "api.channel_message_deleted": (*PopularPostController).MessageDeleted,

		"api.interaction_created": (*PopularPostController).InteractionSaved,
		"api.interaction_deleted": (*PopularPostController).InteractionDeleted,
	}

	ppc.routes = routes
	return ppc
}

func (f *PopularPostController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(f, data)
}

func (f *PopularPostController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent(1, data)
}

func (f *PopularPostController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent(-1, data)
}

func (f *PopularPostController) handleInteractionEvent(incrementCount int, data []byte) error {
	i, err := helper.MapToInteraction(data)
	if err != nil {
		return err
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(i.MessageId); err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if !f.isEligible(c, cm) {
		f.log.Error(fmt.Sprintf("Not eligible Interaction Id:%d", i.Id))
		return nil
	}

	_, err = f.redis.SortedSetIncrBy(GetDailyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetWeeklyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetMonthlyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	return nil

}

func (f *PopularPostController) isEligible(c *models.Channel, cm *models.ChannelMessage) bool {
	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return false
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return false
	}

	return true
}

func PreparePopularPostKey(group, channelName, statisticName string, year, dateNumber int) string {
	return fmt.Sprintf(
		"%s:%s:%s:%s:%d:%s:%d",
		config.Get().Environment,
		group,
		PopularPostKey,
		channelName,
		year,
		statisticName,
		dateNumber,
	)
}

func GetDailyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	day := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		day = i.CreatedAt.UTC().YearDay()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now().UTC()
		day = now.YearDay()
		year, _, _ = now.Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "daily", year, day)
}

func GetWeeklyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	weekNumber := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		_, weekNumber = i.CreatedAt.ISOWeek()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now()
		_, weekNumber = now.ISOWeek()
		year, _, _ = now.UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "weekly", year, weekNumber)
}

func GetMonthlyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	var month time.Month
	year := 2014

	if !i.CreatedAt.IsZero() {
		year, month, _ = i.CreatedAt.UTC().Date()
	} else {
		year, month, _ = time.Now().UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "monthly", year, int(month))
}
<|endoftext|>


<commit_before>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/helper"
	"time"

	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/koding/worker"
	"github.com/streadway/amqp"
)

var (
	PopularPostKey = "popularpost"
)

type Action func(*PopularPostController, []byte) error

type PopularPostController struct {
	routes map[string]Action
	log    logging.Logger
	redis  *redis.RedisSession
}

func (t *PopularPostController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func NewPopularPostController(log logging.Logger, redis *redis.RedisSession) *PopularPostController {
	ppc := &PopularPostController{
		log:   log,
		redis: redis,
	}

	routes := map[string]Action{
		// "api.channel_message_deleted": (*PopularPostController).MessageDeleted,

		"api.interaction_created": (*PopularPostController).InteractionSaved,
		"api.interaction_deleted": (*PopularPostController).InteractionDeleted,
	}

	ppc.routes = routes
	return ppc
}

func (f *PopularPostController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	f.log.Critical("oldumu%s", string(data))

	errr := handler(f, data)
	if errr != nil {
		f.log.Critical("oldumu%s", errr.Error())
	}
	return errr
}

func (f *PopularPostController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent("InteractionAdded", data)
}

func (f *PopularPostController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent("InteractionRemoved", data)
}

func (f *PopularPostController) handleInteractionEvent(eventName string, data []byte) error {
	i, err := helper.MapToInteraction(data)
	if err != nil {
		return err
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(i.MessageId); err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if !f.isEligible(c, cm) {
		f.log.Error("Not eligible Interaction Id:%d", i.Id)
		return nil
	}

	_, err = f.redis.SortedSetIncrBy(GetDailyKey(c, cm, i), 1, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetWeeklyKey(c, cm, i), 1, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetMonthlyKey(c, cm, i), 1, i.MessageId)
	if err != nil {
		return err
	}

	return nil

}

func (f *PopularPostController) isEligible(c *models.Channel, cm *models.ChannelMessage) bool {
	// to-do do not forget to remove return true
	return true

	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return false
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return false
	}

	return true
}

func PreparePopularPostKey(group, channelName, statisticName string, year, dateNumber int) string {
	return fmt.Sprintf(
		"%s:%s:%s:%s:%d:%s:%d",
		config.Get().Environment,
		group,
		PopularPostKey,
		channelName,
		year,
		statisticName,
		dateNumber,
	)
}

func GetDailyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	day := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		day = i.CreatedAt.UTC().YearDay()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now().UTC()
		day = now.YearDay()
		year, _, _ = now.Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "daily", year, day)
}

func GetWeeklyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	weekNumber := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		_, weekNumber = i.CreatedAt.ISOWeek()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now()
		_, weekNumber = now.ISOWeek()
		year, _, _ = now.UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "weekly", year, weekNumber)
}

func GetMonthlyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	var month time.Month
	year := 2014

	if !i.CreatedAt.IsZero() {
		year, month, _ = i.CreatedAt.UTC().Date()
	} else {
		year, month, _ = time.Now().UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "monthly", year, int(month))
}
<commit_msg>Social: cleanup popularposts worker<commit_after>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/helper"
	"time"

	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/koding/worker"
	"github.com/streadway/amqp"
)

var (
	PopularPostKey = "popularpost"
)

type Action func(*PopularPostController, []byte) error

type PopularPostController struct {
	routes map[string]Action
	log    logging.Logger
	redis  *redis.RedisSession
}

func (t *PopularPostController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func NewPopularPostController(log logging.Logger, redis *redis.RedisSession) *PopularPostController {
	ppc := &PopularPostController{
		log:   log,
		redis: redis,
	}

	routes := map[string]Action{
		// "api.channel_message_deleted": (*PopularPostController).MessageDeleted,

		"api.interaction_created": (*PopularPostController).InteractionSaved,
		"api.interaction_deleted": (*PopularPostController).InteractionDeleted,
	}

	ppc.routes = routes
	return ppc
}

func (f *PopularPostController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(f, data)
}

func (f *PopularPostController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent(1, data)
}

func (f *PopularPostController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent(-1, data)
}

func (f *PopularPostController) handleInteractionEvent(incrementCount int, data []byte) error {
	i, err := helper.MapToInteraction(data)
	if err != nil {
		return err
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(i.MessageId); err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if !f.isEligible(c, cm) {
		f.log.Error("Not eligible Interaction Id:%d", i.Id)
		return nil
	}

	_, err = f.redis.SortedSetIncrBy(GetDailyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetWeeklyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	_, err = f.redis.SortedSetIncrBy(GetMonthlyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	return nil

}

func (f *PopularPostController) isEligible(c *models.Channel, cm *models.ChannelMessage) bool {
	// to-do do not forget to remove return true
	return true

	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return false
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return false
	}

	return true
}

func PreparePopularPostKey(group, channelName, statisticName string, year, dateNumber int) string {
	return fmt.Sprintf(
		"%s:%s:%s:%s:%d:%s:%d",
		config.Get().Environment,
		group,
		PopularPostKey,
		channelName,
		year,
		statisticName,
		dateNumber,
	)
}

func GetDailyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	day := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		day = i.CreatedAt.UTC().YearDay()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now().UTC()
		day = now.YearDay()
		year, _, _ = now.Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "daily", year, day)
}

func GetWeeklyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	weekNumber := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		_, weekNumber = i.CreatedAt.ISOWeek()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now()
		_, weekNumber = now.ISOWeek()
		year, _, _ = now.UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "weekly", year, weekNumber)
}

func GetMonthlyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	var month time.Month
	year := 2014

	if !i.CreatedAt.IsZero() {
		year, month, _ = i.CreatedAt.UTC().Date()
	} else {
		year, month, _ = time.Now().UTC().Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, "monthly", year, int(month))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"
	"path/filepath"
	"io/ioutil"
	"net/http"
	"errors"
)

type User struct {
	Name string
	Dir string
}

func GetUsers(installationDir string) ([]User, error) {
	userdataDir := filepath.Join(installationDir, "userdata")
	files, err := ioutil.ReadDir(userdataDir)
	if err != nil {
		return nil, err
	}

	users := make([]User, 0)

	for _, userDir := range files {
		userId := userDir.Name()
		userDir := filepath.Join(userdataDir, userId, "config")
		configFile := filepath.Join(userDir, "localconfig.vdf")

		configBytes, err := ioutil.ReadFile(configFile)
		if err != nil {
			return nil, err
		}
		pattern := regexp.MustCompile(`"PersonaName"\s*"(.+?)"`)
		username := pattern.FindStringSubmatch(string(configBytes))[1]
		users = append(users, User{username, userDir})
	}

	return users, nil
}

const urlFormat = `http://steamcommunity.com/id/%v/games?tab=all`
func GetProfile(username string) (string, error) {
	response, err := http.Get(fmt.Sprintf(urlFormat, username))
	if err != nil {
		return "", err
	}

	contentBytes, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if err != nil {
		return "", err
	}

	return string(contentBytes), nil
}

type Game struct {
	Id string
	Name string
}

const gamePattern = `\{"appid":\s*(\d+),\s*"name":\s*"(.+?)"`
func GetGames(username string) ([]Game, error) {
	pattern := regexp.MustCompile(gamePattern)

	games := make([]Game, 0)

	profile, err := GetProfile(username)
	if err != nil {
		return nil, err
	}

	for _, groups := range pattern.FindAllStringSubmatch(profile, -1) {
		games = append(games, Game{groups[1], groups[2]})
	}

	return games, nil
}

const imageUrlFormat = `http://cdn.steampowered.com/v/gfx/apps/STORE_APP_ID_HERE/header.jpg`
func DownloadImage(gameid string, filename string) error {
	response, err := http.Get(imageUrlFormat)
	if err != nil {
		return err
	}

	imageBytes, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	return ioutil.WriteFile(filename, imageBytes, 0666)
}

func containsSteam(path string) bool {
	_, err := os.Stat(filepath.Join(path, "Steam"))
	return err != nil
}
func GetSteamInstallation() (path string, err error) {
	if len(os.Args) == 2 {
		argDir := os.Args[1]
		_, err := os.Stat(argDir)
		if err == nil {
			return argDir, nil
		} else {
			return "", errors.New("Argument must be a valid Steam directory, or empty for auto detection. Got: " + argDir)
		}
	}

	programFiles86Dir := filepath.Join(os.Getenv("ProgramFiles(x86)"), "Steam")
	if _, err = os.Stat(programFiles86Dir); err == nil {
		return programFiles86Dir, nil
	}

	programFilesDir := filepath.Join(os.Getenv("ProgramFiles"), "Steam")
	if _, err = os.Stat(programFilesDir); err == nil {
		return programFilesDir, nil
	}

	return "", errors.New("Could not find Steam installation folder.")
}

func main() {
	installationDir, err := GetSteamInstallation()
	if err != nil {
		panic(err)
	}

	users, err := GetUsers(installationDir)
	if err != nil {
		panic(err)
	}

	for _, user := range users {
		fmt.Printf("Found user %v. Fetching game list...\n", user.Name)
		continue

		games, err := GetGames(user.Name)
		if err != nil {
			panic(err)
		}

		fmt.Printf("Found %v games. Download images...\n", len(games))
		for _, game := range games {
			gridDir := filepath.Join(user.Dir, "grid")
			err := DownloadImage(game.Id, gridDir)
			fmt.Println(game.Id, game.Name)
			if err != nil {
				panic(err)
			}
		}
	}
}
<commit_msg>Remove unused function<commit_after>package main

import (
	"fmt"
	"os"
	"regexp"
	"path/filepath"
	"io/ioutil"
	"net/http"
	"errors"
)

type User struct {
	Name string
	Dir string
}

func GetUsers(installationDir string) ([]User, error) {
	userdataDir := filepath.Join(installationDir, "userdata")
	files, err := ioutil.ReadDir(userdataDir)
	if err != nil {
		return nil, err
	}

	users := make([]User, 0)

	for _, userDir := range files {
		userId := userDir.Name()
		userDir := filepath.Join(userdataDir, userId, "config")
		configFile := filepath.Join(userDir, "localconfig.vdf")

		configBytes, err := ioutil.ReadFile(configFile)
		if err != nil {
			return nil, err
		}
		pattern := regexp.MustCompile(`"PersonaName"\s*"(.+?)"`)
		username := pattern.FindStringSubmatch(string(configBytes))[1]
		users = append(users, User{username, userDir})
	}

	return users, nil
}

const urlFormat = `http://steamcommunity.com/id/%v/games?tab=all`
func GetProfile(username string) (string, error) {
	response, err := http.Get(fmt.Sprintf(urlFormat, username))
	if err != nil {
		return "", err
	}

	contentBytes, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if err != nil {
		return "", err
	}

	return string(contentBytes), nil
}

type Game struct {
	Id string
	Name string
}

const gamePattern = `\{"appid":\s*(\d+),\s*"name":\s*"(.+?)"`
func GetGames(username string) ([]Game, error) {
	pattern := regexp.MustCompile(gamePattern)

	games := make([]Game, 0)

	profile, err := GetProfile(username)
	if err != nil {
		return nil, err
	}

	for _, groups := range pattern.FindAllStringSubmatch(profile, -1) {
		games = append(games, Game{groups[1], groups[2]})
	}

	return games, nil
}

const imageUrlFormat = `http://cdn.steampowered.com/v/gfx/apps/STORE_APP_ID_HERE/header.jpg`
func DownloadImage(gameid string, filename string) error {
	response, err := http.Get(imageUrlFormat)
	if err != nil {
		return err
	}

	imageBytes, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	return ioutil.WriteFile(filename, imageBytes, 0666)
}

func GetSteamInstallation() (path string, err error) {
	if len(os.Args) == 2 {
		argDir := os.Args[1]
		_, err := os.Stat(argDir)
		if err == nil {
			return argDir, nil
		} else {
			return "", errors.New("Argument must be a valid Steam directory, or empty for auto detection. Got: " + argDir)
		}
	}

	programFiles86Dir := filepath.Join(os.Getenv("ProgramFiles(x86)"), "Steam")
	if _, err = os.Stat(programFiles86Dir); err == nil {
		return programFiles86Dir, nil
	}

	programFilesDir := filepath.Join(os.Getenv("ProgramFiles"), "Steam")
	if _, err = os.Stat(programFilesDir); err == nil {
		return programFilesDir, nil
	}

	return "", errors.New("Could not find Steam installation folder.")
}

func main() {
	installationDir, err := GetSteamInstallation()
	if err != nil {
		panic(err)
	}

	users, err := GetUsers(installationDir)
	if err != nil {
		panic(err)
	}

	for _, user := range users {
		fmt.Printf("Found user %v. Fetching game list...\n", user.Name)
		continue

		games, err := GetGames(user.Name)
		if err != nil {
			panic(err)
		}

		fmt.Printf("Found %v games. Download images...\n", len(games))
		for _, game := range games {
			gridDir := filepath.Join(user.Dir, "grid")
			err := DownloadImage(game.Id, gridDir)
			fmt.Println(game.Id, game.Name)
			if err != nil {
				panic(err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

const (
	htmlMaster = `
	{{define "master"}}

		<title>GJFY{{block "title" .}}{{end}}</title>
		<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
		<link rel="stylesheet" type="text/css" href="custom.css" />

	{{block "content" .}}{{end}}

	{{block "footer" .}}

			gjfy

	{{end}}

	{{end}}
	`
	htmlView = `
	{{define "title"}} - View Secret{{end}}
	{{define "content"}}

The following text is just for you:

		The link you invoked contains a secret (a password for example)
		somebody wants to share with you. It will be valid only for a short
		time and you may not be able to invoke it again. Please make sure
		you memorise the secret or write it down in an appropriate way.

The secret contained in this link is as follows:

{{.Secret}}

	{{end}}
	`
	htmlViewInfo = `
	{{define "title"}} - View Info{{end}}
	{{define "content"}}

Metadata for {{.Id}}

		Id
		MaxClicks
		Clicks
		DateAdded
		AuthToken

		{{.Id}}
		{{.MaxClicks}}
		{{.Clicks}}
		{{.DateAdded}}
		{{.AuthToken}}

	{{end}}
	`
	htmlViewErr = `
	{{define "title"}} - Error{{end}}
	{{define "content"}}

Not available

This ID is not valid anymore. Please request another one from the person who sent you this link.

	{{end}}
	`
	configDir   = "/etc/gjfy"
	cssFileName = "custom.css"
)

var (
	favicon = []byte{0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x10, 0x10, 0x10, 0x0, 0x1,
		0x0, 0x4, 0x0, 0x28, 0x1, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0,
		0x0, 0x10, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff,
		0xff, 0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xff, 0xff,
		0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xff, 0x3f, 0x0, 0x0,
		0xff, 0x9f, 0x0, 0x0, 0xfd, 0xdf, 0x0, 0x0, 0xfc, 0x9f, 0x0, 0x0, 0xfe,
		0x3f, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff,
		0x0, 0x0}
)
<commit_msg>proper html5<commit_after>package main

const (
	htmlMaster = `
	{{define "master"}}
	<!DOCTYPE html>

		<title>GJFY{{block "title" .}}{{end}}</title>
		<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
		<link rel="stylesheet" type="text/css" href="custom.css">

	{{block "content" .}}{{end}}

	{{block "footer" .}}

			gjfy

	{{end}}

	{{end}}
	`
	htmlView = `
	{{define "title"}} - View Secret{{end}}
	{{define "content"}}

The following text is just for you:

		The link you invoked contains a secret (a password for example)
		somebody wants to share with you. It will be valid only for a short
		time and you may not be able to invoke it again. Please make sure
		you memorise the secret or write it down in an appropriate way.

The secret contained in this link is as follows:

{{.Secret}}

	{{end}}
	`
	htmlViewInfo = `
	{{define "title"}} - View Info{{end}}
	{{define "content"}}

Metadata for {{.Id}}

		Id
		MaxClicks
		Clicks
		DateAdded
		AuthToken

		{{.Id}}
		{{.MaxClicks}}
		{{.Clicks}}
		{{.DateAdded}}
		{{.AuthToken}}

	{{end}}
	`
	htmlViewErr = `
	{{define "title"}} - Error{{end}}
	{{define "content"}}

Not available

This ID is not valid anymore. Please request another one from the person who sent you this link.

	{{end}}
	`
	configDir   = "/etc/gjfy"
	cssFileName = "custom.css"
)

var (
	favicon = []byte{0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x10, 0x10, 0x10, 0x0, 0x1,
		0x0, 0x4, 0x0, 0x28, 0x1, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0,
		0x0, 0x10, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff,
		0xff, 0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xff, 0xff,
		0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xfe, 0x7f, 0x0, 0x0, 0xff, 0x3f, 0x0, 0x0,
		0xff, 0x9f, 0x0, 0x0, 0xfd, 0xdf, 0x0, 0x0, 0xfc, 0x9f, 0x0, 0x0, 0xfe,
		0x3f, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff,
		0x0, 0x0}
)
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
)

func pageHTML(title string, content string) string {
	var html bytes.Buffer
	html.WriteString(`<!DOCTYPE html>

<title>`)
	html.WriteString(title)
	html.WriteString(`</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700" rel="stylesheet">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="/static/style.css">

		<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
		Toggle navigation

		</button>
		BookBrowser

Books

Authors

Series

Random Book

			<form class="navbar-form" role="search" method="GET" action="/search/">

				<input type="text" class="form-control" placeholder="Search" name="q" id="q">

					<button class="btn btn-default" type="submit"></button>

			</form>

`)
	html.WriteString(title)
	html.WriteString(`

`)
	html.WriteString(content)
	html.WriteString(`

BookBrowser ` + curversion + `

Copyright 2017 Patrick G

`)
	return html.String()
}

func bookHTML(b *Book, isCard bool) string {
	var html bytes.Buffer
	if isCard {
		html.WriteString(`<div class="book card">`)
	} else {
		html.WriteString(`<div class="book info">`)
	}

	html.WriteString(`<a class="cover" href="/books/` + b.ID + `">`)
	if b.HasCover {
		if isCard {
			html.WriteString(`<img alt="cover" src="/covers/` + b.ID + `_thumb.jpg" />`)
		} else {
			html.WriteString(`<img alt="cover" src="/covers/` + b.ID + `.jpg" />`)
		}
	} else {
		html.WriteString(`<img alt="cover" src="/static/nocover.jpg" />`)
	}
	html.WriteString(`</a>`)

	html.WriteString(`<div class="meta">`)
	html.WriteString(`<a class="title" href="/books/` + b.ID + `">`)
	html.WriteString(b.Title)
	html.WriteString(`</a>`)
	if b.Author != "" {
		html.WriteString(`<a class="author" href="/authors/` + b.AuthorID + `">`)
		html.WriteString(b.Author)
		html.WriteString(`</a>`)
	}
	if b.Series.Name != "" {
		html.WriteString(`<div class="series">`)
		html.WriteString(`<a class="name" href="/series/` + b.Series.ID + `">`)
		html.WriteString(b.Series.Name)
		html.WriteString(`</a> - <span class="index">`)
		html.WriteString(fmt.Sprintf("%v", b.Series.Index))
		html.WriteString(`</span>`)
		html.WriteString(`</div>`)
	}
	if !isCard && b.Description != "" {
		html.WriteString(`<div class="description">`)
		html.WriteString(b.Description)
		html.WriteString(`</div>`)
	}
	html.WriteString(`<a class="download btn btn-default" href="/download/` + b.ID + `.epub">Download EPUB</a>`)
	html.WriteString(`<a class="reader btn btn-default" href="/static/reader/#/download/` + b.ID + `.epub">Read</a>`)
	html.WriteString(`</div>`)

	html.WriteString(`</div>`)
	return html.String()
}

func bookListPageHTML(books []Book, title string, notfoundtext string) (html string, notfound bool) {
	if len(books) == 0 {
		return pageHTML("Not Found", notfoundtext), true
	}

	var booksHTML bytes.Buffer
	booksHTML.WriteString(`<div class="books cards">`)
	for _, b := range books {
		booksHTML.WriteString(bookHTML(&b, true))
	}
	booksHTML.WriteString(`</div>`)

	return pageHTML(title, booksHTML.String()), false
}

func itemCardHTML(title string, description string, link string) string {
	var html bytes.Buffer
	html.WriteString(`<a class="item card" href="` + link + `">`)
	html.WriteString(`<div class="title">`)
	html.WriteString(title)
	html.WriteString(`</div>`)
	html.WriteString(`<div class="description">`)
	html.WriteString(description)
	html.WriteString(`</div>`)
	html.WriteString(`</a>`)
	return html.String()
}
<commit_msg>Limit width of navbar to make it look better<commit_after>package main

import (
	"bytes"
	"fmt"
)

func pageHTML(title string, content string) string {
	var html bytes.Buffer
	html.WriteString(`<!DOCTYPE html>

<title>`)
	html.WriteString(title)
	html.WriteString(`</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700" rel="stylesheet">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="/static/style.css">

			<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
			Toggle navigation

			</button>
			BookBrowser

Books

Authors

Series

Random Book

				<form class="navbar-form" role="search" method="GET" action="/search/">

					<input type="text" class="form-control" placeholder="Search" name="q" id="q">

						<button class="btn btn-default" type="submit"></button>

				</form>

`)
	html.WriteString(title)
	html.WriteString(`

`)
	html.WriteString(content)
	html.WriteString(`

BookBrowser ` + curversion + `

Copyright 2017 Patrick G

`)
	return html.String()
}

func bookHTML(b *Book, isCard bool) string {
	var html bytes.Buffer
	if isCard {
		html.WriteString(`<div class="book card">`)
	} else {
		html.WriteString(`<div class="book info">`)
	}

	html.WriteString(`<a class="cover" href="/books/` + b.ID + `">`)
	if b.HasCover {
		if isCard {
			html.WriteString(`<img alt="cover" src="/covers/` + b.ID + `_thumb.jpg" />`)
		} else {
			html.WriteString(`<img alt="cover" src="/covers/` + b.ID + `.jpg" />`)
		}
	} else {
		html.WriteString(`<img alt="cover" src="/static/nocover.jpg" />`)
	}
	html.WriteString(`</a>`)

	html.WriteString(`<div class="meta">`)
	html.WriteString(`<a class="title" href="/books/` + b.ID + `">`)
	html.WriteString(b.Title)
	html.WriteString(`</a>`)
	if b.Author != "" {
		html.WriteString(`<a class="author" href="/authors/` + b.AuthorID + `">`)
		html.WriteString(b.Author)
		html.WriteString(`</a>`)
	}
	if b.Series.Name != "" {
		html.WriteString(`<div class="series">`)
		html.WriteString(`<a class="name" href="/series/` + b.Series.ID + `">`)
		html.WriteString(b.Series.Name)
		html.WriteString(`</a> - <span class="index">`)
		html.WriteString(fmt.Sprintf("%v", b.Series.Index))
		html.WriteString(`</span>`)
		html.WriteString(`</div>`)
	}
	if !isCard && b.Description != "" {
		html.WriteString(`<div class="description">`)
		html.WriteString(b.Description)
		html.WriteString(`</div>`)
	}
	html.WriteString(`<a class="download btn btn-default" href="/download/` + b.ID + `.epub">Download EPUB</a>`)
	html.WriteString(`<a class="reader btn btn-default" href="/static/reader/#/download/` + b.ID + `.epub">Read</a>`)
	html.WriteString(`</div>`)

	html.WriteString(`</div>`)
	return html.String()
}

func bookListPageHTML(books []Book, title string, notfoundtext string) (html string, notfound bool) {
	if len(books) == 0 {
		return pageHTML("Not Found", notfoundtext), true
	}

	var booksHTML bytes.Buffer
	booksHTML.WriteString(`<div class="books cards">`)
	for _, b := range books {
		booksHTML.WriteString(bookHTML(&b, true))
	}
	booksHTML.WriteString(`</div>`)

	return pageHTML(title, booksHTML.String()), false
}

func itemCardHTML(title string, description string, link string) string {
	var html bytes.Buffer
	html.WriteString(`<a class="item card" href="` + link + `">`)
	html.WriteString(`<div class="title">`)
	html.WriteString(title)
	html.WriteString(`</div>`)
	html.WriteString(`<div class="description">`)
	html.WriteString(description)
	html.WriteString(`</div>`)
	html.WriteString(`</a>`)
	return html.String()
}
<|endoftext|>


<commit_before>package yiigo

import (
	"bytes"
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

// defaultHTTPTimeout default http request timeout
const defaultHTTPTimeout = 10 * time.Second

// httpClientOptions http client options
type httpClientOptions struct {
	dialTimeout           time.Duration
	dialKeepAlive         time.Duration
	fallbackDelay         time.Duration
	maxIdleConns          int
	maxIdleConnsPerHost   int
	maxConnsPerHost       int
	idleConnTimeout       time.Duration
	tlsConfig             *tls.Config
	tlsHandshakeTimeout   time.Duration
	expectContinueTimeout time.Duration
}

// HTTPClientOption configures how we set up the http client
type HTTPClientOption interface {
	apply(options *httpClientOptions)
}

// funcHTTPClientOption implements http client option
type funcHTTPClientOption struct {
	f func(options *httpClientOptions)
}

func (fo *funcHTTPClientOption) apply(o *httpClientOptions) {
	fo.f(o)
}

func newFuncHTTPOption(f func(options *httpClientOptions)) *funcHTTPClientOption {
	return &funcHTTPClientOption{f: f}
}

// WithHTTPDialTimeout specifies the `DialTimeout` to net.Dialer.
func WithHTTPDialTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.dialTimeout = d
	})
}

// WithHTTPDialKeepAlive specifies the `KeepAlive` to net.Dialer.
func WithHTTPDialKeepAlive(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.dialKeepAlive = d
	})
}

// WithHTTPDialFallbackDelay specifies the `FallbackDelay` to net.Dialer.
func WithHTTPDialFallbackDelay(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.fallbackDelay = d
	})
}

// WithHTTPMaxIdleConns specifies the `MaxIdleConns` to http client.
func WithHTTPMaxIdleConns(n int) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.maxIdleConns = n
	})
}

// WithHTTPMaxIdleConnsPerHost specifies the `MaxIdleConnsPerHost` to http client.
func WithHTTPMaxIdleConnsPerHost(n int) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.maxIdleConnsPerHost = n
	})
}

// WithHTTPMaxConnsPerHost specifies the `MaxConnsPerHost` to http client.
func WithHTTPMaxConnsPerHost(n int) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.maxConnsPerHost = n
	})
}

// WithHTTPIdleConnTimeout specifies the `IdleConnTimeout` to http client.
func WithHTTPIdleConnTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.idleConnTimeout = d
	})
}

// WithHTTPTLSConfig specifies the `TLSClientConfig` to http client.
func WithHTTPTLSConfig(c *tls.Config) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.tlsConfig = c
	})
}

// WithHTTPTLSHandshakeTimeout specifies the `TLSHandshakeTimeout` to http client.
func WithHTTPTLSHandshakeTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.tlsHandshakeTimeout = d
	})
}

// WithHTTPExpectContinueTimeout specifies the `ExpectContinueTimeout` to http client.
func WithHTTPExpectContinueTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.expectContinueTimeout = d
	})
}

// httpRequestOptions http request options
type httpRequestOptions struct {
	headers map[string]string
	cookies []*http.Cookie
	close   bool
	timeout time.Duration
}

// HTTPRequestOption configures how we set up the http request
type HTTPRequestOption interface {
	apply(*httpRequestOptions)
}

// funcHTTPRequestOption implements request option
type funcHTTPRequestOption struct {
	f func(*httpRequestOptions)
}

func (fo *funcHTTPRequestOption) apply(r *httpRequestOptions) {
	fo.f(r)
}

func newFuncHTTPRequestOption(f func(*httpRequestOptions)) *funcHTTPRequestOption {
	return &funcHTTPRequestOption{f: f}
}

// WithRequestHeader specifies the header to http request.
func WithRequestHeader(key, value string) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.headers[key] = value
	})
}

// WithRequestCookies specifies the cookies to http request.
func WithRequestCookies(cookies ...*http.Cookie) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.cookies = cookies
	})
}

// WithRequestClose specifies close the connection after
// replying to this request (for servers) or after sending this
// request and reading its response (for clients).
func WithRequestClose(b bool) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.close = b
	})
}

// WithRequestTimeout specifies the timeout to http request.
func WithRequestTimeout(d time.Duration) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.timeout = d
	})
}

// HTTPClient http client
type HTTPClient struct {
	client *http.Client
}

// Get http get request
func (h *HTTPClient) Get(url string, options ...HTTPRequestOption) ([]byte, error) {
	req, err := http.NewRequest("GET", url, nil)

	if err != nil {
		return nil, err
	}

	o := &httpRequestOptions{
		headers: make(map[string]string),
		timeout: defaultHTTPTimeout,
	}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	if len(o.headers) > 0 {
		for k, v := range o.headers {
			req.Header.Set(k, v)
		}
	}

	if len(o.cookies) > 0 {
		for _, v := range o.cookies {
			req.AddCookie(v)
		}
	}

	if o.close {
		req.Close = true
	}

	ctx, cancel := context.WithTimeout(context.TODO(), o.timeout)

	defer cancel()

	resp, err := h.client.Do(req.WithContext(ctx))

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		io.Copy(ioutil.Discard, resp.Body)

		return nil, fmt.Errorf("error http code: %d", resp.StatusCode)
	}

	b, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return b, nil
}

// Post http post request
func (h *HTTPClient) Post(url string, body []byte, options ...HTTPRequestOption) ([]byte, error) {
	req, err := http.NewRequest("POST", url, bytes.NewReader(body))

	if err != nil {
		return nil, err
	}

	o := &httpRequestOptions{
		headers: make(map[string]string),
		timeout: defaultHTTPTimeout,
	}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	if len(o.headers) > 0 {
		for k, v := range o.headers {
			req.Header.Set(k, v)
		}
	}

	if len(o.cookies) > 0 {
		for _, v := range o.cookies {
			req.AddCookie(v)
		}
	}

	if o.close {
		req.Close = true
	}

	ctx, cancel := context.WithTimeout(context.TODO(), o.timeout)

	defer cancel()

	resp, err := h.client.Do(req.WithContext(ctx))

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		io.Copy(ioutil.Discard, resp.Body)

		return nil, fmt.Errorf("error http code: %d", resp.StatusCode)
	}

	b, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return b, nil
}

// defaultHTTPClient default http client
var defaultHTTPClient = &HTTPClient{
	client: &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 60 * time.Second,
			}).DialContext,
			MaxIdleConns:          0,
			MaxIdleConnsPerHost:   1000,
			MaxConnsPerHost:       1000,
			IdleConnTimeout:       60 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	},
}

// NewHTTPClient returns a new http client
func NewHTTPClient(options ...HTTPClientOption) *HTTPClient {
	o := &httpClientOptions{
		dialTimeout:           30 * time.Second,
		dialKeepAlive:         60 * time.Second,
		maxIdleConns:          0,
		maxIdleConnsPerHost:   1000,
		maxConnsPerHost:       1000,
		idleConnTimeout:       60 * time.Second,
		tlsHandshakeTimeout:   10 * time.Second,
		expectContinueTimeout: 1 * time.Second,
	}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   o.dialTimeout,
			KeepAlive: o.dialKeepAlive,
		}).DialContext,
		MaxConnsPerHost:       o.maxConnsPerHost,
		MaxIdleConnsPerHost:   o.maxIdleConnsPerHost,
		MaxIdleConns:          o.maxIdleConns,
		IdleConnTimeout:       o.idleConnTimeout,
		TLSClientConfig:       o.tlsConfig,
		TLSHandshakeTimeout:   o.tlsHandshakeTimeout,
		ExpectContinueTimeout: o.expectContinueTimeout,
	}

	c := &HTTPClient{
		client: &http.Client{
			Transport: t,
		},
	}

	return c
}

// HTTPGet http get request
func HTTPGet(url string, options ...HTTPRequestOption) ([]byte, error) {
	return defaultHTTPClient.Get(url, options...)
}

// HTTPPost http post request
func HTTPPost(url string, body []byte, options ...HTTPRequestOption) ([]byte, error) {
	return defaultHTTPClient.Post(url, body, options...)
}
<commit_msg>Update http.go<commit_after>package yiigo

import (
	"bytes"
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

// defaultHTTPTimeout default http request timeout
const defaultHTTPTimeout = 10 * time.Second

// httpClientOptions http client options
type httpClientOptions struct {
	dialTimeout           time.Duration
	dialKeepAlive         time.Duration
	fallbackDelay         time.Duration
	maxIdleConns          int
	maxIdleConnsPerHost   int
	maxConnsPerHost       int
	idleConnTimeout       time.Duration
	tlsConfig             *tls.Config
	tlsHandshakeTimeout   time.Duration
	expectContinueTimeout time.Duration
}

// HTTPClientOption configures how we set up the http client
type HTTPClientOption interface {
	apply(options *httpClientOptions)
}

// funcHTTPClientOption implements http client option
type funcHTTPClientOption struct {
	f func(options *httpClientOptions)
}

func (fo *funcHTTPClientOption) apply(o *httpClientOptions) {
	fo.f(o)
}

func newFuncHTTPOption(f func(options *httpClientOptions)) *funcHTTPClientOption {
	return &funcHTTPClientOption{f: f}
}

// WithHTTPDialTimeout specifies the `DialTimeout` to net.Dialer.
func WithHTTPDialTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.dialTimeout = d
	})
}

// WithHTTPDialKeepAlive specifies the `KeepAlive` to net.Dialer.
func WithHTTPDialKeepAlive(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.dialKeepAlive = d
	})
}

// WithHTTPDialFallbackDelay specifies the `FallbackDelay` to net.Dialer.
func WithHTTPDialFallbackDelay(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.fallbackDelay = d
	})
}

// WithHTTPMaxIdleConns specifies the `MaxIdleConns` to http client.
func WithHTTPMaxIdleConns(n int) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.maxIdleConns = n
	})
}

// WithHTTPMaxIdleConnsPerHost specifies the `MaxIdleConnsPerHost` to http client.
func WithHTTPMaxIdleConnsPerHost(n int) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.maxIdleConnsPerHost = n
	})
}

// WithHTTPMaxConnsPerHost specifies the `MaxConnsPerHost` to http client.
func WithHTTPMaxConnsPerHost(n int) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.maxConnsPerHost = n
	})
}

// WithHTTPIdleConnTimeout specifies the `IdleConnTimeout` to http client.
func WithHTTPIdleConnTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.idleConnTimeout = d
	})
}

// WithHTTPTLSConfig specifies the `TLSClientConfig` to http client.
func WithHTTPTLSConfig(c *tls.Config) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.tlsConfig = c
	})
}

// WithHTTPTLSHandshakeTimeout specifies the `TLSHandshakeTimeout` to http client.
func WithHTTPTLSHandshakeTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.tlsHandshakeTimeout = d
	})
}

// WithHTTPExpectContinueTimeout specifies the `ExpectContinueTimeout` to http client.
func WithHTTPExpectContinueTimeout(d time.Duration) HTTPClientOption {
	return newFuncHTTPOption(func(o *httpClientOptions) {
		o.expectContinueTimeout = d
	})
}

// httpRequestOptions http request options
type httpRequestOptions struct {
	headers map[string]string
	cookies []*http.Cookie
	close   bool
	timeout time.Duration
}

// HTTPRequestOption configures how we set up the http request
type HTTPRequestOption interface {
	apply(*httpRequestOptions)
}

// funcHTTPRequestOption implements request option
type funcHTTPRequestOption struct {
	f func(*httpRequestOptions)
}

func (fo *funcHTTPRequestOption) apply(r *httpRequestOptions) {
	fo.f(r)
}

func newFuncHTTPRequestOption(f func(*httpRequestOptions)) *funcHTTPRequestOption {
	return &funcHTTPRequestOption{f: f}
}

// WithRequestHeader specifies the header to http request.
func WithRequestHeader(key, value string) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.headers[key] = value
	})
}

// WithRequestCookies specifies the cookies to http request.
func WithRequestCookies(cookies ...*http.Cookie) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.cookies = cookies
	})
}

// WithRequestClose specifies close the connection after
// replying to this request (for servers) or after sending this
// request and reading its response (for clients).
func WithRequestClose(b bool) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.close = b
	})
}

// WithRequestTimeout specifies the timeout to http request.
func WithRequestTimeout(d time.Duration) HTTPRequestOption {
	return newFuncHTTPRequestOption(func(o *httpRequestOptions) {
		o.timeout = d
	})
}

// HTTPClient http client
type HTTPClient struct {
	client *http.Client
}

// Get http get request
func (h *HTTPClient) Get(url string, options ...HTTPRequestOption) ([]byte, error) {
	req, err := http.NewRequest("GET", url, nil)

	if err != nil {
		return nil, err
	}

	o := &httpRequestOptions{
		headers: make(map[string]string),
		timeout: defaultHTTPTimeout,
	}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	// headers
	if len(o.headers) > 0 {
		for k, v := range o.headers {
			req.Header.Set(k, v)
		}
	}

	// cookies
	if len(o.cookies) > 0 {
		for _, v := range o.cookies {
			req.AddCookie(v)
		}
	}

	if o.close {
		req.Close = true
	}

	ctx, cancel := context.WithTimeout(context.TODO(), o.timeout)

	defer cancel()

	resp, err := h.client.Do(req.WithContext(ctx))

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		io.Copy(ioutil.Discard, resp.Body)

		return nil, fmt.Errorf("error http code: %d", resp.StatusCode)
	}

	b, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return b, nil
}

// Post http post request
func (h *HTTPClient) Post(url string, body []byte, options ...HTTPRequestOption) ([]byte, error) {
	req, err := http.NewRequest("POST", url, bytes.NewReader(body))

	if err != nil {
		return nil, err
	}

	o := &httpRequestOptions{
		headers: make(map[string]string),
		timeout: defaultHTTPTimeout,
	}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	// headers
	if len(o.headers) > 0 {
		for k, v := range o.headers {
			req.Header.Set(k, v)
		}
	}

	// cookies
	if len(o.cookies) > 0 {
		for _, v := range o.cookies {
			req.AddCookie(v)
		}
	}

	if o.close {
		req.Close = true
	}

	ctx, cancel := context.WithTimeout(context.TODO(), o.timeout)

	defer cancel()

	resp, err := h.client.Do(req.WithContext(ctx))

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		io.Copy(ioutil.Discard, resp.Body)

		return nil, fmt.Errorf("error http code: %d", resp.StatusCode)
	}

	b, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return b, nil
}

// defaultHTTPClient default http client
var defaultHTTPClient = &HTTPClient{
	client: &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 60 * time.Second,
			}).DialContext,
			MaxIdleConns:          0,
			MaxIdleConnsPerHost:   1000,
			MaxConnsPerHost:       1000,
			IdleConnTimeout:       60 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	},
}

// NewHTTPClient returns a new http client
func NewHTTPClient(options ...HTTPClientOption) *HTTPClient {
	o := &httpClientOptions{
		dialTimeout:           30 * time.Second,
		dialKeepAlive:         60 * time.Second,
		maxIdleConns:          0,
		maxIdleConnsPerHost:   1000,
		maxConnsPerHost:       1000,
		idleConnTimeout:       60 * time.Second,
		tlsHandshakeTimeout:   10 * time.Second,
		expectContinueTimeout: 1 * time.Second,
	}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   o.dialTimeout,
			KeepAlive: o.dialKeepAlive,
		}).DialContext,
		MaxConnsPerHost:       o.maxConnsPerHost,
		MaxIdleConnsPerHost:   o.maxIdleConnsPerHost,
		MaxIdleConns:          o.maxIdleConns,
		IdleConnTimeout:       o.idleConnTimeout,
		TLSClientConfig:       o.tlsConfig,
		TLSHandshakeTimeout:   o.tlsHandshakeTimeout,
		ExpectContinueTimeout: o.expectContinueTimeout,
	}

	c := &HTTPClient{
		client: &http.Client{
			Transport: t,
		},
	}

	return c
}

// HTTPGet http get request
func HTTPGet(url string, options ...HTTPRequestOption) ([]byte, error) {
	return defaultHTTPClient.Get(url, options...)
}

// HTTPPost http post request
func HTTPPost(url string, body []byte, options ...HTTPRequestOption) ([]byte, error) {
	return defaultHTTPClient.Post(url, body, options...)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"errors"
	"github.com/ginuerzh/gosocks5"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"time"
)

const (
	s2cUri = "/s2c"
	c2sUri = "/c2s"
)

type HttpClientConn struct {
	c     net.Conn
	token string
	r     io.ReadCloser
}

func NewHttpClientConn(conn net.Conn) *HttpClientConn {
	return &HttpClientConn{
		c: conn,
	}
}

func (conn *HttpClientConn) Handshake() (err error) {
	//log.Println("remote", conn.c.RemoteAddr().String())
	req := &http.Request{
		Method: "GET",
		URL: &url.URL{
			Host:   Saddr,
			Scheme: "http",
			Path:   s2cUri,
		},
	}
	if len(Proxy) == 0 {
		err = req.Write(conn.c)
	} else {
		err = req.WriteProxy(conn.c)
	}
	if err != nil {
		return err
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn.c), req)
	if err != nil {
		return err
	}

	b := make([]byte, 36)
	if _, err = io.ReadFull(resp.Body, b); err != nil {
		return err
	}
	if uuid.Parse(string(b)) == nil {
		return errors.New("wrong token")
	}
	conn.token = string(b)
	conn.r = resp.Body
	//log.Println(conn.token, "connected")

	return nil
}

func (conn *HttpClientConn) Read(b []byte) (n int, err error) {
	n, err = conn.r.Read(b)
	//log.Println("http r:", n)
	return
}

func (conn *HttpClientConn) Write(b []byte) (n int, err error) {
	q := url.Values{}
	q.Set("token", conn.token)
	req := &http.Request{
		Method:        "POST",
		Body:          ioutil.NopCloser(bytes.NewReader(b)),
		ContentLength: int64(len(b)),
		URL: &url.URL{
			Host:     Saddr,
			Scheme:   "http",
			Path:     c2sUri,
			RawQuery: q.Encode(),
		},
	}
	resp, err := doRequest(req, Proxy)
	if err != nil {
		log.Println(err)
		return
	}
	resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		log.Println(resp.Status)
		return 0, errors.New(resp.Status)
	}
	//log.Println("http w:", len(b))
	return len(b), nil
}

func (conn *HttpClientConn) Close() error {
	conn.Write(nil)
	return conn.r.Close()
}

func (conn *HttpClientConn) LocalAddr() net.Addr {
	return conn.c.LocalAddr()
}

func (conn *HttpClientConn) RemoteAddr() net.Addr {
	return conn.c.RemoteAddr()
}

func (conn *HttpClientConn) SetDeadline(t time.Time) error {
	return conn.c.SetDeadline(t)
}

func (conn *HttpClientConn) SetReadDeadline(t time.Time) error {
	return conn.c.SetReadDeadline(t)
}

func (conn *HttpClientConn) SetWriteDeadline(t time.Time) error {
	return conn.c.SetWriteDeadline(t)
}

type HttpServerConn struct {
	w      http.ResponseWriter
	c      chan []byte
	closed bool
	rb     []byte
}

func NewHttpServerConn(w http.ResponseWriter, c chan []byte) *HttpServerConn {
	return &HttpServerConn{
		w: w,
		c: c,
	}
}

func (conn *HttpServerConn) Read(b []byte) (n int, err error) {
	if len(conn.rb) == 0 {
		var ok bool
		if conn.rb, ok = <-conn.c; !ok {
			return 0, io.EOF
		}
	}
	n = copy(b, conn.rb)
	conn.rb = conn.rb[n:]

	//log.Println("http r:", n)

	return
}

func (conn *HttpServerConn) Write(b []byte) (n int, err error) {
	n, err = conn.w.Write(b)
	if f, ok := conn.w.(http.Flusher); ok {
		f.Flush()
	}
	//log.Println("http w:", n)
	return
}

func (conn *HttpServerConn) Close() error {
	if !conn.closed {
		close(conn.c)
		conn.closed = true
	}
	return nil
}

func (conn *HttpServerConn) LocalAddr() net.Addr {
	return nil
}

func (conn *HttpServerConn) RemoteAddr() net.Addr {
	return nil
}

func (conn *HttpServerConn) SetDeadline(t time.Time) error {
	return nil
}

func (conn *HttpServerConn) SetReadDeadline(t time.Time) error {
	return nil
}

func (conn *HttpServerConn) SetWriteDeadline(t time.Time) error {
	return nil
}

type HttpServer struct {
	Addr  string
	conns map[string]*HttpServerConn
}

func (s *HttpServer) s2c(w http.ResponseWriter, r *http.Request) {
	token := uuid.New()
	ch := make(chan []byte, 8)

	conn := NewHttpServerConn(w, ch)
	if _, err := conn.Write([]byte(token)); err != nil {
		return
	}

	s.conns[token] = conn
	defer delete(s.conns, token)

	socks5Handle(gosocks5.ServerConn(conn, serverConfig))
}

func (s *HttpServer) c2s(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()

	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	token := r.FormValue("token")
	conn := s.conns[token]
	if conn == nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil || len(b) == 0 {
		conn.Close()
		delete(s.conns, token)
		//log.Println(token, "disconnected")
		return
	}
	conn.c <- b
}

func (s *HttpServer) ListenAndServe() error {
	s.conns = make(map[string]*HttpServerConn)
	http.HandleFunc(s2cUri, s.s2c)
	http.HandleFunc(c2sUri, s.c2s)
	return http.ListenAndServe(s.Addr, nil)
}

func doRequest(req *http.Request, proxy string) (*http.Response, error) {
	if len(proxy) > 0 {
		c, err := Connect(proxy)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		defer c.Close()

		if err := req.WriteProxy(c); err != nil {
			log.Println(err)
			return nil, err
		}
		/*
			b, err := ioutil.ReadAll(c)
			if err != nil {
				log.Println(err)
				return nil, err
			}
		*/
		return http.ReadResponse(bufio.NewReader(c), req)
	}

	return http.DefaultClient.Do(req)
}
<commit_msg>improve log print<commit_after>package main

import (
	"bufio"
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"errors"
	"github.com/ginuerzh/gosocks5"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"time"
)

const (
	s2cUri = "/s2c"
	c2sUri = "/c2s"
)

type HttpClientConn struct {
	c     net.Conn
	token string
	r     io.ReadCloser
}

func NewHttpClientConn(conn net.Conn) *HttpClientConn {
	return &HttpClientConn{
		c: conn,
	}
}

func (conn *HttpClientConn) Handshake() (err error) {
	//log.Println("remote", conn.c.RemoteAddr().String())
	req := &http.Request{
		Method: "GET",
		URL: &url.URL{
			Host:   Saddr,
			Scheme: "http",
			Path:   s2cUri,
		},
	}
	if len(Proxy) == 0 {
		err = req.Write(conn.c)
	} else {
		err = req.WriteProxy(conn.c)
	}
	if err != nil {
		return err
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn.c), req)
	if err != nil {
		return err
	}

	b := make([]byte, 36)
	if _, err = io.ReadFull(resp.Body, b); err != nil {
		return err
	}
	if uuid.Parse(string(b)) == nil {
		return errors.New("Handshake: wrong token")
	}
	conn.token = string(b)
	conn.r = resp.Body
	//log.Println(conn.token, "connected")

	return nil
}

func (conn *HttpClientConn) Read(b []byte) (n int, err error) {
	n, err = conn.r.Read(b)
	//log.Println("http r:", n)
	return
}

func (conn *HttpClientConn) Write(b []byte) (n int, err error) {
	q := url.Values{}
	q.Set("token", conn.token)
	req := &http.Request{
		Method:        "POST",
		Body:          ioutil.NopCloser(bytes.NewReader(b)),
		ContentLength: int64(len(b)),
		URL: &url.URL{
			Host:     Saddr,
			Scheme:   "http",
			Path:     c2sUri,
			RawQuery: q.Encode(),
		},
	}
	resp, err := doRequest(req, Proxy)
	if err != nil {
		log.Println(err)
		return
	}
	resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		//log.Println(resp.Status)
		return 0, errors.New(resp.Status)
	}
	//log.Println("http w:", len(b))
	return len(b), nil
}

func (conn *HttpClientConn) Close() error {
	conn.Write(nil)
	return conn.r.Close()
}

func (conn *HttpClientConn) LocalAddr() net.Addr {
	return conn.c.LocalAddr()
}

func (conn *HttpClientConn) RemoteAddr() net.Addr {
	return conn.c.RemoteAddr()
}

func (conn *HttpClientConn) SetDeadline(t time.Time) error {
	return conn.c.SetDeadline(t)
}

func (conn *HttpClientConn) SetReadDeadline(t time.Time) error {
	return conn.c.SetReadDeadline(t)
}

func (conn *HttpClientConn) SetWriteDeadline(t time.Time) error {
	return conn.c.SetWriteDeadline(t)
}

type HttpServerConn struct {
	w      http.ResponseWriter
	c      chan []byte
	closed bool
	rb     []byte
}

func NewHttpServerConn(w http.ResponseWriter, c chan []byte) *HttpServerConn {
	return &HttpServerConn{
		w: w,
		c: c,
	}
}

func (conn *HttpServerConn) Read(b []byte) (n int, err error) {
	if len(conn.rb) == 0 {
		var ok bool
		if conn.rb, ok = <-conn.c; !ok {
			return 0, io.EOF
		}
	}
	n = copy(b, conn.rb)
	conn.rb = conn.rb[n:]

	//log.Println("http r:", n)

	return
}

func (conn *HttpServerConn) Write(b []byte) (n int, err error) {
	n, err = conn.w.Write(b)
	if f, ok := conn.w.(http.Flusher); ok {
		f.Flush()
	}
	//log.Println("http w:", n)
	return
}

func (conn *HttpServerConn) Close() error {
	if !conn.closed {
		close(conn.c)
		conn.closed = true
	}
	return nil
}

func (conn *HttpServerConn) LocalAddr() net.Addr {
	return nil
}

func (conn *HttpServerConn) RemoteAddr() net.Addr {
	return nil
}

func (conn *HttpServerConn) SetDeadline(t time.Time) error {
	return nil
}

func (conn *HttpServerConn) SetReadDeadline(t time.Time) error {
	return nil
}

func (conn *HttpServerConn) SetWriteDeadline(t time.Time) error {
	return nil
}

type HttpServer struct {
	Addr  string
	conns map[string]*HttpServerConn
}

func (s *HttpServer) s2c(w http.ResponseWriter, r *http.Request) {
	token := uuid.New()
	ch := make(chan []byte, 8)

	conn := NewHttpServerConn(w, ch)
	if _, err := conn.Write([]byte(token)); err != nil {
		return
	}

	s.conns[token] = conn
	defer delete(s.conns, token)

	socks5Handle(gosocks5.ServerConn(conn, serverConfig))
}

func (s *HttpServer) c2s(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()

	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	token := r.FormValue("token")
	conn := s.conns[token]
	if conn == nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil || len(b) == 0 {
		conn.Close()
		delete(s.conns, token)
		//log.Println(token, "disconnected")
		return
	}
	conn.c <- b
}

func (s *HttpServer) ListenAndServe() error {
	s.conns = make(map[string]*HttpServerConn)
	http.HandleFunc(s2cUri, s.s2c)
	http.HandleFunc(c2sUri, s.c2s)
	return http.ListenAndServe(s.Addr, nil)
}

func doRequest(req *http.Request, proxy string) (*http.Response, error) {
	if len(proxy) > 0 {
		c, err := Connect(proxy)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		defer c.Close()

		if err := req.WriteProxy(c); err != nil {
			log.Println(err)
			return nil, err
		}
		/*
			b, err := ioutil.ReadAll(c)
			if err != nil {
				log.Println(err)
				return nil, err
			}
		*/
		return http.ReadResponse(bufio.NewReader(c), req)
	}

	return http.DefaultClient.Do(req)
}
<|endoftext|>