<commit_before>package main

import (
	"./uuid"
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"text/template"
)

type ServerConfig struct {
	Hostname     string `json:"hostname"`
	Port         string `json:"port"`
	NotifyPrefix string `json:"notifyPrefix"`
}

var gServerConfig ServerConfig

type Client struct {
	Websocket *websocket.Conn `json:"-"`
	UAID      string          `json:"uaid"`
	Ip        string          `json:"ip"`
	Port      float64         `json:"port"`
}

type Channel struct {
	UAID      string `json:"uaid"`
	ChannelID string `json:"channelID"`
	Version   string `json:"version"`
}

type ChannelIDSet map[string]bool

type ServerState struct {
	// Mapping from a UAID to the Client object
	ConnectedClients map[string]*Client `json:"connectedClients"`

	// Mapping from a UAID to all channelIDs owned by that UAID
	// where channelIDs are represented as a map-backed set
	UAIDToChannelIDs map[string]ChannelIDSet `json:"uaidToChannels"`

	// Mapping from a ChannelID to the cooresponding Channel
	ChannelIDToChannel map[string]*Channel `json:"channelIDToChannel"`
}

var gServerState ServerState

func readConfig() {

	var data []byte
	var err error

	data, err = ioutil.ReadFile("config.json")
	if err != nil {
		log.Println("Not configured.  Could not find config.json")
		os.Exit(-1)
	}

	err = json.Unmarshal(data, &gServerConfig)
	if err != nil {
		log.Println("Could not unmarshal config.json", err)
		os.Exit(-1)
		return
	}
}

func openState() {
	var data []byte
	var err error

	data, err = ioutil.ReadFile("serverstate.json")
	if err == nil {
		err = json.Unmarshal(data, &gServerState)
		if err == nil {
			return
		}
	}

	log.Println(" -> creating new server state")
	gServerState.UAIDToChannelIDs = make(map[string]ChannelIDSet)
	gServerState.ChannelIDToChannel = make(map[string]*Channel)
	gServerState.ConnectedClients = make(map[string]*Client)
}

func saveState() {
	log.Println(" -> saving state..")

	var data []byte
	var err error

	data, err = json.Marshal(gServerState)
	if err != nil {
		return
	}
	ioutil.WriteFile("serverstate.json", data, 0644)
}

