<commit_before>package http

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"strings"

	cmodel "github.com/Cepave/common/model"
	"github.com/Cepave/query/graph"
	"github.com/Cepave/query/proc"
	"regexp"
)

type GraphHistoryParam struct {
	Start            int                     `json:"start"`
	End              int                     `json:"end"`
	CF               string                  `json:"cf"`
	EndpointCounters []cmodel.GraphInfoParam `json:"endpoint_counters"`
}

func configGraphRoutes() {

	// method:post
	http.HandleFunc("/graph/history", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.HistoryRequestCnt.Incr()

		var body GraphHistoryParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body.EndpointCounters) == 0 {
			StdRender(w, "", errors.New("empty_payload"))
			return
		}
		data := []*cmodel.GraphQueryResponse{}
		var result *cmodel.GraphQueryResponse
		isPacketLossRate := false
		for _, ec := range body.EndpointCounters {
			if strings.Contains(ec.Counter, "packet-loss-rate") {
				isPacketLossRate = true
				break
			}
		}
		isAverage := false
		for _, ec := range body.EndpointCounters {
			if strings.Contains(ec.Counter, "average") {
				isAverage = true
				break
			}
		}
		if isPacketLossRate {
			/**
			 * 下面這段，只是想先做一個跟 packets-sent 的 response 一樣的 struct
			 */
			var packetSentCount []cmodel.JsonFloat
			for _, ec := range body.EndpointCounters {
				if strings.Contains(ec.Counter, "packets-sent") {
					request := cmodel.GraphQueryParam{
						Start:     int64(body.Start),
						End:       int64(body.End),
						ConsolFun: body.CF,
						Endpoint:  ec.Endpoint,
						Counter:   ec.Counter,
					}
					result, err = graph.QueryOne(request)
					for i := range result.Values {
						result.Values[i].Value = cmodel.JsonFloat(0.0)
						packetSentCount = append(packetSentCount, cmodel.JsonFloat(0.0))
					}
					if err != nil {
						log.Printf("graph.queryOne fail, %v", err)
					}
					break
				}
			}

			for _, ec := range body.EndpointCounters {
				/**
				 * 此版本中，在 dashboard 查詢 packet-loss-rate 的時候，
				 * dashboard 會以 packets-sent 這個 metric 來呈現搜尋到的結果。
				 */
				if strings.Contains(ec.Counter, "packets-sent") {
					/**
					 * 此版本中，packet-loss-rate 的 data，
					 * 必須跟 packets-sent & packets-eceived 一起看。
					 */
					requestPacketsSent := cmodel.GraphQueryParam{
						Start:     int64(body.Start),
						End:       int64(body.End),
						ConsolFun: body.CF,
						Endpoint:  ec.Endpoint,
						Counter:   ec.Counter,
					}
					resultPacketsSent, err := graph.QueryOne(requestPacketsSent)
					if err != nil {
						log.Printf("graph.queryOne fail, %v", err)
					}
					if resultPacketsSent == nil {
						continue
					}
					data = append(data, resultPacketsSent)

					requestPacketReceived := cmodel.GraphQueryParam{
						Start:     int64(body.Start),
						End:       int64(body.End),
						ConsolFun: body.CF,
						Endpoint:  ec.Endpoint,
						Counter:   strings.Replace(ec.Counter, "packets-sent", "packets-received", 1),
					}
					resultPacketReceived, err := graph.QueryOne(requestPacketReceived)
					if err != nil {
						log.Printf("graph.queryOne fail, %v", err)
					}
					if resultPacketReceived == nil {
						continue
					}
					data = append(data, resultPacketReceived)
					for i := range resultPacketsSent.Values {
						packetLossCount := (resultPacketsSent.Values[i].Value -
							resultPacketReceived.Values[i].Value)
						result.Values[i].Value += packetLossCount
						packetSentCount[i] += resultPacketsSent.Values[i].Value
					}
				}

			}

			result.Endpoint = "all-endpoints"
			result.Counter = "packet-loss-rate"
			for i := range result.Values {
				result.Values[i].Value = result.Values[i].Value / packetSentCount[i]
			}
			result.Values = result.Values
			data = append(data, result)

		} else if isAverage {
			/**
			 * 下面這段，只是想先製造一個跟 transmission-time 的 response 一樣的 struct
			 */
			var packetSentCount []cmodel.JsonFloat
			for _, ec := range body.EndpointCounters {
				if strings.Contains(ec.Counter, "transmission-time") {
					request := cmodel.GraphQueryParam{
						Start:     int64(body.Start),
						End:       int64(body.End),
						ConsolFun: body.CF,
						Endpoint:  ec.Endpoint,
						Counter:   ec.Counter,
					}
					result, err = graph.QueryOne(request)
					for i := range result.Values {
						result.Values[i].Value = cmodel.JsonFloat(0.0)
						packetSentCount = append(packetSentCount, cmodel.JsonFloat(0.0))
					}
					if err != nil {
						log.Printf("graph.queryOne fail, %v", err)
					}
					break
				}
			}
			for _, ec := range body.EndpointCounters {
				/**
				 * 此版本中，在 dashboard 查詢 average 的時候，
				 * dashboard 會以 transmission-time 這個 metric 來呈現搜尋到的結果。
				 */
				if strings.Contains(ec.Counter, "transmission-time") {
					/**
					 * 此版本中，average 的 data，
					 * 必須跟 transmission-time & packets-sent 一起看。
					 */
					requestTransmissionTime := cmodel.GraphQueryParam{
						Start:     int64(body.Start),
						End:       int64(body.End),
						ConsolFun: body.CF,
						Endpoint:  ec.Endpoint,
						Counter:   ec.Counter,
					}
					resultTransmissionTime, err := graph.QueryOne(requestTransmissionTime)
					if err != nil {
						log.Printf("graph.queryOne fail, %v", err)
					}
					if resultTransmissionTime == nil {
						continue
					}
					data = append(data, resultTransmissionTime)

					requestPacketsSent := cmodel.GraphQueryParam{
						Start:     int64(body.Start),
						End:       int64(body.End),
						ConsolFun: body.CF,
						Endpoint:  ec.Endpoint,
						Counter:   strings.Replace(ec.Counter, "transmission-time", "packets-sent", 1),
					}
					resultPacketsSent, err := graph.QueryOne(requestPacketsSent)
					if err != nil {
						log.Printf("graph.queryOne fail, %v", err)
					}
					if resultPacketsSent == nil {
						continue
					}
					data = append(data, resultPacketsSent)
					for i := range resultTransmissionTime.Values {
						result.Values[i].Value += resultTransmissionTime.Values[i].Value * resultPacketsSent.Values[i].Value
						packetSentCount[i] += resultPacketsSent.Values[i].Value
					}
				}

			}

			result.Endpoint = "all-endpoints"
			result.Counter = "average"
			for i := range result.Values {
				result.Values[i].Value = result.Values[i].Value / packetSentCount[i]
			}
			result.Values = result.Values
			data = append(data, result)

		} else {
			regx, _ := regexp.Compile("(\\.\\$\\s*|\\s*)$")
			for _, ec := range body.EndpointCounters {
				request := cmodel.GraphQueryParam{
					Start:     int64(body.Start),
					End:       int64(body.End),
					ConsolFun: body.CF,
					Endpoint:  regx.ReplaceAllString(ec.Endpoint, ""),
					Counter:   regx.ReplaceAllString(ec.Counter, ""),
				}
				result, err := graph.QueryOne(request)
				if err != nil {
					log.Printf("graph.queryOne fail, %v", err)
				}
				if result == nil {
					continue
				}
				data = append(data, result)
			}
			log.Println("got length of data: ", len(data))
		}

		// statistics
		proc.HistoryResponseCounterCnt.IncrBy(int64(len(data)))
		for _, item := range data {
			proc.HistoryResponseItemCnt.IncrBy(int64(len(item.Values)))
		}

		StdRender(w, data, nil)
	})

	// post, info
	http.HandleFunc("/graph/info", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.InfoRequestCnt.Incr()

		var body []*cmodel.GraphInfoParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body) == 0 {
			StdRender(w, "", errors.New("empty"))
			return
		}

		data := []*cmodel.GraphFullyInfo{}
		for _, param := range body {
			if param == nil {
				continue
			}
			info, err := graph.Info(*param)
			if err != nil {
				log.Printf("graph.info fail, resp: %v, err: %v", info, err)
			}
			if info == nil {
				continue
			}
			data = append(data, info)
		}

		StdRender(w, data, nil)
	})

	// post, last
	http.HandleFunc("/graph/last", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.LastRequestCnt.Incr()

		var body []*cmodel.GraphLastParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body) == 0 {
			StdRender(w, "", errors.New("empty"))
			return
		}

		data := []*cmodel.GraphLastResp{}
		for _, param := range body {
			if param == nil {
				continue
			}
			last, err := graph.Last(*param)
			if err != nil {
				log.Printf("graph.last fail, resp: %v, err: %v", last, err)
			}
			if last == nil {
				continue
			}
			data = append(data, last)
		}

		// statistics
		proc.LastRequestItemCnt.IncrBy(int64(len(data)))

		StdRender(w, data, nil)
	})

	// post, last/raw
	http.HandleFunc("/graph/last/raw", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.LastRawRequestCnt.Incr()

		var body []*cmodel.GraphLastParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body) == 0 {
			StdRender(w, "", errors.New("empty"))
			return
		}

		data := []*cmodel.GraphLastResp{}
		for _, param := range body {
			if param == nil {
				continue
			}
			last, err := graph.LastRaw(*param)
			if err != nil {
				log.Printf("graph.last.raw fail, resp: %v, err: %v", last, err)
			}
			if last == nil {
				continue
			}
			data = append(data, last)
		}
		// statistics
		proc.LastRawRequestItemCnt.IncrBy(int64(len(data)))
		StdRender(w, data, nil)
	})

}
<commit_msg>code refinement<commit_after>package http

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"strings"

	cmodel "github.com/Cepave/common/model"
	"github.com/Cepave/query/graph"
	"github.com/Cepave/query/proc"
	"regexp"
)

type GraphHistoryParam struct {
	Start            int                     `json:"start"`
	End              int                     `json:"end"`
	CF               string                  `json:"cf"`
	EndpointCounters []cmodel.GraphInfoParam `json:"endpoint_counters"`
}

func graphQueryOne(body GraphHistoryParam, ec cmodel.GraphInfoParam, endpoint string, counter string) {
	if endpoint == nil {
		endpoint = ec.Endpoint
	}
	if counter == nil {
		counter = ec.Counter
	}
	request := cmodel.GraphQueryParam{
		Start:     int64(body.Start),
		End:       int64(body.End),
		ConsolFun: body.CF,
		Endpoint:  endpoint,
		Counter:   counter,
	}
	result, err := graph.QueryOne(request)
	if err != nil {
		log.Printf("graph.queryOne fail, %v, (endpoint, counter) = (%s, %s)", err, endpoint, counter)
	}
	return result
}

