<commit_before>// TODO: disconnect user if name not set

package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"net"
	"os"
)

func error_(err error, r int) {
	fmt.Printf("Error: %v\n", err)
	if r >= 0 {
		os.Exit(r)
	}
}

type clientMap map[string]net.Conn

func (cm clientMap) Write(buf []byte) (n int, err error) {
	for _, c := range cm {
		go c.Write(buf)
	}
	n = len(buf)
	return
}

func (cm clientMap) Add(name string, c net.Conn) bool {
	_, present := cm[name]
	if present {
		return false
	}
	cm[name] = c
	return true
}

const HEADER_BYTE byte = '\xde'
const MAX_NAME_LENGTH int = 65535

// All packets have the following format:
// [header (1)] [packet length (4)] [type (1)] [payload]
//
// The packet length field specifies the number of bytes in the packet
// excluding the header
//
// The payload varies for each command or response
//
const (
	// Commands from client
	CMD_MSGALL = iota	// [data]
	CMD_MSGTO			// [target name length (2)] [target name] [data]
	CMD_IDENT			// [name]
	CMD_WHO

	// Responses from server
	SVR_NOTICE			// [plaintext message]
	SVR_MSG				// [sender name length (2)] [sender name] [data]
)

type ClientInfo struct {
	conn net.Conn
	name string
}

var clients clientMap

func init() {
	clients = make(clientMap)
}

// Packet:
// [header] [packet length excluding header (4)] [type (1)] [packet data]
func client(c net.Conn) {
	defer c.Close()

	var info ClientInfo
	info.conn = c

	br := bufio.NewReader(c)

	packet, err := readPacket(br)
	if packet[0] != CMD_IDENT {
		return
	}
	ok := cmd_ident(&info, packet[1:])
	if ok {
		fmt.Printf("user %s identified\n", info.name)
	} else {
		return
	}

	for {
		packet, err = readPacket(br)
		if err != nil {
			break
		}
		parse(&info, packet)
	}

	// On disconnet
	svr_notice_all(info.name + " disconnected")
	fmt.Printf("%v disconnected\n", info.name)
	delete(clients, info.name)
}

func readPacket(br *bufio.Reader) ([]byte, error) {
	// Drop bytes preceding HEADER_BYTE
	_, err := br.ReadBytes(HEADER_BYTE)
	if err != nil {
		return nil, err
	}

	// Get packet length field
	packet := make([]byte, 4)
	read_bytes := 0
	for read_bytes < 4 {
		tmp := make([]byte, 4)
		nread, err := br.Read(tmp)
		if err != nil {
			return nil, err
		}
		copy(packet[read_bytes:], tmp[:nread])
		read_bytes += nread
	}
	pktlen := int(binary.BigEndian.Uint32(packet))

	// Get rest of packet
	packet = make([]byte, pktlen)
	read_bytes = 0
	for read_bytes < pktlen {
		tmp := make([]byte, pktlen)
		nread, err := br.Read(tmp)
		if err != nil {
			return nil, err
		}
		copy(packet[read_bytes:], tmp[:nread])
		read_bytes += nread
	}
	return packet, nil
}

// Parse incoming packet
func parse(info *ClientInfo, packet []byte) {
	// fmt.Printf("rx: ")
	// for i := 0; i < len(packet); i++ {
	// 	fmt.Printf("%02x ", packet[i])
	// }
	// fmt.Printf("\n")

	cmd := packet[0]
	switch {
	case cmd == CMD_MSGALL:
		cmd_msgall(info, packet[1:])
	case cmd == CMD_MSGTO:
		cmd_msgto(info, packet[1:])
	case cmd == CMD_WHO:
		cmd_who(info)
	default:
	}
}

// Helper function to set common packet fields
func packetize(packet_type byte, payload []byte) []byte {
	var buf bytes.Buffer
	buf.Write([]byte{ HEADER_BYTE })
	binary.Write(&buf, binary.BigEndian, uint32(len(payload)+1))
	buf.Write([]byte{ packet_type })
	buf.Write(payload)

//	var pkt []byte = buf.Bytes()
//	fmt.Printf("tx: ")
//	for i := 5; i < len(pkt); i++ {
//		fmt.Printf("%02x ", pkt[i])
//	}
//	fmt.Printf("\n")
	return buf.Bytes()
}

func svr_notice(c net.Conn, msg string) (int, error) {
	return c.Write(packetize(byte(SVR_NOTICE), []byte(msg)))
}

func svr_notice_all(msg string) (int, error) {
	return clients.Write(packetize(byte(SVR_NOTICE), []byte(msg)))
}

func cmd_msgall(info *ClientInfo, packet []byte) (int, error) {
	if info.name == "" {
		svr_notice(info.conn, "please identify yourself")
		return -1, nil
	}

	var buf bytes.Buffer
	binary.Write(&buf, binary.BigEndian, uint16(len(info.name)))
	buf.Write([]byte(info.name))
	buf.Write(packet)
	return clients.Write(packetize(SVR_MSG, buf.Bytes()))
}

func cmd_msgto(info *ClientInfo, packet []byte) (int, error) {
	if info.name == "" {
		svr_notice(info.conn, "please identify yourself")
		return -1, nil
	}

	targetlen := int(binary.BigEndian.Uint16(packet[0:2]))
	target := string(packet[2:2+targetlen])
	data := packet[2+targetlen:]

	c, present := clients[target]
	if !present {
		svr_notice(info.conn, fmt.Sprintf("unknown recipient %s", target))
		return -1, nil
	}

	var buf bytes.Buffer
	binary.Write(&buf, binary.BigEndian, uint16(len(info.name)))
	buf.Write([]byte(info.name))
	buf.Write(data)
	return c.Write(packetize(byte(SVR_MSG), buf.Bytes()))
}

func cmd_ident(info *ClientInfo, packet []byte) bool {
	name := string(packet)

	if len(name) > MAX_NAME_LENGTH {
		svr_notice(info.conn, "invalid name")
		return false
	}

	if !clients.Add(name, info.conn) {
		svr_notice(info.conn, name + " is already in use")
		return false
	}

	svr_notice_all(name + " connected")
	info.name = name
	return true
}

func cmd_who(info *ClientInfo) {
	if info.name == "" {
		svr_notice(info.conn, "please identify yourself")
		return
	} else {
	        msg := fmt.Sprintf("Who (%v users):\n", len(clients))
		for key, _ := range clients {
		         msg += fmt.Sprintf("  %v\n", key)
		}
		svr_notice(info.conn, msg)
        }
}

func main() {
	var (
		port int
		help bool
	)
	flag.IntVar(&port, "port", 6150, "Port to listen on")
	flag.BoolVar(&help, "help", false, "Display this")
	flag.Parse()

	if help {
		flag.Usage()
		return
	}

	fmt.Printf("Listening for clients on port %v\n", port)
	lis, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
	if err != nil {
		error_(err, 1)
	}

	for {
		c, err := lis.Accept()
		if err != nil {
			error_(err, -1)
			continue
		}

		go client(c)
	}
}
<commit_msg>add check for read error on initial connect<commit_after>// TODO: disconnect user if name not set

package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"net"
	"os"
)

func error_(err error, r int) {
	fmt.Printf("Error: %v\n", err)
	if r >= 0 {
		os.Exit(r)
	}
}

type clientMap map[string]net.Conn

func (cm clientMap) Write(buf []byte) (n int, err error) {
	for _, c := range cm {
		go c.Write(buf)
	}
	n = len(buf)
	return
}

func (cm clientMap) Add(name string, c net.Conn) bool {
	_, present := cm[name]
	if present {
		return false
	}
	cm[name] = c
	return true
}

const HEADER_BYTE byte = '\xde'
const MAX_NAME_LENGTH int = 65535

// All packets have the following format:
// [header (1)] [packet length (4)] [type (1)] [payload]
//
// The packet length field specifies the number of bytes in the packet
// excluding the header
//
// The payload varies for each command or response
//
const (
	// Commands from client
	CMD_MSGALL = iota	// [data]
	CMD_MSGTO			// [target name length (2)] [target name] [data]
	CMD_IDENT			// [name]
	CMD_WHO

	// Responses from server
	SVR_NOTICE			// [plaintext message]
	SVR_MSG				// [sender name length (2)] [sender name] [data]
)

type ClientInfo struct {
	conn net.Conn
	name string
}

var clients clientMap

func init() {
	clients = make(clientMap)
}

// Packet:
// [header] [packet length excluding header (4)] [type (1)] [packet data]
func client(c net.Conn) {
	defer c.Close()

	var info ClientInfo
	info.conn = c

	br := bufio.NewReader(c)

	packet, err := readPacket(br)
	if (err != nil) || (packet[0] != CMD_IDENT) {
		return
	}
	ok := cmd_ident(&info, packet[1:])
	if ok {
		fmt.Printf("user %s identified\n", info.name)
	} else {
		return
	}

	for {
		packet, err = readPacket(br)
		if err != nil {
			break
		}
		parse(&info, packet)
	}

	// On disconnet
	svr_notice_all(info.name + " disconnected")
	fmt.Printf("%v disconnected\n", info.name)
	delete(clients, info.name)
}

func readPacket(br *bufio.Reader) ([]byte, error) {
	// Drop bytes preceding HEADER_BYTE
	_, err := br.ReadBytes(HEADER_BYTE)
	if err != nil {
		return nil, err
	}

	// Get packet length field
	packet := make([]byte, 4)
	read_bytes := 0
	for read_bytes < 4 {
		tmp := make([]byte, 4)
		nread, err := br.Read(tmp)
		if err != nil {
			return nil, err
		}
		copy(packet[read_bytes:], tmp[:nread])
		read_bytes += nread
	}
	pktlen := int(binary.BigEndian.Uint32(packet))

	// Get rest of packet
	packet = make([]byte, pktlen)
	read_bytes = 0
	for read_bytes < pktlen {
		tmp := make([]byte, pktlen)
		nread, err := br.Read(tmp)
		if err != nil {
			return nil, err
		}
		copy(packet[read_bytes:], tmp[:nread])
		read_bytes += nread
	}
	return packet, nil
}

// Parse incoming packet
func parse(info *ClientInfo, packet []byte) {
	// fmt.Printf("rx: ")
	// for i := 0; i < len(packet); i++ {
	// 	fmt.Printf("%02x ", packet[i])
	// }
	// fmt.Printf("\n")

	cmd := packet[0]
	switch {
	case cmd == CMD_MSGALL:
		cmd_msgall(info, packet[1:])
	case cmd == CMD_MSGTO:
		cmd_msgto(info, packet[1:])
	case cmd == CMD_WHO:
		cmd_who(info)
	default:
	}
}

// Helper function to set common packet fields
func packetize(packet_type byte, payload []byte) []byte {
	var buf bytes.Buffer
	buf.Write([]byte{ HEADER_BYTE })
	binary.Write(&buf, binary.BigEndian, uint32(len(payload)+1))
	buf.Write([]byte{ packet_type })
	buf.Write(payload)

//	var pkt []byte = buf.Bytes()
//	fmt.Printf("tx: ")
//	for i := 5; i < len(pkt); i++ {
//		fmt.Printf("%02x ", pkt[i])
//	}
//	fmt.Printf("\n")
	return buf.Bytes()
}

func svr_notice(c net.Conn, msg string) (int, error) {
	return c.Write(packetize(byte(SVR_NOTICE), []byte(msg)))
}

func svr_notice_all(msg string) (int, error) {
	return clients.Write(packetize(byte(SVR_NOTICE), []byte(msg)))
}

func cmd_msgall(info *ClientInfo, packet []byte) (int, error) {
	if info.name == "" {
		svr_notice(info.conn, "please identify yourself")
		return -1, nil
	}

	var buf bytes.Buffer
	binary.Write(&buf, binary.BigEndian, uint16(len(info.name)))
	buf.Write([]byte(info.name))
	buf.Write(packet)
	return clients.Write(packetize(SVR_MSG, buf.Bytes()))
}

