<commit_before>package dockerapiproxy

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"

	"github.com/rancher/go-rancher/hostaccess"
	rancher "github.com/rancher/go-rancher/v2"
)

type IoOps interface {
	io.Writer
	Read() ([]byte, error)
}

type Proxy struct {
	client       *rancher.RancherClient
	host, listen string
	TlsConfig    *tls.Config
	listener     net.Listener
	rancherHost  *rancher.Host
}

func NewProxy(client *rancher.RancherClient, host, listen string) *Proxy {
	return &Proxy{
		client: client,
		host:   host,
		listen: listen,
	}
}

func (p *Proxy) Close() error {
	return p.listener.Close()
}

func (p *Proxy) getSocket(url string) (net.Listener, error) {
	proto := "tcp"
	address := url

	parts := strings.SplitN(url, "://", 2)
	if len(parts) == 2 {
		proto = parts[0]
		address = parts[1]
	}

	if proto == "unix" {
		os.Remove(address)
	}

	l, err := net.Listen(proto, address)
	if err != nil {
		return nil, err
	}

	if proto == "tcp" && p.TlsConfig != nil {
		p.TlsConfig.NextProtos = []string{"http/1.1"}
		l = tls.NewListener(l, p.TlsConfig)
	}

	return l, err
}

func (p *Proxy) Listen() error {
	host, err := p.getHost()
	if err != nil {
		return err
	}

	os.Remove(p.listen)

	l, err := p.getSocket(p.listen)
	if err != nil {
		return err
	}

	p.listener = l
	p.rancherHost = host
	return nil
}

func (p *Proxy) Serve() error {
	for {
		conn, err := p.listener.Accept()
		if err != nil {
			return err
		}

		logrus.Debug("New connection")
		go p.handle(p.rancherHost, conn)
	}
}

func (p *Proxy) ListenAndServe() error {
	if err := p.Listen(); err != nil {
		return err
	}
	return p.Serve()
}

func (p *Proxy) handle(host *rancher.Host, client net.Conn) {
	if err := p.handleError(host, client); err != nil {
		logrus.Errorf("Failed to handle connection: %v", err)
	}
}

func (p *Proxy) handleError(host *rancher.Host, conn net.Conn) error {
	defer conn.Close()

	hostAccessClient := hostaccess.RancherWebsocketClient(*p.client)
	websocket, err := hostAccessClient.GetHostAccess(host.Resource, "dockersocket", nil)
	if err != nil {
		return err
	}

	server := &WebSocketIo{Conn: websocket}
	client := &SocketIo{Conn: conn}

	wg := sync.WaitGroup{}
	wg.Add(2)

	abort := func() {
		wg.Done()
		conn.Close()
		websocket.Close()
	}

	go func() {
		defer abort()
		p.copyLoop(client, server)
	}()

	go func() {
		defer abort()
		p.copyLoop(server, client)
	}()

	wg.Wait()

	logrus.Debugf("Disconnecting")

	return nil
}

func (p *Proxy) copyLoop(from, to IoOps) error {
	con := true

	for con {
		buf, err := from.Read()
		if err != nil {
			return err
		}
		logrus.Debugf("Read %d bytes", len(buf))
		if _, err := to.Write(buf); err != nil {
			return err
		}
		logrus.Debugf("Wrote %d bytes", len(buf))
	}

	return nil
}

func (p *Proxy) getHost() (*rancher.Host, error) {
	host, err := p.client.Host.ById(p.host)
	if err != nil {
		return nil, err
	}

	if host != nil {
		return host, nil
	}

	hosts, err := p.client.Host.List(&rancher.ListOpts{
		Filters: map[string]interface{}{
			"uuid": p.host,
		},
	})
	if err != nil {
		return nil, err
	}

	if len(hosts.Data) == 0 {
		hosts, err = p.client.Host.List(&rancher.ListOpts{
			Filters: map[string]interface{}{
				"name": p.host,
			},
		})
		if err != nil {
			return nil, err
		}
	}

	if len(hosts.Data) == 0 {
		return nil, fmt.Errorf("Failed to find host: %s", p.host)
	}

	return &hosts.Data[0], nil
}
<commit_msg>Support lookup by hostname and name<commit_after>package dockerapiproxy

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
	"github.com/rancher/go-rancher/hostaccess"
	rancher "github.com/rancher/go-rancher/v2"
)

type IoOps interface {
	io.Writer
	Read() ([]byte, error)
}

type Proxy struct {
	client       *rancher.RancherClient
	host, listen string
	TlsConfig    *tls.Config
	listener     net.Listener
	rancherHost  *rancher.Host
}

func NewProxy(client *rancher.RancherClient, host, listen string) *Proxy {
	return &Proxy{
		client: client,
		host:   host,
		listen: listen,
	}
}

func (p *Proxy) Close() error {
	return p.listener.Close()
}

func (p *Proxy) getSocket(url string) (net.Listener, error) {
	proto := "tcp"
	address := url

	parts := strings.SplitN(url, "://", 2)
	if len(parts) == 2 {
		proto = parts[0]
		address = parts[1]
	}

	if proto == "unix" {
		os.Remove(address)
	}

	l, err := net.Listen(proto, address)
	if err != nil {
		return nil, err
	}

	if proto == "tcp" && p.TlsConfig != nil {
		p.TlsConfig.NextProtos = []string{"http/1.1"}
		l = tls.NewListener(l, p.TlsConfig)
	}

	return l, err
}

func (p *Proxy) Listen() error {
	host, err := p.getHost()
	if err != nil {
		return err
	}

	os.Remove(p.listen)

	l, err := p.getSocket(p.listen)
	if err != nil {
		return err
	}

	p.listener = l
	p.rancherHost = host
	return nil
}

func (p *Proxy) Serve() error {
	for {
		conn, err := p.listener.Accept()
		if err != nil {
			return err
		}

		logrus.Debug("New connection")
		go p.handle(p.rancherHost, conn)
	}
}

func (p *Proxy) ListenAndServe() error {
	if err := p.Listen(); err != nil {
		return err
	}
	return p.Serve()
}

func (p *Proxy) handle(host *rancher.Host, client net.Conn) {
	if err := p.handleError(host, client); err != nil {
		logrus.Errorf("Failed to handle connection: %v", err)
	}
}

func (p *Proxy) openConnection(host *rancher.Host) (*websocket.Conn, error) {
	hostAccessClient := hostaccess.RancherWebsocketClient(*p.client)
	return hostAccessClient.GetHostAccess(host.Resource, "dockersocket", nil)
}

func (p *Proxy) handleError(host *rancher.Host, conn net.Conn) error {
	defer conn.Close()

	websocket, err := p.openConnection(host)
	if err != nil {
		return err
	}

	server := &WebSocketIo{Conn: websocket}
	client := &SocketIo{Conn: conn}

	wg := sync.WaitGroup{}
	wg.Add(2)

	abort := func() {
		wg.Done()
		conn.Close()
		websocket.Close()
	}

	go func() {
		defer abort()
		p.copyLoop(client, server)
	}()

	go func() {
		defer abort()
		p.copyLoop(server, client)
	}()

	wg.Wait()

	logrus.Debugf("Disconnecting")

	return nil
}

func (p *Proxy) copyLoop(from, to IoOps) error {
	con := true

	for con {
		buf, err := from.Read()
		if err != nil {
			return err
		}
		logrus.Debugf("Read %d bytes", len(buf))
		if _, err := to.Write(buf); err != nil {
			return err
		}
		logrus.Debugf("Wrote %d bytes", len(buf))
	}

	return nil
}

func (p *Proxy) getHost() (*rancher.Host, error) {
	host, err := p.client.Host.ById(p.host)
	if err != nil {
		return nil, err
	}

	if host != nil {
		return host, nil
	}

	hosts, err := p.client.Host.List(&rancher.ListOpts{
		Filters: map[string]interface{}{
			"uuid": p.host,
		},
	})
	if err != nil {
		return nil, err
	}

	if len(hosts.Data) == 0 {
		hosts, err = p.client.Host.List(&rancher.ListOpts{
			Filters: map[string]interface{}{
				"name": p.host,
			},
		})
		if err != nil {
			return nil, err
		}
	}

	if len(hosts.Data) == 0 {
		hosts, err = p.client.Host.List(&rancher.ListOpts{
			Filters: map[string]interface{}{
				"hostname": p.host,
			},
		})
		if err != nil {
			return nil, err
		}
	}

	if len(hosts.Data) == 0 {
		return nil, fmt.Errorf("Failed to find host: %s", p.host)
	}

	return &hosts.Data[0], nil
}
<|endoftext|>


<commit_before>package realm

import (
	"fmt"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/miekg/dns"
)

// A Server listens for DNS requests over UDP and responds with answers from the provided Zone.
type Server struct {
	server   *dns.Server
	registry *Registry
	statsd   *statsd.Client
}

// NewServer returns a new initialized *Server that will bind to listen and will look up answers from zone.
func NewServer(listen string, registry *Registry, statsdHost string) (*Server, error) {
	var err error
	var s *Server
	s = &Server{registry: registry}
	s.server = &dns.Server{
		Addr:    listen,
		Net:     "udp",
		Handler: s,
	}
	if statsdHost != "" {
		s.statsd, err = statsd.New(statsdHost)
		if err != nil {
			return nil, err
		}
		s.statsd.Namespace = "realm."
	}
	return s, err
}

// ListenAndServe will start the nameserver on the configured address.
func (s *Server) ListenAndServe() error {
	return s.server.ListenAndServe()
}

// ServeDNS will be called for every DNS request to this server.
// It will attempt to provide answers to all questions from the configured zone.
func (s *Server) ServeDNS(w dns.ResponseWriter, request *dns.Msg) {
	// Capture starting time for measuring message response time
	var start float64
	var end float64
	var elapsed float64
	start = float64(time.Now().Unix()) * 1000.0

	// Setup the default response
	var response *dns.Msg
	response = &dns.Msg{}
	response.SetReply(request)
	response.Compress = true

	// Lookup answers to any of the questions
	for _, question := range request.Question {
		// Capture starting time for measuring lookup
		var lookupStart float64
		var lookupEnd float64
		var lookupElapsed float64
		lookupStart = float64(time.Now().Unix()) * 1000.0

		// Perform lookup for this question
		var records []dns.RR
		records = s.registry.Lookup(question.Name, question.Qtype, question.Qclass)

		// Capture ending and elapsed time
		lookupEnd = float64(time.Now().Unix()) * 1000.0
		lookupElapsed = lookupEnd - lookupStart

		// Append results to the response
		response.Answer = append(response.Answer, records...)

		// If StatsD is enabled, record some metrics
		if s.statsd != nil {
			s.statsd.TimeInMilliseconds("lookup.time", lookupElapsed, nil, 1)
			s.statsd.Histogram("lookup.answer", float64(len(records)), nil, 1)

			var tags []string
			tags = []string{
				fmt.Sprintf("name:%s", question.Name),
				fmt.Sprintf("qtype:%s", dns.TypeToString[question.Qtype]),
				fmt.Sprintf("qclass:%s", dns.ClassToString[question.Qclass]),
			}
			s.statsd.Count("request.question", 1, tags, 1)
		}
	}

	// Respond to the request
	w.WriteMsg(response)

	// Record any ending metrics
	end = float64(time.Now().Unix()) * 1000.0
	elapsed = end - start
	if s.statsd != nil {
		s.statsd.TimeInMilliseconds("request.time", elapsed, nil, 1)
	}
}
<commit_msg>more precision timing<commit_after>package realm

import (
	"fmt"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/miekg/dns"
)

// A Server listens for DNS requests over UDP and responds with answers from the provided Zone.
type Server struct {
	server   *dns.Server
	registry *Registry
	statsd   *statsd.Client
}

// NewServer returns a new initialized *Server that will bind to listen and will look up answers from zone.
func NewServer(listen string, registry *Registry, statsdHost string) (*Server, error) {
	var err error
	var s *Server
	s = &Server{registry: registry}
	s.server = &dns.Server{
		Addr:    listen,
		Net:     "udp",
		Handler: s,
	}
	if statsdHost != "" {
		s.statsd, err = statsd.New(statsdHost)
		if err != nil {
			return nil, err
		}
		s.statsd.Namespace = "realm."
	}
	return s, err
}

// ListenAndServe will start the nameserver on the configured address.
func (s *Server) ListenAndServe() error {
	return s.server.ListenAndServe()
}