func configGraphRoutes() {

	// method:post
	http.HandleFunc("/graph/history", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.HistoryRequestCnt.Incr()

		var body GraphHistoryParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body.EndpointCounters) == 0 {
			StdRender(w, "", errors.New("empty_payload"))
			return
		}

		data := []*cmodel.GraphQueryResponse{}
		var result *cmodel.GraphQueryResponse
		for _, ec := range body.EndpointCounters {
			if strings.Contains(ec.Counter, "packets-sent") || strings.Contains(ec.Counter, "transmission-time") {
				// NQM Case
				var packetSentCount []cmodel.JsonFloat
				// clone a response
				result = graphQueryOne(ec, body, nil, nil)
				for i := range result.Values {
					result.Values[i].Value = cmodel.JsonFloat(0.0)
					packetSentCount = append(packetSentCount, cmodel.JsonFloat(0.0))
				}
				if result == nil {
					continue
				}

				resultSent = graphQueryOne(ec, body, nil, nil)
				if resultSent == nil {
					continue
				}
				data = append(data, resultSent)

				if strings.Contains(ec.Counter, "packets-sent") {
					counter = strings.Replace(ec.Counter, "packets-sent", "packets-received", 1)
					if resultReceived = graphQueryOne(ec, body, nil, counter); resultReceived {
						data = append(data, result)
						for i := range resultSent.Values {
							packetLossCount := (resultSent.Values[i].Value - resultReceived.Values[i].Value)
							result.Values[i].Value += packetLossCount
							packetSentCount[i] += resultSent.Values[i].Value
						}
					}
				} else if strings.Contains(ec.Counter, "transmission-time") {
					counter = strings.Replace(ec.Counter, "transmission-time", "packets-sent", 1)
					if resultTransmissionTime = graphQueryOne(ec, body, nil, counter); resultTransmissionTime {
						data = append(data, resultPacketsSent)
						for i := range resultTransmissionTime.Values {
							result.Values[i].Value += resultTransmissionTime.Values[i].Value * resultPacketsSent.Values[i].Value
							packetSentCount[i] += resultPacketsSent.Values[i].Value
						}
					}
				}

				if strings.Contains(ec.Counter, "packets-sent") {
					result.Endpoint = "all-endpoints"
					result.Counter = "packet-loss-rate"
				} else if strings.Contains(ec.Counter, "transmission-time") {
					result.Endpoint = "all-endpoints"
					result.Counter = "average"
				}
				for i := range result.Values {
					result.Values[i].Value = result.Values[i].Value / packetSentCount[i]
				}
				result.Values = result.Values
				data = append(data, result)

			} else {
				regx, _ := regexp.Compile("(\\.\\$\\s*|\\s*)$")
				endpoint = regx.ReplaceAllString(ec.Endpoint, "")
				counter = regx.ReplaceAllString(ec.Counter, "")
				result = graphQueryOne(ec, body, endpoint, counter)
				if result == nil {
					continue
				}
				data = append(data, result)
			}
		}
		log.Println("got length of data: ", len(data))

		// statistics
		proc.HistoryResponseCounterCnt.IncrBy(int64(len(data)))
		for _, item := range data {
			proc.HistoryResponseItemCnt.IncrBy(int64(len(item.Values)))
		}

		StdRender(w, data, nil)
	})

	// post, info
	http.HandleFunc("/graph/info", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.InfoRequestCnt.Incr()

		var body []*cmodel.GraphInfoParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body) == 0 {
			StdRender(w, "", errors.New("empty"))
			return
		}

		data := []*cmodel.GraphFullyInfo{}
		for _, param := range body {
			if param == nil {
				continue
			}
			info, err := graph.Info(*param)
			if err != nil {
				log.Printf("graph.info fail, resp: %v, err: %v", info, err)
			}
			if info == nil {
				continue
			}
			data = append(data, info)
		}

		StdRender(w, data, nil)
	})

	// post, last
	http.HandleFunc("/graph/last", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.LastRequestCnt.Incr()

		var body []*cmodel.GraphLastParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body) == 0 {
			StdRender(w, "", errors.New("empty"))
			return
		}

		data := []*cmodel.GraphLastResp{}
		for _, param := range body {
			if param == nil {
				continue
			}
			last, err := graph.Last(*param)
			if err != nil {
				log.Printf("graph.last fail, resp: %v, err: %v", last, err)
			}
			if last == nil {
				continue
			}
			data = append(data, last)
		}

		// statistics
		proc.LastRequestItemCnt.IncrBy(int64(len(data)))

		StdRender(w, data, nil)
	})

	// post, last/raw
	http.HandleFunc("/graph/last/raw", func(w http.ResponseWriter, r *http.Request) {
		// statistics
		proc.LastRawRequestCnt.Incr()

		var body []*cmodel.GraphLastParam
		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&body)
		if err != nil {
			StdRender(w, "", err)
			return
		}

		if len(body) == 0 {
			StdRender(w, "", errors.New("empty"))
			return
		}

		data := []*cmodel.GraphLastResp{}
		for _, param := range body {
			if param == nil {
				continue
			}
			last, err := graph.LastRaw(*param)
			if err != nil {
				log.Printf("graph.last.raw fail, resp: %v, err: %v", last, err)
			}
			if last == nil {
				continue
			}
			data = append(data, last)
		}
		// statistics
		proc.LastRawRequestItemCnt.IncrBy(int64(len(data)))
		StdRender(w, data, nil)
	})

}
<|endoftext|>


<commit_before>package httpfwd

import (
    "fmt"
    "time"
    "io"
    "bufio"
    "net"
    "net/http"
    "log"
    "os"
    "strings"
    "code.google.com/p/gopacket"
    "code.google.com/p/gopacket/layers"
    "code.google.com/p/gopacket/pcap"
    "code.google.com/p/gopacket/tcpassembly"
    "code.google.com/p/gopacket/tcpassembly/tcpreader"
)

type Forwarder struct {
    Destination string
    ForwarderOptions

    transport http.Transport
    bufsize int32
    timeout time.Duration

    req *log.Logger
    err *log.Logger
}

type ForwarderOptions struct {
    Interfaces []string
    Port int
    ReplaceHost bool
    Verbose bool
}

func NewForwarder(dst string, opt ForwarderOptions) (*Forwarder) {
    fwd := &Forwarder {
        Destination: dst,
        ForwarderOptions: opt,

        bufsize: 65535,
        timeout: 10 * time.Millisecond,

        req: log.New(os.Stdout, "", log.Ltime | log.Ldate),
        err: log.New(os.Stderr, "", log.Ltime | log.Ldate),
    }

    fwd.transport.MaxIdleConnsPerHost = 6

    return fwd
}

func PcapVersion() (string) {
    return pcap.Version()
}

func (fwd *Forwarder) Start() {
    pool := tcpassembly.NewStreamPool(fwd)
    assembler := tcpassembly.NewAssembler(pool)

    packets := fwd.packets()
    ticker := time.Tick(time.Minute)

    for {
        select {
        case packet := <- packets:
            assembler.Assemble(
                packet.NetworkLayer().NetworkFlow(),
                packet.TransportLayer().(*layers.TCP))
        case <- ticker:
            assembler.FlushOlderThan(time.Now().Add(-2 * time.Minute))
        }
    }
}

func (fwd *Forwarder) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {
    reader := tcpreader.NewReaderStream()
    go fwd.handleStream(&netFlow, &reader)
    return &reader
}

func (fwd *Forwarder) newSource(intf string, filter string) (*gopacket.PacketSource) {
    handle, err := pcap.OpenLive(intf, fwd.bufsize, false, fwd.timeout)
    if err != nil {
        fwd.err.Fatalln("Fatal error:", err)
    }

    if err := handle.SetBPFFilter(filter); err != nil {
        fwd.err.Fatalln("Fatal error:", err)
    }

    source := gopacket.NewPacketSource(handle, handle.LinkType())
    source.DecodeOptions = gopacket.NoCopy

    return source
}

func (fwd *Forwarder) packets() (chan gopacket.Packet) {
    filt := fwd.buildFilter()
    intfs := fwd.retrieveInterfaces()

    fwd.err.Printf("Wiretapping on port %d, forwarding to %s\n", fwd.Port, fwd.Destination)

    if fwd.Verbose {
        fwd.err.Printf("Listening on interfaces: %s\n", strings.Join(intfs, ", "))
        fwd.err.Printf("Using pcap filter: \"%s\"\n", filt)
    }

    channel := make(chan gopacket.Packet, 1000)
    for _, intf := range intfs {
        go fwd.packetsToChannel(fwd.newSource(intf, filt), channel)
    }
    return channel
}

func (fwd *Forwarder) buildFilter() (string) {
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        fwd.err.Fatalln("Fatal error:", err)
    }

    filt := fmt.Sprintf("tcp dst port %d and (", fwd.Port)
    for i, addr := range addrs {
        if i > 0 {
            filt += " or "
        }
        filt += fmt.Sprintf("dst host %s", addr.(*net.IPNet).IP)
    }
    filt += ")"

    return filt
}

func (fwd* Forwarder) retrieveInterfaces() ([]string) {
    var names []string

    intfs, err := net.Interfaces()
    if err != nil {
        fwd.err.Fatalln("Fatal error:", err)
    }

    for _, intf := range intfs {
        addrs, err := intf.Addrs()
        if err != nil {
            fwd.err.Fatalln("Fatal error:", err)
        }

        if net.FlagUp & intf.Flags == net.FlagUp && len(addrs) > 0 {
            names = append(names, intf.Name)
        }
    }

    return names
}

func (fwd *Forwarder) packetsToChannel(source *gopacket.PacketSource, channel chan gopacket.Packet) {
    for {
        packet, err := source.NextPacket()
        switch err {
        default:
            fwd.err.Fatalln("Fatal error:", "Cannot activate wiretap")
        case io.EOF:
            close(channel)
            return
        case nil:
            channel <- packet
        case pcap.NextErrorTimeoutExpired:
        case pcap.NextErrorReadError:
            /* Ignore nonfatal read errors. */
        }
    }
}

func (fwd *Forwarder) handleStream(netFlow *gopacket.Flow, stream *tcpreader.ReaderStream) {
    buf := bufio.NewReader(stream)
    for {
        req, err := http.ReadRequest(buf)
        if err == io.EOF {
            return
        } else if err != nil {
            fwd.err.Println("Error:", err)
        } else {
            go fwd.forwardRequest(netFlow, req)
        }
    }
}

func (fwd *Forwarder) forwardRequest(netFlow *gopacket.Flow, req *http.Request) {
    /* Make the HTTP transporter happy by supplying scheme and host. */
    req.URL.Scheme = "http"
    req.URL.Host = canonicalAddr(fwd.Destination)

    /* Copy the URL to set the host from the HTTP headers. */
    url := *req.URL
    url.Host = req.Host

    if fwd.ReplaceHost {
        req.Host = fwd.Destination
    }

    res, err := fwd.transport.RoundTrip(req)
    if err != nil {
        fwd.err.Println("Error:", err)
    } else {
        /* "The client must close the response body when finished with it." */
        defer res.Body.Close()
        fwd.req.Printf("%s %s %s %d\n", netFlow.Src(), req.Method, &url, res.StatusCode)
        if fwd.Verbose {
            req.Body = nil
            req.Write(os.Stdout)
        }
    }

}

func hasPort(s string) bool {
    return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
}

func canonicalAddr(addr string) string {
    if !hasPort(addr) {
        return addr + ":80"
    }
    return addr
}
<commit_msg>Use pcap to read interfaces.<commit_after>package httpfwd

import (
    "fmt"
    "time"
    "io"
    "bufio"
    "net/http"
    "log"
    "os"
    "strings"
    "code.google.com/p/gopacket"
    "code.google.com/p/gopacket/layers"
    "code.google.com/p/gopacket/pcap"
    "code.google.com/p/gopacket/tcpassembly"
    "code.google.com/p/gopacket/tcpassembly/tcpreader"
)

type Forwarder struct {
    Destination string
    ForwarderOptions

    transport http.Transport
    bufsize int32
    timeout time.Duration

    req *log.Logger
    err *log.Logger
}

type ForwarderOptions struct {
    Interfaces []string
    Port int
    ReplaceHost bool
    Verbose bool
}

func NewForwarder(dst string, opt ForwarderOptions) (*Forwarder) {
    fwd := &Forwarder {
        Destination: dst,
        ForwarderOptions: opt,

        bufsize: 65535,
        timeout: 10 * time.Millisecond,

        req: log.New(os.Stdout, "", log.Ltime | log.Ldate),
        err: log.New(os.Stderr, "", log.Ltime | log.Ldate),
    }

    fwd.transport.MaxIdleConnsPerHost = 6

    return fwd
}

func PcapVersion() (string) {
    return pcap.Version()
}