func cmd_msgto(info *ClientInfo, packet []byte) (int, error) {
	if info.name == "" {
		svr_notice(info.conn, "please identify yourself")
		return -1, nil
	}

	targetlen := int(binary.BigEndian.Uint16(packet[0:2]))
	target := string(packet[2:2+targetlen])
	data := packet[2+targetlen:]

	c, present := clients[target]
	if !present {
		svr_notice(info.conn, fmt.Sprintf("unknown recipient %s", target))
		return -1, nil
	}

	var buf bytes.Buffer
	binary.Write(&buf, binary.BigEndian, uint16(len(info.name)))
	buf.Write([]byte(info.name))
	buf.Write(data)
	return c.Write(packetize(byte(SVR_MSG), buf.Bytes()))
}

func cmd_ident(info *ClientInfo, packet []byte) bool {
	name := string(packet)

	if len(name) > MAX_NAME_LENGTH {
		svr_notice(info.conn, "invalid name")
		return false
	}

	if !clients.Add(name, info.conn) {
		svr_notice(info.conn, name + " is already in use")
		return false
	}

	svr_notice_all(name + " connected")
	info.name = name
	return true
}

func cmd_who(info *ClientInfo) {
	if info.name == "" {
		svr_notice(info.conn, "please identify yourself")
		return
	} else {
	        msg := fmt.Sprintf("Who (%v users):\n", len(clients))
		for key, _ := range clients {
		         msg += fmt.Sprintf("  %v\n", key)
		}
		svr_notice(info.conn, msg)
        }
}

func main() {
	var (
		port int
		help bool
	)
	flag.IntVar(&port, "port", 6150, "Port to listen on")
	flag.BoolVar(&help, "help", false, "Display this")
	flag.Parse()

	if help {
		flag.Usage()
		return
	}

	fmt.Printf("Listening for clients on port %v\n", port)
	lis, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
	if err != nil {
		error_(err, 1)
	}

	for {
		c, err := lis.Accept()
		if err != nil {
			error_(err, -1)
			continue
		}

		go client(c)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"

	goyaml "gopkg.in/yaml.v2"
)

func main() {
	err := _main()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	os.Exit(0)
}
func _main() error {
	var data interface{}
	input, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return err
	}
	err = goyaml.Unmarshal(input, &data)
	if err != nil {
		return err
	}
	data, err = transformData(data)
	if err != nil {
		return err
	}

	output, err := json.Marshal(data)
	if err != nil {
		return err
	}
	_, err = os.Stdout.Write([]byte(output))
	return err
}
func transformData(in interface{}) (out interface{}, err error) {
	switch in.(type) {
	case map[interface{}]interface{}:
		o := make(map[string]interface{})
		for k, v := range in.(map[interface{}]interface{}) {
			sk := ""
			switch k.(type) {
			case string:
				sk = k.(string)
			case int:
				sk = strconv.Itoa(k.(int))
			default:
				return nil, errors.New(
					fmt.Sprintf("type not match: expect map key string or int get: %T", k))
			}
			v, err = transformData(v)
			if err != nil {
				return nil, err
			}
			o[sk] = v
		}
		return o, nil
	case []interface{}:
		in1 := in.([]interface{})
		len1 := len(in1)
		o := make([]interface{}, len1)
		for i := 0; i < len1; i++ {
			o[i], err = transformData(in1[i])
			if err != nil {
				return nil, err
			}
		}
		return o, nil
	default:
		return in, nil
	}
	return in, nil
}
<commit_msg>Faster! Convert in place<commit_after>package main

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

	goyaml "gopkg.in/yaml.v2"
)

func main() {
	err := _main()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	os.Exit(0)
}

func _main() error {
	var data interface{}
	input, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return err
	}
	err = goyaml.Unmarshal(input, &data)
	if err != nil {
		return err
	}
	err = transformData(&data)
	if err != nil {
		return err
	}

	output, err := json.Marshal(data)
	if err != nil {
		return err
	}
	_, err = os.Stdout.Write([]byte(output))
	return err
}