// ServeDNS will be called for every DNS request to this server.
// It will attempt to provide answers to all questions from the configured zone.
func (s *Server) ServeDNS(w dns.ResponseWriter, request *dns.Msg) {
	// Capture starting time for measuring message response time
	var start time.Time
	start = time.Now()

	// Setup the default response
	var response *dns.Msg
	response = &dns.Msg{}
	response.SetReply(request)
	response.Compress = true

	// Lookup answers to any of the questions
	for _, question := range request.Question {
		// Capture starting time for measuring lookup
		var lookupStart time.Time
		lookupStart = time.Now()

		// Perform lookup for this question
		var records []dns.RR
		records = s.registry.Lookup(question.Name, question.Qtype, question.Qclass)

		// Capture ending and elapsed time
		var lookupElapsed time.Duration
		lookupElapsed = time.Since(lookupStart)

		// Append results to the response
		response.Answer = append(response.Answer, records...)

		// If StatsD is enabled, record some metrics
		if s.statsd != nil {
			var tags []string
			tags = []string{
				fmt.Sprintf("name:%s", question.Name),
				fmt.Sprintf("qtype:%s", dns.TypeToString[question.Qtype]),
				fmt.Sprintf("qclass:%s", dns.ClassToString[question.Qclass]),
			}

			s.statsd.TimeInMilliseconds("lookup.time", lookupElapsed.Seconds()*1000.0, tags, 1)
			s.statsd.Histogram("lookup.answer", float64(len(records)), tags, 1)
			s.statsd.Count("request.question", 1, tags, 1)
		}
	}

	// Respond to the request
	w.WriteMsg(response)

	// Record any ending metrics
	if s.statsd != nil {
		var elapsed time.Duration
		elapsed = time.Since(start)
		s.statsd.TimeInMilliseconds("request.time", elapsed.Seconds()*1000.0, nil, 1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Jacob Taylor jacob.taylor@gmail.com
// License: Apache2
package main

import (
    "fmt"
    "net"
    "./utils"
    "bufio"
    "encoding/binary"
)

func main() {
    controlC := [...]byte{255, 244, 255, 253, 6}
    listener, err := net.Listen("tcp", "192.168.214.1:8000")
    utils.ErrorCheck(err)

    fmt.Printf("Hello World, we have %v\n", listener)
    reply_magic := make([]byte, 4)
    binary.BigEndian.PutUint32(reply_magic, utils.NBD_REPLY_MAGIC)

    for {
        conn, err := listener.Accept()
        utils.ErrorCheck(err)

        fmt.Printf("We have a new connection from: %s", conn.RemoteAddr())
        output := bufio.NewWriter(conn)

        output.WriteString("NBDMAGIC")      // init password
        //output.Write(reply_magic)
        output.Flush()
        output.WriteString("IHAVEOPT")      // Magic
        output.Flush()
        output.Write([]byte{0, 3})          // Flags (3 = supports list)
        output.Flush()
        //output.WriteString("\n")

        data := make([]byte, 1024)
        length, err := conn.Read(data)
        utils.ErrorCheck(err)
        utils.LogData("A", length, data)

        data = make([]byte, 1024)
        length, err = conn.Read(data)
        utils.ErrorCheck(err)
        utils.LogData("B", length, data)

        endian := binary.BigEndian

        // Send the first export

        //output.Write(reply_magic)
        data = make([]byte, 1024)
        offset := 0
        endian.PutUint64(data[offset:], utils.NBD_SERVER_SEND_REPLY_MAGIC)
        offset += 8
        //output.WriteString(">EVZ")
        //output.Write(data[0:7])
        //output.Flush()
        //output.WriteString("IHAVEOPT")      // Magic
        //output.Flush()
        endian.PutUint32(data[offset:], uint32(3))  // Flags (3 = supports list)
        offset += 4
        //output.Write([]byte{0, 3})
        //output.Flush()
        // reply type
        endian.PutUint32(data[offset:], uint32(2))  // reply_type: NBD_REP_SERVER
        offset += 4

        //output.Write([]byte{0, 2})          // reply_type: NBD_REP_SERVER
        //output.Flush()

        export_name := "happy_export"
        length = len(export_name)

        // length of export name package
        endian.PutUint32(data[offset:], uint32(length + 4))  // length of string
        offset += 4

        // length of export name
        endian.PutUint32(data[offset:], uint32(length))  // length of string
        offset += 4

        // export name
        copy(data[offset:], export_name)
        offset += length

        output.Write(data)
        output.Flush()

        utils.LogData("Just sent:", offset, data)

        //output.Write(utils.EncodeInt(len(export_name)))
        //// export name
        //output.WriteString(export_name)
        //output.Write([]byte{})
        //output.Flush()

        //data = make([]byte, 1024)
        //length, err = conn.Read(data)
        //utils.ErrorCheck(err)
        //utils.LogData("C", length, data)

        input := bufio.NewScanner(conn)
        for input.Scan() {
            if len(input.Bytes()) == 5 {
                temp := [...]byte{0, 0, 0, 0, 0}
                copy(temp[:], input.Bytes())
                if temp == controlC {
                    fmt.Printf("Control-C received. Bye\n")
                    break
                }
            }
            //fmt.Printf("Error is: %+v", input.Err())
            fmt.Printf("%s echo: %s '%v'\n", conn.RemoteAddr().String(), input.Text(), input.Bytes())
        }
        conn.Close()

    }

}
<commit_msg>finishing refactor to send_message function. export list is working. Time for cleanup<commit_after>// Copyright 2016 Jacob Taylor jacob.taylor@gmail.com
// License: Apache2
package main

import (
    "fmt"
    "net"
    "./utils"
    "bufio"
    "encoding/binary"
)

func send_message(output *bufio.Writer, reply_type uint32, length uint32, data []byte ) {
    endian := binary.BigEndian
    buffer := make([]byte, 1024)
    offset := 0

    endian.PutUint64(buffer[offset:], utils.NBD_SERVER_SEND_REPLY_MAGIC)
    offset += 8

    endian.PutUint32(buffer[offset:], uint32(3))  // Flags (3 = supports list)
    offset += 4

    endian.PutUint32(buffer[offset:], reply_type)  // reply_type: NBD_REP_SERVER
    offset += 4

    // length of export name package
    endian.PutUint32(buffer[offset:], length)  // length of string
    offset += 4

    fmt.Printf("offset is: %4d  length is: %4d\n", offset, length)
    //if length > 0 {
        copy(buffer[offset:], data[0:length])
        offset += int(length)
    //}

    data_to_send := buffer[:offset]
    output.Write(data_to_send)
    output.Flush()

    utils.LogData("Just sent:", offset, data_to_send)
}

func main() {
    controlC := [...]byte{255, 244, 255, 253, 6}
    listener, err := net.Listen("tcp", "192.168.214.1:8000")
    utils.ErrorCheck(err)

    fmt.Printf("Hello World, we have %v\n", listener)
    reply_magic := make([]byte, 4)
    binary.BigEndian.PutUint32(reply_magic, utils.NBD_REPLY_MAGIC)

    for {
        conn, err := listener.Accept()
        utils.ErrorCheck(err)

        fmt.Printf("We have a new connection from: %s", conn.RemoteAddr())
        output := bufio.NewWriter(conn)

        output.WriteString("NBDMAGIC")      // init password
        //output.Write(reply_magic)
        output.Flush()
        output.WriteString("IHAVEOPT")      // Magic
        output.Flush()
        output.Write([]byte{0, 3})          // Flags (3 = supports list)
        output.Flush()
        //output.WriteString("\n")

        data := make([]byte, 1024)
        length, err := conn.Read(data)
        utils.ErrorCheck(err)
        utils.LogData("A", length, data)

        data = make([]byte, 1024)
        length, err = conn.Read(data)
        utils.ErrorCheck(err)
        utils.LogData("B", length, data)

        // Send the first export
        endian := binary.BigEndian
        data = make([]byte, 1024)
        export_name := "happy_export"
        length = len(export_name)

        offset := 0

        //// length of export name package
        //endian.PutUint32(data[offset:], uint32(length + 4))  // length of all data (string + size)
        //offset += 4

        // length of export name
        endian.PutUint32(data[offset:], uint32(length))  // length of string
        offset += 4

        // export name
        copy(data[offset:], export_name)
        offset += length

        reply_type := uint32(2)     // reply_type: NBD_REP_SERVER
        utils.LogData("Calling send_message with:", offset, data)
        send_message(output, reply_type, uint32(offset), data)

        // Send the second export
        data = make([]byte, 1024)
        export_name = "Very_happy_export"
        length = len(export_name)
        offset = 0

        // length of export name
        endian.PutUint32(data[offset:], uint32(length))  // length of string
        offset += 4

        // export name
        copy(data[offset:], export_name)
        offset += length

        reply_type = uint32(2)     // reply_type: NBD_REP_SERVER
        utils.LogData("Calling send_message with:", offset, data)
        send_message(output, reply_type, uint32(offset), data)

        // Send acknowledgement that the list is done.

        reply_type = uint32(1)      // reply_type: NBD_REP_ACK

        data = make([]byte, 1024)
        send_message(output, reply_type, 0, data)

        input := bufio.NewScanner(conn)
        for input.Scan() {
            if len(input.Bytes()) == 5 {
                temp := [...]byte{0, 0, 0, 0, 0}
                copy(temp[:], input.Bytes())
                if temp == controlC {
                    fmt.Printf("Control-C received. Bye\n")
                    break
                }
            }
            //fmt.Printf("Error is: %+v", input.Err())
            fmt.Printf("%s echo: %s '%v'\n", conn.RemoteAddr().String(), input.Text(), input.Bytes())
        }
        conn.Close()

    }

}
<|endoftext|>


<commit_before>package mqtt

import (
	"errors"
	"fmt"
	"log"
	"net"
)

////////////////////Interface//////////////////////////////

type ServerSession interface {
	Session

	Will() Message
	Forward(msg Message) error
	AcknowledgeConnect(pktconnack PacketConnack) error
	AcknowledgeSubscribe(pktsuback PacketSuback) error
}

////////////////////Implementation////////////////////////

type serverSession struct {
	session

	conn net.Conn

	//Connect
	connectFlags byte
	keepAlive    uint16
	clientId     string
	willTopic    string
	willMessage  string

	//Publish
	packetId  uint16
	PacketIds map[uint32]uint16

	//Subscribe
	topics map[string]string
	qos    map[string]QOS

	//private
	keepAliveAccumulated uint16
	topicsToBeAdded      []string
	qosToBeAdded         []QOS
	will                 Message
}

func newServerSession(conn net.Conn) *serverSession {
	this := &serverSession{}

	this.conn = conn
	this.err = nil
	this.state = SESSION_STATE_CREATED
	this.quit = make(chan bool)
	this.packetId = 1
	this.PacketIds = make(map[uint32]uint16)
	this.keepAlive = 0
	this.keepAliveAccumulated = 0
	this.topics = make(map[string]string)
	this.qos = make(map[string]QOS)
	this.will = nil

	return this
}

func (this *serverSession) Forward(msg Message) error {
	if this.state == SESSION_STATE_CONNECTED && msg.GetClientId() != this.clientId {
		if _, ok := this.topics[msg.GetTopic()]; ok {
			if _, err := this.conn.Write(msg.Packetize(this.packetId).Bytes()); err != nil {
				log.Println(err.Error())
				return err
			}

			if msg.GetQos() == QOS_TWO || msg.GetQos() == QOS_ONE {
				this.PacketIds[uint32(this.packetId)] = this.packetId
				if this.packetId++; this.packetId == 0 {
					this.packetId++
				}
			}
		}
	}

	return nil
}

func (this *serverSession) AcknowledgeConnect(pktconnack PacketConnack) error {
	switch this.state {
	case SESSION_STATE_CREATED:
		if _, err := this.conn.Write(pktconnack.Bytes()); err != nil {
			log.Println(err.Error())
			return err
		} else {
			log.Println("SENT CONNACK")
		}
		if pktconnack.GetReturnCode() == CONNACK_RETURNCODE_ACCEPTED {
			this.state = SESSION_STATE_CONNECTED
		} else {
			this.state = SESSION_STATE_TERMINATED
			this.err = fmt.Errorf("Listener Refused Connection with Return Code %x\n", pktconnack.GetReturnCode())
		}
		return nil
	default:
		return errors.New("Invalid ServerSession State\n")
	}
}

func (this *serverSession) AcknowledgeSubscribe(pktsuback PacketSuback) error {
	switch this.state {
	case SESSION_STATE_CONNECTED:
		retCodes := pktsuback.GetReturnCodes()
		if len(this.qosToBeAdded) != len(retCodes) {
			return errors.New("Invalid Return Codes Length in PacketSuback\n")
		}
		if _, err := this.conn.Write(pktsuback.Bytes()); err != nil {
			log.Println(err.Error())
			return err
		} else {
			log.Println("SENT SUBACK")
		}
		for i := 0; i < len(retCodes); i++ {
			if retCodes[i] <= 0x02 {
				this.topics[this.topicsToBeAdded[i]] = this.topicsToBeAdded[i]
				this.qos[this.topicsToBeAdded[i]] = QOS(retCodes[i])
			}
		}
		return nil
	default:
		return errors.New("Invalid ServerSession State\n")
	}
}

func (this *serverSession) Process(buf []byte) Event {
	pkt, err := Packetize(buf)
	if err != nil {
		if this.state != SESSION_STATE_TERMINATED {
			this.state = SESSION_STATE_TERMINATED
			this.err = err
			return newEventIOException(this, this.conn.RemoteAddr())
		} else {
			return nil
		}
	}

	switch this.state {
	case SESSION_STATE_CREATED:
		switch pkt.GetType() {
		case PACKET_CONNECT:
			return this.ProcessConnect(pkt.(PacketConnect))
		default:
			return this.ProcessTerminate("Invalid First CONNECT Packet Received\n", false)
		}
	case SESSION_STATE_CONNECTED:
		switch pkt.GetType() {
		case PACKET_CONNECT:
			return this.ProcessTerminate("Invalid Second or Multiple CONNECT Packets Received\n", false)
		case PACKET_PUBLISH:
			return this.ProcessPublish(pkt.(PacketPublish))
		case PACKET_SUBSCRIBE:
			return this.ProcessSubscribe(pkt.(PacketSubscribe))
		case PACKET_UNSUBSCRIBE:
			return this.ProcessUnsubscribe(pkt.(PacketUnsubscribe))
		case PACKET_PINGREQ:
			log.Println("PINGREQ Packet Received")
			pkgpingresp := NewPacket(PACKET_PINGRESP)
			if _, err := this.conn.Write(pkgpingresp.Bytes()); err != nil {
				log.Println(err.Error())
			} else {
				log.Println("SENT PINGRESP")
			}
		case PACKET_PUBREL:
			clientPacketId := uint32(pkt.(PacketPubrel).GetPacketId()) << 16
			if _, ok := this.PacketIds[clientPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubRel PacketId %x Received\n", clientPacketId>>16), false)
			} else {
				delete(this.PacketIds, clientPacketId)
				pkgpubcomp := NewPacketAcks(PACKET_PUBCOMP)
				pkgpubcomp.SetPacketId(uint16(clientPacketId >> 16))
				if _, err := this.conn.Write(pkgpubcomp.Bytes()); err != nil {
					log.Println(err.Error())
				} else {
					log.Println("SENT PUBCOMP")
				}
			}
		case PACKET_PUBACK:
			serverPacketId := uint32(pkt.(PacketPuback).GetPacketId())
			if _, ok := this.PacketIds[serverPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubAck PacketId %x Received\n", serverPacketId), false)
			} else {
				delete(this.PacketIds, serverPacketId)
			}
		case PACKET_PUBREC:
			serverPacketId := uint32(pkt.(PacketPuback).GetPacketId())
			if _, ok := this.PacketIds[serverPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubRec PacketId %x Received\n", serverPacketId), false)
			} else {
				pkgpubrel := NewPacketAcks(PACKET_PUBREL)
				pkgpubrel.SetPacketId(uint16(serverPacketId))
				if _, err := this.conn.Write(pkgpubrel.Bytes()); err != nil {
					log.Println(err.Error())
				} else {
					log.Println("SENT PUBREL")
				}
			}
		case PACKET_PUBCOMP:
			serverPacketId := uint32(pkt.(PacketPuback).GetPacketId())
			if _, ok := this.PacketIds[serverPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubComp PacketId %x Received\n", serverPacketId), false)
			} else {
				delete(this.PacketIds, serverPacketId)
			}
		case PACKET_DISCONNECT:
			return this.ProcessTerminate("DISCONNECT Packet Received\n", true)
		default:
			return this.ProcessTerminate(fmt.Sprintf("Unexpected %s Packet Received\n", PACKET_TYPE_STRINGS[pkt.GetType()]), false)
		}
	case SESSION_STATE_TERMINATED:
	default:
	}

	return nil
}

func (this *serverSession) ProcessConnect(pkgconn PacketConnect) Event {
	if pkgconn.GetProtocolLevel() != 0x04 {
		pkgconnack := NewPacketConnack()
		pkgconnack.SetSPFlag(false)
		pkgconnack.SetReturnCode(CONNACK_RETURNCODE_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION)
		if _, err := this.conn.Write(pkgconnack.Bytes()); err != nil {
			log.Println(err.Error())
		} else {
			log.Println("SENT CONNACK")
		}

		this.state = SESSION_STATE_TERMINATED
		this.err = fmt.Errorf("Invalid %x Control Packet Protocol Level %x\n", pkgconn.GetType(), pkgconn.GetProtocolLevel())
		return newEventSessionTerminated(this, this.Error(), nil)
	} else {
		this.connectFlags = pkgconn.GetConnectFlags()
		this.keepAlive = pkgconn.GetKeepAlive()
		this.clientId = pkgconn.GetClientId()
		this.willTopic = pkgconn.GetWillTopic()
		this.willMessage = pkgconn.GetWillMessage()
		//fmt.Printf("%v\n", []byte(this.willMessage))
		if (this.connectFlags & CONNECT_FLAG_WILL_FLAG) != 0 {
			var retain bool
			if (this.connectFlags & CONNECT_FLAG_WILL_RETAIN) != 0 {
				retain = true
			} else {
				retain = false
			}
			this.will = NewMessage(false,
				QOS((this.connectFlags&(CONNECT_FLAG_WILL_QOS_BIT3|CONNECT_FLAG_WILL_QOS_BIT4))>>3),
				retain,
				this.willTopic,
				this.willMessage)
			this.will.SetClientId(this.clientId)
		} else {
			this.will = nil
		}

		return newEventConnect(this, pkgconn)
	}
}

func (this *serverSession) ProcessPublish(pktpub PacketPublish) Event {
	pktpub.GetMessage().SetClientId(this.clientId)
	qos := pktpub.GetMessage().GetQos()
	if qos == QOS_TWO {
		clientPacketId := pktpub.GetPacketId()
		this.PacketIds[uint32(clientPacketId)<<16] = clientPacketId
		pkgpubrec := NewPacketAcks(PACKET_PUBREC)
		pkgpubrec.SetPacketId(clientPacketId)
		if _, err := this.conn.Write(pkgpubrec.Bytes()); err != nil {
			log.Println(err.Error())
		} else {
			log.Println("SENT PUBREC")
		}
	} else if qos == QOS_ONE {
		pkgpuback := NewPacketAcks(PACKET_PUBACK)
		pkgpuback.SetPacketId(pktpub.GetPacketId())
		if _, err := this.conn.Write(pkgpuback.Bytes()); err != nil {
			log.Println(err.Error())
		} else {
			log.Println("SENT PUBACK")
		}
	}
	return newEventPublish(this, pktpub.GetMessage())
}

func (this *serverSession) ProcessSubscribe(pktsub PacketSubscribe) Event {
	this.topicsToBeAdded = make([]string, len(pktsub.GetSubscribeTopics()))
	copy(this.topicsToBeAdded, pktsub.GetSubscribeTopics())

	this.qosToBeAdded = make([]QOS, len(pktsub.GetQoSs()))
	copy(this.qosToBeAdded, pktsub.GetQoSs())

	return newEventSubscribe(this, pktsub.GetPacketId(), pktsub.GetSubscribeTopics(), pktsub.GetQoSs())
}

func (this *serverSession) ProcessUnsubscribe(pktunsub PacketUnsubscribe) Event {
	topics := pktunsub.GetUnsubscribeTopics()
	for i := 0; i < len(topics); i++ {
		delete(this.topics, topics[i])
		delete(this.qos, topics[i])
	}

	pkgsuback := NewPacketAcks(PACKET_UNSUBACK)
	pkgsuback.SetPacketId(pktunsub.GetPacketId())
	if _, err := this.conn.Write(pkgsuback.Bytes()); err != nil {
		log.Println(err.Error())
	} else {
		log.Println("SENT UNSUBACK")
	}

	return newEventUnsubscribe(this, topics)
}

func (this *serverSession) ProcessTerminate(msg string, disconnected bool) Event {
	if disconnected {
		this.will = nil
	}
	this.state = SESSION_STATE_TERMINATED
	this.err = errors.New(msg)
	return newEventSessionTerminated(this, msg, this.will)
}

func (this *serverSession) Will() Message {
	return this.will
}
<commit_msg>refactoring sever.go<commit_after>package mqtt

import (
	"errors"
	"fmt"
	"log"
	"net"
)

////////////////////Interface//////////////////////////////

type ServerSession interface {
	Session

	Will() Message
	Forward(msg Message) error
	AcknowledgeConnect(pktconnack PacketConnack) error
	AcknowledgeSubscribe(pktsuback PacketSuback) error
}

////////////////////Implementation////////////////////////

type serverSession struct {
	session

	conn net.Conn

	//Connect
	keepAlive uint16
	clientId  string
	will      Message

	//Publish
	packetId  uint16
	PacketIds map[uint32]uint16

	//Subscribe
	topics map[string]string
	qos    map[string]QOS

	//private
	keepAliveAccumulated uint16
	topicsToBeAdded      []string
	qosToBeAdded         []QOS
}

func newServerSession(conn net.Conn) *serverSession {
	this := &serverSession{}

	this.conn = conn
	this.err = nil
	this.state = SESSION_STATE_CREATED
	this.quit = make(chan bool)
	this.packetId = 1
	this.PacketIds = make(map[uint32]uint16)
	this.keepAlive = 0
	this.keepAliveAccumulated = 0
	this.topics = make(map[string]string)
	this.qos = make(map[string]QOS)
	this.will = nil

	return this
}

func (this *serverSession) Forward(msg Message) error {
	if this.state == SESSION_STATE_CONNECTED && msg.GetClientId() != this.clientId {
		if _, ok := this.topics[msg.GetTopic()]; ok {
			if _, err := this.conn.Write(msg.Packetize(this.packetId).Bytes()); err != nil {
				log.Println(err.Error())
				return err
			}

			if msg.GetQos() == QOS_TWO || msg.GetQos() == QOS_ONE {
				this.PacketIds[uint32(this.packetId)] = this.packetId
				if this.packetId++; this.packetId == 0 {
					this.packetId++
				}
			}
		}
	}

	return nil
}

func (this *serverSession) AcknowledgeConnect(pktconnack PacketConnack) error {
	switch this.state {
	case SESSION_STATE_CREATED:
		if _, err := this.conn.Write(pktconnack.Bytes()); err != nil {
			log.Println(err.Error())
			return err
		} else {
			log.Println("SENT CONNACK")
		}
		if pktconnack.GetReturnCode() == CONNACK_RETURNCODE_ACCEPTED {
			this.state = SESSION_STATE_CONNECTED
		} else {
			this.state = SESSION_STATE_TERMINATED
			this.err = fmt.Errorf("Listener Refused Connection with Return Code %x\n", pktconnack.GetReturnCode())
		}
		return nil
	default:
		return errors.New("Invalid ServerSession State\n")
	}
}

func (this *serverSession) AcknowledgeSubscribe(pktsuback PacketSuback) error {
	switch this.state {
	case SESSION_STATE_CONNECTED:
		retCodes := pktsuback.GetReturnCodes()
		if len(this.qosToBeAdded) != len(retCodes) {
			return errors.New("Invalid Return Codes Length in PacketSuback\n")
		}
		if _, err := this.conn.Write(pktsuback.Bytes()); err != nil {
			log.Println(err.Error())
			return err
		} else {
			log.Println("SENT SUBACK")
		}
		for i := 0; i < len(retCodes); i++ {
			if retCodes[i] <= 0x02 {
				this.topics[this.topicsToBeAdded[i]] = this.topicsToBeAdded[i]
				this.qos[this.topicsToBeAdded[i]] = QOS(retCodes[i])
			}
		}
		return nil
	default:
		return errors.New("Invalid ServerSession State\n")
	}
}

func (this *serverSession) Process(buf []byte) Event {
	pkt, err := Packetize(buf)
	if err != nil {
		if this.state != SESSION_STATE_TERMINATED {
			this.state = SESSION_STATE_TERMINATED
			this.err = err
			return newEventIOException(this, this.conn.RemoteAddr())
		} else {
			return nil
		}
	}

	switch this.state {
	case SESSION_STATE_CREATED:
		switch pkt.GetType() {
		case PACKET_CONNECT:
			return this.ProcessConnect(pkt.(PacketConnect))
		default:
			return this.ProcessTerminate("Invalid First CONNECT Packet Received\n", false)
		}
	case SESSION_STATE_CONNECTED:
		switch pkt.GetType() {
		case PACKET_CONNECT:
			return this.ProcessTerminate("Invalid Second or Multiple CONNECT Packets Received\n", false)
		case PACKET_PUBLISH:
			return this.ProcessPublish(pkt.(PacketPublish))
		case PACKET_SUBSCRIBE:
			return this.ProcessSubscribe(pkt.(PacketSubscribe))
		case PACKET_UNSUBSCRIBE:
			return this.ProcessUnsubscribe(pkt.(PacketUnsubscribe))
		case PACKET_PINGREQ:
			log.Println("PINGREQ Packet Received")
			pkgpingresp := NewPacket(PACKET_PINGRESP)
			if _, err := this.conn.Write(pkgpingresp.Bytes()); err != nil {
				log.Println(err.Error())
			} else {
				log.Println("SENT PINGRESP")
			}
		case PACKET_PUBREL:
			clientPacketId := uint32(pkt.(PacketPubrel).GetPacketId()) << 16
			if _, ok := this.PacketIds[clientPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubRel PacketId %x Received\n", clientPacketId>>16), false)
			} else {
				delete(this.PacketIds, clientPacketId)
				pkgpubcomp := NewPacketAcks(PACKET_PUBCOMP)
				pkgpubcomp.SetPacketId(uint16(clientPacketId >> 16))
				if _, err := this.conn.Write(pkgpubcomp.Bytes()); err != nil {
					log.Println(err.Error())
				} else {
					log.Println("SENT PUBCOMP")
				}
			}
		case PACKET_PUBACK:
			serverPacketId := uint32(pkt.(PacketPuback).GetPacketId())
			if _, ok := this.PacketIds[serverPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubAck PacketId %x Received\n", serverPacketId), false)
			} else {
				delete(this.PacketIds, serverPacketId)
			}
		case PACKET_PUBREC:
			serverPacketId := uint32(pkt.(PacketPuback).GetPacketId())
			if _, ok := this.PacketIds[serverPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubRec PacketId %x Received\n", serverPacketId), false)
			} else {
				pkgpubrel := NewPacketAcks(PACKET_PUBREL)
				pkgpubrel.SetPacketId(uint16(serverPacketId))
				if _, err := this.conn.Write(pkgpubrel.Bytes()); err != nil {
					log.Println(err.Error())
				} else {
					log.Println("SENT PUBREL")
				}
			}
		case PACKET_PUBCOMP:
			serverPacketId := uint32(pkt.(PacketPuback).GetPacketId())
			if _, ok := this.PacketIds[serverPacketId]; !ok {
				return this.ProcessTerminate(fmt.Sprintf("Invalid PubComp PacketId %x Received\n", serverPacketId), false)
			} else {
				delete(this.PacketIds, serverPacketId)
			}
		case PACKET_DISCONNECT:
			return this.ProcessTerminate("DISCONNECT Packet Received\n", true)
		default:
			return this.ProcessTerminate(fmt.Sprintf("Unexpected %s Packet Received\n", PACKET_TYPE_STRINGS[pkt.GetType()]), false)
		}
	case SESSION_STATE_TERMINATED:
	default:
	}

	return nil
}

func (this *serverSession) ProcessConnect(pkgconn PacketConnect) Event {
	if pkgconn.GetProtocolLevel() != 0x04 {
		pkgconnack := NewPacketConnack()
		pkgconnack.SetSPFlag(false)
		pkgconnack.SetReturnCode(CONNACK_RETURNCODE_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION)
		if _, err := this.conn.Write(pkgconnack.Bytes()); err != nil {
			log.Println(err.Error())
		} else {
			log.Println("SENT CONNACK")
		}

		this.state = SESSION_STATE_TERMINATED
		this.err = fmt.Errorf("Invalid %x Control Packet Protocol Level %x\n", pkgconn.GetType(), pkgconn.GetProtocolLevel())
		return newEventSessionTerminated(this, this.Error(), nil)
	} else {
		this.keepAlive = pkgconn.GetKeepAlive()
		this.clientId = pkgconn.GetClientId()

		connectFlags := pkgconn.GetConnectFlags()
		willTopic := pkgconn.GetWillTopic()
		willMessage := pkgconn.GetWillMessage()

		if (connectFlags & CONNECT_FLAG_WILL_FLAG) != 0 {
			var retain bool
			if (connectFlags & CONNECT_FLAG_WILL_RETAIN) != 0 {
				retain = true
			} else {
				retain = false
			}
			this.will = NewMessage(false,
				QOS((connectFlags&(CONNECT_FLAG_WILL_QOS_BIT3|CONNECT_FLAG_WILL_QOS_BIT4))>>3),
				retain,
				willTopic,
				willMessage)
			this.will.SetClientId(this.clientId)
		} else {
			this.will = nil
		}

		return newEventConnect(this, pkgconn)
	}
}

func (this *serverSession) ProcessPublish(pktpub PacketPublish) Event {
	pktpub.GetMessage().SetClientId(this.clientId)
	qos := pktpub.GetMessage().GetQos()
	if qos == QOS_TWO {
		clientPacketId := pktpub.GetPacketId()
		this.PacketIds[uint32(clientPacketId)<<16] = clientPacketId
		pkgpubrec := NewPacketAcks(PACKET_PUBREC)
		pkgpubrec.SetPacketId(clientPacketId)
		if _, err := this.conn.Write(pkgpubrec.Bytes()); err != nil {
			log.Println(err.Error())
		} else {
			log.Println("SENT PUBREC")
		}
	} else if qos == QOS_ONE {
		pkgpuback := NewPacketAcks(PACKET_PUBACK)
		pkgpuback.SetPacketId(pktpub.GetPacketId())
		if _, err := this.conn.Write(pkgpuback.Bytes()); err != nil {
			log.Println(err.Error())
		} else {
			log.Println("SENT PUBACK")
		}
	}
	return newEventPublish(this, pktpub.GetMessage())
}

func (this *serverSession) ProcessSubscribe(pktsub PacketSubscribe) Event {
	this.topicsToBeAdded = make([]string, len(pktsub.GetSubscribeTopics()))
	copy(this.topicsToBeAdded, pktsub.GetSubscribeTopics())

	this.qosToBeAdded = make([]QOS, len(pktsub.GetQoSs()))
	copy(this.qosToBeAdded, pktsub.GetQoSs())

	return newEventSubscribe(this, pktsub.GetPacketId(), pktsub.GetSubscribeTopics(), pktsub.GetQoSs())
}

func (this *serverSession) ProcessUnsubscribe(pktunsub PacketUnsubscribe) Event {
	topics := pktunsub.GetUnsubscribeTopics()
	for i := 0; i < len(topics); i++ {
		delete(this.topics, topics[i])
		delete(this.qos, topics[i])
	}

	pkgsuback := NewPacketAcks(PACKET_UNSUBACK)
	pkgsuback.SetPacketId(pktunsub.GetPacketId())
	if _, err := this.conn.Write(pkgsuback.Bytes()); err != nil {
		log.Println(err.Error())
	} else {
		log.Println("SENT UNSUBACK")
	}

	return newEventUnsubscribe(this, topics)
}

func (this *serverSession) ProcessTerminate(msg string, disconnected bool) Event {
	if disconnected {
		this.will = nil
	}
	this.state = SESSION_STATE_TERMINATED
	this.err = errors.New(msg)
	return newEventSessionTerminated(this, msg, this.will)
}

func (this *serverSession) Will() Message {
	return this.will
}
<|endoftext|>


<commit_before>package main

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

	"github.com/byuoitav/device-monitoring-microservice/statusinfrastructure"
	"github.com/byuoitav/event-router-microservice/eventinfrastructure"
	"github.com/byuoitav/touchpanel-ui-microservice/events"
	"github.com/byuoitav/touchpanel-ui-microservice/handlers"
	"github.com/byuoitav/touchpanel-ui-microservice/socket"
	"github.com/byuoitav/touchpanel-ui-microservice/uiconfig"
	"github.com/jessemillar/health"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func main() {
	filters := []string{eventinfrastructure.UI, eventinfrastructure.UIFeature}
	en := eventinfrastructure.NewEventNode("Touchpanel UI", filters, os.Getenv("EVENT_ROUTER_ADDRESS"))

	// websocket hub
	hub := socket.NewHub()
	go events.WriteEventsToSocket(en, hub)
	go events.SendRefresh(hub, time.NewTimer(time.Second*10))

	port := ":8888"
	router := echo.New()
	router.Pre(middleware.RemoveTrailingSlash())
	router.Use(middleware.CORS())

	router.GET("/health", echo.WrapHandler(http.HandlerFunc(health.Check)))
	router.GET("/mstatus", GetStatus)

	// event endpoints
	router.POST("/publish", handlers.PublishEvent, BindEventNode(en))
	router.POST("/publishfeature", handlers.PublishFeature, BindEventNode(en))

	// websocket
	router.GET("/websocket", func(context echo.Context) error {
		socket.ServeWebsocket(hub, context.Response().Writer, context.Request())
		return nil
	})

	// socket endpoints
	router.PUT("/screenoff", func(context echo.Context) error {
		events.SendScreenTimeout(hub)
		return nil
	})
	router.PUT("/refresh", func(context echo.Context) error {
		events.SendRefresh(hub, time.NewTimer(0))
		return nil
	})
	router.GET("/wsinfo", func(context echo.Context) error {
		si, _ := socket.GetSocketInfo(hub)
		return context.JSON(http.StatusOK, si)
	})
	router.PUT("/socketTest", func(context echo.Context) error {
		events.SendTest(hub)
		return context.JSON(http.StatusOK, "sent")
	})

	router.GET("/pihostname", handlers.GetPiHostname)
	router.GET("/hostname", handlers.GetHostname)
	router.GET("/deviceinfo", handlers.GetDeviceInfo)
	router.GET("/reboot", handlers.Reboot)
	router.GET("/dockerstatus", handlers.GetDockerStatus)

	router.GET("/uiconfig", uiconfig.GetUIConfig)
	router.GET("/uipath", uiconfig.GetUIPath)
	router.GET("/api", uiconfig.GetAPI)
	router.GET("/nextapi", uiconfig.NextAPI)

	router.POST("/help", handlers.Help)
	router.POST("/confirmhelp", handlers.ConfirmHelp)
	router.POST("/cancelhelp", handlers.CancelHelp)

	// all the different ui's
	router.Static("/", "redirect.html")
	router.Any("/404", redirect)
	router.Static("/blueberry", "blueberry")

	router.Start(port)
}

func BindEventNode(en *eventinfrastructure.EventNode) echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			c.Set(eventinfrastructure.ContextEventNode, en)
			return next(c)
		}
	}
}