func (fwd *Forwarder) Start() {
    pool := tcpassembly.NewStreamPool(fwd)
    assembler := tcpassembly.NewAssembler(pool)

    packets := fwd.packets()
    ticker := time.Tick(time.Minute)

    for {
        select {
        case packet := <- packets:
            assembler.Assemble(
                packet.NetworkLayer().NetworkFlow(),
                packet.TransportLayer().(*layers.TCP))
        case <- ticker:
            assembler.FlushOlderThan(time.Now().Add(-2 * time.Minute))
        }
    }
}

func (fwd *Forwarder) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {
    reader := tcpreader.NewReaderStream()
    go fwd.handleStream(&netFlow, &reader)
    return &reader
}

func (fwd *Forwarder) newSource(intfName string, filter string) (*gopacket.PacketSource) {
    handle, err := pcap.OpenLive(intfName, fwd.bufsize, false, fwd.timeout)
    if err != nil {
        fwd.err.Fatalln("Fatal error:", err)
    }

    if err := handle.SetBPFFilter(filter); err != nil {
        fwd.err.Fatalln("Fatal error:", err)
    }

    source := gopacket.NewPacketSource(handle, handle.LinkType())
    source.DecodeOptions = gopacket.NoCopy

    return source
}

func (fwd *Forwarder) packets() (chan gopacket.Packet) {
    intfs, err := pcap.FindAllDevs()
    if err != nil {
        fwd.err.Fatalln("Fatal error:", err)
    } else if len(intfs) == 0 {
        fwd.err.Fatalln("Fatal error:", "No interfaces found, are you root?")
    }

    filter := fwd.buildFilter(intfs)
    intfNames := fwd.interfaceNames(intfs)

    fwd.err.Printf("Wiretapping on port %d, forwarding to %s\n", fwd.Port, fwd.Destination)

    if fwd.Verbose {
        fwd.err.Printf("Listening on interfaces: %s\n", strings.Join(intfNames, ", "))
        fwd.err.Printf("Using pcap filter: \"%s\"\n", filter)
    }

    channel := make(chan gopacket.Packet, 1000)
    for _, intfName := range intfNames {
        go fwd.packetsToChannel(fwd.newSource(intfName, filter), channel)
    }
    return channel
}

func (fwd *Forwarder) buildFilter(intfs []pcap.Interface) (string) {
    filter := fmt.Sprintf("tcp dst port %d and (", fwd.Port)
    i := 0
    for _, intf := range intfs {
        for _, addr := range intf.Addresses {
            if i > 0 {
                filter += " or "
            }
            filter += fmt.Sprintf("dst host %s", addr.IP)
            i++
        }
    }
    filter += ")"

    return filter
}

func (fwd* Forwarder) interfaceNames(intfs []pcap.Interface) ([]string) {
    var names []string

    for _, intf := range intfs {
        if len(intf.Addresses) > 0 {
            names = append(names, intf.Name)
        }
    }

    return names
}

func (fwd *Forwarder) packetsToChannel(source *gopacket.PacketSource, channel chan gopacket.Packet) {
    for {
        packet, err := source.NextPacket()
        if err == io.EOF {
            close(channel)
            return
        } else if err == nil {
            channel <- packet
        }
    }
}

func (fwd *Forwarder) handleStream(netFlow *gopacket.Flow, stream *tcpreader.ReaderStream) {
    buf := bufio.NewReader(stream)
    for {
        req, err := http.ReadRequest(buf)
        if err == io.EOF {
            return
        } else if err != nil {
            fwd.err.Println("Error:", err)
        } else {
            go fwd.forwardRequest(netFlow, req)
        }
    }
}

func (fwd *Forwarder) forwardRequest(netFlow *gopacket.Flow, req *http.Request) {
    /* Make the HTTP transporter happy by supplying scheme and host. */
    req.URL.Scheme = "http"
    req.URL.Host = canonicalAddr(fwd.Destination)

    /* Copy the URL to set the host from the HTTP headers. */
    url := *req.URL
    url.Host = req.Host

    if fwd.ReplaceHost {
        req.Host = fwd.Destination
    }

    res, err := fwd.transport.RoundTrip(req)
    if err != nil {
        fwd.err.Println("Error:", err)
    } else {
        /* "The client must close the response body when finished with it." */
        defer res.Body.Close()
        fwd.req.Printf("%s %s %s %d\n", netFlow.Src(), req.Method, &url, res.StatusCode)
        if fwd.Verbose {
            req.Body = nil
            req.Write(os.Stdout)
        }
    }

}

func hasPort(s string) bool {
    return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
}

func canonicalAddr(addr string) string {
    if !hasPort(addr) {
        return addr + ":80"
    }
    return addr
}
<|endoftext|>


<commit_before>package boomer

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"strings"
	"sync"
	"sync/atomic"
	"syscall"
	"time"
)

var masterHost string
var masterPort int

var maxRPS int64
var requestIncreaseRate string
var runTasks string
var memoryProfile string
var memoryProfileDuration time.Duration
var cpuProfile string
var cpuProfileDuration time.Duration

var initted uint32
var initMutex = sync.Mutex{}

// Init boomer
func initBoomer() {
	if atomic.LoadUint32(&initted) == 1 {
		panic("Don't call boomer.Run() more than once.")
	}

	initEvents()
	defaultStats.start()

	// done
	atomic.StoreUint32(&initted, 1)
}

// Run tasks without connecting to the master.
func runTasksForTest(tasks ...*Task) {
	taskNames := strings.Split(runTasks, ",")
	for _, task := range tasks {
		if task.Name == "" {
			continue
		} else {
			for _, name := range taskNames {
				if name == task.Name {
					log.Println("Running " + task.Name)
					task.Fn()
				}
			}
		}
	}
}

// Run accepts a slice of Task and connects
// to a locust master.
func Run(tasks ...*Task) {
	if !flag.Parsed() {
		flag.Parse()
	}

	if runTasks != "" {
		runTasksForTest(tasks...)
		return
	}

	// support go version below 1.5
	runtime.GOMAXPROCS(runtime.NumCPU())

	// init boomer
	initMutex.Lock()
	initBoomer()
	initMutex.Unlock()

	runner := newRunner(tasks, maxRPS, requestIncreaseRate)
	runner.masterHost = masterHost
	runner.masterPort = masterPort
	runner.getReady()

	if memoryProfile != "" {
		startMemoryProfile(memoryProfile, memoryProfileDuration)
	}

	if cpuProfile != "" {
		startCPUProfile(cpuProfile, cpuProfileDuration)
	}

	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGINT)

	<-c
	Events.Publish("boomer:quit")

	// wait for quit message is sent to master
	<-runner.client.disconnectedChannel()
	log.Println("shut down")
}

func startMemoryProfile(file string, duration time.Duration) {
	f, err := os.Create(file)
	if err != nil {
		log.Fatal(err)
	}
	time.AfterFunc(duration, func() {
		err = pprof.WriteHeapProfile(f)
		if err != nil {
			log.Println(err)
			return
		}
		f.Close()
		log.Println("Stop memory profiling after", duration)
	})
}

func startCPUProfile(file string, duration time.Duration) {
	f, err := os.Create(file)
	if err != nil {
		log.Fatal(err)
	}

	err = pprof.StartCPUProfile(f)
	if err != nil {
		log.Println(err)
		f.Close()
		return
	}

	time.AfterFunc(duration, func() {
		pprof.StopCPUProfile()
		f.Close()
		log.Println("Stop CPU profiling after", duration)
	})
}

func init() {
	flag.Int64Var(&maxRPS, "max-rps", 0, "Max RPS that boomer can generate, disabled by default.")
	flag.StringVar(&requestIncreaseRate, "request-increase-rate", "-1", "Request increase rate, disabled by default.")
	flag.StringVar(&runTasks, "run-tasks", "", "Run tasks without connecting to the master, multiply tasks is separated by comma. Usually, it's for debug purpose.")
	flag.StringVar(&masterHost, "master-host", "127.0.0.1", "Host or IP address of locust master for distributed load testing. Defaults to 127.0.0.1.")
	flag.IntVar(&masterPort, "master-port", 5557, "The port to connect to that is used by the locust master for distributed load testing. Defaults to 5557.")
	flag.StringVar(&memoryProfile, "mem-profile", "", "Enable memory profiling.")
	flag.DurationVar(&memoryProfileDuration, "mem-profile-duration", 30*time.Second, "Memory profile duration. Defaults to 30 seconds.")
	flag.StringVar(&cpuProfile, "cpu-profile", "", "Enable CPU profiling.")
	flag.DurationVar(&cpuProfileDuration, "cpu-profile-duration", 30*time.Second, "CPU profile duration. Defaults to 30 seconds.")
}
<commit_msg>aslo catch the signal sent by kill command<commit_after>package boomer

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"strings"
	"sync"
	"sync/atomic"
	"syscall"
	"time"
)

var masterHost string
var masterPort int

var maxRPS int64
var requestIncreaseRate string
var runTasks string
var memoryProfile string
var memoryProfileDuration time.Duration
var cpuProfile string
var cpuProfileDuration time.Duration

var initted uint32
var initMutex = sync.Mutex{}

// Init boomer
func initBoomer() {
	if atomic.LoadUint32(&initted) == 1 {
		panic("Don't call boomer.Run() more than once.")
	}

	initEvents()
	defaultStats.start()

	// done
	atomic.StoreUint32(&initted, 1)
}

// Run tasks without connecting to the master.
func runTasksForTest(tasks ...*Task) {
	taskNames := strings.Split(runTasks, ",")
	for _, task := range tasks {
		if task.Name == "" {
			continue
		} else {
			for _, name := range taskNames {
				if name == task.Name {
					log.Println("Running " + task.Name)
					task.Fn()
				}
			}
		}
	}
}

// Run accepts a slice of Task and connects
// to a locust master.
func Run(tasks ...*Task) {
	if !flag.Parsed() {
		flag.Parse()
	}

	if runTasks != "" {
		runTasksForTest(tasks...)
		return
	}

	// support go version below 1.5
	runtime.GOMAXPROCS(runtime.NumCPU())

	// init boomer
	initMutex.Lock()
	initBoomer()
	initMutex.Unlock()

	runner := newRunner(tasks, maxRPS, requestIncreaseRate)
	runner.masterHost = masterHost
	runner.masterPort = masterPort
	runner.getReady()

	if memoryProfile != "" {
		startMemoryProfile(memoryProfile, memoryProfileDuration)
	}

	if cpuProfile != "" {
		startCPUProfile(cpuProfile, cpuProfileDuration)
	}

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

	<-c
	Events.Publish("boomer:quit")

	// wait for quit message is sent to master
	<-runner.client.disconnectedChannel()
	log.Println("shut down")
}

func startMemoryProfile(file string, duration time.Duration) {
	f, err := os.Create(file)
	if err != nil {
		log.Fatal(err)
	}
	time.AfterFunc(duration, func() {
		err = pprof.WriteHeapProfile(f)
		if err != nil {
			log.Println(err)
			return
		}
		f.Close()
		log.Println("Stop memory profiling after", duration)
	})
}

func startCPUProfile(file string, duration time.Duration) {
	f, err := os.Create(file)
	if err != nil {
		log.Fatal(err)
	}

	err = pprof.StartCPUProfile(f)
	if err != nil {
		log.Println(err)
		f.Close()
		return
	}

	time.AfterFunc(duration, func() {
		pprof.StopCPUProfile()
		f.Close()
		log.Println("Stop CPU profiling after", duration)
	})
}