func transformData(pIn *interface{}) (err error) {
	switch in := (*pIn).(type) {
	case map[interface{}]interface{}:
		m := make(map[string]interface{}, len(in))
		for k, v := range in {
			if err = transformData(&v); err != nil {
				return err
			}
			var sk string
			switch k.(type) {
			case string:
				sk = k.(string)
			case int:
				sk = strconv.Itoa(k.(int))
			default:
				return fmt.Errorf("type mismatch: expect map key string or int; got: %T", k)
			}
			m[sk] = v
		}
		*pIn = m
	case []interface{}:
		for i := len(in) - 1; i >= 0; i-- {
			if err = transformData(&in[i]); err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import "os"

func main() {
	parser := NewParser()
	var err error
	if len(os.Args) < 2 {
		err = parser.ParseInput()
	} else {
		err = parser.ParseFile(os.Args[1])
	}
	if err != nil {
		panic(err)
	}
}
<commit_msg>Print to stderr instead of panicking<commit_after>package main

import (
	"fmt"
	"os"
)

func main() {
	parser := NewParser()
	var err error
	if len(os.Args) < 2 {
		err = parser.ParseInput()
	} else {
		err = parser.ParseFile(os.Args[1])
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, "Parse error:", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/deferpanic/deferclient/deferstats"
	"github.com/fzzy/radix/extra/pool"
	"github.com/fzzy/radix/redis"
	"github.com/google/go-github/github"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"github.com/namsral/flag"
	"github.com/unrolled/render"
	// "github.com/deferpanic/deferclient/errors"
	"golang.org/x/oauth2"
	githuboauth "golang.org/x/oauth2/github"
	"math/rand"
	"net/http"
	"runtime"
	"strings"
	"time"
)

// func QueryScore(terms []string, title) float32 {
// 	return 1.0
// }

func errHndlr(err error) {
	if err != nil {
		fmt.Println("error:", err)
		panic(err)
	}
}

func isOnHTTPS(req *http.Request) bool {
	if req.Header.Get("is-secure") == "true" {
		return true
	}
	// default is to use the flag
	// which is only really useful for local development
	return usingHTTPS
}

type domainRow struct {
	Key    string
	Domain string
}

func indexHandler(w http.ResponseWriter, req *http.Request) {
	context := map[string]interface{}{
		"staticPrefix": staticPrefix,
		"isNotDebug":   !debug,
		"Username":     "",
		"domains":      make([]string, 0),
	}

	cookie, err := req.Cookie("username")
	if err == nil {
		var username string
		if err = sCookie.Decode("username", cookie.Value, &username); err == nil {
			// Yay! You're signed in!

			context["Username"] = username
			c, err := redisPool.Get()
			errHndlr(err)
			defer redisPool.CarefullyPut(c, &err)

			userdomainsKey := fmt.Sprintf("$userdomains$%v", username)
			replies, err := c.Cmd("SMEMBERS", userdomainsKey).List()
			errHndlr(err)

			var domains []domainRow

			var domain string
			for _, key := range replies {
				reply := c.Cmd("HGET", "$domainkeys", key)
				if reply.Type != redis.NilReply {
					domain, err = reply.Str()
					errHndlr(err)
					domains = append(domains, domainRow{
						Key:    key,
						Domain: domain,
					})
				}
			}
			context["domains"] = domains
		}
	}
	// this assumes there's a `templates/index.tmpl` file
	renderer.HTML(w, http.StatusOK, "index", context)
}

func logoutHandler(w http.ResponseWriter, req *http.Request) {
	expire := time.Now().AddDate(0, 0, -1)
	secureCookie := isOnHTTPS(req)
	cookie := &http.Cookie{
		Name:     "username",
		Value:    "*deleted*",
		Path:     "/",
		Expires:  expire,
		MaxAge:   -1,
		Secure:   secureCookie,
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, req, "/#loggedout", http.StatusTemporaryRedirect)
}

func handleGitHubLogin(w http.ResponseWriter, req *http.Request) {
	url := oauthConf.AuthCodeURL(oauthStateString, oauth2.AccessTypeOnline)
	http.Redirect(w, req, url, http.StatusTemporaryRedirect)
}

func handleGitHubCallback(w http.ResponseWriter, req *http.Request) {
	state := req.FormValue("state")
	if state != oauthStateString {
		fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
		http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
		return
	}

	code := req.FormValue("code")
	token, err := oauthConf.Exchange(oauth2.NoContext, code)
	if err != nil {
		fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err)
		http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
		return
	}

	oauthClient := oauthConf.Client(oauth2.NoContext, token)
	client := github.NewClient(oauthClient)
	// the second item here is the github.Rate config
	user, _, err := client.Users.Get("")

	if err != nil {
		fmt.Printf("client.Users.Get() faled with '%s'\n", err)
		http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
		return
	}

	fmt.Printf("Logged in as GitHub user: %s\n", *user.Login)
	// fmt.Printf("Logged in as GitHub user: %s\n", *user)
	encoded, err := sCookie.Encode("username", *user.Login)
	errHndlr(err)
	expire := time.Now().AddDate(0, 0, 1) // how long is this?
	secureCookie := isOnHTTPS(req)

	cookie := &http.Cookie{
		Name:     "username",
		Value:    encoded,
		Path:     "/",
		Expires:  expire,
		MaxAge:   60 * 60 * 24 * 30, // 30 days
		Secure:   secureCookie,
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, req, "/#auth", http.StatusTemporaryRedirect)
}

var letters = []rune(
	"abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ123456789",
)

func randString(n int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

func domainkeyNewHandler(w http.ResponseWriter, req *http.Request) {
	domain := strings.Trim(req.FormValue("domain"), " ")
	if domain != "" {
		cookie, err := req.Cookie("username")
		if err == nil {
			var username string
			if err = sCookie.Decode("username", cookie.Value, &username); err == nil {
				c, err := redisPool.Get()
				errHndlr(err)
				defer redisPool.CarefullyPut(c, &err)

				key := randString(24)
				userdomainsKey := fmt.Sprintf("$userdomains$%v", username)
				err = c.Cmd("SADD", userdomainsKey, key).Err
				errHndlr(err)
				err = c.Cmd("HSET", "$domainkeys", key, domain).Err
				errHndlr(err)
			}
		}
	}

	// http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
	http.Redirect(w, req, "/#auth", http.StatusFound)
}

func domainkeyDeleteHandler(w http.ResponseWriter, req *http.Request) {
	key := strings.Trim(req.FormValue("key"), " ")
	if key != "" {
		cookie, err := req.Cookie("username")
		if err == nil {
			var username string
			if err = sCookie.Decode("username", cookie.Value, &username); err == nil {
				// Yay! You're signed in!
				c, err := redisPool.Get()
				errHndlr(err)
				defer redisPool.CarefullyPut(c, &err)

				userdomainsKey := fmt.Sprintf("$userdomains$%v", username)
				err = c.Cmd("SREM", userdomainsKey, key).Err
				errHndlr(err)
				err = c.Cmd("HDEL", "$domainkeys", key).Err
				errHndlr(err)
			} // else, we should yield some sort of 403 message maybe

		}
	}

	http.Redirect(w, req, "/#auth", http.StatusFound)
}

var (
	redisPool    *pool.Pool
	procs        int
	debug        = true
	renderer     = render.New()
	redisURL     = "127.0.0.1:6379"
	staticPrefix = ""
	usingHTTPS   = false
	sCookie      *securecookie.SecureCookie
)

var (
	// You must register the app at https://github.com/settings/applications
	// Set callback to http://127.0.0.1:7000/github_oauth_cb
	// Set ClientId and ClientSecret to
	oauthConf = &oauth2.Config{
		ClientID:     "",
		ClientSecret: "",
		Scopes:       []string{"user:email"},
		Endpoint:     githuboauth.Endpoint,
	}
	// random string for oauth2 API calls to protect against CSRF
	oauthStateString = randString(24)
)

func main() {
	var (
		port          = 3001
		redisDatabase = 0
		redisPoolSize = 10
		clientID      = ""
		clientSecret  = ""
		hashKey       = "randomishstringthatsi32charslong"
		blockKey      = "randomishstringthatsi32charslong"
		deferPanicKey = ""
	)
	flag.IntVar(&port, "port", port, "Port to start the server on")
	flag.IntVar(&procs, "procs", 1, "Number of CPU processors (0 to use max)")
	flag.BoolVar(&debug, "debug", false, "Debug mode")
	flag.StringVar(
		&redisURL, "redisURL", redisURL,
		"Redis URL to tcp connect to")
	flag.StringVar(
		&staticPrefix, "staticPrefix", staticPrefix,
		"Prefix in front of static assets in HTML")
	flag.IntVar(&redisDatabase, "redisDatabase", redisDatabase,
		"Redis database number to connect to")
	flag.StringVar(
		&clientID, "clientID", clientID,
		"OAuth Client ID")
	flag.StringVar(
		&clientSecret, "clientSecret", clientSecret,
		"OAuth Client Secret")
	flag.BoolVar(&usingHTTPS, "usingHTTPS", usingHTTPS,
		"Whether requests are made under HTTPS")
	flag.StringVar(
		&hashKey, "hashKey", hashKey,
		"HMAC hash key to use for encoding cookies")
	flag.StringVar(
		&blockKey, "blockKey", blockKey,
		"Block key to encrypt cookie values")
	flag.StringVar(
		&deferPanicKey, "deferPanicKey", deferPanicKey,
		"Auth key for deferpanic.com")
	flag.Parse()

	if deferPanicKey != "" {
		deferstats.Token = deferPanicKey
		go deferstats.CaptureStats()
	}

	oauthConf.ClientID = clientID
	oauthConf.ClientSecret = clientSecret

	sCookie = securecookie.New([]byte(hashKey), []byte(blockKey))

	fmt.Println("REDIS DATABASE:", redisDatabase)
	fmt.Println("DEBUG MODE:", debug)
	fmt.Println("STATIC PREFIX:", staticPrefix)

	if !debug {
		redisPoolSize = 100
	}

	// Figuring out how many processors to use.
	maxProcs := runtime.NumCPU()
	if procs == 0 {
		procs = maxProcs
	} else if procs < 0 {
		panic("PROCS < 0")
	} else if procs > maxProcs {
		panic(fmt.Sprintf("PROCS > max (%v)", maxProcs))
	}
	fmt.Println("PROCS:", procs)
	runtime.GOMAXPROCS(procs)

	renderer = render.New(render.Options{
		IndentJSON:    debug,
		IsDevelopment: debug,
	})

	df := func(network, addr string) (*redis.Client, error) {
		client, err := redis.Dial(network, addr)
		if err != nil {
			return nil, err
		}
		err = client.Cmd("SELECT", redisDatabase).Err
		if err != nil {
			return nil, err
		}
		// if err = client.Cmd("AUTH", "SUPERSECRET").Err; err != nil {
		// 	client.Close()
		// 	return nil, err
		// }
		return client, nil
	}

	var err error
	redisPool, err = pool.NewCustomPool("tcp", redisURL, redisPoolSize, df)
	errHndlr(err)

	mux := mux.NewRouter()
	mux.HandleFunc("/", deferstats.HTTPHandler(indexHandler)).Methods("GET", "HEAD")
	mux.HandleFunc("/v1/ping", deferstats.HTTPHandler(pingHandler)).Methods("GET", "HEAD")
	mux.HandleFunc("/v1", deferstats.HTTPHandler(fetchHandler)).Methods("GET", "HEAD")
	mux.HandleFunc("/v1", deferstats.HTTPHandler(updateHandler)).Methods("POST", "PUT")
	mux.HandleFunc("/v1", deferstats.HTTPHandler(deleteHandler)).Methods("DELETE")
	mux.HandleFunc("/v1/stats", deferstats.HTTPHandler(privateStatsHandler)).Methods("GET")
	mux.HandleFunc("/v1/flush", deferstats.HTTPHandler(flushHandler)).Methods("DELETE")
	mux.HandleFunc("/v1/bulk", deferstats.HTTPHandler(bulkHandler)).Methods("POST", "PUT")
	mux.HandleFunc("/login", deferstats.HTTPHandler(handleGitHubLogin)).Methods("GET")
	mux.HandleFunc("/logout", deferstats.HTTPHandler(logoutHandler)).Methods("GET", "POST")
	mux.HandleFunc("/github_oauth_cb", deferstats.HTTPHandler(handleGitHubCallback)).Methods("GET")
	mux.HandleFunc("/domainkeys/new", deferstats.HTTPHandler(domainkeyNewHandler)).Methods("POST")
	mux.HandleFunc("/domainkeys/delete", deferstats.HTTPHandler(domainkeyDeleteHandler)).Methods("POST")

	n := negroni.Classic()

	n.UseHandler(mux)
	n.Run(fmt.Sprintf(":%d", port))
}
<commit_msg>use the new way to set deferpanic token<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/deferpanic/deferclient/deferstats"
	"github.com/fzzy/radix/extra/pool"
	"github.com/fzzy/radix/redis"
	"github.com/google/go-github/github"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"github.com/namsral/flag"
	"github.com/unrolled/render"
	// "github.com/deferpanic/deferclient/errors"
	"golang.org/x/oauth2"
	githuboauth "golang.org/x/oauth2/github"
	"math/rand"
	"net/http"
	"runtime"
	"strings"
	"time"
)

// func QueryScore(terms []string, title) float32 {
// 	return 1.0
// }

func errHndlr(err error) {
	if err != nil {
		fmt.Println("error:", err)
		panic(err)
	}
}

func isOnHTTPS(req *http.Request) bool {
	if req.Header.Get("is-secure") == "true" {
		return true
	}
	// default is to use the flag
	// which is only really useful for local development
	return usingHTTPS
}

type domainRow struct {
	Key    string
	Domain string
}

func indexHandler(w http.ResponseWriter, req *http.Request) {
	context := map[string]interface{}{
		"staticPrefix": staticPrefix,
		"isNotDebug":   !debug,
		"Username":     "",
		"domains":      make([]string, 0),
	}

	cookie, err := req.Cookie("username")
	if err == nil {
		var username string
		if err = sCookie.Decode("username", cookie.Value, &username); err == nil {
			// Yay! You're signed in!

			context["Username"] = username
			c, err := redisPool.Get()
			errHndlr(err)
			defer redisPool.CarefullyPut(c, &err)

			userdomainsKey := fmt.Sprintf("$userdomains$%v", username)
			replies, err := c.Cmd("SMEMBERS", userdomainsKey).List()
			errHndlr(err)

			var domains []domainRow

			var domain string
			for _, key := range replies {
				reply := c.Cmd("HGET", "$domainkeys", key)
				if reply.Type != redis.NilReply {
					domain, err = reply.Str()
					errHndlr(err)
					domains = append(domains, domainRow{
						Key:    key,
						Domain: domain,
					})
				}
			}
			context["domains"] = domains
		}
	}
	// this assumes there's a `templates/index.tmpl` file
	renderer.HTML(w, http.StatusOK, "index", context)
}

func logoutHandler(w http.ResponseWriter, req *http.Request) {
	expire := time.Now().AddDate(0, 0, -1)
	secureCookie := isOnHTTPS(req)
	cookie := &http.Cookie{
		Name:     "username",
		Value:    "*deleted*",
		Path:     "/",
		Expires:  expire,
		MaxAge:   -1,
		Secure:   secureCookie,
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, req, "/#loggedout", http.StatusTemporaryRedirect)
}

func handleGitHubLogin(w http.ResponseWriter, req *http.Request) {
	url := oauthConf.AuthCodeURL(oauthStateString, oauth2.AccessTypeOnline)
	http.Redirect(w, req, url, http.StatusTemporaryRedirect)
}

func handleGitHubCallback(w http.ResponseWriter, req *http.Request) {
	state := req.FormValue("state")
	if state != oauthStateString {
		fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
		http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
		return
	}

	code := req.FormValue("code")
	token, err := oauthConf.Exchange(oauth2.NoContext, code)
	if err != nil {
		fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err)
		http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
		return
	}

	oauthClient := oauthConf.Client(oauth2.NoContext, token)
	client := github.NewClient(oauthClient)
	// the second item here is the github.Rate config
	user, _, err := client.Users.Get("")

	if err != nil {
		fmt.Printf("client.Users.Get() faled with '%s'\n", err)
		http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
		return
	}

	fmt.Printf("Logged in as GitHub user: %s\n", *user.Login)
	// fmt.Printf("Logged in as GitHub user: %s\n", *user)
	encoded, err := sCookie.Encode("username", *user.Login)
	errHndlr(err)
	expire := time.Now().AddDate(0, 0, 1) // how long is this?
	secureCookie := isOnHTTPS(req)

	cookie := &http.Cookie{
		Name:     "username",
		Value:    encoded,
		Path:     "/",
		Expires:  expire,
		MaxAge:   60 * 60 * 24 * 30, // 30 days
		Secure:   secureCookie,
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, req, "/#auth", http.StatusTemporaryRedirect)
}

var letters = []rune(
	"abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ123456789",
)

func randString(n int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

func domainkeyNewHandler(w http.ResponseWriter, req *http.Request) {
	domain := strings.Trim(req.FormValue("domain"), " ")
	if domain != "" {
		cookie, err := req.Cookie("username")
		if err == nil {
			var username string
			if err = sCookie.Decode("username", cookie.Value, &username); err == nil {
				c, err := redisPool.Get()
				errHndlr(err)
				defer redisPool.CarefullyPut(c, &err)

				key := randString(24)
				userdomainsKey := fmt.Sprintf("$userdomains$%v", username)
				err = c.Cmd("SADD", userdomainsKey, key).Err
				errHndlr(err)
				err = c.Cmd("HSET", "$domainkeys", key, domain).Err
				errHndlr(err)
			}
		}
	}

	// http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
	http.Redirect(w, req, "/#auth", http.StatusFound)
}

func domainkeyDeleteHandler(w http.ResponseWriter, req *http.Request) {
	key := strings.Trim(req.FormValue("key"), " ")
	if key != "" {
		cookie, err := req.Cookie("username")
		if err == nil {
			var username string
			if err = sCookie.Decode("username", cookie.Value, &username); err == nil {
				// Yay! You're signed in!
				c, err := redisPool.Get()
				errHndlr(err)
				defer redisPool.CarefullyPut(c, &err)

				userdomainsKey := fmt.Sprintf("$userdomains$%v", username)
				err = c.Cmd("SREM", userdomainsKey, key).Err
				errHndlr(err)
				err = c.Cmd("HDEL", "$domainkeys", key).Err
				errHndlr(err)
			} // else, we should yield some sort of 403 message maybe

		}
	}

	http.Redirect(w, req, "/#auth", http.StatusFound)
}

var (
	redisPool    *pool.Pool
	procs        int
	debug        = true
	renderer     = render.New()
	redisURL     = "127.0.0.1:6379"
	staticPrefix = ""
	usingHTTPS   = false
	sCookie      *securecookie.SecureCookie
)

var (
	// You must register the app at https://github.com/settings/applications
	// Set callback to http://127.0.0.1:7000/github_oauth_cb
	// Set ClientId and ClientSecret to
	oauthConf = &oauth2.Config{
		ClientID:     "",
		ClientSecret: "",
		Scopes:       []string{"user:email"},
		Endpoint:     githuboauth.Endpoint,
	}
	// random string for oauth2 API calls to protect against CSRF
	oauthStateString = randString(24)
)

func main() {
	var (
		port          = 3001
		redisDatabase = 0
		redisPoolSize = 10
		clientID      = ""
		clientSecret  = ""
		hashKey       = "randomishstringthatsi32charslong"
		blockKey      = "randomishstringthatsi32charslong"
		deferPanicKey = ""
	)
	flag.IntVar(&port, "port", port, "Port to start the server on")
	flag.IntVar(&procs, "procs", 1, "Number of CPU processors (0 to use max)")
	flag.BoolVar(&debug, "debug", false, "Debug mode")
	flag.StringVar(
		&redisURL, "redisURL", redisURL,
		"Redis URL to tcp connect to")
	flag.StringVar(
		&staticPrefix, "staticPrefix", staticPrefix,
		"Prefix in front of static assets in HTML")
	flag.IntVar(&redisDatabase, "redisDatabase", redisDatabase,
		"Redis database number to connect to")
	flag.StringVar(
		&clientID, "clientID", clientID,
		"OAuth Client ID")
	flag.StringVar(
		&clientSecret, "clientSecret", clientSecret,
		"OAuth Client Secret")
	flag.BoolVar(&usingHTTPS, "usingHTTPS", usingHTTPS,
		"Whether requests are made under HTTPS")
	flag.StringVar(
		&hashKey, "hashKey", hashKey,
		"HMAC hash key to use for encoding cookies")
	flag.StringVar(
		&blockKey, "blockKey", blockKey,
		"Block key to encrypt cookie values")
	flag.StringVar(
		&deferPanicKey, "deferPanicKey", deferPanicKey,
		"Auth key for deferpanic.com")
	flag.Parse()

	dfs := deferstats.NewClient(deferPanicKey)
	go dfs.CaptureStats()
	// if deferPanicKey != "" {
	// 	dfs := deferstats.NewClient(deferPanicKey)
	// 	go dfs.CaptureStats()
	// }

	oauthConf.ClientID = clientID
	oauthConf.ClientSecret = clientSecret

	sCookie = securecookie.New([]byte(hashKey), []byte(blockKey))

	fmt.Println("REDIS DATABASE:", redisDatabase)
	fmt.Println("DEBUG MODE:", debug)
	fmt.Println("STATIC PREFIX:", staticPrefix)

	if !debug {
		redisPoolSize = 100
	}

	// Figuring out how many processors to use.
	maxProcs := runtime.NumCPU()
	if procs == 0 {
		procs = maxProcs
	} else if procs < 0 {
		panic("PROCS < 0")
	} else if procs > maxProcs {
		panic(fmt.Sprintf("PROCS > max (%v)", maxProcs))
	}
	fmt.Println("PROCS:", procs)
	runtime.GOMAXPROCS(procs)

	renderer = render.New(render.Options{
		IndentJSON:    debug,
		IsDevelopment: debug,
	})

	df := func(network, addr string) (*redis.Client, error) {
		client, err := redis.Dial(network, addr)
		if err != nil {
			return nil, err
		}
		err = client.Cmd("SELECT", redisDatabase).Err
		if err != nil {
			return nil, err
		}
		// if err = client.Cmd("AUTH", "SUPERSECRET").Err; err != nil {
		// 	client.Close()
		// 	return nil, err
		// }
		return client, nil
	}

	var err error
	redisPool, err = pool.NewCustomPool("tcp", redisURL, redisPoolSize, df)
	errHndlr(err)

	mux := mux.NewRouter()
	mux.HandleFunc("/", dfs.HTTPHandler(indexHandler)).Methods("GET", "HEAD")
	mux.HandleFunc("/v1/ping", dfs.HTTPHandler(pingHandler)).Methods("GET", "HEAD")
	mux.HandleFunc("/v1", dfs.HTTPHandler(fetchHandler)).Methods("GET", "HEAD")
	mux.HandleFunc("/v1", dfs.HTTPHandler(updateHandler)).Methods("POST", "PUT")
	mux.HandleFunc("/v1", dfs.HTTPHandler(deleteHandler)).Methods("DELETE")
	mux.HandleFunc("/v1/stats", dfs.HTTPHandler(privateStatsHandler)).Methods("GET")
	mux.HandleFunc("/v1/flush", dfs.HTTPHandler(flushHandler)).Methods("DELETE")
	mux.HandleFunc("/v1/bulk", dfs.HTTPHandler(bulkHandler)).Methods("POST", "PUT")
	mux.HandleFunc("/login", dfs.HTTPHandler(handleGitHubLogin)).Methods("GET")
	mux.HandleFunc("/logout", dfs.HTTPHandler(logoutHandler)).Methods("GET", "POST")
	mux.HandleFunc("/github_oauth_cb", dfs.HTTPHandler(handleGitHubCallback)).Methods("GET")
	mux.HandleFunc("/domainkeys/new", dfs.HTTPHandler(domainkeyNewHandler)).Methods("POST")
	mux.HandleFunc("/domainkeys/delete", dfs.HTTPHandler(domainkeyDeleteHandler)).Methods("POST")

	n := negroni.Classic()

	n.UseHandler(mux)
	n.Run(fmt.Sprintf(":%d", port))
}
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"rsc.io/letsencrypt"

	"github.com/beevik/etree"
	"github.com/gorilla/mux"
	"github.com/urfave/negroni"
)

// Metadata metadata struct
type Metadata struct {
	Title      string    `json:"title"`
	Author     string    `json:"author"`
	Identifier string    `json:"identifier"`
	Language   string    `json:"language"`
	Modified   time.Time `json:"modified"`
}

// Link link struct
type Link struct {
	Rel      string `json:"rel,omitempty"`
	Href     string `json:"href"`
	TypeLink string `json:"type"`
	Height   int    `json:"height,omitempty"`
	Width    int    `json:"width,omitempty"`
}

// Manifest manifest struct
type Manifest struct {
	Metadata  Metadata `json:"metadata"`
	Links     []Link   `json:"links"`
	Spine     []Link   `json:"spine,omitempty"`
	Resources []Link   `json:"resources"`
}

// Icon icon struct for AppInstall
type Icon struct {
	Src       string `json:"src"`
	Size      string `json:"size"`
	MediaType string `json:"type"`
}

// AppInstall struct for app install banner
type AppInstall struct {
	ShortName string `json:"short_name"`
	Name      string `json:"name"`
	StartURL  string `json:"start_url"`
	Display   string `json:"display"`
	Icons     Icon   `json:"icons"`
}

func main() {

	n := negroni.Classic()
	n.Use(negroni.NewStatic(http.Dir("public")))
	n.UseHandler(loanHandler(false))

	var m letsencrypt.Manager
	if err := m.CacheFile("letsencrypt.cache"); err != nil {
		log.Fatal(err)
	}

	if len(os.Args) > 1 && os.Args[1] == "dev" {
		s := &http.Server{
			Handler:        n,
			ReadTimeout:    10 * time.Second,
			WriteTimeout:   10 * time.Second,
			MaxHeaderBytes: 1 << 20,
			Addr:           ":8080",
		}

		//		log.Fatal(s.ListenAndServeTLS("test.cert", "test.key"))
		log.Fatal(s.ListenAndServe())
	} else {

		s := &http.Server{
			Handler:        n,
			ReadTimeout:    10 * time.Second,
			WriteTimeout:   10 * time.Second,
			MaxHeaderBytes: 1 << 20,
			Addr:           ":443",
			TLSConfig: &tls.Config{
				GetCertificate: m.GetCertificate,
			},
		}

		log.Fatal(s.ListenAndServeTLS("", ""))
	}
}

func loanHandler(test bool) http.Handler {
	serv := mux.NewRouter()

	serv.HandleFunc("/index.html", getBooks)
	serv.HandleFunc("/", getBooks)
	serv.HandleFunc("/viewer.js", viewer)
	serv.HandleFunc("/sw.js", sw)
	serv.HandleFunc("/{filename}/", bookIndex)
	serv.HandleFunc("/{filename}/manifest.json", getManifest)
	serv.HandleFunc("/{filename}/webapp.webmanifest", getWebAppManifest)
	serv.HandleFunc("/{filename}/index.html", bookIndex)
	serv.HandleFunc("/{filename}/{asset:.*}", getAsset)
	return serv
}

func getManifest(w http.ResponseWriter, req *http.Request) {
	var opfFileName string
	var manifestStruct Manifest
	var metaStruct Metadata

	metaStruct.Modified = time.Now()

	vars := mux.Vars(req)
	filename := vars["filename"]
	filename_path := "books/" + filename

	self := Link{
		Rel:      "self",
		Href:     "http://" + req.Host + "/" + filename + "/manifest.json",
		TypeLink: "application/json",
	}
	manifestStruct.Links = make([]Link, 1)
	manifestStruct.Resources = make([]Link, 0)
	manifestStruct.Resources = make([]Link, 0)
	manifestStruct.Links[0] = self

	zipReader, err := zip.OpenReader(filename_path)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, f := range zipReader.File {
		if f.Name == "META-INF/container.xml" {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			doc := etree.NewDocument()
			_, err = doc.ReadFrom(rc)
			if err == nil {
				root := doc.SelectElement("container")
				rootFiles := root.SelectElements("rootfiles")
				for _, rootFileTag := range rootFiles {
					rootFile := rootFileTag.SelectElement("rootfile")
					if rootFile != nil {
						opfFileName = rootFile.SelectAttrValue("full-path", "")
					}
				}
			} else {
				fmt.Println(err)
			}
			rc.Close()
		}
	}

	if opfFileName != "" {
		for _, f := range zipReader.File {
			if f.Name == opfFileName {
				rc, errOpen := f.Open()
				if errOpen != nil {
					fmt.Println("error openging " + f.Name)
				}
				doc := etree.NewDocument()
				_, err = doc.ReadFrom(rc)
				if err == nil {
					root := doc.SelectElement("package")
					meta := root.SelectElement("metadata")

					titleTag := meta.SelectElement("title")
					metaStruct.Title = titleTag.Text()

					langTag := meta.SelectElement("language")
					metaStruct.Language = langTag.Text()

					identifierTag := meta.SelectElement("identifier")
					metaStruct.Identifier = identifierTag.Text()

					creatorTag := meta.SelectElement("creator")
					metaStruct.Author = creatorTag.Text()

					bookManifest := root.SelectElement("manifest")
					itemsManifest := bookManifest.SelectElements("item")
					for _, item := range itemsManifest {
						linkItem := Link{}
						linkItem.TypeLink = item.SelectAttrValue("media-type", "")
						linkItem.Href = item.SelectAttrValue("href", "")
						if linkItem.TypeLink == "application/xhtml+xml" {
							manifestStruct.Spine = append(manifestStruct.Spine, linkItem)
						} else {
							manifestStruct.Resources = append(manifestStruct.Resources, linkItem)
						}
					}

					manifestStruct.Metadata = metaStruct
					j, _ := json.Marshal(manifestStruct)
					w.Header().Set("Content-Type", "application/json")
					w.Header().Set("Access-Control-Allow-Origin", "*")
					w.Write(j)
					return
				}
			}
		}
	}

}

func getAsset(w http.ResponseWriter, req *http.Request) {
	var opfFileName string
	var buff string

	vars := mux.Vars(req)
	filename := "books/" + vars["filename"]
	assetname := vars["asset"]
	jsInject := req.URL.Query().Get("js")

	zipReader, err := zip.OpenReader(filename)
	if err != nil {
		fmt.Println(err)
	}

	for _, f := range zipReader.File {
		if f.Name == "META-INF/container.xml" {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			doc := etree.NewDocument()
			_, err = doc.ReadFrom(rc)
			if err == nil {
				root := doc.SelectElement("container")
				rootFiles := root.SelectElements("rootfiles")
				for _, rootFileTag := range rootFiles {
					rootFile := rootFileTag.SelectElement("rootfile")
					if rootFile != nil {
						opfFileName = rootFile.SelectAttrValue("full-path", "")
					}
				}
			} else {
				fmt.Println(err)
			}
			rc.Close()
		}
	}

	resourcePath := strings.Split(opfFileName, "/")[0]

	for _, f := range zipReader.File {
		//fmt.Println(f.Name)
		if f.Name == resourcePath+"/"+assetname {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			defer rc.Close()

			extension := filepath.Ext(f.Name)
			if extension == ".css" {
				w.Header().Set("Content-Type", "text/css")
			}
			if extension == ".xml" {
				w.Header().Set("Content-Type", "application/xhtml+xml")
			}
			if extension == ".js" {
				w.Header().Set("Content-Type", "text/javascript")
			}
			w.Header().Set("Access-Control-Allow-Origin", "*")

			if jsInject != "" {
				scanner := bufio.NewScanner(rc)
				for scanner.Scan() {
					if strings.Contains(scanner.Text(), "") {
						buff += strings.Replace(scanner.Text(), "", "", 1) + "\n"
					} else {
						buff += scanner.Text() + "\n"
					}
				}
			} else {
				buffByte, _ := ioutil.ReadAll(rc)
				buff = string(buffByte)
			}

			buffReader := strings.NewReader(string(buff))
			http.ServeContent(w, req, assetname, f.ModTime(), buffReader)
			return
		}
	}

}

func getWebAppManifest(w http.ResponseWriter, req *http.Request) {
	var opfFileName string
	var webapp AppInstall

	vars := mux.Vars(req)
	filename := "books/" + vars["filename"]

	webapp.Display = "standalone"
	webapp.StartURL = "index.html"
	webapp.Icons = Icon{
		Size:      "144x144",
		Src:       "/logo.png",
		MediaType: "image/png",
	}

	zipReader, err := zip.OpenReader(filename)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, f := range zipReader.File {
		if f.Name == "META-INF/container.xml" {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			doc := etree.NewDocument()
			_, err = doc.ReadFrom(rc)
			if err == nil {
				root := doc.SelectElement("container")
				rootFiles := root.SelectElements("rootfiles")
				for _, rootFileTag := range rootFiles {
					rootFile := rootFileTag.SelectElement("rootfile")
					if rootFile != nil {
						opfFileName = rootFile.SelectAttrValue("full-path", "")
					}
				}
			} else {
				fmt.Println(err)
			}
			rc.Close()
		}
	}

	if opfFileName != "" {
		for _, f := range zipReader.File {
			if f.Name == opfFileName {
				rc, errOpen := f.Open()
				if errOpen != nil {
					fmt.Println("error openging " + f.Name)
				}
				doc := etree.NewDocument()
				_, err = doc.ReadFrom(rc)
				if err == nil {
					root := doc.SelectElement("package")
					meta := root.SelectElement("metadata")

					titleTag := meta.SelectElement("title")
					webapp.Name = titleTag.Text()
					webapp.ShortName = titleTag.Text()

					j, _ := json.Marshal(webapp)
					w.Header().Set("Content-Type", "application/json")
					w.Header().Set("Access-Control-Allow-Origin", "*")
					w.Write(j)
					return
				}
			}
		}
	}

}

func bookIndex(w http.ResponseWriter, req *http.Request) {
	var err error

	vars := mux.Vars(req)
	filename := "books/" + vars["filename"]

	t, err := template.ParseFiles("index.html") // Parse template file.
	if err != nil {
		fmt.Println(err)
	}
	t.Execute(w, filename) // merge.
}

func getBooks(w http.ResponseWriter, req *http.Request) {
	var books []string

	files, _ := ioutil.ReadDir("books")
	for _, f := range files {
		fmt.Println(f.Name())
		books = append(books, f.Name())
	}

	t, err := template.ParseFiles("book_index.html") // Parse template file.
	if err != nil {
		fmt.Println(err)
	}
	t.Execute(w, books)
}

func viewer(w http.ResponseWriter, req *http.Request) {

	f, _ := os.OpenFile("public/viewer.js", os.O_RDONLY, 666)
	buff, _ := ioutil.ReadAll(f)

	w.Header().Set("Content-Type", "text/javascript")
	w.Write(buff)
}

func sw(w http.ResponseWriter, req *http.Request) {

	f, _ := os.OpenFile("public/sw.js", os.O_RDONLY, 666)
	buff, _ := ioutil.ReadAll(f)

	w.Header().Set("Content-Type", "text/javascript")
	w.Write(buff)
}
<commit_msg>inject css in asset html<commit_after>package main

import (
	"archive/zip"
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"rsc.io/letsencrypt"

	"github.com/beevik/etree"
	"github.com/gorilla/mux"
	"github.com/urfave/negroni"
)

// Metadata metadata struct
type Metadata struct {
	Title      string    `json:"title"`
	Author     string    `json:"author"`
	Identifier string    `json:"identifier"`
	Language   string    `json:"language"`
	Modified   time.Time `json:"modified"`
}

// Link link struct
type Link struct {
	Rel      string `json:"rel,omitempty"`
	Href     string `json:"href"`
	TypeLink string `json:"type"`
	Height   int    `json:"height,omitempty"`
	Width    int    `json:"width,omitempty"`
}

// Manifest manifest struct
type Manifest struct {
	Metadata  Metadata `json:"metadata"`
	Links     []Link   `json:"links"`
	Spine     []Link   `json:"spine,omitempty"`
	Resources []Link   `json:"resources"`
}

// Icon icon struct for AppInstall
type Icon struct {
	Src       string `json:"src"`
	Size      string `json:"size"`
	MediaType string `json:"type"`
}

// AppInstall struct for app install banner
type AppInstall struct {
	ShortName string `json:"short_name"`
	Name      string `json:"name"`
	StartURL  string `json:"start_url"`
	Display   string `json:"display"`
	Icons     Icon   `json:"icons"`
}

func main() {

	n := negroni.Classic()
	n.Use(negroni.NewStatic(http.Dir("public")))
	n.UseHandler(loanHandler(false))

	var m letsencrypt.Manager
	if err := m.CacheFile("letsencrypt.cache"); err != nil {
		log.Fatal(err)
	}

	if len(os.Args) > 1 && os.Args[1] == "dev" {
		s := &http.Server{
			Handler:        n,
			ReadTimeout:    10 * time.Second,
			WriteTimeout:   10 * time.Second,
			MaxHeaderBytes: 1 << 20,
			Addr:           ":8080",
		}

		//		log.Fatal(s.ListenAndServeTLS("test.cert", "test.key"))
		log.Fatal(s.ListenAndServe())
	} else {

		s := &http.Server{
			Handler:        n,
			ReadTimeout:    10 * time.Second,
			WriteTimeout:   10 * time.Second,
			MaxHeaderBytes: 1 << 20,
			Addr:           ":443",
			TLSConfig: &tls.Config{
				GetCertificate: m.GetCertificate,
			},
		}

		log.Fatal(s.ListenAndServeTLS("", ""))
	}
}

func loanHandler(test bool) http.Handler {
	serv := mux.NewRouter()

	serv.HandleFunc("/index.html", getBooks)
	serv.HandleFunc("/", getBooks)
	serv.HandleFunc("/viewer.js", viewer)
	serv.HandleFunc("/sw.js", sw)
	serv.HandleFunc("/{filename}/", bookIndex)
	serv.HandleFunc("/{filename}/manifest.json", getManifest)
	serv.HandleFunc("/{filename}/webapp.webmanifest", getWebAppManifest)
	serv.HandleFunc("/{filename}/index.html", bookIndex)
	serv.HandleFunc("/{filename}/{asset:.*}", getAsset)
	return serv
}

func getManifest(w http.ResponseWriter, req *http.Request) {
	var opfFileName string
	var manifestStruct Manifest
	var metaStruct Metadata

	metaStruct.Modified = time.Now()

	vars := mux.Vars(req)
	filename := vars["filename"]
	filename_path := "books/" + filename

	self := Link{
		Rel:      "self",
		Href:     "http://" + req.Host + "/" + filename + "/manifest.json",
		TypeLink: "application/json",
	}
	manifestStruct.Links = make([]Link, 1)
	manifestStruct.Resources = make([]Link, 0)
	manifestStruct.Resources = make([]Link, 0)
	manifestStruct.Links[0] = self

	zipReader, err := zip.OpenReader(filename_path)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, f := range zipReader.File {
		if f.Name == "META-INF/container.xml" {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			doc := etree.NewDocument()
			_, err = doc.ReadFrom(rc)
			if err == nil {
				root := doc.SelectElement("container")
				rootFiles := root.SelectElements("rootfiles")
				for _, rootFileTag := range rootFiles {
					rootFile := rootFileTag.SelectElement("rootfile")
					if rootFile != nil {
						opfFileName = rootFile.SelectAttrValue("full-path", "")
					}
				}
			} else {
				fmt.Println(err)
			}
			rc.Close()
		}
	}

	if opfFileName != "" {
		for _, f := range zipReader.File {
			if f.Name == opfFileName {
				rc, errOpen := f.Open()
				if errOpen != nil {
					fmt.Println("error openging " + f.Name)
				}
				doc := etree.NewDocument()
				_, err = doc.ReadFrom(rc)
				if err == nil {
					root := doc.SelectElement("package")
					meta := root.SelectElement("metadata")

					titleTag := meta.SelectElement("title")
					metaStruct.Title = titleTag.Text()

					langTag := meta.SelectElement("language")
					metaStruct.Language = langTag.Text()

					identifierTag := meta.SelectElement("identifier")
					metaStruct.Identifier = identifierTag.Text()

					creatorTag := meta.SelectElement("creator")
					metaStruct.Author = creatorTag.Text()

					bookManifest := root.SelectElement("manifest")
					itemsManifest := bookManifest.SelectElements("item")
					for _, item := range itemsManifest {
						linkItem := Link{}
						linkItem.TypeLink = item.SelectAttrValue("media-type", "")
						linkItem.Href = item.SelectAttrValue("href", "")
						if linkItem.TypeLink == "application/xhtml+xml" {
							manifestStruct.Spine = append(manifestStruct.Spine, linkItem)
						} else {
							manifestStruct.Resources = append(manifestStruct.Resources, linkItem)
						}
					}

					manifestStruct.Metadata = metaStruct
					j, _ := json.Marshal(manifestStruct)
					w.Header().Set("Content-Type", "application/json")
					w.Header().Set("Access-Control-Allow-Origin", "*")
					w.Write(j)
					return
				}
			}
		}
	}

}

func getAsset(w http.ResponseWriter, req *http.Request) {
	var opfFileName string
	var buff string

	vars := mux.Vars(req)
	filename := "books/" + vars["filename"]
	assetname := vars["asset"]
	jsInject := req.URL.Query().Get("js")
	cssInject := req.URL.Query().Get("css")

	zipReader, err := zip.OpenReader(filename)
	if err != nil {
		fmt.Println(err)
	}

	for _, f := range zipReader.File {
		if f.Name == "META-INF/container.xml" {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			doc := etree.NewDocument()
			_, err = doc.ReadFrom(rc)
			if err == nil {
				root := doc.SelectElement("container")
				rootFiles := root.SelectElements("rootfiles")
				for _, rootFileTag := range rootFiles {
					rootFile := rootFileTag.SelectElement("rootfile")
					if rootFile != nil {
						opfFileName = rootFile.SelectAttrValue("full-path", "")
					}
				}
			} else {
				fmt.Println(err)
			}
			rc.Close()
		}
	}

	resourcePath := strings.Split(opfFileName, "/")[0]

	for _, f := range zipReader.File {
		//fmt.Println(f.Name)
		if f.Name == resourcePath+"/"+assetname {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			defer rc.Close()

			extension := filepath.Ext(f.Name)
			if extension == ".css" {
				w.Header().Set("Content-Type", "text/css")
			}
			if extension == ".xml" {
				w.Header().Set("Content-Type", "application/xhtml+xml")
			}
			if extension == ".js" {
				w.Header().Set("Content-Type", "text/javascript")
			}
			w.Header().Set("Access-Control-Allow-Origin", "*")

			scanner := bufio.NewScanner(rc)
			for scanner.Scan() {
				if strings.Contains(scanner.Text(), "") {
					headBuff := ""
					if jsInject != "" {
						headBuff += strings.Replace(scanner.Text(), "", "", 1)
					}
					if cssInject != "" {
						if headBuff == "" {
							headBuff += strings.Replace(scanner.Text(), "", "<link rel='stylesheet' type='text/css' href='/"+cssInject+"'></script>", 1)
						} else {
							headBuff = strings.Replace(headBuff, "", "<link rel='stylesheet' type='text/css' href='/"+cssInject+"'>", 1)
						}
					}
					buff += headBuff + "\n"
				} else {
					buff += scanner.Text() + "\n"
				}
			}

			buffReader := strings.NewReader(string(buff))
			http.ServeContent(w, req, assetname, f.ModTime(), buffReader)
			return
		}
	}

}

func getWebAppManifest(w http.ResponseWriter, req *http.Request) {
	var opfFileName string
	var webapp AppInstall

	vars := mux.Vars(req)
	filename := "books/" + vars["filename"]

	webapp.Display = "standalone"
	webapp.StartURL = "index.html"
	webapp.Icons = Icon{
		Size:      "144x144",
		Src:       "/logo.png",
		MediaType: "image/png",
	}

	zipReader, err := zip.OpenReader(filename)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, f := range zipReader.File {
		if f.Name == "META-INF/container.xml" {
			rc, errOpen := f.Open()
			if errOpen != nil {
				fmt.Println("error openging " + f.Name)
			}
			doc := etree.NewDocument()
			_, err = doc.ReadFrom(rc)
			if err == nil {
				root := doc.SelectElement("container")
				rootFiles := root.SelectElements("rootfiles")
				for _, rootFileTag := range rootFiles {
					rootFile := rootFileTag.SelectElement("rootfile")
					if rootFile != nil {
						opfFileName = rootFile.SelectAttrValue("full-path", "")
					}
				}
			} else {
				fmt.Println(err)
			}
			rc.Close()
		}
	}

	if opfFileName != "" {
		for _, f := range zipReader.File {
			if f.Name == opfFileName {
				rc, errOpen := f.Open()
				if errOpen != nil {
					fmt.Println("error openging " + f.Name)
				}
				doc := etree.NewDocument()
				_, err = doc.ReadFrom(rc)
				if err == nil {
					root := doc.SelectElement("package")
					meta := root.SelectElement("metadata")

					titleTag := meta.SelectElement("title")
					webapp.Name = titleTag.Text()
					webapp.ShortName = titleTag.Text()

					j, _ := json.Marshal(webapp)
					w.Header().Set("Content-Type", "application/json")
					w.Header().Set("Access-Control-Allow-Origin", "*")
					w.Write(j)
					return
				}
			}
		}
	}

}

func bookIndex(w http.ResponseWriter, req *http.Request) {
	var err error

	vars := mux.Vars(req)
	filename := "books/" + vars["filename"]

	t, err := template.ParseFiles("index.html") // Parse template file.
	if err != nil {
		fmt.Println(err)
	}
	t.Execute(w, filename) // merge.
}

func getBooks(w http.ResponseWriter, req *http.Request) {
	var books []string

	files, _ := ioutil.ReadDir("books")
	for _, f := range files {
		fmt.Println(f.Name())
		books = append(books, f.Name())
	}

	t, err := template.ParseFiles("book_index.html") // Parse template file.
	if err != nil {
		fmt.Println(err)
	}
	t.Execute(w, books)
}

func viewer(w http.ResponseWriter, req *http.Request) {

	f, _ := os.OpenFile("public/viewer.js", os.O_RDONLY, 666)
	buff, _ := ioutil.ReadAll(f)

	w.Header().Set("Content-Type", "text/javascript")
	w.Write(buff)
}

func sw(w http.ResponseWriter, req *http.Request) {

	f, _ := os.OpenFile("public/sw.js", os.O_RDONLY, 666)
	buff, _ := ioutil.ReadAll(f)

	w.Header().Set("Content-Type", "text/javascript")
	w.Write(buff)
}
<|endoftext|>


<commit_before>package dotweb

import (
	"fmt"
	"github.com/devfeel/dotweb/framework/convert"
	"github.com/devfeel/dotweb/framework/exception"
	"github.com/devfeel/dotweb/framework/json"
	"github.com/devfeel/dotweb/framework/log"
	"github.com/devfeel/dotweb/session"
	"net/http"
	"strings"
	"sync"
	"time"

	"compress/gzip"
	"github.com/devfeel/dotweb/config"
	"github.com/devfeel/dotweb/routers"
	"golang.org/x/net/websocket"
	"io"
	"net/url"
)

const (
	DefaultGzipLevel = 9
	gzipScheme       = "gzip"
)

type (
	//HttpModule定义
	HttpModule struct {
		//响应请求时作为 HTTP 执行管线链中的第一个事件发生
		OnBeginRequest func(*HttpContext)
		//响应请求时作为 HTTP 执行管线链中的最后一个事件发生。
		OnEndRequest func(*HttpContext)
	}

	//HttpServer定义
	HttpServer struct {
		router         Router
		DotApp         *DotWeb
		sessionManager *session.SessionManager
		lock_session   *sync.RWMutex
		pool           *pool
		ServerConfig   *config.ServerConfig
		SessionConfig  *config.SessionConfig
		binder         Binder
		render         Renderer
		offline        bool
	}

	//pool定义
	pool struct {
		response sync.Pool
		context  sync.Pool
	}
)

// Handle is a function that can be registered to a route to handle HTTP
// requests. Like http.HandlerFunc, but has a third parameter for the values of
// wildcards (variables).
type HttpHandle func(*HttpContext)

func NewHttpServer() *HttpServer {
	server := &HttpServer{
		pool: &pool{
			response: sync.Pool{
				New: func() interface{} {
					return &Response{}
				},
			},
			context: sync.Pool{
				New: func() interface{} {
					return &HttpContext{}
				},
			},
		},
		ServerConfig:  config.NewServerConfig(),
		SessionConfig: config.NewSessionConfig(),
		lock_session:  new(sync.RWMutex),
		binder:        newBinder(),
	}
	//设置router
	server.router = NewRouter(server)
	return server
}

//ServeHTTP make sure request can be handled correctly
func (server *HttpServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	//针对websocket与调试信息特殊处理
	if checkIsWebSocketRequest(req) {
		http.DefaultServeMux.ServeHTTP(w, req)
	} else {
		//设置header信息
		w.Header().Set(HeaderServer, DefaultServerName)
		//处理维护
		if server.IsOffline() {
			server.DotApp.OfflineServer.ServeHTTP(w, req)
		} else {
			server.Router().ServeHTTP(w, req)
		}
	}
}

//IsOffline check server is set offline state
func (server *HttpServer) IsOffline() bool {
	return server.offline
}

//SetOffline set server offline config
func (server *HttpServer) SetOffline(offline bool, offlineText string, offlineUrl string) {
	server.offline = offline
}

//set session store config
func (server *HttpServer) SetSessionConfig(storeConfig *session.StoreConfig) {
	//sync session config
	server.SessionConfig.Timeout = storeConfig.Maxlifetime
	server.SessionConfig.SessionMode = storeConfig.StoreName
	server.SessionConfig.ServerIP = storeConfig.ServerIP
}

//init session manager
func (server *HttpServer) InitSessionManager() {
	storeConfig := new(session.StoreConfig)
	storeConfig.Maxlifetime = server.SessionConfig.Timeout
	storeConfig.StoreName = server.SessionConfig.SessionMode
	storeConfig.ServerIP = server.SessionConfig.ServerIP

	if server.sessionManager == nil {
		//设置Session
		server.lock_session.Lock()
		if manager, err := session.NewDefaultSessionManager(storeConfig); err != nil {
			//panic error with create session manager
			panic(err.Error())
		} else {
			server.sessionManager = manager
		}
		server.lock_session.Unlock()
	}
}

/*
* 关联当前HttpServer实例对应的DotServer实例
 */
func (server *HttpServer) setDotApp(dotApp *DotWeb) {
	server.DotApp = dotApp
}

//get session manager in current httpserver
func (server *HttpServer) GetSessionManager() *session.SessionManager {
	if !server.SessionConfig.EnabledSession {
		return nil
	}
	return server.sessionManager
}

//get router interface in server
func (server *HttpServer) Router() Router {
	return server.router
}

//get binder interface in server
func (server *HttpServer) Binder() Binder {
	return server.binder
}

//get renderer interface in server
func (server *HttpServer) Renderer() Renderer {
	return server.render
}

//set custom renderer in server
func (server *HttpServer) SetRenderer(r Renderer) {
	server.render = r
}

//set EnabledAutoHEAD true or false
func (server *HttpServer) SetEnabledAutoHEAD(autoHEAD bool) {
	server.ServerConfig.EnabledAutoHEAD = autoHEAD
}

type LogJson struct {
	RequestUrl string
	HttpHeader string
	HttpBody   string
}

//wrap HttpHandle to httprouter.Handle
func (server *HttpServer) wrapRouterHandle(handle HttpHandle, isHijack bool) routers.Handle {
	return func(w http.ResponseWriter, r *http.Request, params routers.Params) {
		//get from pool
		res := server.pool.response.Get().(*Response)
		res.Reset(w)
		httpCtx := server.pool.context.Get().(*HttpContext)
		httpCtx.Reset(res, r, server, params)

		//gzip
		if server.ServerConfig.EnabledGzip {
			gw, err := gzip.NewWriterLevel(w, DefaultGzipLevel)
			if err != nil {
				panic("use gzip error -> " + err.Error())
			}
			grw := &gzipResponseWriter{Writer: gw, ResponseWriter: w}
			res.Reset(grw)
			httpCtx.SetHeader(HeaderContentEncoding, gzipScheme)
		}
		//增加状态计数
		GlobalState.AddRequestCount(1)

		//session
		//if exists client-sessionid, use it
		//if not exists client-sessionid, new one
		if server.SessionConfig.EnabledSession {
			sessionId, err := server.GetSessionManager().GetClientSessionID(r)
			if err == nil && sessionId != "" {
				httpCtx.SessionID = sessionId
			} else {
				httpCtx.SessionID = server.GetSessionManager().NewSessionID()
				cookie := http.Cookie{
					Name:  server.sessionManager.CookieName,
					Value: url.QueryEscape(httpCtx.SessionID),
					Path:  "/",
				}
				httpCtx.WriteCookieObj(cookie)
			}
			//特殊的，如果为Cookie存储方式
			if server.SessionConfig.SessionMode == session.SessionMode_Cookie {

			}
		}

		//hijack处理
		if isHijack {
			_, hijack_err := httpCtx.Hijack()
			if hijack_err != nil {
				//输出内容
				httpCtx.Response.WriteHeader(http.StatusInternalServerError)
				httpCtx.Response.Header().Set(HeaderContentType, CharsetUTF8)
				httpCtx.WriteString(hijack_err.Error())
				return
			}
		}

		startTime := time.Now()
		defer func() {
			var errmsg string
			if err := recover(); err != nil {
				errmsg = exception.CatchError("httpserver::RouterHandle", LogTarget_HttpServer, err)

				//默认异常处理
				if server.DotApp.ExceptionHandler != nil {
					server.DotApp.ExceptionHandler(httpCtx, err)
				}

				//记录访问日志
				headinfo := fmt.Sprintln(httpCtx.Response.Header)
				logJson := LogJson{
					RequestUrl: httpCtx.Request.RequestURI,
					HttpHeader: headinfo,
					HttpBody:   errmsg,
				}
				logString := jsonutil.GetJsonString(logJson)
				logger.Log(logString, LogTarget_HttpServer, LogLevel_Error)

				//增加错误计数
				GlobalState.AddErrorCount(1)
			}
			timetaken := int64(time.Now().Sub(startTime) / time.Millisecond)
			//HttpServer Logging
			logger.Log(httpCtx.Url()+" "+logContext(httpCtx, timetaken), LogTarget_HttpRequest, LogLevel_Debug)

			if server.ServerConfig.EnabledGzip {
				var w io.Writer
				w = res.Writer().(*gzipResponseWriter).Writer
				w.(*gzip.Writer).Close()
			}
			// Return to pool
			server.pool.response.Put(res)
			//release context
			httpCtx.release()
			server.pool.context.Put(httpCtx)
		}()

		//处理前置Module集合
		for _, module := range server.DotApp.Modules {
			if module.OnBeginRequest != nil {
				module.OnBeginRequest(httpCtx)
			}
		}

		//处理用户handle
		//if already set HttpContext.End,ignore user handler - fixed issue #5
		if !httpCtx.IsEnd() {
			handle(httpCtx)
		}

		//处理后置Module集合
		for _, module := range server.DotApp.Modules {
			if module.OnEndRequest != nil {
				module.OnEndRequest(httpCtx)
			}
		}

	}
}

//wrap fileHandler to httprouter.Handle
func (server *HttpServer) wrapFileHandle(fileHandler http.Handler) routers.Handle {
	return func(w http.ResponseWriter, r *http.Request, params routers.Params) {
		//增加状态计数
		GlobalState.AddRequestCount(1)
		startTime := time.Now()
		r.URL.Path = params.ByName("filepath")
		fileHandler.ServeHTTP(w, r)
		timetaken := int64(time.Now().Sub(startTime) / time.Millisecond)
		//HttpServer Logging
		logger.Log(r.URL.String()+" "+logRequest(r, timetaken), LogTarget_HttpRequest, LogLevel_Debug)
	}
}

//wrap HttpHandle to websocket.Handle
func (server *HttpServer) wrapWebSocketHandle(handle HttpHandle) websocket.Handler {
	return func(ws *websocket.Conn) {
		//get from pool
		httpCtx := server.pool.context.Get().(*HttpContext)
		httpCtx.Reset(nil, ws.Request(), server, nil)
		httpCtx.WebSocket = &WebSocket{
			Conn: ws,
		}
		httpCtx.IsWebSocket = true

		startTime := time.Now()
		defer func() {
			var errmsg string
			if err := recover(); err != nil {
				errmsg = exception.CatchError("httpserver::WebsocketHandle", LogTarget_HttpServer, err)

				//记录访问日志
				headinfo := fmt.Sprintln(httpCtx.WebSocket.Conn.Request().Header)
				logJson := LogJson{
					RequestUrl: httpCtx.WebSocket.Conn.Request().RequestURI,
					HttpHeader: headinfo,
					HttpBody:   errmsg,
				}
				logString := jsonutil.GetJsonString(logJson)
				logger.Log(logString, LogTarget_HttpServer, LogLevel_Error)

				//增加错误计数
				GlobalState.AddErrorCount(1)
			}
			timetaken := int64(time.Now().Sub(startTime) / time.Millisecond)
			//HttpServer Logging
			logger.Log(httpCtx.Url()+" "+logContext(httpCtx, timetaken), LogTarget_HttpRequest, LogLevel_Debug)

			// Return to pool
			server.pool.context.Put(httpCtx)
		}()

		handle(httpCtx)

		//增加状态计数
		GlobalState.AddRequestCount(1)
	}
}

//get default log string
func logContext(ctx *HttpContext, timetaken int64) string {
	var reqbytelen, resbytelen, method, proto, status, userip string
	if ctx != nil {
		if !ctx.IsWebSocket {
			reqbytelen = convert.Int642String(ctx.Request.ContentLength)
			resbytelen = convert.Int642String(ctx.Response.Size)
			method = ctx.Request.Method
			proto = ctx.Request.Proto
			status = convert.Int2String(ctx.Response.Status)
			userip = ctx.RemoteIP()
		} else {
			reqbytelen = convert.Int642String(ctx.Request.ContentLength)
			resbytelen = "0"
			method = ctx.Request.Method
			proto = ctx.Request.Proto
			status = "0"
			userip = ctx.RemoteIP()
		}
	}

	log := method + " "
	log += userip + " "
	log += proto + " "
	log += status + " "
	log += reqbytelen + " "
	log += resbytelen + " "
	log += convert.Int642String(timetaken)

	return log
}

func logRequest(req *http.Request, timetaken int64) string {
	var reqbytelen, resbytelen, method, proto, status, userip string
	reqbytelen = convert.Int642String(req.ContentLength)
	resbytelen = ""
	method = req.Method
	proto = req.Proto
	status = "200"
	userip = req.RemoteAddr

	log := method + " "
	log += userip + " "
	log += proto + " "
	log += status + " "
	log += reqbytelen + " "
	log += resbytelen + " "
	log += convert.Int642String(timetaken)

	return log
}

//check request is the websocket request
func checkIsWebSocketRequest(req *http.Request) bool {
	if req.Header.Get("Connection") == "Upgrade" {
		return true
	}
	return false
}

//check request is startwith /debug/
func checkIsDebugRequest(req *http.Request) bool {
	if strings.Index(req.RequestURI, "/debug/") == 0 {
		return true
	}
	return false
}
<commit_msg>extension function and fixed bugs: 1、HttpContext.Redirect: add set nocache and support set statuscode 2、HrrpContext add Write(code, content), you can set code to write bytes 3、SessionStoreConfig remove UserName&Password field 4、add des\tripdes code<commit_after>package dotweb

import (
	"fmt"
	"github.com/devfeel/dotweb/framework/convert"
	"github.com/devfeel/dotweb/framework/exception"
	"github.com/devfeel/dotweb/framework/json"
	"github.com/devfeel/dotweb/framework/log"
	"github.com/devfeel/dotweb/session"
	"net/http"
	"strings"
	"sync"
	"time"

	"compress/gzip"
	"github.com/devfeel/dotweb/config"
	"github.com/devfeel/dotweb/routers"
	"golang.org/x/net/websocket"
	"io"
	"net/url"
)

const (
	DefaultGzipLevel = 9
	gzipScheme       = "gzip"
)

type (
	//HttpModule定义
	HttpModule struct {
		//响应请求时作为 HTTP 执行管线链中的第一个事件发生
		OnBeginRequest func(*HttpContext)
		//响应请求时作为 HTTP 执行管线链中的最后一个事件发生。
		OnEndRequest func(*HttpContext)
	}

	//HttpServer定义
	HttpServer struct {
		router         Router
		DotApp         *DotWeb
		sessionManager *session.SessionManager
		lock_session   *sync.RWMutex
		pool           *pool
		ServerConfig   *config.ServerConfig
		SessionConfig  *config.SessionConfig
		binder         Binder
		render         Renderer
		offline        bool
	}

	//pool定义
	pool struct {
		response sync.Pool
		context  sync.Pool
	}
)

// Handle is a function that can be registered to a route to handle HTTP
// requests. Like http.HandlerFunc, but has a third parameter for the values of
// wildcards (variables).
type HttpHandle func(*HttpContext)

func NewHttpServer() *HttpServer {
	server := &HttpServer{
		pool: &pool{
			response: sync.Pool{
				New: func() interface{} {
					return &Response{}
				},
			},
			context: sync.Pool{
				New: func() interface{} {
					return &HttpContext{}
				},
			},
		},
		ServerConfig:  config.NewServerConfig(),
		SessionConfig: config.NewSessionConfig(),
		lock_session:  new(sync.RWMutex),
		binder:        newBinder(),
	}
	//设置router
	server.router = NewRouter(server)
	return server
}

//ServeHTTP make sure request can be handled correctly
func (server *HttpServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	//针对websocket与调试信息特殊处理
	if checkIsWebSocketRequest(req) {
		http.DefaultServeMux.ServeHTTP(w, req)
	} else {
		//设置header信息
		w.Header().Set(HeaderServer, DefaultServerName)
		//处理维护
		if server.IsOffline() {
			server.DotApp.OfflineServer.ServeHTTP(w, req)
		} else {
			server.Router().ServeHTTP(w, req)
		}
	}
}

//IsOffline check server is set offline state
func (server *HttpServer) IsOffline() bool {
	return server.offline
}

//SetOffline set server offline config
func (server *HttpServer) SetOffline(offline bool, offlineText string, offlineUrl string) {
	server.offline = offline
}

//set session store config
func (server *HttpServer) SetSessionConfig(storeConfig *session.StoreConfig) {
	//sync session config
	server.SessionConfig.Timeout = storeConfig.Maxlifetime
	server.SessionConfig.SessionMode = storeConfig.StoreName
	server.SessionConfig.ServerIP = storeConfig.ServerIP
}

//init session manager
func (server *HttpServer) InitSessionManager() {
	storeConfig := new(session.StoreConfig)
	storeConfig.Maxlifetime = server.SessionConfig.Timeout
	storeConfig.StoreName = server.SessionConfig.SessionMode
	storeConfig.ServerIP = server.SessionConfig.ServerIP

	if server.sessionManager == nil {
		//设置Session
		server.lock_session.Lock()
		if manager, err := session.NewDefaultSessionManager(storeConfig); err != nil {
			//panic error with create session manager
			panic(err.Error())
		} else {
			server.sessionManager = manager
		}
		server.lock_session.Unlock()
	}
}

/*
* 关联当前HttpServer实例对应的DotServer实例
 */
func (server *HttpServer) setDotApp(dotApp *DotWeb) {
	server.DotApp = dotApp
}

//get session manager in current httpserver
func (server *HttpServer) GetSessionManager() *session.SessionManager {
	if !server.SessionConfig.EnabledSession {
		return nil
	}
	return server.sessionManager
}

//get router interface in server
func (server *HttpServer) Router() Router {
	return server.router
}

//get binder interface in server
func (server *HttpServer) Binder() Binder {
	return server.binder
}

//get renderer interface in server
func (server *HttpServer) Renderer() Renderer {
	return server.render
}

//set custom renderer in server
func (server *HttpServer) SetRenderer(r Renderer) {
	server.render = r
}

//set EnabledAutoHEAD true or false
func (server *HttpServer) SetEnabledAutoHEAD(autoHEAD bool) {
	server.ServerConfig.EnabledAutoHEAD = autoHEAD
}

type LogJson struct {
	RequestUrl string
	HttpHeader string
	HttpBody   string
}

//wrap HttpHandle to httprouter.Handle
func (server *HttpServer) wrapRouterHandle(handle HttpHandle, isHijack bool) routers.Handle {
	return func(w http.ResponseWriter, r *http.Request, params routers.Params) {
		//get from pool
		res := server.pool.response.Get().(*Response)
		res.Reset(w)
		httpCtx := server.pool.context.Get().(*HttpContext)
		httpCtx.Reset(res, r, server, params)

		//gzip
		if server.ServerConfig.EnabledGzip {
			gw, err := gzip.NewWriterLevel(w, DefaultGzipLevel)
			if err != nil {
				panic("use gzip error -> " + err.Error())
			}
			grw := &gzipResponseWriter{Writer: gw, ResponseWriter: w}
			res.Reset(grw)
			httpCtx.SetHeader(HeaderContentEncoding, gzipScheme)
		}
		//增加状态计数
		GlobalState.AddRequestCount(1)

		//session
		//if exists client-sessionid, use it
		//if not exists client-sessionid, new one
		if server.SessionConfig.EnabledSession {
			sessionId, err := server.GetSessionManager().GetClientSessionID(r)
			if err == nil && sessionId != "" {
				httpCtx.SessionID = sessionId
			} else {
				httpCtx.SessionID = server.GetSessionManager().NewSessionID()
				cookie := http.Cookie{
					Name:  server.sessionManager.CookieName,
					Value: url.QueryEscape(httpCtx.SessionID),
					Path:  "/",
				}
				httpCtx.WriteCookieObj(cookie)
			}

		}

		//hijack处理
		if isHijack {
			_, hijack_err := httpCtx.Hijack()
			if hijack_err != nil {
				//输出内容
				httpCtx.Response.WriteHeader(http.StatusInternalServerError)
				httpCtx.Response.Header().Set(HeaderContentType, CharsetUTF8)
				httpCtx.WriteString(hijack_err.Error())
				return
			}
		}

		startTime := time.Now()
		defer func() {
			var errmsg string
			if err := recover(); err != nil {
				errmsg = exception.CatchError("httpserver::RouterHandle", LogTarget_HttpServer, err)

				//默认异常处理
				if server.DotApp.ExceptionHandler != nil {
					server.DotApp.ExceptionHandler(httpCtx, err)
				}

				//记录访问日志
				headinfo := fmt.Sprintln(httpCtx.Response.Header)
				logJson := LogJson{
					RequestUrl: httpCtx.Request.RequestURI,
					HttpHeader: headinfo,
					HttpBody:   errmsg,
				}
				logString := jsonutil.GetJsonString(logJson)
				logger.Log(logString, LogTarget_HttpServer, LogLevel_Error)

				//增加错误计数
				GlobalState.AddErrorCount(1)
			}
			timetaken := int64(time.Now().Sub(startTime) / time.Millisecond)
			//HttpServer Logging
			logger.Log(httpCtx.Url()+" "+logContext(httpCtx, timetaken), LogTarget_HttpRequest, LogLevel_Debug)

			if server.ServerConfig.EnabledGzip {
				var w io.Writer
				w = res.Writer().(*gzipResponseWriter).Writer
				w.(*gzip.Writer).Close()
			}
			// Return to pool
			server.pool.response.Put(res)
			//release context
			httpCtx.release()
			server.pool.context.Put(httpCtx)
		}()

		//处理前置Module集合
		for _, module := range server.DotApp.Modules {
			if module.OnBeginRequest != nil {
				module.OnBeginRequest(httpCtx)
			}
		}

		//处理用户handle
		//if already set HttpContext.End,ignore user handler - fixed issue #5
		if !httpCtx.IsEnd() {
			handle(httpCtx)
		}

		//处理后置Module集合
		for _, module := range server.DotApp.Modules {
			if module.OnEndRequest != nil {
				module.OnEndRequest(httpCtx)
			}
		}

	}
}

//wrap fileHandler to httprouter.Handle
func (server *HttpServer) wrapFileHandle(fileHandler http.Handler) routers.Handle {
	return func(w http.ResponseWriter, r *http.Request, params routers.Params) {
		//增加状态计数
		GlobalState.AddRequestCount(1)
		startTime := time.Now()
		r.URL.Path = params.ByName("filepath")
		fileHandler.ServeHTTP(w, r)
		timetaken := int64(time.Now().Sub(startTime) / time.Millisecond)
		//HttpServer Logging
		logger.Log(r.URL.String()+" "+logRequest(r, timetaken), LogTarget_HttpRequest, LogLevel_Debug)
	}
}

//wrap HttpHandle to websocket.Handle
func (server *HttpServer) wrapWebSocketHandle(handle HttpHandle) websocket.Handler {
	return func(ws *websocket.Conn) {
		//get from pool
		httpCtx := server.pool.context.Get().(*HttpContext)
		httpCtx.Reset(nil, ws.Request(), server, nil)
		httpCtx.WebSocket = &WebSocket{
			Conn: ws,
		}
		httpCtx.IsWebSocket = true

		startTime := time.Now()
		defer func() {
			var errmsg string
			if err := recover(); err != nil {
				errmsg = exception.CatchError("httpserver::WebsocketHandle", LogTarget_HttpServer, err)

				//记录访问日志
				headinfo := fmt.Sprintln(httpCtx.WebSocket.Conn.Request().Header)
				logJson := LogJson{
					RequestUrl: httpCtx.WebSocket.Conn.Request().RequestURI,
					HttpHeader: headinfo,
					HttpBody:   errmsg,
				}
				logString := jsonutil.GetJsonString(logJson)
				logger.Log(logString, LogTarget_HttpServer, LogLevel_Error)

				//增加错误计数
				GlobalState.AddErrorCount(1)
			}
			timetaken := int64(time.Now().Sub(startTime) / time.Millisecond)
			//HttpServer Logging
			logger.Log(httpCtx.Url()+" "+logContext(httpCtx, timetaken), LogTarget_HttpRequest, LogLevel_Debug)

			// Return to pool
			server.pool.context.Put(httpCtx)
		}()

		handle(httpCtx)

		//增加状态计数
		GlobalState.AddRequestCount(1)
	}
}

//get default log string
func logContext(ctx *HttpContext, timetaken int64) string {
	var reqbytelen, resbytelen, method, proto, status, userip string
	if ctx != nil {
		if !ctx.IsWebSocket {
			reqbytelen = convert.Int642String(ctx.Request.ContentLength)
			resbytelen = convert.Int642String(ctx.Response.Size)
			method = ctx.Request.Method
			proto = ctx.Request.Proto
			status = convert.Int2String(ctx.Response.Status)
			userip = ctx.RemoteIP()
		} else {
			reqbytelen = convert.Int642String(ctx.Request.ContentLength)
			resbytelen = "0"
			method = ctx.Request.Method
			proto = ctx.Request.Proto
			status = "0"
			userip = ctx.RemoteIP()
		}
	}

	log := method + " "
	log += userip + " "
	log += proto + " "
	log += status + " "
	log += reqbytelen + " "
	log += resbytelen + " "
	log += convert.Int642String(timetaken)

	return log
}

func logRequest(req *http.Request, timetaken int64) string {
	var reqbytelen, resbytelen, method, proto, status, userip string
	reqbytelen = convert.Int642String(req.ContentLength)
	resbytelen = ""
	method = req.Method
	proto = req.Proto
	status = "200"
	userip = req.RemoteAddr

	log := method + " "
	log += userip + " "
	log += proto + " "
	log += status + " "
	log += reqbytelen + " "
	log += resbytelen + " "
	log += convert.Int642String(timetaken)

	return log
}

//check request is the websocket request
func checkIsWebSocketRequest(req *http.Request) bool {
	if req.Header.Get("Connection") == "Upgrade" {
		return true
	}
	return false
}

//check request is startwith /debug/
func checkIsDebugRequest(req *http.Request) bool {
	if strings.Index(req.RequestURI, "/debug/") == 0 {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"github.com/codegangsta/negroni"
	"github.com/dst-hackathon/socialradar-api/question"
	"github.com/dst-hackathon/socialradar-api/user"
	"github.com/dst-hackathon/socialradar-api/configuration"
	"github.com/dst-hackathon/socialradar-api/login"
	"github.com/dst-hackathon/socialradar-api/signup"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"gopkg.in/unrolled/render.v1"
	"log"
	"net/http"
	"fmt"
)

var config configuration.Configuration = configuration.ReadFile();

func main() {
	router := mux.NewRouter().StrictSlash(false)

	question.Init(router)
	user.Init(router)
	login.Init(router)
	signup.Init(router)

	n := negroni.Classic()
	n.Use(negroni.HandlerFunc(RenderIntializer))
	n.Use(negroni.HandlerFunc(ConfigInitializer))
	n.Use(negroni.HandlerFunc(DbInitializer))

	n.UseHandler(router)
	n.Run(":3000")
}

func RenderIntializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	render := render.New(render.Options{
		IndentJSON: true,
	})
	context.Set(r, "render", render)

	next(rw, r)
}

func ConfigInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	context.Set(r, "config", config)
	next(rw, r)
}

func DbInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	dbUrl := fmt.Sprintf("postgres://%s:%s@%s:%s/socialradar_development?sslmode=disable", config.DbUser, config.DbPassword, config.DbHost, config.DbPort)
	db, err := sql.Open("postgres", dbUrl)
	if err != nil {
		log.Fatal(err)
		http.Error(rw, err.Error(), 500)
		return
	}

	context.Set(r, "db", db)
	next(rw, r)

	db.Close()
}
<commit_msg>Incoporated cors package to server web request.<commit_after>package main