func GetStatus(context echo.Context) error {
	var s statusinfrastructure.Status
	var err error

	s.Version, err = statusinfrastructure.GetVersion("version.txt")
	if err != nil {
		s.Version = "missing"
		s.Status = statusinfrastructure.StatusSick
		s.StatusInfo = fmt.Sprintf("Error: %s", err.Error())
	} else {
		s.Status = statusinfrastructure.StatusOK
		s.StatusInfo = ""
	}

	return context.JSON(http.StatusOK, s)
}

func redirect(context echo.Context) error {
	http.Redirect(context.Response().Writer, context.Request(), "http://github.com/404", 302)
	return nil
}
<commit_msg>server correct folder<commit_after>package main

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

	"github.com/byuoitav/device-monitoring-microservice/statusinfrastructure"
	"github.com/byuoitav/event-router-microservice/eventinfrastructure"
	"github.com/byuoitav/touchpanel-ui-microservice/events"
	"github.com/byuoitav/touchpanel-ui-microservice/handlers"
	"github.com/byuoitav/touchpanel-ui-microservice/socket"
	"github.com/byuoitav/touchpanel-ui-microservice/uiconfig"
	"github.com/jessemillar/health"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func main() {
	filters := []string{eventinfrastructure.UI, eventinfrastructure.UIFeature}
	en := eventinfrastructure.NewEventNode("Touchpanel UI", filters, os.Getenv("EVENT_ROUTER_ADDRESS"))

	// websocket hub
	hub := socket.NewHub()
	go events.WriteEventsToSocket(en, hub)
	go events.SendRefresh(hub, time.NewTimer(time.Second*10))

	port := ":8888"
	router := echo.New()
	router.Pre(middleware.RemoveTrailingSlash())
	router.Use(middleware.CORS())

	router.GET("/health", echo.WrapHandler(http.HandlerFunc(health.Check)))
	router.GET("/mstatus", GetStatus)

	// event endpoints
	router.POST("/publish", handlers.PublishEvent, BindEventNode(en))
	router.POST("/publishfeature", handlers.PublishFeature, BindEventNode(en))

	// websocket
	router.GET("/websocket", func(context echo.Context) error {
		socket.ServeWebsocket(hub, context.Response().Writer, context.Request())
		return nil
	})

	// socket endpoints
	router.PUT("/screenoff", func(context echo.Context) error {
		events.SendScreenTimeout(hub)
		return nil
	})
	router.PUT("/refresh", func(context echo.Context) error {
		events.SendRefresh(hub, time.NewTimer(0))
		return nil
	})
	router.GET("/wsinfo", func(context echo.Context) error {
		si, _ := socket.GetSocketInfo(hub)
		return context.JSON(http.StatusOK, si)
	})
	router.PUT("/socketTest", func(context echo.Context) error {
		events.SendTest(hub)
		return context.JSON(http.StatusOK, "sent")
	})

	router.GET("/pihostname", handlers.GetPiHostname)
	router.GET("/hostname", handlers.GetHostname)
	router.GET("/deviceinfo", handlers.GetDeviceInfo)
	router.GET("/reboot", handlers.Reboot)
	router.GET("/dockerstatus", handlers.GetDockerStatus)

	router.GET("/uiconfig", uiconfig.GetUIConfig)
	router.GET("/uipath", uiconfig.GetUIPath)
	router.GET("/api", uiconfig.GetAPI)
	router.GET("/nextapi", uiconfig.NextAPI)

	router.POST("/help", handlers.Help)
	router.POST("/confirmhelp", handlers.ConfirmHelp)
	router.POST("/cancelhelp", handlers.CancelHelp)

	// all the different ui's
	router.Static("/", "redirect.html")
	router.Any("/404", redirect)
	router.Static("/blueberry", "blueberry-dist")

	router.Start(port)
}