func init() {
	flag.Int64Var(&maxRPS, "max-rps", 0, "Max RPS that boomer can generate, disabled by default.")
	flag.StringVar(&requestIncreaseRate, "request-increase-rate", "-1", "Request increase rate, disabled by default.")
	flag.StringVar(&runTasks, "run-tasks", "", "Run tasks without connecting to the master, multiply tasks is separated by comma. Usually, it's for debug purpose.")
	flag.StringVar(&masterHost, "master-host", "127.0.0.1", "Host or IP address of locust master for distributed load testing. Defaults to 127.0.0.1.")
	flag.IntVar(&masterPort, "master-port", 5557, "The port to connect to that is used by the locust master for distributed load testing. Defaults to 5557.")
	flag.StringVar(&memoryProfile, "mem-profile", "", "Enable memory profiling.")
	flag.DurationVar(&memoryProfileDuration, "mem-profile-duration", 30*time.Second, "Memory profile duration. Defaults to 30 seconds.")
	flag.StringVar(&cpuProfile, "cpu-profile", "", "Enable CPU profiling.")
	flag.DurationVar(&cpuProfileDuration, "cpu-profile-duration", 30*time.Second, "CPU profile duration. Defaults to 30 seconds.")
}
<|endoftext|>


<commit_before>package writer

import (
	"goposm/cache"
	"goposm/database"
	"goposm/element"
	"goposm/geom"
	"goposm/geom/geos"
	"goposm/mapping"
	"goposm/proj"
	"goposm/stats"
	"log"
	"sync"
)

type WayWriter struct {
	OsmElemWriter
	ways                 chan *element.Way
	lineStringTagMatcher *mapping.TagMatcher
	polygonTagMatcher    *mapping.TagMatcher
}

func NewWayWriter(osmCache *cache.OSMCache, diffCache *cache.DiffCache, ways chan *element.Way,
	insertBuffer database.RowInserter, lineStringTagMatcher *mapping.TagMatcher,
	polygonTagMatcher *mapping.TagMatcher, progress *stats.Statistics, srid int) *OsmElemWriter {
	ww := WayWriter{
		OsmElemWriter: OsmElemWriter{
			osmCache:     osmCache,
			diffCache:    diffCache,
			progress:     progress,
			wg:           &sync.WaitGroup{},
			insertBuffer: insertBuffer,
			srid:         srid,
		},
		ways:                 ways,
		lineStringTagMatcher: lineStringTagMatcher,
		polygonTagMatcher:    polygonTagMatcher,
	}
	ww.OsmElemWriter.writer = &ww
	return &ww.OsmElemWriter
}

func (ww *WayWriter) loop() {
	geos := geos.NewGeos()
	geos.SetHandleSrid(ww.srid)
	defer geos.Finish()
	for w := range ww.ways {
		ww.progress.AddWays(1)
		inserted, err := ww.osmCache.InsertedWays.IsInserted(w.Id)
		if err != nil {
			log.Println(err)
			continue
		}
		if inserted {
			continue
		}

		err = ww.osmCache.Coords.FillWay(w)
		if err != nil {
			continue
		}
		proj.NodesToMerc(w.Nodes)

		inserted = false
		if matches := ww.lineStringTagMatcher.Match(&w.Tags); len(matches) > 0 {
			ww.buildAndInsert(geos, w, matches, geom.LineString)
			inserted = true
		}
		if w.IsClosed() {
			if matches := ww.polygonTagMatcher.Match(&w.Tags); len(matches) > 0 {
				ww.buildAndInsert(geos, w, matches, geom.Polygon)
				inserted = true
			}
		}

		if inserted && ww.expireTiles != nil {
			ww.expireTiles.ExpireFromNodes(w.Nodes)
		}
		if ww.diffCache != nil {
			ww.diffCache.Coords.AddFromWay(w)
		}
	}
	ww.wg.Done()
}

type geomBuilder func(*geos.Geos, []element.Node) (*geos.Geom, error)

func (ww *WayWriter) buildAndInsert(geos *geos.Geos, w *element.Way, matches []mapping.Match, builder geomBuilder) {
	var err error
	// make copy to avoid interference with polygon/linestring matches
	way := element.Way(*w)
	geosgeom, err := builder(geos, way.Nodes)
	if err != nil {
		if err, ok := err.(ErrorLevel); ok {
			if err.Level() <= 0 {
				return
			}
		}
		log.Println(err)
		return
	}

	way.Geom, err = geom.AsGeomElement(geos, geosgeom)
	if err != nil {
		log.Println(err)
		return
	}

	if ww.clipper != nil {
		parts, err := ww.clipper.Clip(way.Geom.Geom)
		if err != nil {
			log.Println(err)
			return
		}
		for _, g := range parts {
			way := element.Way(*w)
			way.Geom = &element.Geometry{g, geos.AsEwkbHex(g)}
			ww.insertMatches(&way.OSMElem, matches)
		}
	} else {
		ww.insertMatches(&way.OSMElem, matches)
	}
}
<commit_msg>skip ways without tags<commit_after>package writer

import (
	"goposm/cache"
	"goposm/database"
	"goposm/element"
	"goposm/geom"
	"goposm/geom/geos"
	"goposm/mapping"
	"goposm/proj"
	"goposm/stats"
	"log"
	"sync"
)

type WayWriter struct {
	OsmElemWriter
	ways                 chan *element.Way
	lineStringTagMatcher *mapping.TagMatcher
	polygonTagMatcher    *mapping.TagMatcher
}

func NewWayWriter(osmCache *cache.OSMCache, diffCache *cache.DiffCache, ways chan *element.Way,
	insertBuffer database.RowInserter, lineStringTagMatcher *mapping.TagMatcher,
	polygonTagMatcher *mapping.TagMatcher, progress *stats.Statistics, srid int) *OsmElemWriter {
	ww := WayWriter{
		OsmElemWriter: OsmElemWriter{
			osmCache:     osmCache,
			diffCache:    diffCache,
			progress:     progress,
			wg:           &sync.WaitGroup{},
			insertBuffer: insertBuffer,
			srid:         srid,
		},
		ways:                 ways,
		lineStringTagMatcher: lineStringTagMatcher,
		polygonTagMatcher:    polygonTagMatcher,
	}
	ww.OsmElemWriter.writer = &ww
	return &ww.OsmElemWriter
}

func (ww *WayWriter) loop() {
	geos := geos.NewGeos()
	geos.SetHandleSrid(ww.srid)
	defer geos.Finish()
	for w := range ww.ways {
		if len(w.Tags) == 0 {
			continue
		}
		ww.progress.AddWays(1)
		inserted, err := ww.osmCache.InsertedWays.IsInserted(w.Id)
		if err != nil {
			log.Println(err)
			continue
		}
		if inserted {
			continue
		}

		err = ww.osmCache.Coords.FillWay(w)
		if err != nil {
			continue
		}
		proj.NodesToMerc(w.Nodes)

		inserted = false
		if matches := ww.lineStringTagMatcher.Match(&w.Tags); len(matches) > 0 {
			ww.buildAndInsert(geos, w, matches, geom.LineString)
			inserted = true
		}
		if w.IsClosed() {
			if matches := ww.polygonTagMatcher.Match(&w.Tags); len(matches) > 0 {
				ww.buildAndInsert(geos, w, matches, geom.Polygon)
				inserted = true
			}
		}

		if inserted && ww.expireTiles != nil {
			ww.expireTiles.ExpireFromNodes(w.Nodes)
		}
		if ww.diffCache != nil {
			ww.diffCache.Coords.AddFromWay(w)
		}
	}
	ww.wg.Done()
}

type geomBuilder func(*geos.Geos, []element.Node) (*geos.Geom, error)

func (ww *WayWriter) buildAndInsert(geos *geos.Geos, w *element.Way, matches []mapping.Match, builder geomBuilder) {
	var err error
	// make copy to avoid interference with polygon/linestring matches
	way := element.Way(*w)
	geosgeom, err := builder(geos, way.Nodes)
	if err != nil {
		if err, ok := err.(ErrorLevel); ok {
			if err.Level() <= 0 {
				return
			}
		}
		log.Println(err)
		return
	}

	way.Geom, err = geom.AsGeomElement(geos, geosgeom)
	if err != nil {
		log.Println(err)
		return
	}

	if ww.clipper != nil {
		parts, err := ww.clipper.Clip(way.Geom.Geom)
		if err != nil {
			log.Println(err)
			return
		}
		for _, g := range parts {
			way := element.Way(*w)
			way.Geom = &element.Geometry{g, geos.AsEwkbHex(g)}
			ww.insertMatches(&way.OSMElem, matches)
		}
	} else {
		ww.insertMatches(&way.OSMElem, matches)
	}
}
<|endoftext|>


<commit_before>package hwaflib

func (ctx *Context) Version() string {
	version := "20140116"
	return version
}

func (ctx *Context) Revision() string {
	revision := "fc8f7d1"
	return revision
}

// EOF

<commit_msg>version: 20140814<commit_after>package hwaflib

func (ctx *Context) Version() string {
	version := "20140814"
	return version
}

func (ctx *Context) Revision() string {
	revision := "09081c1"
	return revision
}

// EOF

<|endoftext|>


<commit_before>package hwaflib

func (ctx *Context) Version() string {
	version := "20131206"
	return version
}

func (ctx *Context) Revision() string {
	revision := "76b771a"
	return revision
}

// EOF

<commit_msg>version: 20131206<commit_after>package hwaflib

func (ctx *Context) Version() string {
	version := "20131206"
	return version
}

func (ctx *Context) Revision() string {
	revision := "b201ca9"
	return revision
}

// EOF

<|endoftext|>


<commit_before>package smux

import (
	"bytes"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

// Stream implements io.ReadWriteCloser
type Stream struct {
	id            uint32
	rstflag       int32
	sess          *Session
	buffer        bytes.Buffer
	bufferLock    sync.Mutex
	frameSize     int
	chReadEvent   chan struct{} // notify a read event
	die           chan struct{} // flag the stream has closed
	dieLock       sync.Mutex
	readDeadline  atomic.Value
	writeDeadline atomic.Value
}

// newStream initiates a Stream struct
func newStream(id uint32, frameSize int, sess *Session) *Stream {
	s := new(Stream)
	s.id = id
	s.chReadEvent = make(chan struct{}, 1)
	s.frameSize = frameSize
	s.sess = sess
	s.die = make(chan struct{})
	return s
}

// ID returns the unique stream ID.
func (s *Stream) ID() uint32 {
    return s.id
}

// Read implements io.ReadWriteCloser
func (s *Stream) Read(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(d.Sub(time.Now()))
		defer timer.Stop()
		deadline = timer.C
	}

READ:
	select {
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	case <-deadline:
		return n, errTimeout
	default:
	}

	s.bufferLock.Lock()
	n, err = s.buffer.Read(b)
	s.bufferLock.Unlock()

	if n > 0 {
		s.sess.returnTokens(n)
		return n, nil
	} else if atomic.LoadInt32(&s.rstflag) == 1 {
		_ = s.Close()
		return 0, io.EOF
	}

	select {
	case <-s.chReadEvent:
		goto READ
	case <-deadline:
		return n, errTimeout
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	}
}

// Write implements io.ReadWriteCloser
func (s *Stream) Write(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(d.Sub(time.Now()))
		defer timer.Stop()
		deadline = timer.C
	}

	select {
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	default:
	}

	frames := s.split(b, cmdPSH, s.id)
	sent := 0
	for k := range frames {
		req := writeRequest{
			frame:  frames[k],
			result: make(chan writeResult, 1),
		}

		select {
		case s.sess.writes <- req:
		case <-s.die:
			return sent, errors.New(errBrokenPipe)
		case <-deadline:
			return sent, errTimeout
		}

		select {
		case result := <-req.result:
			sent += result.n
			if result.err != nil {
				return sent, result.err
			}
		case <-s.die:
			return sent, errors.New(errBrokenPipe)
		case <-deadline:
			return sent, errTimeout
		}
	}
	return sent, nil
}

// Close implements io.ReadWriteCloser
func (s *Stream) Close() error {
	s.dieLock.Lock()

	select {
	case <-s.die:
		s.dieLock.Unlock()
		return errors.New(errBrokenPipe)
	default:
		close(s.die)
		s.dieLock.Unlock()
		s.sess.streamClosed(s.id)
		_, err := s.sess.writeFrame(newFrame(cmdRST, s.id))
		return err
	}
}