import (
	"database/sql"
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/dst-hackathon/socialradar-api/configuration"
	"github.com/dst-hackathon/socialradar-api/login"
	"github.com/dst-hackathon/socialradar-api/question"
	"github.com/dst-hackathon/socialradar-api/signup"
	"github.com/dst-hackathon/socialradar-api/user"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"github.com/rs/cors"
	"gopkg.in/unrolled/render.v1"
	"log"
	"net/http"
)

var config configuration.Configuration = configuration.ReadFile()

func main() {
	router := mux.NewRouter().StrictSlash(false)

	question.Init(router)
	user.Init(router)
	login.Init(router)
	signup.Init(router)

	c := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
	})

	n := negroni.Classic()
	n.Use(negroni.HandlerFunc(RenderIntializer))
	n.Use(negroni.HandlerFunc(ConfigInitializer))
	n.Use(negroni.HandlerFunc(DbInitializer))
	n.Use(c)

	n.UseHandler(router)
	n.Run(":3000")
}

func RenderIntializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	render := render.New(render.Options{
		IndentJSON: true,
	})
	context.Set(r, "render", render)

	next(rw, r)
}

func ConfigInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	context.Set(r, "config", config)
	next(rw, r)
}

func DbInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	dbUrl := fmt.Sprintf("postgres://%s:%s@%s:%s/socialradar_development?sslmode=disable", config.DbUser, config.DbPassword, config.DbHost, config.DbPort)
	db, err := sql.Open("postgres", dbUrl)
	if err != nil {
		log.Fatal(err)
		http.Error(rw, err.Error(), 500)
		return
	}

	context.Set(r, "db", db)
	next(rw, r)

	db.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/tj/docopt"
	"github.com/ttacon/chalk"
	"os"
	"path/filepath"
)