func BindEventNode(en *eventinfrastructure.EventNode) echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			c.Set(eventinfrastructure.ContextEventNode, en)
			return next(c)
		}
	}
}

func GetStatus(context echo.Context) error {
	var s statusinfrastructure.Status
	var err error

	s.Version, err = statusinfrastructure.GetVersion("version.txt")
	if err != nil {
		s.Version = "missing"
		s.Status = statusinfrastructure.StatusSick
		s.StatusInfo = fmt.Sprintf("Error: %s", err.Error())
	} else {
		s.Status = statusinfrastructure.StatusOK
		s.StatusInfo = ""
	}

	return context.JSON(http.StatusOK, s)
}

func redirect(context echo.Context) error {
	http.Redirect(context.Response().Writer, context.Request(), "http://github.com/404", 302)
	return nil
}
<|endoftext|>


<commit_before>package dockerapiproxy

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"

	rancher "github.com/rancher/go-rancher/client"
	"github.com/rancher/go-rancher/hostaccess"
)

type IoOps interface {
	io.Writer
	Read() ([]byte, error)
}

type Proxy struct {
	client       *rancher.RancherClient
	host, listen string
	TlsConfig    *tls.Config
}

func NewProxy(client *rancher.RancherClient, host, listen string) *Proxy {
	return &Proxy{
		client: client,
		host:   host,
		listen: listen,
	}
}