// SetReadDeadline sets the read deadline as defined by
// net.Conn.SetReadDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline.Store(t)
	return nil
}

// SetWriteDeadline sets the write deadline as defined by
// net.Conn.SetWriteDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline.Store(t)
	return nil
}

// SetDeadline sets both read and write deadlines as defined by
// net.Conn.SetDeadline.
// A zero time value disables the deadlines.
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return err
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// session closes the stream
func (s *Stream) sessionClose() {
	s.dieLock.Lock()
	defer s.dieLock.Unlock()

	select {
	case <-s.die:
	default:
		close(s.die)
	}
}

// LocalAddr satisfies net.Conn interface
func (s *Stream) LocalAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		LocalAddr() net.Addr
	}); ok {
		return ts.LocalAddr()
	}
	return nil
}

// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		RemoteAddr() net.Addr
	}); ok {
		return ts.RemoteAddr()
	}
	return nil
}

// pushBytes a slice into buffer
func (s *Stream) pushBytes(p []byte) {
	s.bufferLock.Lock()
	s.buffer.Write(p)
	s.bufferLock.Unlock()
}

// recycleTokens transform remaining bytes to tokens(will truncate buffer)
func (s *Stream) recycleTokens() (n int) {
	s.bufferLock.Lock()
	n = s.buffer.Len()
	s.buffer.Reset()
	s.bufferLock.Unlock()
	return
}

// split large byte buffer into smaller frames, reference only
func (s *Stream) split(bts []byte, cmd byte, sid uint32) []Frame {
	var frames []Frame
	for len(bts) > s.frameSize {
		frame := newFrame(cmd, sid)
		frame.data = bts[:s.frameSize]
		bts = bts[s.frameSize:]
		frames = append(frames, frame)
	}
	if len(bts) > 0 {
		frame := newFrame(cmd, sid)
		frame.data = bts
		frames = append(frames, frame)
	}
	return frames
}

// notify read event
func (s *Stream) notifyReadEvent() {
	select {
	case s.chReadEvent <- struct{}{}:
	default:
	}
}

// mark this stream has been reset
func (s *Stream) markRST() {
	atomic.StoreInt32(&s.rstflag, 1)
}

var errTimeout error = &timeoutError{}

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }
<commit_msg>update comments<commit_after>package smux

import (
	"bytes"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

// Stream implements net.Conn
type Stream struct {
	id            uint32
	rstflag       int32
	sess          *Session
	buffer        bytes.Buffer
	bufferLock    sync.Mutex
	frameSize     int
	chReadEvent   chan struct{} // notify a read event
	die           chan struct{} // flag the stream has closed
	dieLock       sync.Mutex
	readDeadline  atomic.Value
	writeDeadline atomic.Value
}

// newStream initiates a Stream struct
func newStream(id uint32, frameSize int, sess *Session) *Stream {
	s := new(Stream)
	s.id = id
	s.chReadEvent = make(chan struct{}, 1)
	s.frameSize = frameSize
	s.sess = sess
	s.die = make(chan struct{})
	return s
}

// ID returns the unique stream ID.
func (s *Stream) ID() uint32 {
	return s.id
}

// Read implements net.Conn
func (s *Stream) Read(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(d.Sub(time.Now()))
		defer timer.Stop()
		deadline = timer.C
	}

READ:
	select {
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	case <-deadline:
		return n, errTimeout
	default:
	}

	s.bufferLock.Lock()
	n, err = s.buffer.Read(b)
	s.bufferLock.Unlock()

	if n > 0 {
		s.sess.returnTokens(n)
		return n, nil
	} else if atomic.LoadInt32(&s.rstflag) == 1 {
		_ = s.Close()
		return 0, io.EOF
	}

	select {
	case <-s.chReadEvent:
		goto READ
	case <-deadline:
		return n, errTimeout
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	}
}

// Write implements net.Conn
func (s *Stream) Write(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(d.Sub(time.Now()))
		defer timer.Stop()
		deadline = timer.C
	}

	select {
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	default:
	}

	frames := s.split(b, cmdPSH, s.id)
	sent := 0
	for k := range frames {
		req := writeRequest{
			frame:  frames[k],
			result: make(chan writeResult, 1),
		}

		select {
		case s.sess.writes <- req:
		case <-s.die:
			return sent, errors.New(errBrokenPipe)
		case <-deadline:
			return sent, errTimeout
		}

		select {
		case result := <-req.result:
			sent += result.n
			if result.err != nil {
				return sent, result.err
			}
		case <-s.die:
			return sent, errors.New(errBrokenPipe)
		case <-deadline:
			return sent, errTimeout
		}
	}
	return sent, nil
}

// Close implements net.Conn
func (s *Stream) Close() error {
	s.dieLock.Lock()

	select {
	case <-s.die:
		s.dieLock.Unlock()
		return errors.New(errBrokenPipe)
	default:
		close(s.die)
		s.dieLock.Unlock()
		s.sess.streamClosed(s.id)
		_, err := s.sess.writeFrame(newFrame(cmdRST, s.id))
		return err
	}
}

// SetReadDeadline sets the read deadline as defined by
// net.Conn.SetReadDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline.Store(t)
	return nil
}

// SetWriteDeadline sets the write deadline as defined by
// net.Conn.SetWriteDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline.Store(t)
	return nil
}

// SetDeadline sets both read and write deadlines as defined by
// net.Conn.SetDeadline.
// A zero time value disables the deadlines.
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return err
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// session closes the stream
func (s *Stream) sessionClose() {
	s.dieLock.Lock()
	defer s.dieLock.Unlock()

	select {
	case <-s.die:
	default:
		close(s.die)
	}
}

// LocalAddr satisfies net.Conn interface
func (s *Stream) LocalAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		LocalAddr() net.Addr
	}); ok {
		return ts.LocalAddr()
	}
	return nil
}

// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		RemoteAddr() net.Addr
	}); ok {
		return ts.RemoteAddr()
	}
	return nil
}

// pushBytes a slice into buffer
func (s *Stream) pushBytes(p []byte) {
	s.bufferLock.Lock()
	s.buffer.Write(p)
	s.bufferLock.Unlock()
}

// recycleTokens transform remaining bytes to tokens(will truncate buffer)
func (s *Stream) recycleTokens() (n int) {
	s.bufferLock.Lock()
	n = s.buffer.Len()
	s.buffer.Reset()
	s.bufferLock.Unlock()
	return
}

// split large byte buffer into smaller frames, reference only
func (s *Stream) split(bts []byte, cmd byte, sid uint32) []Frame {
	var frames []Frame
	for len(bts) > s.frameSize {
		frame := newFrame(cmd, sid)
		frame.data = bts[:s.frameSize]
		bts = bts[s.frameSize:]
		frames = append(frames, frame)
	}
	if len(bts) > 0 {
		frame := newFrame(cmd, sid)
		frame.data = bts
		frames = append(frames, frame)
	}
	return frames
}

// notify read event
func (s *Stream) notifyReadEvent() {
	select {
	case s.chReadEvent <- struct{}{}:
	default:
	}
}

// mark this stream has been reset
func (s *Stream) markRST() {
	atomic.StoreInt32(&s.rstflag, 1)
}

var errTimeout error = &timeoutError{}

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }
<|endoftext|>


<commit_before>package slices

import "bytes"

// Return true if a slice contains 0 elements
// or if all elements in a slice have lengths of 0.
func IsEmpty(slc []string) bool {

	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		if slc[i] != "" {
			return false
		}
	}
	return true
}

// Return true if the elements of both slices have equal values.
// In order for elements to equal each other,
//   they do NOT need to point to the same memory location;
//   they only need to hold an equal value.
func Equals(slc1 []string, slc2 []string) bool {

	lenSlc1 := len(slc1)
	lenSlc2 := len(slc2)

	if lenSlc1 != lenSlc2 {
		return false
	}

	iMax := lenSlc1
	for i := 0; i < iMax; i++ {
		if slc1[i] != slc2[i] {
			return false
		}
	}
	return true
}

// Return true if both slices point to the same array.
// Probably not useful.
func IsSameArray(slc1 []string, slc2 []string) bool {
	return &slc1 == &slc2
}

// Concatenate/Join all elements of a slice into a single string.
func Concat(slc []string) string {

	var b bytes.Buffer
	defer b.Reset()

	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		b.WriteString(slc[i])
	}

	return b.String()
}

// Concatenate/Join all elements of a slice into a single string.
// Separate each element by a provided string.
func Join(slc []string, by string) string {

	lenSlc := len(slc)

	if isEmpty := (lenSlc == 0); isEmpty {
		return ""
	}

	var b bytes.Buffer
	defer b.Reset()

	iEnd := lenSlc - 1
	for i := 0; i < iEnd; i++ {
		b.WriteString(slc[i])
		b.WriteString(by)
	}
	b.WriteString(slc[iEnd])

	return b.String()
}

// Get a slice of a slice.
// Get the elements of a slice from index 'iBeg' to index 'iEnd'.
// 'iBeg' is inclusive (include the element at that index).
// 'iEnd' is exclusive (do not include the element at that index).
func Cut(slc []string, iBeg int, iEnd int) []string {

	if copyRequested := (iBeg == 0 && iEnd == -1); copyRequested {
		return Duplicate(slc)
	}

	if goToEnd := (iEnd == -1); goToEnd {
		return slc[iBeg:]
	}

	return slc[iBeg:iEnd]
}

// func Cut(slc []string, iBeg int, iEnd int) []string {

// 	if copyAll := (iBeg == 0 && iEnd == -1); copyAll {
// 		return Duplicate(slc)
// 	}

// 	if goToEnd := (iEnd == -1); goToEnd {
// 		return slc[iBeg:]
// 	}

// 	return slc[iBeg:iEnd]
// }

func Duplicate(slc []string) []string {
	newSlc := make([]string, len(slc))
	copy(newSlc, slc)
	return newSlc
}

// Remove the last element of a slice.
// Return the removed element along with the modified slice.
func Pop(slc []string) (string, []string) {

	iEnd := len(slc) - 1

	popped := slc[iEnd]
	cut := slc[:iEnd]

	return popped, cut
}

// func shift(slc []string) (string, []string) {
// 	return slc[0], slc[1:]
// }

// func unshift(slc []string, s string) []string {
// 	return append([]string{s}, slc...)
// }

func slcContains(slc []string, s string) bool {
	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		if slc[i] == s {
			return true
		}
	}
	return false
}

func slcIsEmpty(slc []string) bool {
	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		if slc[i] != "" {
			return false
		}
	}
	return true
}

func compact(args ...string) (compacted []string) {
	lenArgs := len(args)
	for i := 0; i < lenArgs; i++ {
		if s := args[i]; s != "" {
			compacted = append(compacted, s)
		}
	}
	return
}

func isEmpty(args ...string) bool {
	lenArgs := len(args)
	for i := 0; i < lenArgs; i++ {
		if notEmpty := args[i] != ""; notEmpty {
			return false
		}
	}
	return true
}

func filter(unfiltered []string, unwanted ...string) (filtered []string) {

	lenUnfiltered := len(unfiltered)
	for i := 0; i < lenUnfiltered; i++ {
		s := unfiltered[i]

		if isUnwanted := slcContains(unwanted, s); isUnwanted {
			continue
		}

		filtered = append(filtered, s)
	}

	return
}

func extract(excess []string, wanted ...string) (extracted []string) {

	lenExcess := len(excess)
	for i := 0; i < lenExcess; i++ {
		s := excess[i]

		if isWanted := slcContains(wanted, s); isWanted {
			extracted = append(extracted, s)
		}
	}

	return
}
<commit_msg>Fix IsSameArray()<commit_after>package slices

import "bytes"

// Return true if a slice contains 0 elements
// or if all elements in a slice have lengths of 0.
func IsEmpty(slc []string) bool {

	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		if slc[i] != "" {
			return false
		}
	}
	return true
}