func handleRegister(client *Client, f map[string]interface{}) {
	type RegisterResponse struct {
		Name         string `json:"messageType"`
		Status       int    `json:"status"`
		PushEndpoint string `json:"pushEndpoint"`
		ChannelID    string `json:"channelID"`
	}

	if f["channelID"] == nil {
		log.Println("channelID is missing!")
		return
	}

	var channelID = f["channelID"].(string)

	register := RegisterResponse{"register", 0, "", channelID}

	prevEntry, exists := gServerState.ChannelIDToChannel[channelID]
	if exists && prevEntry.UAID != client.UAID {
		register.Status = 409
	} else {
		// TODO https!
		var pushEndpoint = "http://" + gServerConfig.Hostname + ":" + gServerConfig.Port + gServerConfig.NotifyPrefix + channelID

		channel := &Channel{client.UAID, channelID, ""}

		if gServerState.UAIDToChannelIDs[client.UAID] == nil {
			gServerState.UAIDToChannelIDs[client.UAID] = make(ChannelIDSet)
		}
		gServerState.UAIDToChannelIDs[client.UAID][channelID] = true
		gServerState.ChannelIDToChannel[channelID] = channel

		register.Status = 200
		register.PushEndpoint = pushEndpoint
	}

	if register.Status == 0 {
		panic("Register(): status field was left unset when replying to client")
	}

	j, err := json.Marshal(register)
	if err != nil {
		log.Println("Could not convert register response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleUnregister(client *Client, f map[string]interface{}) {

	if f["channelID"] == nil {
		log.Println("channelID is missing!")
		return
	}

	var channelID = f["channelID"].(string)
	_, ok := gServerState.ChannelIDToChannel[channelID]
	if ok {
		// only delete if UA owns this channel
		_, owns := gServerState.UAIDToChannelIDs[client.UAID][channelID]
		if owns {
			// remove ownership
			delete(gServerState.UAIDToChannelIDs[client.UAID], channelID)
			// delete the channel itself
			delete(gServerState.ChannelIDToChannel, channelID)
		}
	}

	type UnregisterResponse struct {
		Name      string `json:"messageType"`
		Status    int    `json:"status"`
		ChannelID string `json:"channelID"`
	}

	unregister := UnregisterResponse{"unregister", 200, channelID}

	j, err := json.Marshal(unregister)
	if err != nil {
		log.Println("Could not convert unregister response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleHello(client *Client, f map[string]interface{}) {

	status := 200

	if f["uaid"] == nil {
		uaid, err := uuid.GenUUID()
		if err != nil {
			status = 400
			log.Println("GenUUID error %s", err)
		}
		client.UAID = uaid
	} else {
		client.UAID = f["uaid"].(string)

		// BUG(nikhilm): Does not deal with sending
		// a new UAID if their is a channel that was sent
		// by the UA which the server does not know about.
		// Which means in the case of this memory only server
		// it should actually always send a new UAID when it was
		// restarted
		if f["channelIDs"] != nil {
			for _, foo := range f["channelIDs"].([]interface{}) {
				channelID := foo.(string)

				c := &Channel{client.UAID, channelID, ""}
				gServerState.UAIDToChannelIDs[client.UAID][channelID] = true
				gServerState.ChannelIDToChannel[channelID] = c
			}
		}
	}

	gServerState.ConnectedClients[client.UAID] = client

	if f["interface"] != nil {
		m := f["interface"].(map[string]interface{})
		client.Ip = m["ip"].(string)
		client.Port = m["port"].(float64)
	}

	type HelloResponse struct {
		Name   string `json:"messageType"`
		Status int    `json:"status"`
		UAID   string `json:"uaid"`
	}

	hello := HelloResponse{"hello", status, client.UAID}

	j, err := json.Marshal(hello)
	if err != nil {
		log.Println("Could not convert hello response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleAck(client *Client, f map[string]interface{}) {
}

func pushHandler(ws *websocket.Conn) {

	client := &Client{ws, "", "", 0}

	for {
		var f map[string]interface{}

		var err error
		if err = websocket.JSON.Receive(ws, &f); err != nil {
			log.Println("Websocket Disconnected.", err.Error())
			break
		}

		log.Println("pushHandler msg: ", f["messageType"])

		switch f["messageType"] {
		case "hello":
			handleHello(client, f)
			break

		case "register":
			handleRegister(client, f)
			break

		case "unregister":
			handleUnregister(client, f)
			break

		case "ack":
			handleAck(client, f)
			break

		default:
			log.Println(" -> Unknown", f)
			break
		}

		saveState()
	}

	log.Println("Closing Websocket!")
	ws.Close()

	gServerState.ConnectedClients[client.UAID].Websocket = nil
}

func notifyHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Got notification from app server ", r.URL)

	if r.Method != "PUT" {
		log.Println("NOT A PUT")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Method must be PUT."))
		return
	}

	channelID := strings.Replace(r.URL.Path, gServerConfig.NotifyPrefix, "", 1)

	if strings.Contains(channelID, "/") {
		log.Println("Could not find a valid channelID")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find a valid channelID."))
		return
	}

	value := r.FormValue("version")

	if value == "" {
		log.Println("Could not find version")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find version."))
		return
	}

	channel, found := gServerState.ChannelIDToChannel[channelID]
	if !found {
		log.Println("Could not find channel " + channelID)
		return
	}
	channel.Version = value

	client := gServerState.ConnectedClients[channel.UAID]

	saveState()

	if client == nil {
		log.Println("no known client for the channel.")
	} else if client.Websocket == nil {
		wakeupClient(client)
	} else {
		sendNotificationToClient(client, channel)
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}

func wakeupClient(client *Client) {

	// TODO probably want to do this a few times before
	// giving up.

	log.Println("wakeupClient: ", client)
	service := fmt.Sprintf("%s:%g", client.Ip, client.Port)

	udpAddr, err := net.ResolveUDPAddr("up4", service)
	if err != nil {
		log.Println("ResolveUDPAddr error ", err.Error())
		return
	}

	conn, err := net.DialUDP("udp", nil, udpAddr)
	if err != nil {
		log.Println("DialUDP error ", err.Error())
		return
	}

	_, err = conn.Write([]byte(""))
	if err != nil {
		log.Println("UDP Write error ", err.Error())
		return
	}

}

func sendNotificationToClient(client *Client, channel *Channel) {

	type NotificationResponse struct {
		Name     string    `json:"messageType"`
		Channels []Channel `json:"updates"`
	}

	var channels []Channel
	channels = append(channels, *channel)

	notification := NotificationResponse{"notification", channels}

	j, err := json.Marshal(notification)
	if err != nil {
		log.Println("Could not convert hello response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", channel, err.Error())
	}
}

func admin(w http.ResponseWriter, r *http.Request) {

	type User struct {
		UAID      string
		Connected bool
		Channels  []*Channel
	}

	type Arguments struct {
		PushEndpointPrefix string
		Users              []User
	}

	// TODO https!
	arguments := Arguments{"http://" + gServerConfig.Hostname + ":" + gServerConfig.Port + gServerConfig.NotifyPrefix, nil}

	for uaid, channelIDSet := range gServerState.UAIDToChannelIDs {
		log.Println("Foo ", uaid)
		connected := gServerState.ConnectedClients[uaid] != nil
		var channels []*Channel
		for channelID, _ := range channelIDSet {
			channels = append(channels, gServerState.ChannelIDToChannel[channelID])
		}
		u := User{uaid, connected, channels}
		arguments.Users = append(arguments.Users, u)
	}

	t := template.New("users.template")
	s1, _ := t.ParseFiles("templates/users.template")
	s1.Execute(w, arguments)
}

func main() {

	readConfig()

	openState()

	http.HandleFunc("/admin", admin)

	http.Handle("/", websocket.Handler(pushHandler))

	http.HandleFunc(gServerConfig.NotifyPrefix, notifyHandler)

	log.Println("Listening on", gServerConfig.Hostname+":"+gServerConfig.Port)
	log.Fatal(http.ListenAndServe(gServerConfig.Hostname+":"+gServerConfig.Port, nil))
}
<commit_msg>Make ChannelIDSet channelID -> *Channel<commit_after>package main

import (
	"./uuid"
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"text/template"
)

type ServerConfig struct {
	Hostname     string `json:"hostname"`
	Port         string `json:"port"`
	NotifyPrefix string `json:"notifyPrefix"`
}

var gServerConfig ServerConfig

type Client struct {
	Websocket *websocket.Conn `json:"-"`
	UAID      string          `json:"uaid"`
	Ip        string          `json:"ip"`
	Port      float64         `json:"port"`
}

type Channel struct {
	UAID      string `json:"uaid"`
	ChannelID string `json:"channelID"`
	Version   string `json:"version"`
}

type ChannelIDSet map[string]*Channel

type ServerState struct {
	// Mapping from a UAID to the Client object
	ConnectedClients map[string]*Client `json:"connectedClients"`

	// Mapping from a UAID to all channelIDs owned by that UAID
	// where channelIDs are represented as a map-backed set
	UAIDToChannelIDs map[string]ChannelIDSet `json:"uaidToChannels"`

	// Mapping from a ChannelID to the cooresponding Channel
	ChannelIDToChannel ChannelIDSet `json:"channelIDToChannel"`
}

var gServerState ServerState

func readConfig() {

	var data []byte
	var err error

	data, err = ioutil.ReadFile("config.json")
	if err != nil {
		log.Println("Not configured.  Could not find config.json")
		os.Exit(-1)
	}

	err = json.Unmarshal(data, &gServerConfig)
	if err != nil {
		log.Println("Could not unmarshal config.json", err)
		os.Exit(-1)
		return
	}
}

func openState() {
	var data []byte
	var err error

	data, err = ioutil.ReadFile("serverstate.json")
	if err == nil {
		err = json.Unmarshal(data, &gServerState)
		if err == nil {
			return
		}
	}

	log.Println(" -> creating new server state")
	gServerState.UAIDToChannelIDs = make(map[string]ChannelIDSet)
	gServerState.ChannelIDToChannel = make(ChannelIDSet)
	gServerState.ConnectedClients = make(map[string]*Client)
}

func saveState() {
	log.Println(" -> saving state..")

	var data []byte
	var err error

	data, err = json.Marshal(gServerState)
	if err != nil {
		return
	}
	ioutil.WriteFile("serverstate.json", data, 0644)
}

func handleRegister(client *Client, f map[string]interface{}) {
	type RegisterResponse struct {
		Name         string `json:"messageType"`
		Status       int    `json:"status"`
		PushEndpoint string `json:"pushEndpoint"`
		ChannelID    string `json:"channelID"`
	}

	if f["channelID"] == nil {
		log.Println("channelID is missing!")
		return
	}

	var channelID = f["channelID"].(string)

	register := RegisterResponse{"register", 0, "", channelID}

	prevEntry, exists := gServerState.ChannelIDToChannel[channelID]
	if exists && prevEntry.UAID != client.UAID {
		register.Status = 409
	} else {
		// TODO https!
		var pushEndpoint = "http://" + gServerConfig.Hostname + ":" + gServerConfig.Port + gServerConfig.NotifyPrefix + channelID

		channel := &Channel{client.UAID, channelID, ""}

		if gServerState.UAIDToChannelIDs[client.UAID] == nil {
			gServerState.UAIDToChannelIDs[client.UAID] = make(ChannelIDSet)
		}
		gServerState.UAIDToChannelIDs[client.UAID][channelID] = channel
		gServerState.ChannelIDToChannel[channelID] = channel

		register.Status = 200
		register.PushEndpoint = pushEndpoint
	}

	if register.Status == 0 {
		panic("Register(): status field was left unset when replying to client")
	}

	j, err := json.Marshal(register)
	if err != nil {
		log.Println("Could not convert register response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleUnregister(client *Client, f map[string]interface{}) {

	if f["channelID"] == nil {
		log.Println("channelID is missing!")
		return
	}

	var channelID = f["channelID"].(string)
	_, ok := gServerState.ChannelIDToChannel[channelID]
	if ok {
		// only delete if UA owns this channel
		_, owns := gServerState.UAIDToChannelIDs[client.UAID][channelID]
		if owns {
			// remove ownership
			delete(gServerState.UAIDToChannelIDs[client.UAID], channelID)
			// delete the channel itself
			delete(gServerState.ChannelIDToChannel, channelID)
		}
	}

	type UnregisterResponse struct {
		Name      string `json:"messageType"`
		Status    int    `json:"status"`
		ChannelID string `json:"channelID"`
	}

	unregister := UnregisterResponse{"unregister", 200, channelID}

	j, err := json.Marshal(unregister)
	if err != nil {
		log.Println("Could not convert unregister response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleHello(client *Client, f map[string]interface{}) {

	status := 200

	if f["uaid"] == nil {
		uaid, err := uuid.GenUUID()
		if err != nil {
			status = 400
			log.Println("GenUUID error %s", err)
		}
		client.UAID = uaid
	} else {
		client.UAID = f["uaid"].(string)

		// BUG(nikhilm): Does not deal with sending
		// a new UAID if their is a channel that was sent
		// by the UA which the server does not know about.
		// Which means in the case of this memory only server
		// it should actually always send a new UAID when it was
		// restarted
		if f["channelIDs"] != nil {
			for _, foo := range f["channelIDs"].([]interface{}) {
				channelID := foo.(string)

				c := &Channel{client.UAID, channelID, ""}
				gServerState.UAIDToChannelIDs[client.UAID][channelID] = c
				gServerState.ChannelIDToChannel[channelID] = c
			}
		}
	}

	gServerState.ConnectedClients[client.UAID] = client

	if f["interface"] != nil {
		m := f["interface"].(map[string]interface{})
		client.Ip = m["ip"].(string)
		client.Port = m["port"].(float64)
	}

	type HelloResponse struct {
		Name   string `json:"messageType"`
		Status int    `json:"status"`
		UAID   string `json:"uaid"`
	}

	hello := HelloResponse{"hello", status, client.UAID}

	j, err := json.Marshal(hello)
	if err != nil {
		log.Println("Could not convert hello response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleAck(client *Client, f map[string]interface{}) {
}

func pushHandler(ws *websocket.Conn) {

	client := &Client{ws, "", "", 0}

	for {
		var f map[string]interface{}

		var err error
		if err = websocket.JSON.Receive(ws, &f); err != nil {
			log.Println("Websocket Disconnected.", err.Error())
			break
		}

		log.Println("pushHandler msg: ", f["messageType"])

		switch f["messageType"] {
		case "hello":
			handleHello(client, f)
			break

		case "register":
			handleRegister(client, f)
			break

		case "unregister":
			handleUnregister(client, f)
			break

		case "ack":
			handleAck(client, f)
			break

		default:
			log.Println(" -> Unknown", f)
			break
		}

		saveState()
	}

	log.Println("Closing Websocket!")
	ws.Close()

	gServerState.ConnectedClients[client.UAID].Websocket = nil
}

func notifyHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Got notification from app server ", r.URL)

	if r.Method != "PUT" {
		log.Println("NOT A PUT")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Method must be PUT."))
		return
	}

	channelID := strings.Replace(r.URL.Path, gServerConfig.NotifyPrefix, "", 1)

	if strings.Contains(channelID, "/") {
		log.Println("Could not find a valid channelID")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find a valid channelID."))
		return
	}

	value := r.FormValue("version")

	if value == "" {
		log.Println("Could not find version")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find version."))
		return
	}

	channel, found := gServerState.ChannelIDToChannel[channelID]
	if !found {
		log.Println("Could not find channel " + channelID)
		return
	}
	channel.Version = value

	client := gServerState.ConnectedClients[channel.UAID]

	saveState()

	if client == nil {
		log.Println("no known client for the channel.")
	} else if client.Websocket == nil {
		wakeupClient(client)
	} else {
		sendNotificationToClient(client, channel)
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}

func wakeupClient(client *Client) {

	// TODO probably want to do this a few times before
	// giving up.

	log.Println("wakeupClient: ", client)
	service := fmt.Sprintf("%s:%g", client.Ip, client.Port)

	udpAddr, err := net.ResolveUDPAddr("up4", service)
	if err != nil {
		log.Println("ResolveUDPAddr error ", err.Error())
		return
	}

	conn, err := net.DialUDP("udp", nil, udpAddr)
	if err != nil {
		log.Println("DialUDP error ", err.Error())
		return
	}

	_, err = conn.Write([]byte(""))
	if err != nil {
		log.Println("UDP Write error ", err.Error())
		return
	}

}

func sendNotificationToClient(client *Client, channel *Channel) {

	type NotificationResponse struct {
		Name     string    `json:"messageType"`
		Channels []Channel `json:"updates"`
	}

	var channels []Channel
	channels = append(channels, *channel)

	notification := NotificationResponse{"notification", channels}

	j, err := json.Marshal(notification)
	if err != nil {
		log.Println("Could not convert hello response to json %s", err)
		return
	}

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", channel, err.Error())
	}
}

func admin(w http.ResponseWriter, r *http.Request) {

	type User struct {
		UAID      string
		Connected bool
		Channels  []*Channel
	}

	type Arguments struct {
		PushEndpointPrefix string
		Users              []User
	}

	// TODO https!
	arguments := Arguments{"http://" + gServerConfig.Hostname + ":" + gServerConfig.Port + gServerConfig.NotifyPrefix, nil}

	for uaid, channelIDSet := range gServerState.UAIDToChannelIDs {
		log.Println("Foo ", uaid)
		connected := gServerState.ConnectedClients[uaid] != nil
		var channels []*Channel
		for _, channel := range channelIDSet {
			channels = append(channels, channel)
		}
		u := User{uaid, connected, channels}
		arguments.Users = append(arguments.Users, u)
	}

	t := template.New("users.template")
	s1, _ := t.ParseFiles("templates/users.template")
	s1.Execute(w, arguments)
}

func main() {

	readConfig()

	openState()

	http.HandleFunc("/admin", admin)

	http.Handle("/", websocket.Handler(pushHandler))

	http.HandleFunc(gServerConfig.NotifyPrefix, notifyHandler)

	log.Println("Listening on", gServerConfig.Hostname+":"+gServerConfig.Port)
	log.Fatal(http.ListenAndServe(gServerConfig.Hostname+":"+gServerConfig.Port, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"html/template"
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
)

func runServer() {
	http.HandleFunc("/", status)
	http.HandleFunc("/example", showExampleConfig)
	http.HandleFunc("/config", showConfig)
	http.HandleFunc("/execute", execute)

	tcpSocket := ":8080"
	log.Infof("listening on %s", tcpSocket)
	http.ListenAndServe(tcpSocket, nil)
}

func status(w http.ResponseWriter, r *http.Request) {
	log.WithFields(log.Fields{
		"host":   r.RemoteAddr,
		"method": r.Method,
		"path":   r.URL.Path,
	}).Info()

	t, err := template.ParseFiles("index.html")
	check(err)

	routes := make(map[string]string)
	routes["/"] = "display this message"
	routes["/example"] = "display an example config"
	routes["/config"] = "display the current config"
	routes["/execute"] = "execute the current config"

	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)

	data := struct {
		AppName string
		Routes  map[string]string
		Config  string
		Foo     time.Duration
		Request *http.Request
	}{
		AppName: appID,
		Routes:  routes,
		Config:  string(output),
		Foo:     config.Duration(),
		Request: r,
	}

	err = t.Execute(w, data)
	check(err)
}

func showExampleConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(ExampleConfig(), "", "    ")
	check(err)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(output)
}

func showConfig(w http.ResponseWriter, r *http.Request) {
	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(output)
}

func execute(w http.ResponseWriter, r *http.Request) {
	raid := config
	summary := raid.Conduct()
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(summary)
}
<commit_msg>server will eventually have to refresh its client's token<commit_after>package main

import (
	"encoding/json"
	"html/template"
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
)

func runServer() {
	http.HandleFunc("/", status)
	http.HandleFunc("/example", showExampleConfig)
	http.HandleFunc("/config", showConfig)
	http.HandleFunc("/execute", execute)
	// TODO http.HandleFunc("/refresh_token", refreshToken)

	tcpSocket := ":8080"
	log.Infof("listening on %s", tcpSocket)
	http.ListenAndServe(tcpSocket, nil)
}

func status(w http.ResponseWriter, r *http.Request) {
	log.WithFields(log.Fields{
		"host":   r.RemoteAddr,
		"method": r.Method,
		"path":   r.URL.Path,
	}).Info()

	t, err := template.ParseFiles("index.html")
	check(err)

	routes := make(map[string]string)
	routes["/"] = "display this message"
	routes["/example"] = "display an example config"
	routes["/config"] = "display the current config"
	routes["/execute"] = "execute the current config"

	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)

	data := struct {
		AppName string
		Routes  map[string]string
		Config  string
		Foo     time.Duration
		Request *http.Request
	}{
		AppName: appID,
		Routes:  routes,
		Config:  string(output),
		Foo:     config.Duration(),
		Request: r,
	}

	err = t.Execute(w, data)
	check(err)
}

func showExampleConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(ExampleConfig(), "", "    ")
	check(err)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(output)
}

func showConfig(w http.ResponseWriter, r *http.Request) {
	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(output)
}

func execute(w http.ResponseWriter, r *http.Request) {
	raid := config
	summary := raid.Conduct()
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(summary)
}
<|endoftext|>


<commit_before>package main
import (
	"os/user"
	"strconv"
	"os"
	"net"
	"time"
	"fmt"
	"bufio"
	"io/ioutil"
	"errors"
	"strings"
	"os/exec"
	"syscall"
	"path/filepath"
)

type Server struct {
	Name     string `json:"name"`
	Ip       string `json:"ip"`
	Port     int `json:"port"`
	Mod      string `json:"mod"`
	BasePath string `json:"basePath"`
	HomePath string `json:"homePath"`
	Configs  []string `json:"configs"`
	User     string `json:"user"`
	Running  bool `json:"running"`
	Pid      int `json:"pid"`
}

//
// Returns the status response of the server in a parsed format
func (s *Server) GetStatus() (*StatusResponse, error) {
	conn, err := net.DialTimeout("udp", fmt.Sprintf("%s:%d", s.Ip, s.Port), 1000 * time.Millisecond)
	if err != nil {
		return &StatusResponse{}, err
	}
	conn.SetReadDeadline(time.Now().Add(1 * time.Second))
	defer conn.Close()

	if err != nil {
		return &StatusResponse{}, err
	}

	fmt.Fprintf(conn, "\xff\xff\xff\xffgetstatus")
	p := make([]byte, 1024)
	_, err = bufio.NewReader(conn).Read(p)

	if err != nil {
		return &StatusResponse{}, err
	}

	sr := ParseStatusResponse(string(p))
	return sr, nil
}

//
// Prints the server status
func (s *Server) PrintStatus(verbose bool) {
	if !s.Running {
		fmt.Printf("Server \"%s\" is not running.\n", s.Name)
		return
	}

	if (verbose) {
		status, err := s.GetStatus()
		if err != nil {
			fmt.Printf("Could not get server status: %s\n", err)
			return
		}

		fmt.Println("----------------------------------------")
		fmt.Printf("Server:\t\t%s\n", s.Name)
		fmt.Println("----------------------------------------")
		fmt.Printf("Address:\t%s:%d\n", s.Ip, s.Port)
		fmt.Println("----------------------------------------")
		fmt.Printf("Hostname:\t%s\n", StripColors(status.Keys["sv_hostname"]))
		fmt.Printf("Players:\t%d/%s\n", len(status.Players), status.Keys["sv_maxclients"])
		fmt.Printf("Map:\t\t%s\n", status.Keys["mapname"])
		fmt.Println("----------------------------------------")
		if len(status.Players) > 0 {
			fmt.Printf("Players:\t%s\n", status.Players[0])
			for _, player := range status.Players[1:] {
				fmt.Printf("\t\t%s\n", StripColors(player))
			}
			fmt.Println("----------------------------------------")
		}

	} else {
		fmt.Println("----------------------------------------")
		fmt.Printf("Server:\t\t%s\n", s.Name)
		fmt.Println("----------------------------------------")
		fmt.Printf("Address:\t%s:%d\n", s.Ip, s.Port)
		fmt.Printf("Running:\t%t\n", s.Running)
		fmt.Println("----------------------------------------")
	}
}

//
// Checks if the server is running and starts the server process if not
// Informs the user of the current server status
func (s *Server) Start() (error) {
	contains, err := s.basePathContainsNecessaryFiles()
	if !contains {
		return errors.New(fmt.Sprintf("Server \"%s\" could not be started: %s\n", s.Name, err))
	}

	if !s.homePathExistsInFS() {
		return errors.New(fmt.Sprintf("Server \"%s\" has an invalid homepath\n", s.Name))
	}

	if s.Running {
		if !s.CheckServer() {
			fmt.Printf("Server \"%s\" should be running, but it is not. Restarting server.\n", s.Name)
			_, err := s.StartServerProcess()
			return err
		} else {
			fmt.Printf("Server \"%s\" is already running. (Process ID: %d)\n", s.Name, s.Pid)
			fmt.Printf("Run \"screen -drS %s\" to open server console.\n", s.Name + strconv.Itoa(s.Port))
			return nil
		}
	}

	serverProcess, err := s.StartServerProcess()
	if err != nil {
		fmt.Printf("Could not start server process: %s\n", err)
		return err
	}

	err = SaveConfig(configFile, globalConfiguration)
	if err != nil {
		fmt.Printf("Error: could not save config \"%s\": %s\n", err)
	}

	fmt.Printf("Started server \"%s\" with process ID: %d\n", s.Name, serverProcess.Process.Pid + 1)
	fmt.Printf("%v\n", serverProcess)
	return nil
}

//
// Starts the server process if it's not already running
func (s *Server) StartServerProcess() (*exec.Cmd, error) {
	user, err := user.Lookup(s.User)
	if err != nil {
		return &exec.Cmd{}, err
	}

	parameters := []string{
		"-dmS",
		s.Name + strconv.Itoa(s.Port),
		globalConfiguration.ExecutablePath,
		fmt.Sprintf("+set fs_game \"%s\"", s.Mod),
	}

	parameters = append(parameters, fmt.Sprintf("+set com_hunkmegs \"128\""))
	parameters = append(parameters, fmt.Sprintf("+set fs_basepath \"%s\"", s.BasePath))
	parameters = append(parameters, fmt.Sprintf("+set fs_homepath \"%s\"", s.HomePath))
	parameters = append(parameters, fmt.Sprintf("+set net_ip \"%s\"", s.Ip))
	parameters = append(parameters, fmt.Sprintf("+set net_port \"%d\"", s.Port))
	parameters = append(parameters, fmt.Sprintf("+map oasis"))

	for _, config := range s.Configs {
		parameters = append(parameters, fmt.Sprintf("exec \"%s\"", config))
	}

	serverProcess := exec.Command(globalConfiguration.ScreenPath, parameters...)
	serverProcess.Dir = filepath.Dir(globalConfiguration.ExecutablePath)
	// Needed for ET Process, saves .etwolf folder here
	serverProcess.Env = []string{fmt.Sprintf("HOME=%s", user.HomeDir)}

	uid, err := strconv.Atoi(user.Uid)
	if err != nil {
		return serverProcess, err
	}
	gid, err := strconv.Atoi(user.Gid)
	if err != nil {
		return serverProcess, err
	}

	serverProcess.SysProcAttr = &syscall.SysProcAttr{}
	serverProcess.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
	err = serverProcess.Run()
	if err != nil {
		return serverProcess, err
	}
	s.Running = true
	s.Pid = serverProcess.Process.Pid + 1
	return serverProcess, nil

}

//
// Stops the server if it's running
func (s *Server) Stop() (err error) {
	err = nil
	if s.Running {
		fmt.Printf("Stopping server \"%s\"\n", s.Name)

		proc := exec.Command(globalConfiguration.KillPath, strconv.Itoa(s.Pid))
		err := proc.Run()
		fmt.Printf("Stopping server with pid: %d\n", s.Pid)
		if err != nil {
			fmt.Printf("Could not stop server: %s\n", err)
			return err
		}
		s.Running = false
		err = SaveConfig(configFile, globalConfiguration)
		fmt.Printf("Stopped server \"%s\"\n", s.Name)
	} else {
		fmt.Printf("Server \"%s\" is not running.\n", s.Name)
	}
	return err
}

//
// Restarts the server
func (s *Server) Restart() {
	s.Stop()
	fmt.Printf("Waiting 2 seconds")

	fmt.Printf(".")
	time.Sleep(700 * time.Millisecond)
	fmt.Printf(".")
	time.Sleep(700 * time.Millisecond)
	fmt.Printf(".\n")
	time.Sleep(600 * time.Millisecond)

	s.Start()
}

//
// Checks that the server is still running
func (s *Server) CheckServer() (bool) {
	if s.Running {
		pgrep := exec.Command(globalConfiguration.PgrepPath, "-f", s.Name + strconv.Itoa(s.Port))

		_, err := pgrep.Output()
		if err != nil {
			return false
		}
	}
	return true
}

//
// Checks that the calling user is allowed to execute the script
func (s *Server) SecurityCheck() (bool, error) {
	owner, err := user.Lookup(s.User)
	if err != nil {
		return false, err
	}

	serverOwnerUid, err := strconv.Atoi(owner.Uid)
	if err != nil {
		return false, err
	}

	currentUid := os.Geteuid()
	if currentUid == serverOwnerUid || currentUid == 0 {
		return true, nil
	}
	return false, nil
}

//
// Checks if the basepath contains necessary files to host an ET server
// pak0.pk3, pak1.pk3, pak2.pk3 and mp_bin.pk3
func (s *Server) basePathContainsNecessaryFiles() (bool, error) {
	fileInfos, err := ioutil.ReadDir(filepath.Join(s.BasePath, "etmain"))
	if err != nil {
		return false, errors.New(fmt.Sprintf("Basepath check failed: %s", err))
	}

	requiredFiles := map[string]bool{
		"pak0.pk3": false,
		"pak1.pk3": false,
		"pak2.pk3": false,
		"mp_bin.pk3": false,
	}

	// Check that required files can be found at the basepath etmain
	for _, fi := range fileInfos {
		if _, ok := requiredFiles[fi.Name()]; ok {
			requiredFiles[fi.Name()] = true
		}
	}

	missingFiles := []string{}
	for file, exists := range requiredFiles {
		if !exists {
			missingFiles = append(missingFiles, file)
		}
	}

	if len(missingFiles) > 0 {
		return false, errors.New(fmt.Sprintf("Missing the following files: %s", strings.Join(missingFiles, ", ")))
	}
	return true, nil
}

//
// Checks if server's homepath actually exists in file system
func (s *Server) homePathExistsInFS() (bool) {
	fileInfo, err := os.Stat(s.HomePath)

	if err != nil {
		return false
	}

	return fileInfo.IsDir()
}<commit_msg>Minor fix..<commit_after>package main
import (
	"os/user"
	"strconv"
	"os"
	"net"
	"time"
	"fmt"
	"bufio"
	"io/ioutil"
	"errors"
	"strings"
	"os/exec"
	"syscall"
	"path/filepath"
)

type Server struct {
	Name     string `json:"name"`
	Ip       string `json:"ip"`
	Port     int `json:"port"`
	Mod      string `json:"mod"`
	BasePath string `json:"basePath"`
	HomePath string `json:"homePath"`
	Configs  []string `json:"configs"`
	User     string `json:"user"`
	Running  bool `json:"running"`
	Pid      int `json:"pid"`
}

//
// Returns the status response of the server in a parsed format
func (s *Server) GetStatus() (*StatusResponse, error) {
	conn, err := net.DialTimeout("udp", fmt.Sprintf("%s:%d", s.Ip, s.Port), 1000 * time.Millisecond)
	if err != nil {
		return &StatusResponse{}, err
	}
	conn.SetReadDeadline(time.Now().Add(1 * time.Second))
	defer conn.Close()

	if err != nil {
		return &StatusResponse{}, err
	}

	fmt.Fprintf(conn, "\xff\xff\xff\xffgetstatus")
	p := make([]byte, 1024)
	_, err = bufio.NewReader(conn).Read(p)

	if err != nil {
		return &StatusResponse{}, err
	}

	sr := ParseStatusResponse(string(p))
	return sr, nil
}

//
// Prints the server status
func (s *Server) PrintStatus(verbose bool) {
	if !s.Running {
		fmt.Printf("Server \"%s\" is not running.\n", s.Name)
		return
	}

	if (verbose) {
		status, err := s.GetStatus()
		if err != nil {
			fmt.Printf("Could not get server status: %s\n", err)
			return
		}

		fmt.Println("----------------------------------------")
		fmt.Printf("Server:\t\t%s\n", s.Name)
		fmt.Println("----------------------------------------")
		fmt.Printf("Address:\t%s:%d\n", s.Ip, s.Port)
		fmt.Println("----------------------------------------")
		fmt.Printf("Hostname:\t%s\n", StripColors(status.Keys["sv_hostname"]))
		fmt.Printf("Players:\t%d/%s\n", len(status.Players), status.Keys["sv_maxclients"])
		fmt.Printf("Map:\t\t%s\n", status.Keys["mapname"])
		fmt.Println("----------------------------------------")
		if len(status.Players) > 0 {
			fmt.Printf("Players:\t%s\n", status.Players[0])
			for _, player := range status.Players[1:] {
				fmt.Printf("\t\t%s\n", StripColors(player))
			}
			fmt.Println("----------------------------------------")
		}

	} else {
		fmt.Println("----------------------------------------")
		fmt.Printf("Server:\t\t%s\n", s.Name)
		fmt.Println("----------------------------------------")
		fmt.Printf("Address:\t%s:%d\n", s.Ip, s.Port)
		fmt.Printf("Running:\t%t\n", s.Running)
		fmt.Println("----------------------------------------")
	}
}

//
// Checks if the server is running and starts the server process if not
// Informs the user of the current server status
func (s *Server) Start() (error) {
	contains, err := s.basePathContainsNecessaryFiles()
	if !contains {
		return errors.New(fmt.Sprintf("Server \"%s\" could not be started: %s\n", s.Name, err))
	}

	if !s.homePathExistsInFS() {
		return errors.New(fmt.Sprintf("Server \"%s\" has an invalid homepath\n", s.Name))
	}

	if s.Running {
		if !s.CheckServer() {
			fmt.Printf("Server \"%s\" should be running, but it is not. Restarting server.\n", s.Name)
			_, err := s.StartServerProcess()
			return err
		} else {
			fmt.Printf("Server \"%s\" is already running. (Process ID: %d)\n", s.Name, s.Pid)
			fmt.Printf("Run \"screen -drS %s\" to open server console.\n", s.Name + strconv.Itoa(s.Port))
			return nil
		}
	}

	serverProcess, err := s.StartServerProcess()
	if err != nil {
		fmt.Printf("Could not start server process: %s\n", err)
		return err
	}

	err = SaveConfig(configFile, globalConfiguration)
	if err != nil {
		fmt.Printf("Error: could not save config \"%s\": %s\n", err)
	}

	fmt.Printf("Started server \"%s\" with process ID: %d\n", s.Name, serverProcess.Process.Pid + 1)
	fmt.Printf("%v\n", serverProcess)
	return nil
}

//
// Starts the server process if it's not already running
func (s *Server) StartServerProcess() (*exec.Cmd, error) {
	user, err := user.Lookup(s.User)
	if err != nil {
		return &exec.Cmd{}, err
	}

	parameters := []string{
		"-dmS",
		s.Name + strconv.Itoa(s.Port),
		globalConfiguration.ExecutablePath,
		fmt.Sprintf("+set fs_game \"%s\"", s.Mod),
	}

	parameters = append(parameters, fmt.Sprintf("+set com_hunkmegs \"128\""))
	parameters = append(parameters, fmt.Sprintf("+set fs_basepath \"%s\"", s.BasePath))
	parameters = append(parameters, fmt.Sprintf("+set fs_homepath \"%s\"", s.HomePath))
	parameters = append(parameters, fmt.Sprintf("+set net_ip \"%s\"", s.Ip))
	parameters = append(parameters, fmt.Sprintf("+set net_port \"%d\"", s.Port))
	parameters = append(parameters, fmt.Sprintf("+map oasis"))

	for _, config := range s.Configs {
		parameters = append(parameters, fmt.Sprintf("+exec %s", config))
	}

	serverProcess := exec.Command(globalConfiguration.ScreenPath, parameters...)
	serverProcess.Dir = filepath.Dir(globalConfiguration.ExecutablePath)
	// Needed for ET Process, saves .etwolf folder here
	serverProcess.Env = []string{fmt.Sprintf("HOME=%s", user.HomeDir)}

	uid, err := strconv.Atoi(user.Uid)
	if err != nil {
		return serverProcess, err
	}
	gid, err := strconv.Atoi(user.Gid)
	if err != nil {
		return serverProcess, err
	}

	serverProcess.SysProcAttr = &syscall.SysProcAttr{}
	serverProcess.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
	err = serverProcess.Run()
	if err != nil {
		return serverProcess, err
	}
	s.Running = true
	s.Pid = serverProcess.Process.Pid + 1
	return serverProcess, nil

}

//
// Stops the server if it's running
func (s *Server) Stop() (err error) {
	err = nil
	if s.Running {
		fmt.Printf("Stopping server \"%s\"\n", s.Name)

		proc := exec.Command(globalConfiguration.KillPath, strconv.Itoa(s.Pid))
		err := proc.Run()
		fmt.Printf("Stopping server with pid: %d\n", s.Pid)
		if err != nil {
			fmt.Printf("Could not stop server: %s\n", err)
			return err
		}
		s.Running = false
		err = SaveConfig(configFile, globalConfiguration)
		fmt.Printf("Stopped server \"%s\"\n", s.Name)
	} else {
		fmt.Printf("Server \"%s\" is not running.\n", s.Name)
	}
	return err
}

//
// Restarts the server
func (s *Server) Restart() {
	s.Stop()
	fmt.Printf("Waiting 2 seconds")

	fmt.Printf(".")
	time.Sleep(700 * time.Millisecond)
	fmt.Printf(".")
	time.Sleep(700 * time.Millisecond)
	fmt.Printf(".\n")
	time.Sleep(600 * time.Millisecond)

	s.Start()
}

//
// Checks that the server is still running
func (s *Server) CheckServer() (bool) {
	if s.Running {
		pgrep := exec.Command(globalConfiguration.PgrepPath, "-f", s.Name + strconv.Itoa(s.Port))

		_, err := pgrep.Output()
		if err != nil {
			return false
		}
	}
	return true
}

//
// Checks that the calling user is allowed to execute the script
func (s *Server) SecurityCheck() (bool, error) {
	owner, err := user.Lookup(s.User)
	if err != nil {
		return false, err
	}

	serverOwnerUid, err := strconv.Atoi(owner.Uid)
	if err != nil {
		return false, err
	}

	currentUid := os.Geteuid()
	if currentUid == serverOwnerUid || currentUid == 0 {
		return true, nil
	}
	return false, nil
}

//
// Checks if the basepath contains necessary files to host an ET server
// pak0.pk3, pak1.pk3, pak2.pk3 and mp_bin.pk3
func (s *Server) basePathContainsNecessaryFiles() (bool, error) {
	fileInfos, err := ioutil.ReadDir(filepath.Join(s.BasePath, "etmain"))
	if err != nil {
		return false, errors.New(fmt.Sprintf("Basepath check failed: %s", err))
	}

	requiredFiles := map[string]bool{
		"pak0.pk3": false,
		"pak1.pk3": false,
		"pak2.pk3": false,
		"mp_bin.pk3": false,
	}

	// Check that required files can be found at the basepath etmain
	for _, fi := range fileInfos {
		if _, ok := requiredFiles[fi.Name()]; ok {
			requiredFiles[fi.Name()] = true
		}
	}

	missingFiles := []string{}
	for file, exists := range requiredFiles {
		if !exists {
			missingFiles = append(missingFiles, file)
		}
	}

	if len(missingFiles) > 0 {
		return false, errors.New(fmt.Sprintf("Missing the following files: %s", strings.Join(missingFiles, ", ")))
	}
	return true, nil
}

//
// Checks if server's homepath actually exists in file system
func (s *Server) homePathExistsInFS() (bool) {
	fileInfo, err := os.Stat(s.HomePath)

	if err != nil {
		return false
	}

	return fileInfo.IsDir()
}<|endoftext|>


<commit_before>package main

import (
    "./logger"
    "net"
    "bufio"
)

type Message struct {
    command string
    payload string
}

type Client struct {

    conn net.Conn
    reader *bufio.Reader
    writer *bufio.Writer
    inbound chan Message
    outbound chan Message

}

func (client *Client) Read() {

    for {
        line, _ := client.reader.ReadString("---jsonrpcprotocolboundary---")
        //client.inbound <- line
    }
}

func (client *Client) Write() {
    //for data := range client.outgoing {
    //    client.writer.WriteString(data)
    //    client.writer.Flush()
    //}
}

func (client *Client) Listen() {
    go client.Read()
    go client.Write()
}

func NewClient(conn net.Conn) *Client{
    writer := bufio.NewWriter(conn)
    reader := bufio.NewReader(conn)

    client := &Client {
        inbound: make(chan Message),
        outbound: make(chan Message),
        conn: conn,
        reader: reader,
        writer: writer,
    }

    logger.Info.Printf("Client %v is accepting messages\n", conn.RemoteAddr())
    client.Listen()

    return client
}

func connectionMade(conn net.Conn) {
    logger.Info.Printf("Client %v connected\n", conn.RemoteAddr())

    client := NewClient(conn)
}

func main() {
    logger.Init()

    var ln, err = net.Listen("tcp", ":9000")
    logger.Info.Printf("Started listening on %v\n", ln.Addr())

    if err != nil {
        logger.Error.Println(err)
    }

    for {
        logger.Info.Println("Accepting connections...")
        var conn, err = ln.Accept()

        if err != nil {
            logger.Error.Println(err)
        }

        go connectionMade(conn)
    }

}<commit_msg>We're receiving commands now<commit_after>package main

import (
    "./logger"
    "net"
    "bufio"
    "mime/multipart"
)

type Message struct {
    command string
    payload string
}

type Client struct {

    conn net.Conn
    reader *multipart.Reader
    writer *bufio.Writer
    inbound chan string
    outbound chan string

}

func (client *Client) Read() {

    for {
        part, _ := client.reader.NextPart()
        var line []byte
        part.Read(line)
        client.inbound <- string(line)
    }
}

func (client *Client) Write() {
    for data := range client.outbound {
        logger.Info.Println(data)
        client.writer.WriteString(data)
        client.writer.Flush()
    }
}

func (client *Client) Listen() {
    go client.Read()
    go client.Write()
}

func NewClient(conn net.Conn) *Client{
    writer := bufio.NewWriter(conn)
    reader := multipart.NewReader(conn, "---jsonrpcprotocolboundary---")

    client := &Client {
        inbound: make(chan string),
        outbound: make(chan string),
        conn: conn,
        reader: reader,
        writer: writer,
    }

    logger.Info.Printf("Client %v is accepting messages\n", conn.RemoteAddr())
    client.Listen()

    client.outbound <- "{\"command\": \"authenticate\", \"payload\": \"dasd2342342asfdf234\"} ---jsonrpcprotocolboundary---"

    return client
}

func connectionMade(conn net.Conn) {
    logger.Info.Printf("Client %v connected\n", conn.RemoteAddr())
    NewClient(conn)
}

func main() {
    logger.Init()

    var ln, err = net.Listen("tcp", ":9000")
    logger.Info.Printf("Started listening on %v\n", ln.Addr())

    if err != nil {
        logger.Error.Println(err)
    }

    for {
        logger.Info.Println("Accepting connections...")
        var conn, err = ln.Accept()

        if err != nil {
            logger.Error.Println(err)
        }

        go connectionMade(conn)
    }

}<|endoftext|>


<commit_before>// Copyright 2018 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"container/list"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/Jigsaw-Code/outline-ss-server/metrics"
	"github.com/Jigsaw-Code/outline-ss-server/shadowsocks"
	"github.com/op/go-logging"
	"github.com/oschwald/geoip2-golang"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/shadowsocks/go-shadowsocks2/core"
	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"golang.org/x/crypto/ssh/terminal"
	"gopkg.in/yaml.v2"
)

var logger *logging.Logger

// Set by goreleaser default ldflags. See https://goreleaser.com/customization/build/
var version = "dev"

// 59 seconds is most common timeout for servers that do not respond to invalid requests
const tcpReadTimeout time.Duration = 59 * time.Second

// A UDP NAT timeout of at least 5 minutes is recommended in RFC 4787 Section 4.3.
const defaultNatTimeout time.Duration = 5 * time.Minute

func init() {
	var prefix = "%{level:.1s}%{time:2006-01-02T15:04:05.000Z07:00} %{pid} %{shortfile}]"
	if terminal.IsTerminal(int(os.Stderr.Fd())) {
		// Add color only if the output is the terminal
		prefix = strings.Join([]string{"%{color}", prefix, "%{color:reset}"}, "")
	}
	logging.SetFormatter(logging.MustStringFormatter(strings.Join([]string{prefix, " %{message}"}, "")))
	logging.SetBackend(logging.NewLogBackend(os.Stderr, "", 0))
	logger = logging.MustGetLogger("")
}

type ssPort struct {
	tcpService shadowsocks.TCPService
	udpService shadowsocks.UDPService
	cipherList shadowsocks.CipherList
}

type ssServer struct {
	natTimeout  time.Duration
	m           metrics.ShadowsocksMetrics
	replayCache shadowsocks.ReplayCache
	ports       map[int]*ssPort
}

func (s *ssServer) startPort(portNum int) error {
	listener, err := net.ListenTCP("tcp", &net.TCPAddr{Port: portNum})
	if err != nil {
		return fmt.Errorf("Failed to start TCP on port %v: %v", portNum, err)
	}
	packetConn, err := net.ListenUDP("udp", &net.UDPAddr{Port: portNum})
	if err != nil {
		return fmt.Errorf("Failed to start UDP on port %v: %v", portNum, err)
	}
	logger.Infof("Listening TCP and UDP on port %v", portNum)
	port := &ssPort{cipherList: shadowsocks.NewCipherList()}
	// TODO: Register initial data metrics at zero.
	port.tcpService = shadowsocks.NewTCPService(port.cipherList, &s.replayCache, s.m, tcpReadTimeout)
	port.udpService = shadowsocks.NewUDPService(s.natTimeout, port.cipherList, s.m)
	s.ports[portNum] = port
	go port.tcpService.Serve(listener)
	go port.udpService.Serve(packetConn)
	return nil
}

func (s *ssServer) removePort(portNum int) error {
	port, ok := s.ports[portNum]
	if !ok {
		return fmt.Errorf("Port %v doesn't exist", portNum)
	}
	tcpErr := port.tcpService.Stop()
	udpErr := port.udpService.Stop()
	delete(s.ports, portNum)
	if tcpErr != nil {
		return fmt.Errorf("Failed to close listener on %v: %v", portNum, tcpErr)
	}
	if udpErr != nil {
		return fmt.Errorf("Failed to close packetConn on %v: %v", portNum, udpErr)
	}
	logger.Infof("Stopped TCP and UDP on port %v", portNum)
	return nil
}

func (s *ssServer) loadConfig(filename string) error {
	config, err := readConfig(filename)
	if err != nil {
		return fmt.Errorf("Failed to read config file %v: %v", filename, err)
	}

	portChanges := make(map[int]int)
	portCiphers := make(map[int]*list.List) // Values are *List of *CipherEntry.
	for _, keyConfig := range config.Keys {
		portChanges[keyConfig.Port] = 1
		cipherList, ok := portCiphers[keyConfig.Port]
		if !ok {
			cipherList = list.New()
			portCiphers[keyConfig.Port] = cipherList
		}
		cipher, err := core.PickCipher(keyConfig.Cipher, nil, keyConfig.Secret)
		if err != nil {
			if err == core.ErrCipherNotSupported {
				return fmt.Errorf("Cipher %v for key %v is not supported", keyConfig.Cipher, keyConfig.ID)
			}
			return fmt.Errorf("Failed to create cipher for key %v: %v", keyConfig.ID, err)
		}
		aead, ok := cipher.(shadowaead.Cipher)
		if !ok {
			return fmt.Errorf("Only AEAD ciphers are supported. Found %v", keyConfig.Cipher)
		}
		cipherList.PushBack(shadowsocks.MakeCipherEntry(keyConfig.ID, aead, keyConfig.Secret))
	}
	for port := range s.ports {
		portChanges[port] = portChanges[port] - 1
	}
	for portNum, count := range portChanges {
		if count == -1 {
			if err := s.removePort(portNum); err != nil {
				return fmt.Errorf("Failed to remove port %v: %v", portNum, err)
			}
		} else if count == +1 {
			if err := s.startPort(portNum); err != nil {
				return fmt.Errorf("Failed to start port %v: %v", portNum, err)
			}
		}
	}
	for portNum, cipherList := range portCiphers {
		if err := s.ports[portNum].cipherList.Update(cipherList); err != nil {
			return err
		}
	}
	logger.Infof("Loaded %v access keys", len(config.Keys))
	s.m.SetNumAccessKeys(len(config.Keys), len(portCiphers))
	return nil
}

func runSSServer(filename string, natTimeout time.Duration, sm metrics.ShadowsocksMetrics, replayHistory int) error {
	server := &ssServer{
		natTimeout:  natTimeout,
		m:           sm,
		replayCache: shadowsocks.NewReplayCache(replayHistory),
		ports:       make(map[int]*ssPort),
	}
	err := server.loadConfig(filename)
	if err != nil {
		return fmt.Errorf("Failed to load config file %v: %v", filename, err)
	}
	sigHup := make(chan os.Signal, 1)
	signal.Notify(sigHup, syscall.SIGHUP)
	go func() {
		for range sigHup {
			logger.Info("Updating config")
			if err := server.loadConfig(filename); err != nil {
				logger.Errorf("Could not reload config: %v", err)
			}
		}
	}()
	return nil
}

type Config struct {
	Keys []struct {
		ID     string
		Port   int
		Cipher string
		Secret string
	}
}

func readConfig(filename string) (*Config, error) {
	config := Config{}
	configData, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(configData, &config)
	return &config, err
}

func main() {
	var flags struct {
		ConfigFile    string
		MetricsAddr   string
		IPCountryDB   string
		natTimeout    time.Duration
		replayHistory int
		Verbose       bool
		Version       bool
	}
	flag.StringVar(&flags.ConfigFile, "config", "", "Configuration filename")
	flag.StringVar(&flags.MetricsAddr, "metrics", "", "Address for the Prometheus metrics")
	flag.StringVar(&flags.IPCountryDB, "ip_country_db", "", "Path to the ip-to-country mmdb file")
	flag.DurationVar(&flags.natTimeout, "udptimeout", defaultNatTimeout, "UDP tunnel timeout")
	flag.IntVar(&flags.replayHistory, "replay_history", 0, "Replay buffer size (# of handshakes)")
	flag.BoolVar(&flags.Verbose, "verbose", false, "Enables verbose logging output")
	flag.BoolVar(&flags.Version, "version", false, "The version of the server")

	flag.Parse()

	if flags.Verbose {
		logging.SetLevel(logging.DEBUG, "")
	} else {
		logging.SetLevel(logging.INFO, "")
	}

	if flags.Version {
		fmt.Println(version)
		return
	}

	if flags.ConfigFile == "" {
		flag.Usage()
		return
	}

	if flags.MetricsAddr != "" {
		http.Handle("/metrics", promhttp.Handler())
		go func() {
			logger.Fatal(http.ListenAndServe(flags.MetricsAddr, nil))
		}()
		logger.Infof("Metrics on http://%v/metrics", flags.MetricsAddr)
	}

	var ipCountryDB *geoip2.Reader
	var err error
	if flags.IPCountryDB != "" {
		logger.Infof("Using IP-Country database at %v", flags.IPCountryDB)
		ipCountryDB, err = geoip2.Open(flags.IPCountryDB)
		if err != nil {
			log.Fatalf("Could not open geoip database at %v: %v", flags.IPCountryDB, err)
		}
		defer ipCountryDB.Close()
	}
	m := metrics.NewPrometheusShadowsocksMetrics(ipCountryDB, prometheus.DefaultRegisterer)
	m.SetBuildInfo(version)
	err = runSSServer(flags.ConfigFile, flags.natTimeout, m, flags.replayHistory)
	if err != nil {
		logger.Fatal(err)
	}

	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
	<-sigCh
}
<commit_msg>Correct pointer type in cipher list<commit_after>// Copyright 2018 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"container/list"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/Jigsaw-Code/outline-ss-server/metrics"
	"github.com/Jigsaw-Code/outline-ss-server/shadowsocks"
	"github.com/op/go-logging"
	"github.com/oschwald/geoip2-golang"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/shadowsocks/go-shadowsocks2/core"
	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"golang.org/x/crypto/ssh/terminal"
	"gopkg.in/yaml.v2"
)

var logger *logging.Logger

// Set by goreleaser default ldflags. See https://goreleaser.com/customization/build/
var version = "dev"

// 59 seconds is most common timeout for servers that do not respond to invalid requests
const tcpReadTimeout time.Duration = 59 * time.Second

// A UDP NAT timeout of at least 5 minutes is recommended in RFC 4787 Section 4.3.
const defaultNatTimeout time.Duration = 5 * time.Minute

func init() {
	var prefix = "%{level:.1s}%{time:2006-01-02T15:04:05.000Z07:00} %{pid} %{shortfile}]"
	if terminal.IsTerminal(int(os.Stderr.Fd())) {
		// Add color only if the output is the terminal
		prefix = strings.Join([]string{"%{color}", prefix, "%{color:reset}"}, "")
	}
	logging.SetFormatter(logging.MustStringFormatter(strings.Join([]string{prefix, " %{message}"}, "")))
	logging.SetBackend(logging.NewLogBackend(os.Stderr, "", 0))
	logger = logging.MustGetLogger("")
}

type ssPort struct {
	tcpService shadowsocks.TCPService
	udpService shadowsocks.UDPService
	cipherList shadowsocks.CipherList
}

type ssServer struct {
	natTimeout  time.Duration
	m           metrics.ShadowsocksMetrics
	replayCache shadowsocks.ReplayCache
	ports       map[int]*ssPort
}

func (s *ssServer) startPort(portNum int) error {
	listener, err := net.ListenTCP("tcp", &net.TCPAddr{Port: portNum})
	if err != nil {
		return fmt.Errorf("Failed to start TCP on port %v: %v", portNum, err)
	}
	packetConn, err := net.ListenUDP("udp", &net.UDPAddr{Port: portNum})
	if err != nil {
		return fmt.Errorf("Failed to start UDP on port %v: %v", portNum, err)
	}
	logger.Infof("Listening TCP and UDP on port %v", portNum)
	port := &ssPort{cipherList: shadowsocks.NewCipherList()}
	// TODO: Register initial data metrics at zero.
	port.tcpService = shadowsocks.NewTCPService(port.cipherList, &s.replayCache, s.m, tcpReadTimeout)
	port.udpService = shadowsocks.NewUDPService(s.natTimeout, port.cipherList, s.m)
	s.ports[portNum] = port
	go port.tcpService.Serve(listener)
	go port.udpService.Serve(packetConn)
	return nil
}

func (s *ssServer) removePort(portNum int) error {
	port, ok := s.ports[portNum]
	if !ok {
		return fmt.Errorf("Port %v doesn't exist", portNum)
	}
	tcpErr := port.tcpService.Stop()
	udpErr := port.udpService.Stop()
	delete(s.ports, portNum)
	if tcpErr != nil {
		return fmt.Errorf("Failed to close listener on %v: %v", portNum, tcpErr)
	}
	if udpErr != nil {
		return fmt.Errorf("Failed to close packetConn on %v: %v", portNum, udpErr)
	}
	logger.Infof("Stopped TCP and UDP on port %v", portNum)
	return nil
}

func (s *ssServer) loadConfig(filename string) error {
	config, err := readConfig(filename)
	if err != nil {
		return fmt.Errorf("Failed to read config file %v: %v", filename, err)
	}

	portChanges := make(map[int]int)
	portCiphers := make(map[int]*list.List) // Values are *List of *CipherEntry.
	for _, keyConfig := range config.Keys {
		portChanges[keyConfig.Port] = 1
		cipherList, ok := portCiphers[keyConfig.Port]
		if !ok {
			cipherList = list.New()
			portCiphers[keyConfig.Port] = cipherList
		}
		cipher, err := core.PickCipher(keyConfig.Cipher, nil, keyConfig.Secret)
		if err != nil {
			if err == core.ErrCipherNotSupported {
				return fmt.Errorf("Cipher %v for key %v is not supported", keyConfig.Cipher, keyConfig.ID)
			}
			return fmt.Errorf("Failed to create cipher for key %v: %v", keyConfig.ID, err)
		}
		aead, ok := cipher.(shadowaead.Cipher)
		if !ok {
			return fmt.Errorf("Only AEAD ciphers are supported. Found %v", keyConfig.Cipher)
		}
		entry := shadowsocks.MakeCipherEntry(keyConfig.ID, aead, keyConfig.Secret)
		cipherList.PushBack(&entry)
	}
	for port := range s.ports {
		portChanges[port] = portChanges[port] - 1
	}
	for portNum, count := range portChanges {
		if count == -1 {
			if err := s.removePort(portNum); err != nil {
				return fmt.Errorf("Failed to remove port %v: %v", portNum, err)
			}
		} else if count == +1 {
			if err := s.startPort(portNum); err != nil {
				return fmt.Errorf("Failed to start port %v: %v", portNum, err)
			}
		}
	}
	for portNum, cipherList := range portCiphers {
		if err := s.ports[portNum].cipherList.Update(cipherList); err != nil {
			return err
		}
	}
	logger.Infof("Loaded %v access keys", len(config.Keys))
	s.m.SetNumAccessKeys(len(config.Keys), len(portCiphers))
	return nil
}

func runSSServer(filename string, natTimeout time.Duration, sm metrics.ShadowsocksMetrics, replayHistory int) error {
	server := &ssServer{
		natTimeout:  natTimeout,
		m:           sm,
		replayCache: shadowsocks.NewReplayCache(replayHistory),
		ports:       make(map[int]*ssPort),
	}
	err := server.loadConfig(filename)
	if err != nil {
		return fmt.Errorf("Failed to load config file %v: %v", filename, err)
	}
	sigHup := make(chan os.Signal, 1)
	signal.Notify(sigHup, syscall.SIGHUP)
	go func() {
		for range sigHup {
			logger.Info("Updating config")
			if err := server.loadConfig(filename); err != nil {
				logger.Errorf("Could not reload config: %v", err)
			}
		}
	}()
	return nil
}

type Config struct {
	Keys []struct {
		ID     string
		Port   int
		Cipher string
		Secret string
	}
}

func readConfig(filename string) (*Config, error) {
	config := Config{}
	configData, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(configData, &config)
	return &config, err
}

func main() {
	var flags struct {
		ConfigFile    string
		MetricsAddr   string
		IPCountryDB   string
		natTimeout    time.Duration
		replayHistory int
		Verbose       bool
		Version       bool
	}
	flag.StringVar(&flags.ConfigFile, "config", "", "Configuration filename")
	flag.StringVar(&flags.MetricsAddr, "metrics", "", "Address for the Prometheus metrics")
	flag.StringVar(&flags.IPCountryDB, "ip_country_db", "", "Path to the ip-to-country mmdb file")
	flag.DurationVar(&flags.natTimeout, "udptimeout", defaultNatTimeout, "UDP tunnel timeout")
	flag.IntVar(&flags.replayHistory, "replay_history", 0, "Replay buffer size (# of handshakes)")
	flag.BoolVar(&flags.Verbose, "verbose", false, "Enables verbose logging output")
	flag.BoolVar(&flags.Version, "version", false, "The version of the server")

	flag.Parse()

	if flags.Verbose {
		logging.SetLevel(logging.DEBUG, "")
	} else {
		logging.SetLevel(logging.INFO, "")
	}

	if flags.Version {
		fmt.Println(version)
		return
	}

	if flags.ConfigFile == "" {
		flag.Usage()
		return
	}

	if flags.MetricsAddr != "" {
		http.Handle("/metrics", promhttp.Handler())
		go func() {
			logger.Fatal(http.ListenAndServe(flags.MetricsAddr, nil))
		}()
		logger.Infof("Metrics on http://%v/metrics", flags.MetricsAddr)
	}

	var ipCountryDB *geoip2.Reader
	var err error
	if flags.IPCountryDB != "" {
		logger.Infof("Using IP-Country database at %v", flags.IPCountryDB)
		ipCountryDB, err = geoip2.Open(flags.IPCountryDB)
		if err != nil {
			log.Fatalf("Could not open geoip database at %v: %v", flags.IPCountryDB, err)
		}
		defer ipCountryDB.Close()
	}
	m := metrics.NewPrometheusShadowsocksMetrics(ipCountryDB, prometheus.DefaultRegisterer)
	m.SetBuildInfo(version)
	err = runSSServer(flags.ConfigFile, flags.natTimeout, m, flags.replayHistory)
	if err != nil {
		logger.Fatal(err)
	}

	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
	<-sigCh
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build cgo,!arm,!arm64,!ios

package x509

/*
#cgo CFLAGS: -mmacosx-version-min=10.10 -D__MAC_OS_X_VERSION_MAX_ALLOWED=101300
#cgo LDFLAGS: -framework CoreFoundation -framework Security

#include <errno.h>
#include <sys/sysctl.h>

#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>

static Boolean isSSLPolicy(SecPolicyRef policyRef) {
	if (!policyRef) {
		return false;
	}
	CFDictionaryRef properties = SecPolicyCopyProperties(policyRef);
	if (properties == NULL) {
		return false;
	}
	Boolean isSSL = false;
	CFTypeRef value = NULL;
	if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) {
		isSSL = CFEqual(value, kSecPolicyAppleSSL);
	}
	CFRelease(properties);
	return isSSL;
}

// sslTrustSettingsResult obtains the final kSecTrustSettingsResult value
// for a certificate in the user or admin domain, combining usage constraints
// for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and
// kSecTrustSettingsAllowedError.
// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
static SInt32 sslTrustSettingsResult(SecCertificateRef cert) {
	CFArrayRef trustSettings = NULL;
	OSStatus err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings);

	// According to Apple's SecTrustServer.c, "user trust settings overrule admin trust settings",
	// but the rules of the override are unclear. Let's assume admin trust settings are applicable
	// if and only if user trust settings fail to load or are NULL.
	if (err != errSecSuccess || trustSettings == NULL) {
		if (trustSettings != NULL) CFRelease(trustSettings);
		err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &trustSettings);
	}

	// > no trust settings [...] means "this certificate must be verified to a known trusted certificate”
	// (Should this cause a fallback from user to admin domain? It's unclear.)
	if (err != errSecSuccess || trustSettings == NULL) {
		if (trustSettings != NULL) CFRelease(trustSettings);
		return kSecTrustSettingsResultUnspecified;
	}

	// > An empty trust settings array means "always trust this certificate” with an
	// > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot.
	if (CFArrayGetCount(trustSettings) == 0) {
		CFRelease(trustSettings);
		return kSecTrustSettingsResultTrustRoot;
	}

	// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
	// but the Go linker's internal linking mode can't handle CFSTR relocations.
	// Create our own dynamic string instead and release it below.
	CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString(
		NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
	CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString(
		NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8);
	CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString(
		NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8);

	CFIndex m; SInt32 result = 0;
	for (m = 0; m < CFArrayGetCount(trustSettings); m++) {
		CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m);

		// First, check if this trust setting is constrained to a non-SSL policy.
		SecPolicyRef policyRef;
		if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) {
			if (!isSSLPolicy(policyRef)) {
				continue;
			}
		}

		if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) {
			// Restricted to a hostname, not a root.
			continue;
		}

		CFNumberRef cfNum;
		if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) {
			CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
		} else {
			// > If this key is not present, a default value of
			// > kSecTrustSettingsResultTrustRoot is assumed.
			result = kSecTrustSettingsResultTrustRoot;
		}

		// If multiple dictionaries match, we are supposed to "OR" them,
		// the semantics of which are not clear. Since TrustRoot and TrustAsRoot
		// are mutually exclusive, Deny should probably override, and Invalid and
		// Unspecified be overridden, approximate this by stopping at the first
		// TrustRoot, TrustAsRoot or Deny.
		if (result == kSecTrustSettingsResultTrustRoot) {
			break;
		} else if (result == kSecTrustSettingsResultTrustAsRoot) {
			break;
		} else if (result == kSecTrustSettingsResultDeny) {
			break;
		}
	}

	// If trust settings are present, but none of them match the policy...
	// the docs don't tell us what to do.
	//
	// "Trust settings for a given use apply if any of the dictionaries in the
	// certificate’s trust settings array satisfies the specified use." suggests
	// that it's as if there were no trust settings at all, so we should probably
	// fallback to the admin trust settings. TODO.
	if (result == 0) {
		result = kSecTrustSettingsResultUnspecified;
	}

	CFRelease(_kSecTrustSettingsPolicy);
	CFRelease(_kSecTrustSettingsPolicyString);
	CFRelease(_kSecTrustSettingsResult);
	CFRelease(trustSettings);

	return result;
}

// isRootCertificate reports whether Subject and Issuer match.
static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) {
	CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef);
	if (*errRef != NULL) {
		return false;
	}
	CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef);
	if (*errRef != NULL) {
		CFRelease(subjectName);
		return false;
	}
	Boolean equal = CFEqual(subjectName, issuerName);
	CFRelease(subjectName);
	CFRelease(issuerName);
	return equal;
}

// CopyPEMRoots fetches the system's list of trusted X.509 root certificates
// for the kSecTrustSettingsPolicy SSL.
//
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
// certificates of the system. On failure, the function returns -1.
// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
//
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
// be released (using CFRelease) after we've consumed its content.
int CopyPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) {
	int i;

	if (debugDarwinRoots) {
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified);
	}

	// Get certificates from all domains, not just System, this lets
	// the user add CAs to their "login" keychain, and Admins to add
	// to the "System" keychain
	SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
		kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainUser };

	int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
	if (pemRoots == NULL || untrustedPemRoots == NULL) {
		return -1;
	}

	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
	CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
	for (i = 0; i < numDomains; i++) {
		int j;
		CFArrayRef certs = NULL;
		OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
		if (err != noErr) {
			continue;
		}

		CFIndex numCerts = CFArrayGetCount(certs);
		for (j = 0; j < numCerts; j++) {
			SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
			if (cert == NULL) {
				continue;
			}

			SInt32 result;
			if (domains[i] == kSecTrustSettingsDomainSystem) {
				// Certs found in the system domain are always trusted. If the user
				// configures "Never Trust" on such a cert, it will also be found in the
				// admin or user domain, causing it to be added to untrustedPemRoots. The
				// Go code will then clean this up.
				result = kSecTrustSettingsResultTrustRoot;
			} else {
				result = sslTrustSettingsResult(cert);
				if (debugDarwinRoots) {
					CFErrorRef errRef = NULL;
					CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef);
					if (errRef != NULL) {
						fprintf(stderr, "crypto/x509: SecCertificateCopyShortDescription failed\n");
						CFRelease(errRef);
						continue;
					}

					CFIndex length = CFStringGetLength(summary);
					CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
					char *buffer = malloc(maxSize);
					if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) {
						fprintf(stderr, "crypto/x509: %s returned %d\n", buffer, (int)result);
					}
					free(buffer);
					CFRelease(summary);
				}
			}

			CFMutableDataRef appendTo;
			// > Note the distinction between the results kSecTrustSettingsResultTrustRoot
			// > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to
			// > root (self-signed) certificates; the latter can only be applied to
			// > non-root certificates.
			if (result == kSecTrustSettingsResultTrustRoot) {
				CFErrorRef errRef = NULL;
				if (!isRootCertificate(cert, &errRef) || errRef != NULL) {
					if (errRef != NULL) CFRelease(errRef);
					continue;
				}

				appendTo = combinedData;
			} else if (result == kSecTrustSettingsResultTrustAsRoot) {
				CFErrorRef errRef = NULL;
				if (isRootCertificate(cert, &errRef) || errRef != NULL) {
					if (errRef != NULL) CFRelease(errRef);
					continue;
				}

				appendTo = combinedData;
			} else if (result == kSecTrustSettingsResultDeny) {
				appendTo = combinedUntrustedData;
			} else if (result == kSecTrustSettingsResultUnspecified) {
				// Certificates with unspecified trust should probably be added to a pool of
				// intermediates for chain building, or checked for transitive trust and
				// added to the root pool (which is an imprecise approximation because it
				// cuts chains short) but we don't support either at the moment. TODO.
				continue;
			} else {
				continue;
			}

			CFDataRef data = NULL;
			err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
			if (err != noErr) {
				continue;
			}
			if (data != NULL) {
				CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
				CFRelease(data);
			}
		}
		CFRelease(certs);
	}
	*pemRoots = combinedData;
	*untrustedPemRoots = combinedUntrustedData;
	return 0;
}
*/
import "C"
import (
	"errors"
	"unsafe"
)

func loadSystemRoots() (*CertPool, error) {
	var data, untrustedData C.CFDataRef
	err := C.CopyPEMRoots(&data, &untrustedData, C.bool(debugDarwinRoots))
	if err == -1 {
		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
	}
	defer C.CFRelease(C.CFTypeRef(data))
	defer C.CFRelease(C.CFTypeRef(untrustedData))

	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
	roots := NewCertPool()
	roots.AppendCertsFromPEM(buf)

	if C.CFDataGetLength(untrustedData) == 0 {
		return roots, nil
	}

	buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
	untrustedRoots := NewCertPool()
	untrustedRoots.AppendCertsFromPEM(buf)

	trustedRoots := NewCertPool()
	for _, c := range roots.certs {
		if !untrustedRoots.contains(c) {
			trustedRoots.AddCert(c)
		}
	}
	return trustedRoots, nil
}
<commit_msg>crypto/x509: this change modifies C.CopyPEMRoots to static function<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build cgo,!arm,!arm64,!ios

package x509

/*
#cgo CFLAGS: -mmacosx-version-min=10.10 -D__MAC_OS_X_VERSION_MAX_ALLOWED=101300
#cgo LDFLAGS: -framework CoreFoundation -framework Security

#include <errno.h>
#include <sys/sysctl.h>

#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>

static Boolean isSSLPolicy(SecPolicyRef policyRef) {
	if (!policyRef) {
		return false;
	}
	CFDictionaryRef properties = SecPolicyCopyProperties(policyRef);
	if (properties == NULL) {
		return false;
	}
	Boolean isSSL = false;
	CFTypeRef value = NULL;
	if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) {
		isSSL = CFEqual(value, kSecPolicyAppleSSL);
	}
	CFRelease(properties);
	return isSSL;
}

// sslTrustSettingsResult obtains the final kSecTrustSettingsResult value
// for a certificate in the user or admin domain, combining usage constraints
// for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and
// kSecTrustSettingsAllowedError.
// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
static SInt32 sslTrustSettingsResult(SecCertificateRef cert) {
	CFArrayRef trustSettings = NULL;
	OSStatus err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings);

	// According to Apple's SecTrustServer.c, "user trust settings overrule admin trust settings",
	// but the rules of the override are unclear. Let's assume admin trust settings are applicable
	// if and only if user trust settings fail to load or are NULL.
	if (err != errSecSuccess || trustSettings == NULL) {
		if (trustSettings != NULL) CFRelease(trustSettings);
		err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &trustSettings);
	}

	// > no trust settings [...] means "this certificate must be verified to a known trusted certificate”
	// (Should this cause a fallback from user to admin domain? It's unclear.)
	if (err != errSecSuccess || trustSettings == NULL) {
		if (trustSettings != NULL) CFRelease(trustSettings);
		return kSecTrustSettingsResultUnspecified;
	}

	// > An empty trust settings array means "always trust this certificate” with an
	// > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot.
	if (CFArrayGetCount(trustSettings) == 0) {
		CFRelease(trustSettings);
		return kSecTrustSettingsResultTrustRoot;
	}

	// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
	// but the Go linker's internal linking mode can't handle CFSTR relocations.
	// Create our own dynamic string instead and release it below.
	CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString(
		NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
	CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString(
		NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8);
	CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString(
		NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8);

	CFIndex m; SInt32 result = 0;
	for (m = 0; m < CFArrayGetCount(trustSettings); m++) {
		CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m);

		// First, check if this trust setting is constrained to a non-SSL policy.
		SecPolicyRef policyRef;
		if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) {
			if (!isSSLPolicy(policyRef)) {
				continue;
			}
		}

		if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) {
			// Restricted to a hostname, not a root.
			continue;
		}

		CFNumberRef cfNum;
		if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) {
			CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
		} else {
			// > If this key is not present, a default value of
			// > kSecTrustSettingsResultTrustRoot is assumed.
			result = kSecTrustSettingsResultTrustRoot;
		}

		// If multiple dictionaries match, we are supposed to "OR" them,
		// the semantics of which are not clear. Since TrustRoot and TrustAsRoot
		// are mutually exclusive, Deny should probably override, and Invalid and
		// Unspecified be overridden, approximate this by stopping at the first
		// TrustRoot, TrustAsRoot or Deny.
		if (result == kSecTrustSettingsResultTrustRoot) {
			break;
		} else if (result == kSecTrustSettingsResultTrustAsRoot) {
			break;
		} else if (result == kSecTrustSettingsResultDeny) {
			break;
		}
	}

	// If trust settings are present, but none of them match the policy...
	// the docs don't tell us what to do.
	//
	// "Trust settings for a given use apply if any of the dictionaries in the
	// certificate’s trust settings array satisfies the specified use." suggests
	// that it's as if there were no trust settings at all, so we should probably
	// fallback to the admin trust settings. TODO.
	if (result == 0) {
		result = kSecTrustSettingsResultUnspecified;
	}

	CFRelease(_kSecTrustSettingsPolicy);
	CFRelease(_kSecTrustSettingsPolicyString);
	CFRelease(_kSecTrustSettingsResult);
	CFRelease(trustSettings);

	return result;
}

// isRootCertificate reports whether Subject and Issuer match.
static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) {
	CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef);
	if (*errRef != NULL) {
		return false;
	}
	CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef);
	if (*errRef != NULL) {
		CFRelease(subjectName);
		return false;
	}
	Boolean equal = CFEqual(subjectName, issuerName);
	CFRelease(subjectName);
	CFRelease(issuerName);
	return equal;
}

// CopyPEMRoots fetches the system's list of trusted X.509 root certificates
// for the kSecTrustSettingsPolicy SSL.
//
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
// certificates of the system. On failure, the function returns -1.
// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
//
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
// be released (using CFRelease) after we've consumed its content.
static int CopyPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) {
	int i;

	if (debugDarwinRoots) {
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny);
		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified);
	}

	// Get certificates from all domains, not just System, this lets
	// the user add CAs to their "login" keychain, and Admins to add
	// to the "System" keychain
	SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
		kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainUser };

	int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
	if (pemRoots == NULL || untrustedPemRoots == NULL) {
		return -1;
	}

	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
	CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
	for (i = 0; i < numDomains; i++) {
		int j;
		CFArrayRef certs = NULL;
		OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
		if (err != noErr) {
			continue;
		}

		CFIndex numCerts = CFArrayGetCount(certs);
		for (j = 0; j < numCerts; j++) {
			SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
			if (cert == NULL) {
				continue;
			}

			SInt32 result;
			if (domains[i] == kSecTrustSettingsDomainSystem) {
				// Certs found in the system domain are always trusted. If the user
				// configures "Never Trust" on such a cert, it will also be found in the
				// admin or user domain, causing it to be added to untrustedPemRoots. The
				// Go code will then clean this up.
				result = kSecTrustSettingsResultTrustRoot;
			} else {
				result = sslTrustSettingsResult(cert);
				if (debugDarwinRoots) {
					CFErrorRef errRef = NULL;
					CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef);
					if (errRef != NULL) {
						fprintf(stderr, "crypto/x509: SecCertificateCopyShortDescription failed\n");
						CFRelease(errRef);
						continue;
					}

					CFIndex length = CFStringGetLength(summary);
					CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
					char *buffer = malloc(maxSize);
					if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) {
						fprintf(stderr, "crypto/x509: %s returned %d\n", buffer, (int)result);
					}
					free(buffer);
					CFRelease(summary);
				}
			}

			CFMutableDataRef appendTo;
			// > Note the distinction between the results kSecTrustSettingsResultTrustRoot
			// > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to
			// > root (self-signed) certificates; the latter can only be applied to
			// > non-root certificates.
			if (result == kSecTrustSettingsResultTrustRoot) {
				CFErrorRef errRef = NULL;
				if (!isRootCertificate(cert, &errRef) || errRef != NULL) {
					if (errRef != NULL) CFRelease(errRef);
					continue;
				}

				appendTo = combinedData;
			} else if (result == kSecTrustSettingsResultTrustAsRoot) {
				CFErrorRef errRef = NULL;
				if (isRootCertificate(cert, &errRef) || errRef != NULL) {
					if (errRef != NULL) CFRelease(errRef);
					continue;
				}

				appendTo = combinedData;
			} else if (result == kSecTrustSettingsResultDeny) {
				appendTo = combinedUntrustedData;
			} else if (result == kSecTrustSettingsResultUnspecified) {
				// Certificates with unspecified trust should probably be added to a pool of
				// intermediates for chain building, or checked for transitive trust and
				// added to the root pool (which is an imprecise approximation because it
				// cuts chains short) but we don't support either at the moment. TODO.
				continue;
			} else {
				continue;
			}

			CFDataRef data = NULL;
			err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
			if (err != noErr) {
				continue;
			}
			if (data != NULL) {
				CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
				CFRelease(data);
			}
		}
		CFRelease(certs);
	}
	*pemRoots = combinedData;
	*untrustedPemRoots = combinedUntrustedData;
	return 0;
}
*/
import "C"
import (
	"errors"
	"unsafe"
)

func loadSystemRoots() (*CertPool, error) {
	var data, untrustedData C.CFDataRef
	err := C.CopyPEMRoots(&data, &untrustedData, C.bool(debugDarwinRoots))
	if err == -1 {
		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
	}
	defer C.CFRelease(C.CFTypeRef(data))
	defer C.CFRelease(C.CFTypeRef(untrustedData))

	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
	roots := NewCertPool()
	roots.AppendCertsFromPEM(buf)

	if C.CFDataGetLength(untrustedData) == 0 {
		return roots, nil
	}

	buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
	untrustedRoots := NewCertPool()
	untrustedRoots.AppendCertsFromPEM(buf)

	trustedRoots := NewCertPool()
	for _, c := range roots.certs {
		if !untrustedRoots.contains(c) {
			trustedRoots.AddCert(c)
		}
	}
	return trustedRoots, nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/justinas/alice"
	"github.com/neustar/httprouter"
	"github.com/rs/xlog"
)

// App application
type App struct {
	Name string
}

// InternalHandler internal
type InternalHandler struct {
	h func(w http.ResponseWriter, r *http.Request)
}

// ServeHTTP serve
func (ih InternalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ih.h(w, r)
	return
}

func (app *App) hello(w http.ResponseWriter, r *http.Request) {
	l := xlog.FromRequest(r)
	l.Info("hello handler")
	log.Println("this is usual logger")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "hello")
	return
}

func main() {
	host, _ := os.Hostname()
	conf := xlog.Config{
		Fields: xlog.F{
			"role": "my-service",
			"host": host,
		},
		Output: xlog.NewOutputChannel(xlog.NewConsoleOutput()),
	}

	c := alice.New(
		xlog.NewHandler(conf),
		xlog.MethodHandler("method"),
		xlog.URLHandler("url"),
		xlog.UserAgentHandler("user_agent"),
		xlog.RefererHandler("referer"),
		xlog.RequestIDHandler("req_id", "Request-Id"),
		accessLoggingMiddleware,
	)
	app := App{Name: "my-service"}
	r := httprouter.New()
	// r.GET("/hello", c.Then(InternalHandler{h: app.hello}))
	r.GET("/hello", http.HandlerFunc(app.hello))

	xlog.Info("xlog")
	xlog.Infof("chain: %+v", c)
	log.Println("start server")
	if err := http.ListenAndServe(":8080", r); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Update<commit_after>package main

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

	"github.com/justinas/alice"
	"github.com/neustar/httprouter"
	"github.com/rs/xlog"
)

// App application
type App struct {
	Name string
}

// InternalHandler internal
type InternalHandler struct {
	h func(w http.ResponseWriter, r *http.Request)
}

// ServeHTTP serve
func (ih InternalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ih.h(w, r)
	return
}

func (app *App) hello(w http.ResponseWriter, r *http.Request) {
	l := xlog.FromRequest(r)
	l.Info("hello handler")
	log.Println("this is usual logger")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "hello")
	return
}

func main() {
	host, _ := os.Hostname()
	conf := xlog.Config{
		Fields: xlog.F{
			"role": "my-service",
			"host": host,
		},
		Output: xlog.NewOutputChannel(xlog.NewConsoleOutput()),
	}

	c := alice.New(
		xlog.NewHandler(conf),
		xlog.MethodHandler("method"),
		xlog.URLHandler("url"),
		xlog.UserAgentHandler("user_agent"),
		xlog.RefererHandler("referer"),
		xlog.RequestIDHandler("req_id", "Request-Id"),
		accessLoggingMiddleware,
	)
	app := App{Name: "my-service"}
	r := httprouter.New()
	// r.GET("/hello", c.Then(InternalHandler{h: app.hello}))
	// r.GET("/hello", http.HandlerFunc(c.Then(InternalHandler{h: app.hello})))
	h := c.Then(InternalHandler{h: app.hello})
	r.GET("/hello", http.HandlerFunc(h.ServeHTTP))

	xlog.Info("xlog")
	xlog.Infof("chain: %+v", c)
	log.Println("start server")
	if err := http.ListenAndServe(":8080", r); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>/**
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only. Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"
)

type comment struct {
	Author string `json:"author"`
	Text   string `json:"text"`
}

const dataFile = "./comments.json"

var commentMutex = new(sync.Mutex)

// Handle comments
func handleComments(w http.ResponseWriter, r *http.Request) {
	// Since multiple requests could come in at once, ensure we have a lock
	// around all file operations
	commentMutex.Lock()
	defer commentMutex.Unlock()

	// Stat the file, so we can find its current permissions
	fi, err := os.Stat(dataFile)
	if err != nil {
		http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
		return
	}

	// Read the comments from the file.
	commentData, err := ioutil.ReadFile(dataFile)
	if err != nil {
		http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
		return
	}

	switch r.Method {
	case "POST":
		// Decode the JSON data
		comments := make([]comment, 0)
		if err := json.Unmarshal(commentData, &comments); err != nil {
			http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError)
			return
		}

		// Add a new comment to the in memory slice of comments
		comments = append(comments, comment{Author: r.FormValue("author"), Text: r.FormValue("text")})

		// Marshal the comments to indented json.
		commentData, err = json.MarshalIndent(comments, "", "    ")
		if err != nil {
			http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError)
			return
		}

		// Write out the comments to the file, preserving permissions
		err := ioutil.WriteFile(dataFile, commentData, fi.Mode())
		if err != nil {
			http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Cache-Control", "no-cache")
		io.Copy(w, bytes.NewReader(commentData))

	case "GET":
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Cache-Control", "no-cache")
		// stream the contents of the file to the response
		io.Copy(w, bytes.NewReader(commentData))

	default:
		// Don't know the method, so error
		http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed)
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	http.HandleFunc("/comments.json", handleComments)
	http.Handle("/", http.FileServer(http.Dir("./public")))
	log.Println("Server started: http://localhost:" + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<commit_msg>go lint<commit_after>/**
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only. Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"
)

type comment struct {
	Author string `json:"author"`
	Text   string `json:"text"`
}

const dataFile = "./comments.json"

var commentMutex = new(sync.Mutex)

// Handle comments
func handleComments(w http.ResponseWriter, r *http.Request) {
	// Since multiple requests could come in at once, ensure we have a lock
	// around all file operations
	commentMutex.Lock()
	defer commentMutex.Unlock()

	// Stat the file, so we can find its current permissions
	fi, err := os.Stat(dataFile)
	if err != nil {
		http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
		return
	}

	// Read the comments from the file.
	commentData, err := ioutil.ReadFile(dataFile)
	if err != nil {
		http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
		return
	}

	switch r.Method {
	case "POST":
		// Decode the JSON data
		var comments []comment
		if err := json.Unmarshal(commentData, &comments); err != nil {
			http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError)
			return
		}

		// Add a new comment to the in memory slice of comments
		comments = append(comments, comment{Author: r.FormValue("author"), Text: r.FormValue("text")})

		// Marshal the comments to indented json.
		commentData, err = json.MarshalIndent(comments, "", "    ")
		if err != nil {
			http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError)
			return
		}

		// Write out the comments to the file, preserving permissions
		err := ioutil.WriteFile(dataFile, commentData, fi.Mode())
		if err != nil {
			http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Cache-Control", "no-cache")
		io.Copy(w, bytes.NewReader(commentData))

	case "GET":
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Cache-Control", "no-cache")
		// stream the contents of the file to the response
		io.Copy(w, bytes.NewReader(commentData))

	default:
		// Don't know the method, so error
		http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed)
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	http.HandleFunc("/comments.json", handleComments)
	http.Handle("/", http.FileServer(http.Dir("./public")))
	log.Println("Server started: http://localhost:" + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<|endoftext|>