func (p *Proxy) getSocket(url string) (net.Listener, error) {
	proto := "tcp"
	address := url

	parts := strings.SplitN(url, "://", 2)
	if len(parts) == 2 {
		proto = parts[0]
		address = parts[1]
	}

	l, err := net.Listen(proto, address)
	if err != nil {
		return nil, err
	}

	if proto == "tcp" && p.TlsConfig != nil {
		p.TlsConfig.NextProtos = []string{"http/1.1"}
		l = tls.NewListener(l, p.TlsConfig)
	}

	return l, err
}

func (p *Proxy) ListenAndServe() error {
	host, err := p.getHost()
	if err != nil {
		return err
	}

	os.Remove(p.listen)

	l, err := p.getSocket(p.listen)
	if err != nil {
		return err
	}

	logrus.Infof("Found host: %v", host.Name)

	for {
		conn, err := l.Accept()
		if err != nil {
			return err
		}

		logrus.Debug("New connection")
		go p.handle(host, conn)
	}
}

func (p *Proxy) handle(host *rancher.Host, client net.Conn) {
	if err := p.handleError(host, client); err != nil {
		logrus.Errorf("Failed to handle connection: %v", err)
	}
}

func (p *Proxy) handleError(host *rancher.Host, conn net.Conn) error {
	defer conn.Close()

	hostAccessClient := hostaccess.RancherWebsocketClient(*p.client)
	websocket, err := hostAccessClient.GetHostAccess(host.Resource, "dockersocket", nil)
	if err != nil {
		return err
	}

	server := &WebSocketIo{Conn: websocket}
	client := &SocketIo{Conn: conn}

	wg := sync.WaitGroup{}
	wg.Add(2)

	abort := func() {
		wg.Done()
		conn.Close()
		websocket.Close()
	}

	go func() {
		defer abort()
		p.copyLoop(client, server)
	}()

	go func() {
		defer abort()
		p.copyLoop(server, client)
	}()

	wg.Wait()

	logrus.Debugf("Disconnecting")

	return nil
}

func (p *Proxy) copyLoop(from, to IoOps) error {
	con := true

	for con {
		buf, err := from.Read()
		if err != nil {
			return err
		}
		//if err != nil {
		//	if err == io.EOF {
		//		con = false
		//	} else {
		//		return err
		//	}
		//}
		logrus.Debugf("Read %d bytes", len(buf))
		if _, err := to.Write(buf); err != nil {
			return err
		}
		logrus.Debugf("Wrote %d bytes", len(buf))
	}

	return nil
}

func (p *Proxy) getHost() (*rancher.Host, error) {
	host, err := p.client.Host.ById(p.host)
	if err != nil {
		return nil, err
	}

	if host != nil {
		return host, nil
	}

	hosts, err := p.client.Host.List(&rancher.ListOpts{
		Filters: map[string]interface{}{
			"name": p.host,
		},
	})
	if err != nil {
		return nil, err
	}

	if len(hosts.Data) == 0 {
		return nil, fmt.Errorf("Failed to find host: %s", p.host)
	}

	return &hosts.Data[0], nil
}
<commit_msg>Fixes<commit_after>package dockerapiproxy

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"

	rancher "github.com/rancher/go-rancher/client"
	"github.com/rancher/go-rancher/hostaccess"
)

type IoOps interface {
	io.Writer
	Read() ([]byte, error)
}

type Proxy struct {
	client       *rancher.RancherClient
	host, listen string
	TlsConfig    *tls.Config
	listener     net.Listener
}

func NewProxy(client *rancher.RancherClient, host, listen string) *Proxy {
	return &Proxy{
		client: client,
		host:   host,
		listen: listen,
	}
}

func (p *Proxy) Close() error {
	return p.listener.Close()
}

func (p *Proxy) getSocket(url string) (net.Listener, error) {
	proto := "tcp"
	address := url

	parts := strings.SplitN(url, "://", 2)
	if len(parts) == 2 {
		proto = parts[0]
		address = parts[1]
	}

	l, err := net.Listen(proto, address)
	if err != nil {
		return nil, err
	}

	if proto == "tcp" && p.TlsConfig != nil {
		p.TlsConfig.NextProtos = []string{"http/1.1"}
		l = tls.NewListener(l, p.TlsConfig)
	}

	return l, err
}

func (p *Proxy) ListenAndServe() error {
	host, err := p.getHost()
	if err != nil {
		return err
	}

	os.Remove(p.listen)

	l, err := p.getSocket(p.listen)
	if err != nil {
		return err
	}
	p.listener = l

	logrus.Infof("Found host: %v", host.Name)

	for {
		conn, err := l.Accept()
		if err != nil {
			return err
		}

		logrus.Debug("New connection")
		go p.handle(host, conn)
	}
}

func (p *Proxy) handle(host *rancher.Host, client net.Conn) {
	if err := p.handleError(host, client); err != nil {
		logrus.Errorf("Failed to handle connection: %v", err)
	}
}

func (p *Proxy) handleError(host *rancher.Host, conn net.Conn) error {
	defer conn.Close()

	hostAccessClient := hostaccess.RancherWebsocketClient(*p.client)
	websocket, err := hostAccessClient.GetHostAccess(host.Resource, "dockersocket", nil)
	if err != nil {
		return err
	}

	server := &WebSocketIo{Conn: websocket}
	client := &SocketIo{Conn: conn}

	wg := sync.WaitGroup{}
	wg.Add(2)

	abort := func() {
		wg.Done()
		conn.Close()
		websocket.Close()
	}

	go func() {
		defer abort()
		p.copyLoop(client, server)
	}()

	go func() {
		defer abort()
		p.copyLoop(server, client)
	}()

	wg.Wait()

	logrus.Debugf("Disconnecting")

	return nil
}

func (p *Proxy) copyLoop(from, to IoOps) error {
	con := true

	for con {
		buf, err := from.Read()
		if err != nil {
			return err
		}
		logrus.Debugf("Read %d bytes", len(buf))
		if _, err := to.Write(buf); err != nil {
			return err
		}
		logrus.Debugf("Wrote %d bytes", len(buf))
	}

	return nil
}

func (p *Proxy) getHost() (*rancher.Host, error) {
	host, err := p.client.Host.ById(p.host)
	if err != nil {
		return nil, err
	}

	if host != nil {
		return host, nil
	}

	hosts, err := p.client.Host.List(&rancher.ListOpts{
		Filters: map[string]interface{}{
			"uuid": p.host,
		},
	})
	if err != nil {
		return nil, err
	}

	if len(hosts.Data) == 0 {
		hosts, err = p.client.Host.List(&rancher.ListOpts{
			Filters: map[string]interface{}{
				"name": p.host,
			},
		})
		if err != nil {
			return nil, err
		}
	}

	if len(hosts.Data) == 0 {
		return nil, fmt.Errorf("Failed to find host: %s", p.host)
	}

	return &hosts.Data[0], nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/j-keck/arping"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"time"
)

var leases DHCPLeases

func server() {
	log.Println("startup -  version: ", VERSION)

	if *verboseFlag {
		verboseLog = log.New(os.Stdout, "", log.LstdFlags)
	} else {
		verboseLog = log.New(ioutil.Discard, "", 0)
	}

	//
	// init listeners
	//
	dhcpLeaseChan := make(chan *DHCPLease)
	go dhcpListener(dhcpLeaseChan)

	clientChan := make(chan []byte)
	go clientListener(clientChan)

	//
	// init cleanup old leases routine
	//
	var clearExpiredLeasesTickerChan <-chan time.Time
	var clearOfflineHostsTickerChan <-chan time.Time

	if !*expireBasedFlag {
		log.Printf("enable active check - arping every: %s\n", *cleanupLeaseTimerFlag)
		if hasPermission, err := hasRawSocketPermission(); hasPermission {
			clearOfflineHostsTickerChan = time.NewTicker(cleanupLeaseTimer).C
			// clear one time on startup
			clearOfflineHosts()
		} else {
			log.Printf("enable active check failed: '%s' -  fallback to passive mode\n", err)
			*expireBasedFlag = true
		}
	}

	if *expireBasedFlag {
		timer := cleanupLeaseTimer
		if leaseExpiredDuration < timer {
			timer = leaseExpiredDuration / 2
		}
		log.Printf("enable expired based - check timer: %s, expire duation: %s\n", timer, leaseExpiredDuration)
		clearExpiredLeasesTickerChan = time.NewTicker(timer).C
		// clear on time on startup
		clearExpiredLeases()
	}

	//
	// if persistent leases are enable, load saved leases
	//
	if *keepLeasesOverRestartFlag {
		var err error
		leases, err = loadLeases()
		logOnError(err, "unable to load leases - start with emtpy leases")
	}

	//
	// init CTRL-C - catcher
	//
	go func() {
		sigchan := make(chan os.Signal, 1)
		signal.Notify(sigchan, os.Interrupt)
		// block
		<-sigchan
		shutdown()
	}()

	//
	// main loop
	//
	for {
		select {
		case cmd := <-clientChan:
			switch string(cmd) {
			case "shutdown":
				shutdown()

			case "clearLeases":
				log.Println("clear leases")
				leases.Clear()
				clientChan <- []byte("done")

			case "leases":
				j, err := json.Marshal(leases)
				panicOnError(err)
				clientChan <- j

			case "version":
				clientChan <- []byte(VERSION)

			case "mode":
				if *expireBasedFlag {
					clientChan <- []byte("passive")
				} else {
					clientChan <- []byte("active")
				}
			}

		case l := <-dhcpLeaseChan:
			log.Printf("new DHCP Lease: '%s'\n", l.String())
			leases.UpdateOrAdd(l)

		case <-clearExpiredLeasesTickerChan:
			clearExpiredLeases()

		case <-clearOfflineHostsTickerChan:
			clearOfflineHosts()
		}
	}
}

func shutdown() {
	log.Println("save leases")
	logOnError(saveLeases(leases), "unable to save leases")

	log.Println("shutdown")
	closeListener()
	os.Exit(0)
}