// Return true if the elements of both slices have equal values.
// In order for elements to equal each other,
//   they do NOT need to point to the same memory location;
//   they only need to hold an equal value.
func Equals(slc1 []string, slc2 []string) bool {

	lenSlc1 := len(slc1)
	lenSlc2 := len(slc2)

	if lenSlc1 != lenSlc2 {
		return false
	}

	iMax := lenSlc1
	for i := 0; i < iMax; i++ {
		if slc1[i] != slc2[i] {
			return false
		}
	}
	return true
}

// Return true if both slices point to the same array.
// Probably not useful.
func IsSameArray(slc1 []string, slc2 []string) bool {
	return &slc1 != &slc2
}

// Concatenate/Join all elements of a slice into a single string.
func Concat(slc []string) string {

	var b bytes.Buffer
	defer b.Reset()

	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		b.WriteString(slc[i])
	}

	return b.String()
}

// Concatenate/Join all elements of a slice into a single string.
// Separate each element by a provided string.
func Join(slc []string, by string) string {

	lenSlc := len(slc)

	if isEmpty := (lenSlc == 0); isEmpty {
		return ""
	}

	var b bytes.Buffer
	defer b.Reset()

	iEnd := lenSlc - 1
	for i := 0; i < iEnd; i++ {
		b.WriteString(slc[i])
		b.WriteString(by)
	}
	b.WriteString(slc[iEnd])

	return b.String()
}

// Get a slice of a slice.
// Get the elements of a slice from index 'iBeg' to index 'iEnd'.
// 'iBeg' is inclusive (include the element at that index).
// 'iEnd' is exclusive (do not include the element at that index).
func Cut(slc []string, iBeg int, iEnd int) []string {

	if copyRequested := (iBeg == 0 && iEnd == -1); copyRequested {
		return Duplicate(slc)
	}

	if goToEnd := (iEnd == -1); goToEnd {
		return slc[iBeg:]
	}

	return slc[iBeg:iEnd]
}

// func Cut(slc []string, iBeg int, iEnd int) []string {

// 	if copyAll := (iBeg == 0 && iEnd == -1); copyAll {
// 		return Duplicate(slc)
// 	}

// 	if goToEnd := (iEnd == -1); goToEnd {
// 		return slc[iBeg:]
// 	}

// 	return slc[iBeg:iEnd]
// }

func Duplicate(slc []string) []string {
	newSlc := make([]string, len(slc))
	copy(newSlc, slc)
	return newSlc
}

// Remove the last element of a slice.
// Return the removed element along with the modified slice.
func Pop(slc []string) (string, []string) {

	iEnd := len(slc) - 1

	popped := slc[iEnd]
	cut := slc[:iEnd]

	return popped, cut
}

// func shift(slc []string) (string, []string) {
// 	return slc[0], slc[1:]
// }

// func unshift(slc []string, s string) []string {
// 	return append([]string{s}, slc...)
// }

func slcContains(slc []string, s string) bool {
	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		if slc[i] == s {
			return true
		}
	}
	return false
}

func slcIsEmpty(slc []string) bool {
	lenSlc := len(slc)
	for i := 0; i < lenSlc; i++ {
		if slc[i] != "" {
			return false
		}
	}
	return true
}

func compact(args ...string) (compacted []string) {
	lenArgs := len(args)
	for i := 0; i < lenArgs; i++ {
		if s := args[i]; s != "" {
			compacted = append(compacted, s)
		}
	}
	return
}

func isEmpty(args ...string) bool {
	lenArgs := len(args)
	for i := 0; i < lenArgs; i++ {
		if notEmpty := args[i] != ""; notEmpty {
			return false
		}
	}
	return true
}

func filter(unfiltered []string, unwanted ...string) (filtered []string) {

	lenUnfiltered := len(unfiltered)
	for i := 0; i < lenUnfiltered; i++ {
		s := unfiltered[i]

		if isUnwanted := slcContains(unwanted, s); isUnwanted {
			continue
		}

		filtered = append(filtered, s)
	}

	return
}

func extract(excess []string, wanted ...string) (extracted []string) {

	lenExcess := len(excess)
	for i := 0; i < lenExcess; i++ {
		s := excess[i]

		if isWanted := slcContains(wanted, s); isWanted {
			extracted = append(extracted, s)
		}
	}

	return
}
<|endoftext|>


<commit_before>package tui

import (
	"bytes"
	"image"
)

type testCell struct {
	Rune  rune
	Style Style
}

// A TestSurface implements the Surface interface with local buffers,
// and provides accessors to check the output of a draw operation on the Surface.
type TestSurface struct {
	cells   map[image.Point]testCell
	cursor  image.Point
	size    image.Point
	emptyCh rune
}

func NewTestSurface(w, h int) *TestSurface {
	return &TestSurface{
		cells:   make(map[image.Point]testCell),
		size:    image.Point{w, h},
		emptyCh: '.',
	}
}

func (s *TestSurface) SetCell(x, y int, ch rune, style Style) {
	s.cells[image.Point{x, y}] = testCell{
		Rune:  ch,
		Style: style,
	}
}

func (s *TestSurface) SetCursor(x, y int) {
	s.cursor = image.Point{x, y}
}

func (s *TestSurface) HideCursor() {
	s.cursor = image.Point{}
}

func (s *TestSurface) Begin() {
	s.cells = make(map[image.Point]testCell)
}

func (s *TestSurface) End() {
	// NOP
}

func (s *TestSurface) Size() image.Point {
	return s.size
}

// String writes the TestSurface's characters as a string.
func (s *TestSurface) String() string {
	var buf bytes.Buffer
	buf.WriteRune('\n')
	for j := 0; j < s.size.Y; j++ {
		for i := 0; i < s.size.X; i++ {
			if cell, ok := s.cells[image.Point{i, j}]; ok {
				buf.WriteRune(cell.Rune)
				if w := runeWidth(cell.Rune); w > 1 {
					i += w - 1
				}
			} else {
				buf.WriteRune(s.emptyCh)
			}
		}
		buf.WriteRune('\n')
	}
	return buf.String()
}

// FgColors renders the TestSurface's foreground colors, using the digit 0-7 for painted cells.
func (s *TestSurface) FgColors() string {
	var buf bytes.Buffer
	buf.WriteRune('\n')
	for j := 0; j < s.size.Y; j++ {
		for i := 0; i < s.size.X; i++ {
			if cell, ok := s.cells[image.Point{i, j}]; ok {
				color := cell.Style.Fg
				if cell.Style.Reverse {
					color = cell.Style.Bg
				}
				buf.WriteRune('0' + rune(color))
			} else {
				buf.WriteRune(s.emptyCh)
			}
		}
		buf.WriteRune('\n')
	}
	return buf.String()
}

// BgColors renders the TestSurface's background colors, using the digit 0-7 for painted cells.
func (s *TestSurface) BgColors() string {
	var buf bytes.Buffer
	buf.WriteRune('\n')
	for j := 0; j < s.size.Y; j++ {
		for i := 0; i < s.size.X; i++ {
			if cell, ok := s.cells[image.Point{i, j}]; ok {
				color := cell.Style.Bg
				if cell.Style.Reverse {
					color = cell.Style.Fg
				}
				buf.WriteRune('0' + rune(color))
			} else {
				buf.WriteRune(s.emptyCh)
			}
		}
		buf.WriteRune('\n')
	}
	return buf.String()
}

<commit_msg>Add TestSurface accessors for decorations.<commit_after>package tui

import (
	"bytes"
	"image"
	"strconv"
)

type testCell struct {
	Rune  rune
	Style Style
}

// A TestSurface implements the Surface interface with local buffers,
// and provides accessors to check the output of a draw operation on the Surface.
type TestSurface struct {
	cells   map[image.Point]testCell
	cursor  image.Point
	size    image.Point
	emptyCh rune
}

func NewTestSurface(w, h int) *TestSurface {
	return &TestSurface{
		cells:   make(map[image.Point]testCell),
		size:    image.Point{w, h},
		emptyCh: '.',
	}
}

func (s *TestSurface) SetCell(x, y int, ch rune, style Style) {
	s.cells[image.Point{x, y}] = testCell{
		Rune:  ch,
		Style: style,
	}
}

func (s *TestSurface) SetCursor(x, y int) {
	s.cursor = image.Point{x, y}
}

func (s *TestSurface) HideCursor() {
	s.cursor = image.Point{}
}

func (s *TestSurface) Begin() {
	s.cells = make(map[image.Point]testCell)
}

func (s *TestSurface) End() {
	// NOP
}

func (s *TestSurface) Size() image.Point {
	return s.size
}

// String writes the TestSurface's characters as a string.
func (s *TestSurface) String() string {
	var buf bytes.Buffer
	buf.WriteRune('\n')
	for j := 0; j < s.size.Y; j++ {
		for i := 0; i < s.size.X; i++ {
			if cell, ok := s.cells[image.Point{i, j}]; ok {
				buf.WriteRune(cell.Rune)
				if w := runeWidth(cell.Rune); w > 1 {
					i += w - 1
				}
			} else {
				buf.WriteRune(s.emptyCh)
			}
		}
		buf.WriteRune('\n')
	}
	return buf.String()
}

// FgColors renders the TestSurface's foreground colors, using the digit 0-7 for painted cells.
func (s *TestSurface) FgColors() string {
	var buf bytes.Buffer
	buf.WriteRune('\n')
	for j := 0; j < s.size.Y; j++ {
		for i := 0; i < s.size.X; i++ {
			if cell, ok := s.cells[image.Point{i, j}]; ok {
				color := cell.Style.Fg
				if cell.Style.Reverse {
					color = cell.Style.Bg
				}
				buf.WriteRune('0' + rune(color))
			} else {
				buf.WriteRune(s.emptyCh)
			}
		}
		buf.WriteRune('\n')
	}
	return buf.String()
}

// BgColors renders the TestSurface's background colors, using the digit 0-7 for painted cells.
func (s *TestSurface) BgColors() string {
	var buf bytes.Buffer
	buf.WriteRune('\n')
	for j := 0; j < s.size.Y; j++ {
		for i := 0; i < s.size.X; i++ {
			if cell, ok := s.cells[image.Point{i, j}]; ok {
				color := cell.Style.Bg
				if cell.Style.Reverse {
					color = cell.Style.Fg
				}
				buf.WriteRune('0' + rune(color))
			} else {
				buf.WriteRune(s.emptyCh)
			}
		}
		buf.WriteRune('\n')
	}
	return buf.String()
}

// Decorations renders the testSurface's decorations (Reverse, Bold, Underline) using a bitmask:
//	Reverse: 1
//	Bold: 2
//	Underline: 4
func (s *TestSurface) Decorations() string {
	var buf bytes.Buffer
	buf.WriteRune('\n')
	for j := 0; j < s.size.Y; j++ {
		for i := 0; i < s.size.X; i++ {
			if cell, ok := s.cells[image.Point{i, j}]; ok {
				mask := int64(0)
				if cell.Style.Reverse {
					mask |= 1
				}
				if cell.Style.Bold {
					mask |= 2
				}
				if cell.Style.Underline {
					mask |= 4
				}
				buf.WriteString(strconv.FormatInt(mask, 16))
			} else {
				buf.WriteRune(s.emptyCh)
			}
		}
		buf.WriteRune('\n')
	}
	return buf.String()

}

<|endoftext|>


<commit_before>package http

import (
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego/orm"
	"github.com/Cepave/open-falcon-backend/modules/query/g"
	"github.com/jasonlvhit/gocron"
	log "github.com/Sirupsen/logrus"
)

type Contacts struct {
	Id      int
	Name    string
	Phone   string
	Email   string
	Updated string
}

type Hosts struct {
	Id        int
	Hostname  string
	Exist     int
	Activate  int
	Platform  string
	Platforms string
	Idc       string
	Ip        string
	Isp       string
	Province  string
	City      string
	Status    string
	Updated   string
}