const version = "0.2.0"

const usage = `
Usage:
  master [options] <file-or-directory>
  master -h | --help
  master --version

Options:
  -d, --output-directory string  Specify the output directory (default: <file-or-directory>).
  -s, --schema-directory string  Specify the JSON Schema directory (default: <file-or-directory>).
  -e, --encoding string          CSV file encoding [default: auto]. Supported encodings are https://goo.gl/T3zICN
  -E, --fix-encoding             Fix the CSV file encoding if it is different from --encoding.
  -n, --no-output-file           No file output. If file is given, print JSON string to stdout.
  -S, --output-schema            Output JSON schema from CSV files.
  -V, --skip-validation          Skip validation by JSON Schema.
  -h, --help                     Output help information.
  -v, --version                  Output version.
`

func main() {
	args, err := docopt.Parse(usage, nil, true, version, true)
	if err != nil {
		fatalf("Failed to parse arguments: %v\n%v", args, err)
	}

	var file, dir string
	fileOrDir := resolvePath(args["<file-or-directory>"].(string))
	stat, err := os.Stat(fileOrDir)
	if os.IsNotExist(err) {
		fatalf("No such file or directory: %v\n%v", fileOrDir, err)
	}
	if stat.IsDir() {
		dir = fileOrDir
	} else {
		file = fileOrDir
		dir = filepath.Dir(file)
	}

	if args["--output-directory"] == nil {
		args["--output-directory"] = dir
	}
	if args["--schema-directory"] == nil {
		args["--schema-directory"] = dir
	}

	cli := &Cli{
		dir:            dir,
		file:           file,
		outputDir:      resolvePath(args["--output-directory"].(string)),
		schemaDir:      resolvePath(args["--schema-directory"].(string)),
		encoding:       args["--encoding"].(string),
		fixEncoding:    args["--fix-encoding"].(bool),
		noOutputFile:   args["--no-output-file"].(bool),
		outputSchema:   args["--output-schema"].(bool),
		skipValidation: args["--skip-validation"].(bool),
	}
	cli.run()
}