func clearExpiredLeases() {
	verboseLog.Println("check expired leases")
	leases.Foreach(func(l *DHCPLease) {
		if time.Now().After(l.Expire) {
			log.Printf("expired: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}
func clearOfflineHosts() {
	verboseLog.Println("arping hosts")
	pingHosts(&leases)

	leases.Foreach(func(l *DHCPLease) {
		if l.MissedPings > *missedPingsThresholdFlag {
			log.Printf("remove lease: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}

func pingHosts(leases *DHCPLeases) {
	leases.Foreach(func(l *DHCPLease) {
		if _, _, err := arping.Ping(net.ParseIP(l.IP)); err == arping.ErrTimeout {
			l.MissedPings++
			verboseLog.Printf("%s is offline\n", l.String())
		} else if err != nil {
			log.Printf("unable to execute ping: '%s'\n", err.Error())
		} else {
			verboseLog.Printf("%s is online", l.String())
			l.MissedPings = 0
		}
	})
}
func hasRawSocketPermission() (bool, error) {
	var localIP net.IP

	// find any local ip
	addrs, err := net.InterfaceAddrs()
	panicOnError(err)

	for _, addr := range addrs {
		if ipNet, ok := addr.(*net.IPNet); ok {
			if ipNet.IP.IsLoopback() {
				continue
			}

			localIP = ipNet.IP
			break
		}
	}

	// arping any local ip results always in timeout
	verboseLog.Printf("arping '%s' to check permission\n", localIP.String())
	if _, _, err = arping.Ping(localIP); err == arping.ErrTimeout {
		return true, nil
	}

	return false, err
}

func saveLeases(leases DHCPLeases) error {
	j, err := json.Marshal(leases)
	if err != nil {
		return err
	}

	path := leasesPersistenceFilePath()

	verboseLog.Printf("save leases under %s\n", path)
	return ioutil.WriteFile(path, []byte(j), 0644)
}

func loadLeases() (DHCPLeases, error) {
	var leases DHCPLeases

	path := leasesPersistenceFilePath()

	verboseLog.Printf("load saved leases from %s\n", path)

	if _, err := os.Stat(path); os.IsNotExist(err) {
		return leases, fmt.Errorf("no persistence file found under %s\n", path)
	}

	b, err := ioutil.ReadFile(path)
	if err != nil {
		return leases, err
	}

	err = json.Unmarshal(b, &leases)
	return leases, err
}

func leasesPersistenceFilePath() string {

	var basePath string
	if runtime.GOOS == "windows" {
		basePath = os.Getenv("APPDATA")
	} else {
		basePath = "/var/lib/lsleases"
	}
	return fmt.Sprintf("%s/lsleases.json", basePath)
}
<commit_msg>don't pass 'leases' to function - use from outer scope<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/j-keck/arping"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"time"
)

var leases DHCPLeases

func server() {
	log.Println("startup -  version: ", VERSION)

	if *verboseFlag {
		verboseLog = log.New(os.Stdout, "", log.LstdFlags)
	} else {
		verboseLog = log.New(ioutil.Discard, "", 0)
	}

	//
	// init listeners
	//
	dhcpLeaseChan := make(chan *DHCPLease)
	go dhcpListener(dhcpLeaseChan)

	clientChan := make(chan []byte)
	go clientListener(clientChan)

	//
	// init cleanup old leases routine
	//
	var clearExpiredLeasesTickerChan <-chan time.Time
	var clearOfflineHostsTickerChan <-chan time.Time

	if !*expireBasedFlag {
		log.Printf("enable active check - arping every: %s\n", *cleanupLeaseTimerFlag)
		if hasPermission, err := hasRawSocketPermission(); hasPermission {
			clearOfflineHostsTickerChan = time.NewTicker(cleanupLeaseTimer).C
			// clear one time on startup
			clearOfflineHosts()
		} else {
			log.Printf("enable active check failed: '%s' -  fallback to passive mode\n", err)
			*expireBasedFlag = true
		}
	}

	if *expireBasedFlag {
		timer := cleanupLeaseTimer
		if leaseExpiredDuration < timer {
			timer = leaseExpiredDuration / 2
		}
		log.Printf("enable expired based - check timer: %s, expire duation: %s\n", timer, leaseExpiredDuration)
		clearExpiredLeasesTickerChan = time.NewTicker(timer).C
		// clear on time on startup
		clearExpiredLeases()
	}

	//
	// if persistent leases are enable, load saved leases
	//
	if *keepLeasesOverRestartFlag {
		var err error
		leases, err = loadLeases()
		logOnError(err, "unable to load leases - start with emtpy leases")
	}

	//
	// init CTRL-C - catcher
	//
	go func() {
		sigchan := make(chan os.Signal, 1)
		signal.Notify(sigchan, os.Interrupt)
		// block
		<-sigchan
		shutdown()
	}()

	//
	// main loop
	//
	for {
		select {
		case cmd := <-clientChan:
			switch string(cmd) {
			case "shutdown":
				shutdown()

			case "clearLeases":
				log.Println("clear leases")
				leases.Clear()
				clientChan <- []byte("done")

			case "leases":
				j, err := json.Marshal(leases)
				panicOnError(err)
				clientChan <- j

			case "version":
				clientChan <- []byte(VERSION)

			case "mode":
				if *expireBasedFlag {
					clientChan <- []byte("passive")
				} else {
					clientChan <- []byte("active")
				}
			}

		case l := <-dhcpLeaseChan:
			log.Printf("new DHCP Lease: '%s'\n", l.String())
			leases.UpdateOrAdd(l)

		case <-clearExpiredLeasesTickerChan:
			clearExpiredLeases()

		case <-clearOfflineHostsTickerChan:
			clearOfflineHosts()
		}
	}
}

func shutdown() {
	log.Println("save leases")
	logOnError(saveLeases(), "unable to save leases")

	log.Println("shutdown")
	closeListener()
	os.Exit(0)
}

func clearExpiredLeases() {
	verboseLog.Println("check expired leases")
	leases.Foreach(func(l *DHCPLease) {
		if time.Now().After(l.Expire) {
			log.Printf("expired: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}
func clearOfflineHosts() {
	verboseLog.Println("arping hosts")
	pingHosts()

	leases.Foreach(func(l *DHCPLease) {
		if l.MissedPings > *missedPingsThresholdFlag {
			log.Printf("remove lease: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}

func pingHosts() {
	leases.Foreach(func(l *DHCPLease) {
		if _, _, err := arping.Ping(net.ParseIP(l.IP)); err == arping.ErrTimeout {
			l.MissedPings++
			verboseLog.Printf("%s is offline\n", l.String())
		} else if err != nil {
			log.Printf("unable to execute ping: '%s'\n", err.Error())
		} else {
			verboseLog.Printf("%s is online", l.String())
			l.MissedPings = 0
		}
	})
}
func hasRawSocketPermission() (bool, error) {
	var localIP net.IP

	// find any local ip
	addrs, err := net.InterfaceAddrs()
	panicOnError(err)

	for _, addr := range addrs {
		if ipNet, ok := addr.(*net.IPNet); ok {
			if ipNet.IP.IsLoopback() {
				continue
			}

			localIP = ipNet.IP
			break
		}
	}

	// arping any local ip results always in timeout
	verboseLog.Printf("arping '%s' to check permission\n", localIP.String())
	if _, _, err = arping.Ping(localIP); err == arping.ErrTimeout {
		return true, nil
	}

	return false, err
}

func saveLeases() error {
	j, err := json.Marshal(leases)
	if err != nil {
		return err
	}

	path := leasesPersistenceFilePath()

	verboseLog.Printf("save leases under %s\n", path)
	return ioutil.WriteFile(path, []byte(j), 0644)
}

func loadLeases() (DHCPLeases, error) {
	var leases DHCPLeases

	path := leasesPersistenceFilePath()

	verboseLog.Printf("load saved leases from %s\n", path)

	if _, err := os.Stat(path); os.IsNotExist(err) {
		return leases, fmt.Errorf("no persistence file found under %s\n", path)
	}

	b, err := ioutil.ReadFile(path)
	if err != nil {
		return leases, err
	}

	err = json.Unmarshal(b, &leases)
	return leases, err
}

func leasesPersistenceFilePath() string {

	var basePath string
	if runtime.GOOS == "windows" {
		basePath = os.Getenv("APPDATA")
	} else {
		basePath = "/var/lib/lsleases"
	}
	return fmt.Sprintf("%s/lsleases.json", basePath)
}
<|endoftext|>


<commit_before>// Filen for å eksperimentere med webserver i ICA03 (IS-105)
// Starte server med
// 			go run server.go
// Du kan ha tilgang til server fra nettleser med
// 			http://localhost:3000
//
package main

import (
	"net/http"
	"time"
	//"fmt"
)

func main() {
	http.HandleFunc("/", foo)
	http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
	// Her kan man bl. a. skrive data som skal sendes til nettleser
	// Neste linje kan brukes for å endre koding av sekvensen som sendes til nettleser
	// Standardinstilling er UTF-8
	//w.Header().Set("Content-Type", "text/html;charset=ISO-8859-1")

	// Her skriver man data som er respons til brukeren som har skrevet
	// http://localhost:3000 i sin nettleser
	w.Write([]byte("Hvordan g\u00E5r det, \u16a6 ?
"))
	w.Write([]byte("\u23f0 - Thurs
"))
	w.Write([]byte(time.Now().Format(time.RFC850)))
}<commit_msg>Endret litt på server koden<commit_after>package main

import (
	"net/http"
	"time"

)

func main() {
	http.HandleFunc("/", foo)
	http.ListenAndServe(":3000", nil)
}

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

	w.Write([]byte("Hvordan g\u00E5r det?
"))
	w.Write([]byte(time.Now().Format(time.RFC850)))
	w.Write([]byte("\u23f0"))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/codegangsta/martini"
	"github.com/codegangsta/martini-contrib/render"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
)

const version = "0.1.1"

func printDocument(res render.Render, req *http.Request, w http.ResponseWriter) {
	fetchDocument := func(url string) *os.File {
		os.Mkdir("./tmp", 0700)

		out, _ := ioutil.TempFile("tmp", "document")
		defer out.Close()

		resp, _ := http.Get(url)
		defer resp.Body.Close()

		io.Copy(out, resp.Body)

		return out
	}

	removeDocument := func(name string) error {
		return os.Remove(name)
	}

	sendToPrinter := func(url string, printer string, orientation string, media string) ([]string, string) {
		document := fetchDocument(url)
		defer removeDocument(document.Name())

		var printOptions []string

		if printer != "" {
			printOptions = append(printOptions, "-P", printer)
		}
		if orientation != "" {
			printOptions = append(printOptions, "-o", orientation)
		}
		if media != "" {
			printOptions = append(printOptions, "-o", fmt.Sprintf("media=%s", media))
		}
		printOptions = append(printOptions, document.Name())

		cmd := exec.Command("lpr", printOptions...)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		return printOptions, out.String()
	}

	qs := req.URL.Query()
	printer, orientation, media, url := qs.Get("printer"), qs.Get("orientation"), qs.Get("media"), qs.Get("path")

	go sendToPrinter(url, printer, orientation, media)

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"status": "scheduled",
	})
}

func cancelAll(res render.Render, req *http.Request, w http.ResponseWriter) {
	qs := req.URL.Query()
	printer := qs.Get("printer")

	cmd := exec.Command("cancel", "-a", "-x", printer)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Run()

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"success": true,
		"printer": printer,
	})
}

func currentUser() (s string) {
	u, _ := user.Current()
	return u.Username
}

func main() {
	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(martini.Logger())

	m.Get("/", func() string {
		return "
Receipt Printer
"
	})

	m.Get("/cache/clear", func(res render.Render, w http.ResponseWriter) {
		os.RemoveAll(filepath.Join("./tmp/"))
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true})
	})

	m.Get("/print", printDocument)

	m.Get("/cancel/all", cancelAll)

	m.Get("/self_update", func(res render.Render, w http.ResponseWriter) {
		cmd := exec.Command("./update")
		cmd.Run()
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	m.Get("/status", func(res render.Render, w http.ResponseWriter) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	if err := http.ListenAndServeTLS(":9631", "cert.pem", "key.pem", m); err != nil {
		log.Fatal(err)
	}

	m.Run()
}
<commit_msg>Support both http and https<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/codegangsta/martini"
	"github.com/codegangsta/martini-contrib/render"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
)

const version = "0.1.2"

func printDocument(res render.Render, req *http.Request, w http.ResponseWriter) {
	fetchDocument := func(url string) *os.File {
		os.Mkdir("./tmp", 0700)

		out, _ := ioutil.TempFile("tmp", "document")
		defer out.Close()

		resp, _ := http.Get(url)
		defer resp.Body.Close()

		io.Copy(out, resp.Body)

		return out
	}

	removeDocument := func(name string) error {
		return os.Remove(name)
	}

	sendToPrinter := func(url string, printer string, orientation string, media string) ([]string, string) {
		document := fetchDocument(url)
		defer removeDocument(document.Name())

		var printOptions []string

		if printer != "" {
			printOptions = append(printOptions, "-P", printer)
		}
		if orientation != "" {
			printOptions = append(printOptions, "-o", orientation)
		}
		if media != "" {
			printOptions = append(printOptions, "-o", fmt.Sprintf("media=%s", media))
		}
		printOptions = append(printOptions, document.Name())

		cmd := exec.Command("lpr", printOptions...)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		return printOptions, out.String()
	}

	qs := req.URL.Query()
	printer, orientation, media, url := qs.Get("printer"), qs.Get("orientation"), qs.Get("media"), qs.Get("path")

	go sendToPrinter(url, printer, orientation, media)

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"status": "scheduled",
	})
}

func cancelAll(res render.Render, req *http.Request, w http.ResponseWriter) {
	qs := req.URL.Query()
	printer := qs.Get("printer")

	cmd := exec.Command("cancel", "-a", "-x", printer)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Run()

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"success": true,
		"printer": printer,
	})
}

func currentUser() (s string) {
	u, _ := user.Current()
	return u.Username
}

func main() {
	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(martini.Logger())

	m.Get("/", func() string {
		return "
Receipt Printer
"
	})

	m.Get("/cache/clear", func(res render.Render, w http.ResponseWriter) {
		os.RemoveAll(filepath.Join("./tmp/"))
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true})
	})

	m.Get("/print", printDocument)

	m.Get("/cancel/all", cancelAll)

	m.Get("/self_update", func(res render.Render, w http.ResponseWriter) {
		cmd := exec.Command("./update")
		cmd.Run()
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	m.Get("/status", func(res render.Render, w http.ResponseWriter) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	go func() {
		if err := http.ListenAndServe(":5899", m); err != nil {
			log.Fatal(err)
		}
	}()

	if err := http.ListenAndServeTLS(":9631", "cert.pem", "key.pem", m); err != nil {
		log.Fatal(err)
	}

	m.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
	"strconv"
	"text/template"
)

// serverTmpl stores templates for the web interface.
var serverTmpl = new(template.Template)

func init() {
	serverTmpl.Funcs(template.FuncMap{
		"format": func(num int64, base int) string {
			return strconv.FormatInt(num, base)
		},

		"shortlen": func() string {
			return flags.short.String()
		},
	})

	template.Must(serverTmpl.New("main").Parse(`

		<title>{{.Title}} :: Main</title>

Loading...

Excluding short sessions:

Average session:

Calculated from  sessions.

					A session is short if it lasts less than {{shortlen}}.

Including short sessions:

Average session:

Calculated from  sessions.

Longest session:

Longest active session:

Shortest long session:

Shortest session:

				Currently tracking  active sessions.

				Tracker runtime:

`))
}

// logHandler returns an http.Handler that logs every request that
// gets sent to h.
func logHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		log.Printf("Got %q request for %q", req.Method, req.URL)

		h.ServeHTTP(rw, req)
	})
}

// tmplHandler returns a handler that serves the template t in
// serverTmpl.
func tmplHandler(t string) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		err := serverTmpl.ExecuteTemplate(rw, t, map[string]interface{}{
			"Req":   req,
			"Title": "PS2 Average Login Times",
		})
		if err != nil {
			log.Printf("Failed to execute %q: %v", t, err)
		}
	})
}

// serveSession serves the current session as JSON.
func serveSession(rw http.ResponseWriter, req *http.Request) {
	e := json.NewEncoder(rw)
	err := e.Encode(<-session)
	if err != nil {
		log.Printf("Failed to write session: %v", err)
	}
}

// serveJS serves the javascript for the web interface.
func serveJS(rw http.ResponseWriter, req *http.Request) {
	_, err := io.WriteString(rw, `$(document).ready(function() {
	var loading = $('#loading');
	var main = $('#main');

	var noshort = {
		"average": $('#noshort .average'),
		"num": $('#noshort .num'),
	};
	var total = {
		"average": $('#total .average'),
		"num": $('#total .num'),
	};

	var longest = $('#longest');
	var longestname = $('#longestname');
	var shortestlong = $('#shortestlong');
	var shortest = $('#shortest');

	var oldest = $('#oldest');
	var oldestname = $('#oldestname');

	var online = $('#online');
	var runtime = $('#runtime');

	var error = $('#error');

	function setFields(data)
	{
		loading.hide();
		main.show();

		noshort.average.html(data.noshort.cur);
		noshort.num.html(data.noshort.num);
		total.average.html(data.total.cur);
		total.num.html(data.total.num);

		longest.html(data.longest);
		longestname.html('(' + data.longestname + ')');
		shortestlong.html(data.shortestlong);
		shortest.html(data.shortest);

		oldest.html(data.oldest);
		oldestname.html('(' + data.oldestname + ')');

		online.html(data.numchars);
		runtime.html(data.runtime);

		if (data.err != undefined)
		{
			error.html('Error fetching data from Census API: ' + data.err);
			error.slideDown('fast');
		}
		else
		{
			error.slideUp('fast');
		}
	}

	function getSession()
	{
		$.getJSON('/session').done(setFields).fail(function() {
			error.html('Error connecting to ps2avglogin server.');
			error.slideDown('fast');
		}).always(function() {
			setTimeout(getSession, 30000);
		});
	};

	getSession();
});`)
	if err != nil {
		log.Printf("Failed to write JS: %v", err)
	}
}

// server runs the web interface.
func server() {
	http.Handle("/session", logHandler(http.HandlerFunc(serveSession)))
	http.Handle("/ps2avglogin.js", logHandler(http.HandlerFunc(serveJS)))
	http.Handle("/", logHandler(tmplHandler("main")))

	log.Printf("Starting server at %q...", flags.addr)
	err := http.ListenAndServe(flags.addr, nil)
	if err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}
}
<commit_msg>Increase width to 800px.<commit_after>package main

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
	"strconv"
	"text/template"
)

// serverTmpl stores templates for the web interface.
var serverTmpl = new(template.Template)

func init() {
	serverTmpl.Funcs(template.FuncMap{
		"format": func(num int64, base int) string {
			return strconv.FormatInt(num, base)
		},

		"shortlen": func() string {
			return flags.short.String()
		},
	})

	template.Must(serverTmpl.New("main").Parse(`

		<title>{{.Title}} :: Main</title>

Loading...

Excluding short sessions:

Average session:

Calculated from  sessions.

					A session is short if it lasts less than {{shortlen}}.

Including short sessions:

Average session:

Calculated from  sessions.

Longest session:

Longest active session:

Shortest long session:

Shortest session:

				Currently tracking  active sessions.

				Tracker runtime:

`))
}

// logHandler returns an http.Handler that logs every request that
// gets sent to h.
func logHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		log.Printf("Got %q request for %q", req.Method, req.URL)

		h.ServeHTTP(rw, req)
	})
}

// tmplHandler returns a handler that serves the template t in
// serverTmpl.
func tmplHandler(t string) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		err := serverTmpl.ExecuteTemplate(rw, t, map[string]interface{}{
			"Req":   req,
			"Title": "PS2 Average Login Times",
		})
		if err != nil {
			log.Printf("Failed to execute %q: %v", t, err)
		}
	})
}

// serveSession serves the current session as JSON.
func serveSession(rw http.ResponseWriter, req *http.Request) {
	e := json.NewEncoder(rw)
	err := e.Encode(<-session)
	if err != nil {
		log.Printf("Failed to write session: %v", err)
	}
}

// serveJS serves the javascript for the web interface.
func serveJS(rw http.ResponseWriter, req *http.Request) {
	_, err := io.WriteString(rw, `$(document).ready(function() {
	var loading = $('#loading');
	var main = $('#main');

	var noshort = {
		"average": $('#noshort .average'),
		"num": $('#noshort .num'),
	};
	var total = {
		"average": $('#total .average'),
		"num": $('#total .num'),
	};

	var longest = $('#longest');
	var longestname = $('#longestname');
	var shortestlong = $('#shortestlong');
	var shortest = $('#shortest');

	var oldest = $('#oldest');
	var oldestname = $('#oldestname');

	var online = $('#online');
	var runtime = $('#runtime');

	var error = $('#error');

	function setFields(data)
	{
		loading.hide();
		main.show();

		noshort.average.html(data.noshort.cur);
		noshort.num.html(data.noshort.num);
		total.average.html(data.total.cur);
		total.num.html(data.total.num);

		longest.html(data.longest);
		longestname.html('(' + data.longestname + ')');
		shortestlong.html(data.shortestlong);
		shortest.html(data.shortest);

		oldest.html(data.oldest);
		oldestname.html('(' + data.oldestname + ')');

		online.html(data.numchars);
		runtime.html(data.runtime);

		if (data.err != undefined)
		{
			error.html('Error fetching data from Census API: ' + data.err);
			error.slideDown('fast');
		}
		else
		{
			error.slideUp('fast');
		}
	}

	function getSession()
	{
		$.getJSON('/session').done(setFields).fail(function() {
			error.html('Error connecting to ps2avglogin server.');
			error.slideDown('fast');
		}).always(function() {
			setTimeout(getSession, 30000);
		});
	};

	getSession();
});`)
	if err != nil {
		log.Printf("Failed to write JS: %v", err)
	}
}

// server runs the web interface.
func server() {
	http.Handle("/session", logHandler(http.HandlerFunc(serveSession)))
	http.Handle("/ps2avglogin.js", logHandler(http.HandlerFunc(serveJS)))
	http.Handle("/", logHandler(tmplHandler("main")))

	log.Printf("Starting server at %q...", flags.addr)
	err := http.ListenAndServe(flags.addr, nil)
	if err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/UniversityRadioYork/2016-site/controllers"
	"github.com/UniversityRadioYork/2016-site/structs"
	"github.com/UniversityRadioYork/myradio-go"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
)

// Server is the type of the main 2016site web application.
type Server struct {
	*negroni.Negroni
}

// NewServer creates a 2016site server based on the config c.
func NewServer(c *structs.Config) (*Server, error) {

	s := Server{negroni.Classic()}

	session, err := myradio.NewSessionFromKeyFile()

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

	router := mux.NewRouter().StrictSlash(true)

	getRouter := router.Methods("GET").Subrouter()

	// Routes go in here
	nfc := controllers.NewNotFoundController(c)
	router.NotFoundHandler = http.HandlerFunc(nfc.Get)

	ic := controllers.NewIndexController(session, c)
	getRouter.HandleFunc("/", ic.Get)

	sc := controllers.NewSearchController(session, c)
	getRouter.HandleFunc("/search/", sc.Get)

	showC := controllers.NewShowController(session, c)
	//	getRouter.HandleFunc("/schedule/shows", showC.Get) // @TODO: Implement this
	getRouter.HandleFunc("/schedule/shows/{id:[0-9]+}/", showC.GetShow).Name("show")
	getRouter.HandleFunc("/schedule/shows/timeslots/{id:[0-9]+}/", showC.GetTimeslot).Name("timeslot")
	getRouter.HandleFunc("/schedule/shows/seasons/{id:[0-9]+}/", showC.GetSeason).Name("season")

	/* NOTE: NewScheduleWeekController assumes the following routes are
	   installed BEFORE it is called:

	   - 'timeslot' */
	schedWeekC := controllers.NewScheduleWeekController(session, getRouter, c)
	getRouter.HandleFunc("/schedule/thisweek/", schedWeekC.GetThisWeek).Name("schedule-thisweek")
	getRouter.HandleFunc("/schedule/{year:[1-9][0-9][0-9][0-9]}/w{week:[1-5]?[0-9]}/", schedWeekC.GetByYearWeek).Name("schedule-week")

	pc := controllers.NewPeopleController(session, c)
	getRouter.HandleFunc("/people/{id:[0-9]+}/", pc.Get)

	staticC := controllers.NewStaticController(c)
	getRouter.HandleFunc("/about/", staticC.GetAbout)
	getRouter.HandleFunc("/contact/", staticC.GetContact)
	getRouter.HandleFunc("/getinvolved/", staticC.GetInvolved)
	getRouter.HandleFunc("/competitions/", staticC.GetCompetitions)

	// End routes

	s.UseHandler(router)

	return &s, nil

}
<commit_msg>Add route compatibility<commit_after>package main

import (
	"net/http"

	"github.com/UniversityRadioYork/2016-site/controllers"
	"github.com/UniversityRadioYork/2016-site/structs"
	"github.com/UniversityRadioYork/myradio-go"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
)

// Server is the type of the main 2016site web application.
type Server struct {
	*negroni.Negroni
}

// NewServer creates a 2016site server based on the config c.
func NewServer(c *structs.Config) (*Server, error) {

	s := Server{negroni.Classic()}

	session, err := myradio.NewSessionFromKeyFile()

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

	router := mux.NewRouter().StrictSlash(true)

	getRouter := router.Methods("GET").Subrouter()

	// Routes go in here
	nfc := controllers.NewNotFoundController(c)
	router.NotFoundHandler = http.HandlerFunc(nfc.Get)

	ic := controllers.NewIndexController(session, c)
	getRouter.HandleFunc("/", ic.Get)

	sc := controllers.NewSearchController(session, c)
	getRouter.HandleFunc("/search/", sc.Get)

	showC := controllers.NewShowController(session, c)
	//	getRouter.HandleFunc("/schedule/shows", showC.Get) // @TODO: Implement this
	getRouter.HandleFunc("/schedule/shows/{id:[0-9]+}/", showC.GetShow).Name("show")
	getRouter.HandleFunc("/schedule/shows/timeslots/{id:[0-9]+}/", showC.GetTimeslot).Name("timeslot")
	getRouter.HandleFunc("/schedule/shows/seasons/{id:[0-9]+}/", showC.GetSeason).Name("season")

	/* NOTE: NewScheduleWeekController assumes the following routes are
	   installed BEFORE it is called:

	   - 'timeslot' */
	schedWeekC := controllers.NewScheduleWeekController(session, getRouter, c)
	getRouter.HandleFunc("/schedule/thisweek/", schedWeekC.GetThisWeek).Name("schedule-thisweek")

	getRouter.HandleFunc("/schedule/{year:[1-9][0-9][0-9][0-9]}/w{week:[0-5]?[0-9]}/", schedWeekC.GetByYearWeek).Name("schedule-week")
	// This route exists so that day schedule links from the previous website aren't broken.
	getRouter.HandleFunc("/schedule/{year:[1-9][0-9][0-9][0-9]}/w{week:[0-5]?[0-9]}/{day:[1-7]}/", schedWeekC.GetByYearWeek).Name("schedule-week-day-compat")

	pc := controllers.NewPeopleController(session, c)
	getRouter.HandleFunc("/people/{id:[0-9]+}/", pc.Get)

	staticC := controllers.NewStaticController(c)
	getRouter.HandleFunc("/about/", staticC.GetAbout)
	getRouter.HandleFunc("/contact/", staticC.GetContact)
	getRouter.HandleFunc("/getinvolved/", staticC.GetInvolved)
	getRouter.HandleFunc("/competitions/", staticC.GetCompetitions)

	// End routes

	s.UseHandler(router)

	return &s, nil

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"io"
	"log"
	"net"
	"strconv"
	"strings"
	"time"
)

// TODO: Logging should be configurable (Server has a modifiable log.Logger inside).

type Server struct {
	addr string
	db   *DB
}

func NewServer(addr string) (*Server, error) {
	db, err := NewDB(100e3, 3, time.Hour, "db")
	if err != nil {
		return nil, err
	}
	return &Server{
		addr: addr,
		db:   db,
	}, nil
}

type Request struct {
	Type  RequestType
	Key   []byte
	Value []byte
	Resp  chan *Response
	Err   error // If there was an error reading the request, only this field is set.
}

type RequestType uint8

const (
	RequestSet RequestType = iota + 1
	RequestGet
	RequestPing
	RequestInfo
)

type RedisType uint8

const (
	RedisErr RedisType = iota + 1
	RedisString
	RedisBulk
)

type Response struct {
	Type RedisType
	Msg  []byte
}

func (s *Server) Start() error {
	l, err := net.Listen("tcp", s.addr)
	if err != nil {
		return err
	}
	return s.loop(l)
}

func (s *Server) loop(l net.Listener) error {
	for {
		c, err := l.Accept()
		if err != nil {
			if e, ok := err.(net.Error); ok && e.Temporary() {
				delay := 10 * time.Millisecond
				log.Printf("Accept error: %s; retrying in %s", e, delay)
				time.Sleep(delay)
				continue
			}
			return err
		}
		go s.HandleConn(c)
	}
}

func when(ch chan *Response, pred bool) chan *Response {
	if pred {
		return ch
	}
	return nil
}

func head(q []*Response) *Response {
	if len(q) == 0 {
		return nil
	}
	return q[0]
}

func (s *Server) HandleConn(c net.Conn) {
	log.Printf("Client connected from %s", c.RemoteAddr())

	// readErr and writeErr are how the request reader and response writer goroutines can notify the other that
	// the client (or connection) broke/disconnected.
	// These signal chans are only closed.
	readErr := make(chan struct{})
	writeErr := make(chan struct{})

	// This request goroutine reads requests and sends them into this goroutine to be handled and buffered;
	// Responses are sent off to the response goroutine.
	// This is necessary for Redis pipelining to work.
	requests := make(chan *Request)
	responses := make(chan *Response)

	go s.readRequests(c, requests, readErr, writeErr)
	go s.writeResponses(c, responses, readErr, writeErr)

	var responseQueue []*Response

reqLoop:
	for {
		resp := &Response{Type: RedisString}
		select {
		case r := <-requests:
			if r.Err != nil {
				resp.Msg = []byte(r.Err.Error())
				resp.Type = RedisErr
			} else {
				switch r.Type {
				case RequestSet:
					if _, err := s.db.Put(r.Key, r.Value); err != nil {
						resp = ResponseFromError(err)
						break
					}
					resp.Msg = []byte("OK")
				case RequestGet:
					v, _, err := s.db.Get(r.Key)
					switch err {
					case nil:
						resp.Type = RedisBulk
						resp.Msg = v
					case ErrKeyNotExist:
						// Redis null value
						resp.Type = RedisBulk
					default:
						resp = ResponseFromError(err)
					}
				case RequestPing:
					resp.Msg = []byte("PONG")
				case RequestInfo:
					resp.Msg = []byte("useful info")
				default:
					panic("unexpected request type")
				}
			}
			responseQueue = append(responseQueue, resp)
		case when(responses, len(responseQueue) > 0) <- head(responseQueue):
			responseQueue = responseQueue[1:]
		case <-readErr:
			break reqLoop
		case <-writeErr:
			break reqLoop
		}
	}

	log.Printf("Client disconnected from %s", c.RemoteAddr())
	c.Close()
}

func (s *Server) readRequests(c net.Conn, requests chan<- *Request, readErr, writeErr chan struct{}) {
	br := bufio.NewReader(c)
	for {
		var r Request
		if err := r.Parse(br); err != nil {
			if _, ok := err.(net.Error); ok {
				close(readErr)
				return
			}
			r.Err = err
		}
		requests <- &r
		select {
		case <-writeErr:
			return
		default:
		}
	}
}

func (s *Server) writeResponses(c net.Conn, responses <-chan *Response, readErr, writeErr chan struct{}) {
	for {
		select {
		case resp := <-responses:
			if err := resp.Write(c); err != nil {
				close(writeErr)
				return
			}
		case <-readErr:
			return
		}
	}
}

func ResponseFromError(err error) *Response {
	return &Response{
		Type: RedisErr,
		Msg:  []byte(err.Error()),
	}
}

var (
	ErrMalformedRequest    = errors.New("malformed request")
	ErrUnrecognizedCommand = errors.New("unrecognized command")
	ErrWrongNumArgs        = errors.New("wrong number of arguments for command")
)

func (r *Request) Parse(br *bufio.Reader) error {
	array, err := parseRedisArrayBulkString(br)
	if err != nil {
		return err
	}
	if len(array) == 0 {
		return ErrMalformedRequest
	}
	switch strings.ToLower(array[0]) {
	case "set":
		if len(array) != 3 {
			return ErrWrongNumArgs
		}
		r.Type = RequestSet
		r.Key = []byte(array[1])
		r.Value = []byte(array[2])
	case "get":
		if len(array) != 2 {
			return ErrWrongNumArgs
		}
		r.Type = RequestGet
		r.Key = []byte(array[1])
	case "ping":
		if len(array) != 1 {
			return ErrWrongNumArgs
		}
		r.Type = RequestPing
	case "info":
		if len(array) != 1 {
			return ErrWrongNumArgs
		}
		r.Type = RequestInfo
	default:
		return ErrUnrecognizedCommand
	}
	return nil
}

func (r *Response) Write(w io.Writer) error {
	var msg []byte
	switch r.Type {
	case RedisErr:
		msg = append([]byte("-"), r.Msg...)
		msg = append(msg, "\r\n"...)
	case RedisString:
		msg = append([]byte("+"), r.Msg...)
		msg = append(msg, "\r\n"...)
	case RedisBulk:
		if r.Msg == nil {
			msg = []byte("$-1\r\n")
			break
		}
		msg = append([]byte{'$'}, strconv.Itoa(len(r.Msg))...)
		msg = append(msg, "\r\n"...)
		msg = append(msg, r.Msg...)
		msg = append(msg, "\r\n"...)
	default:
		panic("unexpected response type")
	}
	_, err := w.Write(msg)
	return err
}

func main() {
	const addr = "localhost:5533"
	log.Println("Now listening on", addr)
	server, err := NewServer(addr)
	if err != nil {
		log.Fatal(err)
	}
	log.Fatal(server.Start())
}
<commit_msg>Expand TODO<commit_after>package main

import (
	"bufio"
	"errors"
	"io"
	"log"
	"net"
	"strconv"
	"strings"
	"time"
)

// TODO: Logging should be configurable (Server has a modifiable log.Logger inside). When this works, ensure
// the test doesn't print logs.

type Server struct {
	addr string
	db   *DB
}

func NewServer(addr string) (*Server, error) {
	db, err := NewDB(100e3, 3, time.Hour, "db")
	if err != nil {
		return nil, err
	}
	return &Server{
		addr: addr,
		db:   db,
	}, nil
}

type Request struct {
	Type  RequestType
	Key   []byte
	Value []byte
	Resp  chan *Response
	Err   error // If there was an error reading the request, only this field is set.
}

type RequestType uint8

const (
	RequestSet RequestType = iota + 1
	RequestGet
	RequestPing
	RequestInfo
)

type RedisType uint8

const (
	RedisErr RedisType = iota + 1
	RedisString
	RedisBulk
)

type Response struct {
	Type RedisType
	Msg  []byte
}

func (s *Server) Start() error {
	l, err := net.Listen("tcp", s.addr)
	if err != nil {
		return err
	}
	return s.loop(l)
}

func (s *Server) loop(l net.Listener) error {
	for {
		c, err := l.Accept()
		if err != nil {
			if e, ok := err.(net.Error); ok && e.Temporary() {
				delay := 10 * time.Millisecond
				log.Printf("Accept error: %s; retrying in %s", e, delay)
				time.Sleep(delay)
				continue
			}
			return err
		}
		go s.HandleConn(c)
	}
}

func when(ch chan *Response, pred bool) chan *Response {
	if pred {
		return ch
	}
	return nil
}

func head(q []*Response) *Response {
	if len(q) == 0 {
		return nil
	}
	return q[0]
}

func (s *Server) HandleConn(c net.Conn) {
	log.Printf("Client connected from %s", c.RemoteAddr())

	// readErr and writeErr are how the request reader and response writer goroutines can notify the other that
	// the client (or connection) broke/disconnected.
	// These signal chans are only closed.
	readErr := make(chan struct{})
	writeErr := make(chan struct{})

	// This request goroutine reads requests and sends them into this goroutine to be handled and buffered;
	// Responses are sent off to the response goroutine.
	// This is necessary for Redis pipelining to work.
	requests := make(chan *Request)
	responses := make(chan *Response)

	go s.readRequests(c, requests, readErr, writeErr)
	go s.writeResponses(c, responses, readErr, writeErr)

	var responseQueue []*Response

reqLoop:
	for {
		resp := &Response{Type: RedisString}
		select {
		case r := <-requests:
			if r.Err != nil {
				resp.Msg = []byte(r.Err.Error())
				resp.Type = RedisErr
			} else {
				switch r.Type {
				case RequestSet:
					if _, err := s.db.Put(r.Key, r.Value); err != nil {
						resp = ResponseFromError(err)
						break
					}
					resp.Msg = []byte("OK")
				case RequestGet:
					v, _, err := s.db.Get(r.Key)
					switch err {
					case nil:
						resp.Type = RedisBulk
						resp.Msg = v
					case ErrKeyNotExist:
						// Redis null value
						resp.Type = RedisBulk
					default:
						resp = ResponseFromError(err)
					}
				case RequestPing:
					resp.Msg = []byte("PONG")
				case RequestInfo:
					resp.Msg = []byte("useful info")
				default:
					panic("unexpected request type")
				}
			}
			responseQueue = append(responseQueue, resp)
		case when(responses, len(responseQueue) > 0) <- head(responseQueue):
			responseQueue = responseQueue[1:]
		case <-readErr:
			break reqLoop
		case <-writeErr:
			break reqLoop
		}
	}

	log.Printf("Client disconnected from %s", c.RemoteAddr())
	c.Close()
}

func (s *Server) readRequests(c net.Conn, requests chan<- *Request, readErr, writeErr chan struct{}) {
	br := bufio.NewReader(c)
	for {
		var r Request
		if err := r.Parse(br); err != nil {
			if _, ok := err.(net.Error); ok {
				close(readErr)
				return
			}
			r.Err = err
		}
		requests <- &r
		select {
		case <-writeErr:
			return
		default:
		}
	}
}

func (s *Server) writeResponses(c net.Conn, responses <-chan *Response, readErr, writeErr chan struct{}) {
	for {
		select {
		case resp := <-responses:
			if err := resp.Write(c); err != nil {
				close(writeErr)
				return
			}
		case <-readErr:
			return
		}
	}
}

func ResponseFromError(err error) *Response {
	return &Response{
		Type: RedisErr,
		Msg:  []byte(err.Error()),
	}
}

var (
	ErrMalformedRequest    = errors.New("malformed request")
	ErrUnrecognizedCommand = errors.New("unrecognized command")
	ErrWrongNumArgs        = errors.New("wrong number of arguments for command")
)

func (r *Request) Parse(br *bufio.Reader) error {
	array, err := parseRedisArrayBulkString(br)
	if err != nil {
		return err
	}
	if len(array) == 0 {
		return ErrMalformedRequest
	}
	switch strings.ToLower(array[0]) {
	case "set":
		if len(array) != 3 {
			return ErrWrongNumArgs
		}
		r.Type = RequestSet
		r.Key = []byte(array[1])
		r.Value = []byte(array[2])
	case "get":
		if len(array) != 2 {
			return ErrWrongNumArgs
		}
		r.Type = RequestGet
		r.Key = []byte(array[1])
	case "ping":
		if len(array) != 1 {
			return ErrWrongNumArgs
		}
		r.Type = RequestPing
	case "info":
		if len(array) != 1 {
			return ErrWrongNumArgs
		}
		r.Type = RequestInfo
	default:
		return ErrUnrecognizedCommand
	}
	return nil
}

func (r *Response) Write(w io.Writer) error {
	var msg []byte
	switch r.Type {
	case RedisErr:
		msg = append([]byte("-"), r.Msg...)
		msg = append(msg, "\r\n"...)
	case RedisString:
		msg = append([]byte("+"), r.Msg...)
		msg = append(msg, "\r\n"...)
	case RedisBulk:
		if r.Msg == nil {
			msg = []byte("$-1\r\n")
			break
		}
		msg = append([]byte{'$'}, strconv.Itoa(len(r.Msg))...)
		msg = append(msg, "\r\n"...)
		msg = append(msg, r.Msg...)
		msg = append(msg, "\r\n"...)
	default:
		panic("unexpected response type")
	}
	_, err := w.Write(msg)
	return err
}

func main() {
	const addr = "localhost:5533"
	log.Println("Now listening on", addr)
	server, err := NewServer(addr)
	if err != nil {
		log.Fatal(err)
	}
	log.Fatal(server.Start())
}
<|endoftext|>