type Idcs struct {
	Id        int
	Popid     int
	Idc       string
	Bandwidth int
	Count     int
	Area      string
	Province  string
	City      string
	Updated   string
}

type Ips struct {
	Id       int
	Ip       string
	Exist    int
	Status   int
	Hostname string
	Platform string
	Updated  string
}

type Platforms struct {
	Id        int
	Platform  string
	Contacts  string
	Principal string
	Deputy    string
	Upgrader  string
	Count     int
	Updated   string
}

func SyncHostsAndContactsTable() {
	if g.Config().Hosts.Enabled || g.Config().Contacts.Enabled {
		if g.Config().Hosts.Enabled {
			updateMapData()
			syncHostsTable()
			intervalToSyncHostsTable := uint64(g.Config().Hosts.Interval)
			gocron.Every(intervalToSyncHostsTable).Seconds().Do(syncHostsTable)
		}
		if g.Config().Contacts.Enabled {
			syncContactsTable()
			intervalToSyncContactsTable := uint64(g.Config().Contacts.Interval)
			gocron.Every(intervalToSyncContactsTable).Seconds().Do(syncContactsTable)
		}
		<- gocron.Start()
	}
}

func getIDCMap() map[string]interface{} {
	idcMap := map[string]interface{}{}
	o := orm.NewOrm()
	var idcs []Idc
	sqlcommand := "SELECT pop_id, name, province, city FROM grafana.idc ORDER BY pop_id ASC"
	_, err := o.Raw(sqlcommand).QueryRows(&idcs)
	if err != nil {
		log.Errorf(err.Error())
	}
	for _, idc := range idcs {
		idcMap[strconv.Itoa(idc.Pop_id)] = idc
	}
	return idcMap
}

func updateHostsTable(hostnames []string, hostsMap map[string]map[string]string) {
	log.Debugf("func updateHostsTable()")
	var hosts []Hosts
	o := orm.NewOrm()
	o.Using("boss")
	_, err := o.QueryTable("hosts").All(&hosts)
	if err != nil {
		log.Errorf(err.Error())
	} else {
		format := "2006-01-02 15:04:05"
		for _, host := range hosts {
			updatedTime, _ := time.Parse(format, host.Updated)
			currentTime, _ := time.Parse(format, getNow())
			diff := currentTime.Unix() - updatedTime.Unix()
			if diff > 600 {
				host.Exist = 0
				_, err := o.Update(&host)
				if err != nil {
					log.Errorf(err.Error())
				}
			}
		}
	}

	idcMap := getIDCMap()
	var host Hosts
	for _, hostname := range hostnames {
		item := hostsMap[hostname]
		activate, _ := strconv.Atoi(item["activate"])
		host.Hostname = item["hostname"]
		host.Exist = 1
		host.Activate = activate
		host.Platform = item["platform"]
		host.Ip = item["ip"]
		host.Isp = strings.Split(item["hostname"], "-")[0]
		host.Updated = getNow()
		idcID := item["idcID"]
		if _, ok := idcMap[idcID]; ok {
			idc := idcMap[idcID]
			host.Idc = idc.(Idc).Name
			host.Province = idc.(Idc).Province
			host.City = idc.(Idc).City
		}
		hosts = append(hosts, host)
	}
	for _, item := range hosts {
		err := o.QueryTable("hosts").Filter("hostname", item.Hostname).One(&host)
		if err == orm.ErrNoRows {
			sql := "INSERT INTO boss.hosts("
			sql += "hostname, exist, activate, platform, idc, ip, "
			sql += "isp, province, city, updated) "
			sql += "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
			_, err := o.Raw(sql, item.Hostname, item.Exist, item.Activate, item.Platform, item.Idc, item.Ip, item.Isp, item.Province, item.City, item.Updated).Exec()
			if err != nil {
				log.Errorf(err.Error())
			}
		} else if err != nil {
			log.Errorf(err.Error())
		} else {
			item.Id = host.Id
			_, err := o.Update(&item)
			if err != nil {
				log.Errorf(err.Error())
			}
		}
	}
}

func updatePlatformsTable(platformNames []string, platformsMap map[string]map[string]interface{}) {
	log.Debugf("func updatePlatformsTable()")
	now := getNow()
	o := orm.NewOrm()
	o.Using("boss")
	var platform Platforms
	for _, platformName := range platformNames {
		count, err := o.QueryTable("ips").Filter("platform", platformName).Filter("exist", 1).Filter("status", 1).Exclude("hostname__isnull", true).Count()
		if err != nil {
			count = 0
		}
		group := platformsMap[platformName]
		err = o.QueryTable("platforms").Filter("platform", group["platformName"]).One(&platform)
		if err == orm.ErrNoRows {
			sql := "INSERT INTO boss.platforms(platform, count, updated) VALUES(?, ?, ?)"
			_, err := o.Raw(sql, group["platformName"], count, now).Exec()
			if err != nil {
				log.Errorf(err.Error())
			}
		} else if err != nil {
			log.Errorf(err.Error())
		} else {
			platform.Platform = group["platformName"].(string)
			platform.Count = int(count)
			platform.Updated = now
			_, err := o.Update(&platform)
			if err != nil {
				log.Errorf(err.Error())
			}
		}
	}
}

func updateContactsTable(contactNames []string, contactsMap map[string]map[string]string) {
	log.Debugf("func updateContactsTable()")
	o := orm.NewOrm()
	o.Using("boss")
	var contact Contacts
	for _, contactName := range contactNames {
		user := contactsMap[contactName]
		err := o.QueryTable("contacts").Filter("name", user["name"]).One(&contact)
		if err == orm.ErrNoRows {
			sql := "INSERT INTO boss.contacts(name, phone, email, updated) VALUES(?, ?, ?, ?)"
			_, err := o.Raw(sql, user["name"], user["phone"], user["email"], getNow()).Exec()
			if err != nil {
				log.Errorf(err.Error())
			}
		} else if err != nil {
			log.Errorf(err.Error())
		} else {
			contact.Email = user["email"]
			contact.Phone = user["phone"]
			contact.Updated = getNow()
			_, err := o.Update(&contact)
			if err != nil {
				log.Errorf(err.Error())
			}
		}
	}
}

func addContactsToPlatformsTable(contacts map[string]interface{}) {
	log.Debugf("func addContactsToPlatformsTable()")
	o := orm.NewOrm()
	o.Using("boss")
	var platforms []Platforms
	_, err := o.QueryTable("platforms").All(&platforms)
	if err != nil {
		log.Errorf(err.Error())
	} else {
		for _, platform := range platforms {
			contactsOfPlatform := []string{}
			platformName := platform.Platform
			if users, ok := contacts[platformName]; ok {
				for _, user := range users.([]interface{}) {
					contactName := user.(map[string]interface{})["name"].(string)
					contactsOfPlatform = appendUniqueString(contactsOfPlatform, contactName)
				}
			}
			if len(contactsOfPlatform) > 0 {
				platform.Contacts = strings.Join(contactsOfPlatform, ",")
				platform.Updated = getNow()
				_, err := o.Update(&platform)
				if err != nil {
					log.Errorf(err.Error())
				}
			}
		}
	}
}

func syncHostsTable() {
	o := orm.NewOrm()
	o.Using("boss")
	var rows []orm.Params
	sql := "SELECT updated FROM boss.hosts WHERE exist = 1 ORDER BY updated DESC LIMIT 1"
	num, err := o.Raw(sql).Values(&rows)
	if err != nil {
		log.Errorf(err.Error())
		return
	} else if num > 0 {
		format := "2006-01-02 15:04:05"
		updatedTime, _ := time.Parse(format, rows[0]["updated"].(string))
		currentTime, _ := time.Parse(format, getNow())
		diff := currentTime.Unix() - updatedTime.Unix()
		if int(diff) < g.Config().Hosts.Interval {
			return
		}
	}

	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	getPlatformJSON(nodes, result)
	if nodes["status"] == nil {
		return
	} else if int(nodes["status"].(float64)) != 1 {
		return
	}
	platformNames := []string{}
	platformsMap := map[string]map[string]interface{}{}
	hostnames := []string{}
	hostsMap := map[string]map[string]string{}
	hostnamesMap := map[string]int{}
	idcIDs := []string{}
	hostname := ""
	for _, platform := range nodes["result"].([]interface{}) {
		countOfHosts := 0
		platformName := platform.(map[string]interface{})["platform"].(string)
		platformNames = appendUniqueString(platformNames, platformName)
		for _, device := range platform.(map[string]interface{})["ip_list"].([]interface{}) {
			hostname = device.(map[string]interface{})["hostname"].(string)
			ip := device.(map[string]interface{})["ip"].(string)
			if len(ip) > 0 && ip == getIPFromHostname(hostname, result) {
				if _, ok := hostnamesMap[hostname]; !ok {
					hostnames = append(hostnames, hostname)
					idcID := device.(map[string]interface{})["pop_id"].(string)
					host := map[string]string{
						"hostname": hostname,
						"activate": device.(map[string]interface{})["ip_status"].(string),
						"platform": platformName,
						"idcID":    idcID,
						"ip":       ip,
					}
					hostsMap[hostname] = host
					idcIDs = appendUniqueString(idcIDs, idcID)
					hostnamesMap[hostname] = 1
					countOfHosts++
				}
			}
		}
		platformsMap[platformName] = map[string]interface{}{
			"platformName": platformName,
			"count": countOfHosts,
			"contacts": "",
		}
	}
	sort.Strings(hostnames)
	sort.Strings(platformNames)
	log.Debugf("platformNames =", platformNames)
	updateHostsTable(hostnames, hostsMap)
	updatePlatformsTable(platformNames, platformsMap)
}

func syncContactsTable() {
	log.Debugf("func syncContactsTable()")
	o := orm.NewOrm()
	o.Using("boss")
	var rows []orm.Params
	sql := "SELECT updated FROM boss.contacts ORDER BY updated DESC LIMIT 1"
	num, err := o.Raw(sql).Values(&rows)
	if err != nil {
		log.Errorf(err.Error())
		return
	} else if num > 0 {
		format := "2006-01-02 15:04:05"
		updatedTime, _ := time.Parse(format, rows[0]["updated"].(string))
		currentTime, _ := time.Parse(format, getNow())
		diff := currentTime.Unix() - updatedTime.Unix()
		if int(diff) < g.Config().Contacts.Interval {
			return
		}
	}

	platformNames := []string{}
	sql = "SELECT DISTINCT platform FROM boss.platforms ORDER BY platform ASC"
	num, err = o.Raw(sql).Values(&rows)
	if err != nil {
		log.Errorf(err.Error())
		return
	} else if num > 0 {
		for _, row := range rows {
			platformNames = append(platformNames, row["platform"].(string))
		}
	}

	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	getPlatformContact(strings.Join(platformNames, ","), nodes)
	contactNames := []string{}
	contactsMap := map[string]map[string]interface{}{}
	contacts := nodes["result"].(map[string]interface{})["items"].(map[string]interface{})
	for _, platformName := range platformNames {
		if items, ok := contacts[platformName]; ok {
			for _, user := range items.([]interface{}) {
				contactName := user.(map[string]interface{})["name"].(string)
				if _, ok := contactsMap[contactName]; !ok {
					contactsMap[contactName] = user.(map[string]interface{})
					contactNames = append(contactNames, contactName)
				}
			}
		}
	}
	sort.Strings(contactNames)
	updateContactsTable(contactNames, contactsMap)
	addContactsToPlatformsTable(contacts)
}
<commit_msg>[OWL-1165][query] refine func addContactsToPlatformsTable()<commit_after>package http

import (
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego/orm"
	"github.com/Cepave/open-falcon-backend/modules/query/g"
	"github.com/jasonlvhit/gocron"
	log "github.com/Sirupsen/logrus"
)

type Contacts struct {
	Id      int
	Name    string
	Phone   string
	Email   string
	Updated string
}

type Hosts struct {
	Id        int
	Hostname  string
	Exist     int
	Activate  int
	Platform  string
	Platforms string
	Idc       string
	Ip        string
	Isp       string
	Province  string
	City      string
	Status    string
	Updated   string
}