func fatalf(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "\n"+chalk.Red.Color("[Error]")+" %s\n\n", fmt.Sprintf(msg, args...))
	os.Exit(1)
}

func resolvePath(path string) string {
	resolved, err := filepath.Abs(path)
	if err != nil {
		fatalf("Failed to resolve path: %v\n%v", path, err)
	}
	return resolved
}
<commit_msg>Tweak usage text<commit_after>package main

import (
	"fmt"
	"github.com/tj/docopt"
	"github.com/ttacon/chalk"
	"os"
	"path/filepath"
)

const version = "0.2.0"

const usage = `
Usage:
  master [options] <file-or-directory>
  master -h | --help
  master --version

Options:
  -d, --output-directory string  Specify the output directory (default: <file-or-directory>).
  -s, --schema-directory string  Specify the JSON Schema directory (default: <file-or-directory>).
  -e, --encoding string          CSV file encoding [default: auto]. Supported encodings are https://goo.gl/T3zICN
  -E, --fix-encoding             Fix the CSV file encoding if it is different from --encoding.
  -n, --no-output-file           No file output. If file is given, print JSON string to stdout.
  -S, --output-schema            Output JSON Schema from CSV files.
  -V, --skip-validation          Skip validation by JSON Schema.
  -h, --help                     Output help information.
  -v, --version                  Output version.
`