type Idcs struct {
	Id        int
	Popid     int
	Idc       string
	Bandwidth int
	Count     int
	Area      string
	Province  string
	City      string
	Updated   string
}

type Ips struct {
	Id       int
	Ip       string
	Exist    int
	Status   int
	Hostname string
	Platform string
	Updated  string
}

type Platforms struct {
	Id        int
	Platform  string
	Contacts  string
	Principal string
	Deputy    string
	Upgrader  string
	Count     int
	Updated   string
}

func SyncHostsAndContactsTable() {
	if g.Config().Hosts.Enabled || g.Config().Contacts.Enabled {
		if g.Config().Hosts.Enabled {
			updateMapData()
			syncHostsTable()
			intervalToSyncHostsTable := uint64(g.Config().Hosts.Interval)
			gocron.Every(intervalToSyncHostsTable).Seconds().Do(syncHostsTable)
		}
		if g.Config().Contacts.Enabled {
			syncContactsTable()
			intervalToSyncContactsTable := uint64(g.Config().Contacts.Interval)
			gocron.Every(intervalToSyncContactsTable).Seconds().Do(syncContactsTable)
		}
		<- gocron.Start()
	}
}

func getIDCMap() map[string]interface{} {
	idcMap := map[string]interface{}{}
	o := orm.NewOrm()
	var idcs []Idc
	sqlcommand := "SELECT pop_id, name, province, city FROM grafana.idc ORDER BY pop_id ASC"
	_, err := o.Raw(sqlcommand).QueryRows(&idcs)
	if err != nil {
		log.Errorf(err.Error())
	}
	for _, idc := range idcs {
		idcMap[strconv.Itoa(idc.Pop_id)] = idc
	}
	return idcMap
}

func updateHostsTable(hostnames []string, hostsMap map[string]map[string]string) {
	log.Debugf("func updateHostsTable()")
	var hosts []Hosts
	o := orm.NewOrm()
	o.Using("boss")
	_, err := o.QueryTable("hosts").All(&hosts)
	if err != nil {
		log.Errorf(err.Error())
	} else {
		format := "2006-01-02 15:04:05"
		for _, host := range hosts {
			updatedTime, _ := time.Parse(format, host.Updated)
			currentTime, _ := time.Parse(format, getNow())
			diff := currentTime.Unix() - updatedTime.Unix()
			if diff > 600 {
				host.Exist = 0
				_, err := o.Update(&host)
				if err != nil {
					log.Errorf(err.Error())
				}
			}
		}
	}

	idcMap := getIDCMap()
	var host Hosts
	for _, hostname := range hostnames {
		item := hostsMap[hostname]
		activate, _ := strconv.Atoi(item["activate"])
		host.Hostname = item["hostname"]
		host.Exist = 1
		host.Activate = activate
		host.Platform = item["platform"]
		host.Ip = item["ip"]
		host.Isp = strings.Split(item["hostname"], "-")[0]
		host.Updated = getNow()
		idcID := item["idcID"]
		if _, ok := idcMap[idcID]; ok {
			idc := idcMap[idcID]
			host.Idc = idc.(Idc).Name
			host.Province = idc.(Idc).Province
			host.City = idc.(Idc).City
		}
		hosts = append(hosts, host)
	}
	for _, item := range hosts {
		err := o.QueryTable("hosts").Filter("hostname", item.Hostname).One(&host)
		if err == orm.ErrNoRows {
			sql := "INSERT INTO boss.hosts("
			sql += "hostname, exist, activate, platform, idc, ip, "
			sql += "isp, province, city, updated) "
			sql += "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
			_, err := o.Raw(sql, item.Hostname, item.Exist, item.Activate, item.Platform, item.Idc, item.Ip, item.Isp, item.Province, item.City, item.Updated).Exec()
			if err != nil {
				log.Errorf(err.Error())
			}
		} else if err != nil {
			log.Errorf(err.Error())
		} else {
			item.Id = host.Id
			_, err := o.Update(&item)
			if err != nil {
				log.Errorf(err.Error())
			}
		}
	}
}

func updatePlatformsTable(platformNames []string, platformsMap map[string]map[string]interface{}) {
	log.Debugf("func updatePlatformsTable()")
	now := getNow()
	o := orm.NewOrm()
	o.Using("boss")
	var platform Platforms
	for _, platformName := range platformNames {
		count, err := o.QueryTable("ips").Filter("platform", platformName).Filter("exist", 1).Filter("status", 1).Exclude("hostname__isnull", true).Count()
		if err != nil {
			count = 0
		}
		group := platformsMap[platformName]
		err = o.QueryTable("platforms").Filter("platform", group["platformName"]).One(&platform)
		if err == orm.ErrNoRows {
			sql := "INSERT INTO boss.platforms(platform, count, updated) VALUES(?, ?, ?)"
			_, err := o.Raw(sql, group["platformName"], count, now).Exec()
			if err != nil {
				log.Errorf(err.Error())
			}
		} else if err != nil {
			log.Errorf(err.Error())
		} else {
			platform.Platform = group["platformName"].(string)
			platform.Count = int(count)
			platform.Updated = now
			_, err := o.Update(&platform)
			if err != nil {
				log.Errorf(err.Error())
			}
		}
	}
}

func updateContactsTable(contactNames []string, contactsMap map[string]map[string]string) {
	log.Debugf("func updateContactsTable()")
	o := orm.NewOrm()
	o.Using("boss")
	var contact Contacts
	for _, contactName := range contactNames {
		user := contactsMap[contactName]
		err := o.QueryTable("contacts").Filter("name", user["name"]).One(&contact)
		if err == orm.ErrNoRows {
			sql := "INSERT INTO boss.contacts(name, phone, email, updated) VALUES(?, ?, ?, ?)"
			_, err := o.Raw(sql, user["name"], user["phone"], user["email"], getNow()).Exec()
			if err != nil {
				log.Errorf(err.Error())
			}
		} else if err != nil {
			log.Errorf(err.Error())
		} else {
			contact.Email = user["email"]
			contact.Phone = user["phone"]
			contact.Updated = getNow()
			_, err := o.Update(&contact)
			if err != nil {
				log.Errorf(err.Error())
			}
		}
	}
}

func addContactsToPlatformsTable(contacts map[string]interface{}) {
	log.Debugf("func addContactsToPlatformsTable()")
	now := getNow()
	o := orm.NewOrm()
	o.Using("boss")
	var platforms []Platforms
	_, err := o.QueryTable("platforms").All(&platforms)
	if err != nil {
		log.Errorf(err.Error())
	} else {
		for _, platform := range platforms {
			platformName := platform.Platform
			if items, ok := contacts[platformName]; ok {
				contacts := []string{}
				for role, user := range items.(map[string]map[string]string) {
					if (role == "principal") {
						platform.Principal = user["name"]
					} else if (role == "deputy") {
						platform.Deputy = user["name"]
					} else if (role == "upgrader") {
						platform.Upgrader = user["name"]
					}
				}
				if (len(platform.Principal) > 0) {
					contacts = append(contacts, platform.Principal)
				}
				if (len(platform.Deputy) > 0) {
					contacts = append(contacts, platform.Deputy)
				}
				if (len(platform.Upgrader) > 0) {
					contacts = append(contacts, platform.Upgrader)
				}
				platform.Contacts = strings.Join(contacts, ",")
			}
			platform.Updated = now
			_, err := o.Update(&platform)
			if err != nil {
				log.Errorf(err.Error())
			}
		}
	}
}

func syncHostsTable() {
	o := orm.NewOrm()
	o.Using("boss")
	var rows []orm.Params
	sql := "SELECT updated FROM boss.hosts WHERE exist = 1 ORDER BY updated DESC LIMIT 1"
	num, err := o.Raw(sql).Values(&rows)
	if err != nil {
		log.Errorf(err.Error())
		return
	} else if num > 0 {
		format := "2006-01-02 15:04:05"
		updatedTime, _ := time.Parse(format, rows[0]["updated"].(string))
		currentTime, _ := time.Parse(format, getNow())
		diff := currentTime.Unix() - updatedTime.Unix()
		if int(diff) < g.Config().Hosts.Interval {
			return
		}
	}

	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	getPlatformJSON(nodes, result)
	if nodes["status"] == nil {
		return
	} else if int(nodes["status"].(float64)) != 1 {
		return
	}
	platformNames := []string{}
	platformsMap := map[string]map[string]interface{}{}
	hostnames := []string{}
	hostsMap := map[string]map[string]string{}
	hostnamesMap := map[string]int{}
	idcIDs := []string{}
	hostname := ""
	for _, platform := range nodes["result"].([]interface{}) {
		countOfHosts := 0
		platformName := platform.(map[string]interface{})["platform"].(string)
		platformNames = appendUniqueString(platformNames, platformName)
		for _, device := range platform.(map[string]interface{})["ip_list"].([]interface{}) {
			hostname = device.(map[string]interface{})["hostname"].(string)
			ip := device.(map[string]interface{})["ip"].(string)
			if len(ip) > 0 && ip == getIPFromHostname(hostname, result) {
				if _, ok := hostnamesMap[hostname]; !ok {
					hostnames = append(hostnames, hostname)
					idcID := device.(map[string]interface{})["pop_id"].(string)
					host := map[string]string{
						"hostname": hostname,
						"activate": device.(map[string]interface{})["ip_status"].(string),
						"platform": platformName,
						"idcID":    idcID,
						"ip":       ip,
					}
					hostsMap[hostname] = host
					idcIDs = appendUniqueString(idcIDs, idcID)
					hostnamesMap[hostname] = 1
					countOfHosts++
				}
			}
		}
		platformsMap[platformName] = map[string]interface{}{
			"platformName": platformName,
			"count": countOfHosts,
			"contacts": "",
		}
	}
	sort.Strings(hostnames)
	sort.Strings(platformNames)
	log.Debugf("platformNames =", platformNames)
	updateHostsTable(hostnames, hostsMap)
	updatePlatformsTable(platformNames, platformsMap)
}

func syncContactsTable() {
	log.Debugf("func syncContactsTable()")
	o := orm.NewOrm()
	o.Using("boss")
	var rows []orm.Params
	sql := "SELECT updated FROM boss.contacts ORDER BY updated DESC LIMIT 1"
	num, err := o.Raw(sql).Values(&rows)
	if err != nil {
		log.Errorf(err.Error())
		return
	} else if num > 0 {
		format := "2006-01-02 15:04:05"
		updatedTime, _ := time.Parse(format, rows[0]["updated"].(string))
		currentTime, _ := time.Parse(format, getNow())
		diff := currentTime.Unix() - updatedTime.Unix()
		if int(diff) < g.Config().Contacts.Interval {
			return
		}
	}

	platformNames := []string{}
	sql = "SELECT DISTINCT platform FROM boss.platforms ORDER BY platform ASC"
	num, err = o.Raw(sql).Values(&rows)
	if err != nil {
		log.Errorf(err.Error())
		return
	} else if num > 0 {
		for _, row := range rows {
			platformNames = append(platformNames, row["platform"].(string))
		}
	}

	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	getPlatformContact(strings.Join(platformNames, ","), nodes)
	contactNames := []string{}
	contactsMap := map[string]map[string]interface{}{}
	contacts := nodes["result"].(map[string]interface{})["items"].(map[string]interface{})
	for _, platformName := range platformNames {
		if items, ok := contacts[platformName]; ok {
			for _, user := range items.([]interface{}) {
				contactName := user.(map[string]interface{})["name"].(string)
				if _, ok := contactsMap[contactName]; !ok {
					contactsMap[contactName] = user.(map[string]interface{})
					contactNames = append(contactNames, contactName)
				}
			}
		}
	}
	sort.Strings(contactNames)
	updateContactsTable(contactNames, contactsMap)
	addContactsToPlatformsTable(contacts)
}
<|endoftext|>