func main() {
	args, err := docopt.Parse(usage, nil, true, version, true)
	if err != nil {
		fatalf("Failed to parse arguments: %v\n%v", args, err)
	}

	var file, dir string
	fileOrDir := resolvePath(args["<file-or-directory>"].(string))
	stat, err := os.Stat(fileOrDir)
	if os.IsNotExist(err) {
		fatalf("No such file or directory: %v\n%v", fileOrDir, err)
	}
	if stat.IsDir() {
		dir = fileOrDir
	} else {
		file = fileOrDir
		dir = filepath.Dir(file)
	}

	if args["--output-directory"] == nil {
		args["--output-directory"] = dir
	}
	if args["--schema-directory"] == nil {
		args["--schema-directory"] = dir
	}

	cli := &Cli{
		dir:            dir,
		file:           file,
		outputDir:      resolvePath(args["--output-directory"].(string)),
		schemaDir:      resolvePath(args["--schema-directory"].(string)),
		encoding:       args["--encoding"].(string),
		fixEncoding:    args["--fix-encoding"].(bool),
		noOutputFile:   args["--no-output-file"].(bool),
		outputSchema:   args["--output-schema"].(bool),
		skipValidation: args["--skip-validation"].(bool),
	}
	cli.run()
}

func fatalf(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "\n"+chalk.Red.Color("[Error]")+" %s\n\n", fmt.Sprintf(msg, args...))
	os.Exit(1)
}

func resolvePath(path string) string {
	resolved, err := filepath.Abs(path)
	if err != nil {
		fatalf("Failed to resolve path: %v\n%v", path, err)
	}
	return resolved
}
<|endoftext|>