<commit_before>package convert

import (
	"github.com/hashicorp/terraform/plugin/proto"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// WarnsAndErrorsToProto converts the warnings and errors return by the legacy
// provider to protobuf diagnostics.
func WarnsAndErrsToProto(warns []string, errs []error) (diags []*proto.Diagnostic) {
	for _, w := range warns {
		diags = AppendProtoDiag(diags, w)
	}

	for _, e := range errs {
		diags = AppendProtoDiag(diags, e)
	}

	return diags
}

// AppendProtoDiag appends a new diagnostic from a warning string or an error.
// This panics if d is not a string or error.
func AppendProtoDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic {
	switch d := d.(type) {
	case error:
		diags = append(diags, &proto.Diagnostic{
			Severity: proto.Diagnostic_ERROR,
			Summary:  d.Error(),
		})
	case string:
		diags = append(diags, &proto.Diagnostic{
			Severity: proto.Diagnostic_WARNING,
			Summary:  d,
		})
	case *proto.Diagnostic:
		diags = append(diags, d)
	case []*proto.Diagnostic:
		diags = append(diags, d...)
	}
	return diags
}

// ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics.
func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics
	for _, d := range ds {
		var severity tfdiags.Severity

		switch d.Severity {
		case proto.Diagnostic_ERROR:
			severity = tfdiags.Error
		case proto.Diagnostic_WARNING:
			severity = tfdiags.Warning
		}

		var newDiag tfdiags.Diagnostic

		// if there's an attribute path, we need to create a AttributeValue diagnostic
		if d.Attribute != nil {
			path := AttributePathToPath(d.Attribute)
			newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path)
		} else {
			newDiag = tfdiags.Sourceless(severity, d.Summary, d.Detail)
		}

		diags = diags.Append(newDiag)
	}

	return diags
}

// AttributePathToPath takes the proto encoded path and converts it to a cty.Path
func AttributePathToPath(ap *proto.AttributePath) cty.Path {
	var p cty.Path
	for _, step := range ap.Steps {
		switch selector := step.Selector.(type) {
		case *proto.AttributePath_Step_AttributeName:
			p = p.GetAttr(selector.AttributeName)
		case *proto.AttributePath_Step_ElementKeyString:
			p = p.Index(cty.StringVal(selector.ElementKeyString))
		case *proto.AttributePath_Step_ElementKeyInt:
			p = p.Index(cty.NumberIntVal(selector.ElementKeyInt))
		}
	}
	return p
}
<commit_msg>plugin/convert: Convert cty.PathError to diagnostics with attribute path<commit_after>package convert

import (
	"github.com/hashicorp/terraform/plugin/proto"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// WarnsAndErrorsToProto converts the warnings and errors return by the legacy
// provider to protobuf diagnostics.
func WarnsAndErrsToProto(warns []string, errs []error) (diags []*proto.Diagnostic) {
	for _, w := range warns {
		diags = AppendProtoDiag(diags, w)
	}

	for _, e := range errs {
		diags = AppendProtoDiag(diags, e)
	}

	return diags
}

// AppendProtoDiag appends a new diagnostic from a warning string or an error.
// This panics if d is not a string or error.
func AppendProtoDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic {
	switch d := d.(type) {
	case cty.PathError:
		ap := PathToAttributePath(d.Path)
		diags = append(diags, &proto.Diagnostic{
			Severity:  proto.Diagnostic_ERROR,
			Summary:   d.Error(),
			Attribute: ap,
		})
	case error:
		diags = append(diags, &proto.Diagnostic{
			Severity: proto.Diagnostic_ERROR,
			Summary:  d.Error(),
		})
	case string:
		diags = append(diags, &proto.Diagnostic{
			Severity: proto.Diagnostic_WARNING,
			Summary:  d,
		})
	case *proto.Diagnostic:
		diags = append(diags, d)
	case []*proto.Diagnostic:
		diags = append(diags, d...)
	}
	return diags
}

// ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics.
func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics
	for _, d := range ds {
		var severity tfdiags.Severity

		switch d.Severity {
		case proto.Diagnostic_ERROR:
			severity = tfdiags.Error
		case proto.Diagnostic_WARNING:
			severity = tfdiags.Warning
		}

		var newDiag tfdiags.Diagnostic

		// if there's an attribute path, we need to create a AttributeValue diagnostic
		if d.Attribute != nil {
			path := AttributePathToPath(d.Attribute)
			newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path)
		} else {
			newDiag = tfdiags.Sourceless(severity, d.Summary, d.Detail)
		}

		diags = diags.Append(newDiag)
	}

	return diags
}

// AttributePathToPath takes the proto encoded path and converts it to a cty.Path
func AttributePathToPath(ap *proto.AttributePath) cty.Path {
	var p cty.Path
	for _, step := range ap.Steps {
		switch selector := step.Selector.(type) {
		case *proto.AttributePath_Step_AttributeName:
			p = p.GetAttr(selector.AttributeName)
		case *proto.AttributePath_Step_ElementKeyString:
			p = p.Index(cty.StringVal(selector.ElementKeyString))
		case *proto.AttributePath_Step_ElementKeyInt:
			p = p.Index(cty.NumberIntVal(selector.ElementKeyInt))
		}
	}
	return p
}

// AttributePathToPath takes a cty.Path and converts it to a proto-encoded path.
func PathToAttributePath(p cty.Path) *proto.AttributePath {
	ap := &proto.AttributePath{}
	for _, step := range p {
		switch selector := step.(type) {
		case cty.GetAttrStep:
			ap.Steps = append(ap.Steps, &proto.AttributePath_Step{
				Selector: &proto.AttributePath_Step_AttributeName{
					AttributeName: selector.Name,
				},
			})
		case cty.IndexStep:
			key := selector.Key
			switch key.Type() {
			case cty.String:
				ap.Steps = append(ap.Steps, &proto.AttributePath_Step{
					Selector: &proto.AttributePath_Step_ElementKeyString{
						ElementKeyString: key.AsString(),
					},
				})
			case cty.Number:
				v, _ := key.AsBigFloat().Int64()
				ap.Steps = append(ap.Steps, &proto.AttributePath_Step{
					Selector: &proto.AttributePath_Step_ElementKeyInt{
						ElementKeyInt: v,
					},
				})
			default:
				// We'll bail early if we encounter anything else, and just
				// return the valid prefix.
				return ap
			}
		}
	}
	return ap
}
<|endoftext|>


<commit_before>package main

import (
    "flag"
    "fmt"
    "github.com/abh/geoip"
    "github.com/growse/pcap"
    "github.com/miekg/dns"
    "log"
    "net"
    "os"
    "strings"
)

const (
    TYPE_IP  = 0x0800
    TYPE_ARP = 0x0806
    TYPE_IP6 = 0x86DD

    IP_ICMP = 1
    IP_INIP = 4
    IP_TCP  = 6
    IP_UDP  = 17
)

var (
    device         = flag.String("i", "", "interface")
    statsd_address = flag.String("s", "", "statsd_address")
    verbose        = flag.Bool("v", false, "verbose")
    snaplen        = 65536
)

func flipstringslice(s []string) []string {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
    if len(s[0]) == 0 {
        s = s[1:]
    }
    return s
}

func main() {
    expr := "port 5300"
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "usage: %s [ -i interface ] [ -s statsd address ] [ -v ]\n", os.Args[0])
        os.Exit(1)
    }

    flag.Parse()

    if *statsd_address == "" {
        flag.Usage()
    }

    if *device == "" {
        devs, err := pcap.FindAllDevs()
        fmt.Println(devs)
        if err != "" {
            fmt.Fprintln(os.Stderr, "tcpdump: couldn't find any devices:", err)
        }
        if 0 == len(devs) {
            flag.Usage()
        }
        *device = devs[0].Name
    }
    h, err := pcap.OpenLive(*device, int32(snaplen), true, 500)
    if h == nil {
        log.Fatal(fmt.Sprintf("tcpdump fatal error: %s", err))
    }

    derr := h.SetDirection("out")
    if derr != nil {
        fmt.Println("tcpdump:", derr)
    }

    ferr := h.SetFilter(expr)
    if ferr != nil {
        fmt.Println("tcpdump:", ferr)
    }

    hostname, err := os.Hostname()

    if err == nil {
        statsd_namespace_prefix = fmt.Sprintf("%s.dnscollector.", hostname)
    }

    go statsd_pollloop(*statsd_address)
    geoasn, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION")
    }
    geocountry, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION")
    }
    geoasn6, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION_V6")
    }
    geocountry6, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION_V6")
    }
    var name, country string
    var ipOk bool
    var ipHdr *pcap.Iphdr
    var ip6Hdr *pcap.Ip6hdr
    for pkt, r := h.NextEx(); r >= 0; pkt, r = h.NextEx() {
        if r == 0 {
            // timeout, continue
            continue
        }
        pkt.Decode()
        if *verbose {
            fmt.Println(pkt)
        }
        msg := new(dns.Msg)
        err := msg.Unpack(pkt.Payload)
        // We only want packets which have been successfully unpacked
        // and have at least one answer
        if err == nil && len(msg.Answer) > 0 {
            if *verbose {
                fmt.Println(pkt)
            }
            if len(pkt.Headers) > 0 {
                //IPv4
                if ipHdr, ipOk = pkt.Headers[0].(*pcap.Iphdr); ipOk {
                    ip := net.IPv4(
                        ipHdr.DestIp[0],
                        ipHdr.DestIp[1],
                        ipHdr.DestIp[2],
                        ipHdr.DestIp[3],
                    ).String()
                    name, _ = geoasn.GetName(ip)
                    country, _ = geocountry.GetCountry(ip)
                } else if ip6Hdr, ipOk = pkt.Headers[0].(*pcap.Ip6hdr); ipOk {
                    //IPv6
                    ip := net.IP(ip6Hdr.DestIp).String()
                    name, _ = geoasn6.GetNameV6(ip)
                    country, _ = geocountry6.GetCountry_v6(ip)
                }
            }
            for i := range msg.Question {
                domainparts := strings.Split(strings.ToLower(msg.Question[i].Name), ".")
                domainparts = flipstringslice(domainparts)
                domainname := strings.Join(domainparts, ".")
                domainname = strings.Replace(domainname, ".", ">", -1)
                dnstype := strings.ToUpper(dns.TypeToString[msg.Question[i].Qtype])
                if len(name) == 0 {
                    name = "n/a"
                }
                if len(country) == 0 {
                    country = "n/a"
                }
                name = strings.Replace(name, ".", "{dot}", -1)
                country = strings.Replace(country, ".", "{dot}", -1)
                if *verbose {
                    fmt.Printf("name: %s type: %s asn %s country %s\n", domainname, dnstype, name, country)
                }
                key := fmt.Sprintf("%s.%s.%s.%s", domainname, name, country, dnstype)
                countermap.Lock()
                countermap.m[key]++
                countermap.Unlock()
                bytesmap.Lock()
                bytesmap.m[key] += pkt.Len
                bytesmap.Unlock()
            }
        }
    }
    fmt.Fprintln(os.Stderr, "tcpdump:", h.Geterror())

}
<commit_msg>Added dns port as a switch<commit_after>package main

import (
    "flag"
    "fmt"
    "github.com/abh/geoip"
    "github.com/growse/pcap"
    "github.com/miekg/dns"
    "log"
    "net"
    "os"
    "strings"
)

const (
    TYPE_IP  = 0x0800
    TYPE_ARP = 0x0806
    TYPE_IP6 = 0x86DD

    IP_ICMP = 1
    IP_INIP = 4
    IP_TCP  = 6
    IP_UDP  = 17
)

var (
    device         = flag.String("i", "", "interface")
    statsd_address = flag.String("s", "", "statsd_address")
    verbose        = flag.Bool("v", false, "verbose")
    dnsport        = flag.Int("p", 53, "dnsport")
    snaplen        = 65536
)

func flipstringslice(s []string) []string {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
    if len(s[0]) == 0 {
        s = s[1:]
    }
    return s
}

func main() {
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "usage: %s [ -p dnsport ] [ -i interface ] [ -s statsd address ] [ -v ]\n", os.Args[0])
        os.Exit(1)
    }

    flag.Parse()
    expr := fmt.Sprintf("port %d", *dnsport)

    if *statsd_address == "" {
        flag.Usage()
    }

    if *device == "" {
        devs, err := pcap.FindAllDevs()
        fmt.Println(devs)
        if err != "" {
            fmt.Fprintln(os.Stderr, "tcpdump: couldn't find any devices:", err)
        }
        if 0 == len(devs) {
            flag.Usage()
        }
        *device = devs[0].Name
    }
    h, err := pcap.OpenLive(*device, int32(snaplen), true, 500)
    if h == nil {
        log.Fatal(fmt.Sprintf("tcpdump fatal error: %s", err))
    }

    derr := h.SetDirection("out")
    if derr != nil {
        fmt.Println("tcpdump:", derr)
    }

    ferr := h.SetFilter(expr)
    if ferr != nil {
        fmt.Println("tcpdump:", ferr)
    }

    hostname, err := os.Hostname()

    if err == nil {
        statsd_namespace_prefix = fmt.Sprintf("%s.dnscollector.", hostname)
    }

    go statsd_pollloop(*statsd_address)
    geoasn, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION")
    }
    geocountry, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION")
    }
    geoasn6, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION_V6")
    }
    geocountry6, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION_V6")
    }
    var name, country string
    var ipOk bool
    var ipHdr *pcap.Iphdr
    var ip6Hdr *pcap.Ip6hdr
    for pkt, r := h.NextEx(); r >= 0; pkt, r = h.NextEx() {
        if r == 0 {
            // timeout, continue
            continue
        }
        pkt.Decode()
        if *verbose {
            fmt.Println(pkt)
        }
        msg := new(dns.Msg)
        err := msg.Unpack(pkt.Payload)
        // We only want packets which have been successfully unpacked
        // and have at least one answer
        if err == nil && len(msg.Answer) > 0 {
            if *verbose {
                fmt.Println(pkt)
            }
            if len(pkt.Headers) > 0 {
                //IPv4
                if ipHdr, ipOk = pkt.Headers[0].(*pcap.Iphdr); ipOk {
                    ip := net.IPv4(
                        ipHdr.DestIp[0],
                        ipHdr.DestIp[1],
                        ipHdr.DestIp[2],
                        ipHdr.DestIp[3],
                    ).String()
                    name, _ = geoasn.GetName(ip)
                    country, _ = geocountry.GetCountry(ip)
                } else if ip6Hdr, ipOk = pkt.Headers[0].(*pcap.Ip6hdr); ipOk {
                    //IPv6
                    ip := net.IP(ip6Hdr.DestIp).String()
                    name, _ = geoasn6.GetNameV6(ip)
                    country, _ = geocountry6.GetCountry_v6(ip)
                }
            }
            for i := range msg.Question {
                domainparts := strings.Split(strings.ToLower(msg.Question[i].Name), ".")
                domainparts = flipstringslice(domainparts)
                domainname := strings.Join(domainparts, ".")
                domainname = strings.Replace(domainname, ".", ">", -1)
                dnstype := strings.ToUpper(dns.TypeToString[msg.Question[i].Qtype])
                if len(name) == 0 {
                    name = "n/a"
                }
                if len(country) == 0 {
                    country = "n/a"
                }
                name = strings.Replace(name, ".", "{dot}", -1)
                country = strings.Replace(country, ".", "{dot}", -1)
                if *verbose {
                    fmt.Printf("name: %s type: %s asn %s country %s\n", domainname, dnstype, name, country)
                }
                key := fmt.Sprintf("%s.%s.%s.%s", domainname, name, country, dnstype)
                countermap.Lock()
                countermap.m[key]++
                countermap.Unlock()
                bytesmap.Lock()
                bytesmap.m[key] += pkt.Len
                bytesmap.Unlock()
            }
        }
    }
    fmt.Fprintln(os.Stderr, "tcpdump:", h.Geterror())

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	log "code.google.com/p/log4go"
	"github.com/Terry-Mao/goim/define"
	"github.com/Terry-Mao/goim/libs/io/ioutil"
	"net"
	"sync"
	"time"
)

// InitTCP listen all tcp.bind and start accept connections.
func InitTCP() (err error) {
	var (
		listener *net.TCPListener
		addr     *net.TCPAddr
	)
	for _, bind := range Conf.TCPBind {
		if addr, err = net.ResolveTCPAddr("tcp4", bind); err != nil {
			log.Error("net.ResolveTCPAddr(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		if listener, err = net.ListenTCP("tcp4", addr); err != nil {
			log.Error("net.ListenTCP(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		log.Debug("start tcp listen: \"%s\"", bind)
		// split N core accept
		for i := 0; i < Conf.MaxProc; i++ {
			go acceptTCP(DefaultServer, listener)
		}
	}
	return
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func acceptTCP(server *Server, lis *net.TCPListener) {
	var (
		conn *net.TCPConn
		err  error
		r    int
	)
	for {
		if conn, err = lis.AcceptTCP(); err != nil {
			// if listener close then return
			log.Error("listener.Accept(\"%s\") error(%v)", lis.Addr().String(), err)
			return
		}
		if err = conn.SetKeepAlive(Conf.TCPKeepalive); err != nil {
			log.Error("conn.SetKeepAlive() error(%v)", err)
			return
		}
		if err = conn.SetReadBuffer(Conf.TCPSndbuf); err != nil {
			log.Error("conn.SetReadBuffer() error(%v)", err)
			return
		}
		if err = conn.SetWriteBuffer(Conf.TCPRcvbuf); err != nil {
			log.Error("conn.SetWriteBuffer() error(%v)", err)
			return
		}
		go serveTCP(server, conn, r)
		if r++; r == maxInt {
			r = 0
		}
	}
}

func serveTCP(server *Server, conn *net.TCPConn, r int) {
	var (
		// bufpool
		rrp = server.round.Reader(r) // reader
		wrp = server.round.Writer(r) // writer
		// timer
		tr = server.round.Timer(r)
		// buf
		rr = NewBufioReaderSize(rrp, conn, Conf.ReadBufSize)  // reader buf
		wr = NewBufioWriterSize(wrp, conn, Conf.WriteBufSize) // writer buf
		// ip addr
		lAddr = conn.LocalAddr().String()
		rAddr = conn.RemoteAddr().String()
	)
	log.Debug("start tcp serve \"%s\" with \"%s\"", lAddr, rAddr)
	server.serveTCP(conn, rrp, wrp, rr, wr, tr)
}

func (server *Server) serveTCP(conn *net.TCPConn, rrp, wrp *sync.Pool, rr *bufio.Reader, wr *bufio.Writer, tr *Timer) {
	var (
		b   *Bucket
		p   *Proto
		key string
		hb  time.Duration // heartbeat
		err error
		trd *TimerData
		ch  = NewChannel(Conf.CliProto, Conf.SvrProto, define.NoRoom)
	)
	// auth
	if trd, err = tr.Add(Conf.HandshakeTimeout, conn); err != nil {
		log.Error("handshake: timer.Add() error(%v)", err)
	} else {
		if key, hb, err = server.authTCP(rr, wr, ch); err != nil {
			log.Error("handshake: server.auth error(%v)", err)
		}
		//deltimer
		tr.Del(trd)
	}
	// failed
	if err != nil {
		if err = conn.Close(); err != nil {
			log.Error("handshake: conn.Close() error(%v)", err)
		}
		PutBufioReader(rrp, rr)
		return
	}
	// register key->channel
	b = server.Bucket(key)
	b.Put(key, ch)
	// hanshake ok start dispatch goroutine
	go server.dispatchTCP(conn, wrp, wr, ch, hb, tr)
	for {
		// fetch a proto from channel free list
		if p, err = ch.CliProto.Set(); err != nil {
			// if full, simply close connection
			log.Error("%s fetch client proto error(%v)", key, err)
			goto failed
		}
		// parse request protocol
		if err = server.readTCPRequest(rr, p); err != nil {
			log.Error("%s read client request error(%v)", key, err)
			goto failed
		}
		// send to writer
		ch.CliProto.SetAdv()
		ch.Signal()
	}
failed:
	// dialog finish
	// may call twice
	if err = conn.Close(); err != nil {
		log.Error("reader: conn.Close() error(%v)", err)
	}
	PutBufioReader(rrp, rr)
	b.Del(key)
	log.Debug("wake up dispatch goroutine")
	ch.Finish()
	if err = server.operator.Disconnect(key, ch.RoomId); err != nil {
		log.Error("%s operator do disconnect error(%v)", key, err)
	}
	log.Debug("%s serverconn goroutine exit", key)
	return
}

// dispatch accepts connections on the listener and serves requests
// for each incoming connection.  dispatch blocks; the caller typically
// invokes it in a go statement.
func (server *Server) dispatchTCP(conn *net.TCPConn, wrp *sync.Pool, wr *bufio.Writer, ch *Channel, hb time.Duration, tr *Timer) {
	var (
		p   *Proto
		err error
		trd *TimerData
	)
	log.Debug("start dispatch goroutine")
	if trd, err = tr.Add(hb, conn); err != nil {
		log.Error("dispatch: timer.Add() error(%v)", err)
		goto failed
	}
	for {
		if !ch.Ready() {
			goto failed
		}
		// fetch message from clibox(client send)
		for {
			if p, err = ch.CliProto.Get(); err != nil {
				// must be empty error
				break
			}
			if p.Operation == define.OP_HEARTBEAT {
				// Use a previous timer value if difference between it and a new
				// value is less than TIMER_LAZY_DELAY milliseconds: this allows
				// to minimize the minheap operations for fast connections.
				if !trd.Lazy(hb) {
					tr.Del(trd)
					if trd, err = tr.Add(hb, conn); err != nil {
						log.Error("dispatch: timer.Add() error(%v)", err)
						goto failed
					}
				}
				// heartbeat
				p.Body = nil
				p.Operation = define.OP_HEARTBEAT_REPLY
			} else {
				// process message
				if err = server.operator.Operate(p); err != nil {
					log.Error("operator.Operate() error(%v)", err)
					goto failed
				}
			}
			if err = server.writeTCPResponse(wr, p); err != nil {
				log.Error("server.writeTCPResponse() error(%v)", err)
				goto failed
			}
			ch.CliProto.GetAdv()
		}
		// fetch message from svrbox(server send)
		for {
			if p, err = ch.SvrProto.Get(); err != nil {
				// must be empty error
				break
			}
			// just forward the message
			if err = server.writeTCPResponse(wr, p); err != nil {
				log.Error("server.writeTCPResponse() error(%v)", err)
				goto failed
			}
			ch.SvrProto.GetAdv()
		}
	}
failed:
	// wake reader up
	if err = conn.Close(); err != nil {
		log.Warn("conn.Close() error(%v)", err)
	}
	// deltimer
	tr.Del(trd)
	PutBufioWriter(wrp, wr)
	log.Debug("dispatch goroutine exit")
	return
}

// auth for goim handshake with client, use rsa & aes.
func (server *Server) authTCP(rr *bufio.Reader, wr *bufio.Writer, ch *Channel) (subKey string, heartbeat time.Duration, err error) {
	var p *Proto
	// WARN
	// don't adv the cli proto, after auth simply discard it.
	if p, err = ch.CliProto.Set(); err != nil {
		return
	}
	if err = server.readTCPRequest(rr, p); err != nil {
		return
	}
	if p.Operation != define.OP_AUTH {
		log.Warn("auth operation not valid: %d", p.Operation)
		err = ErrOperation
		return
	}
	if subKey, ch.RoomId, heartbeat, err = server.operator.Connect(p); err != nil {
		log.Error("operator.Connect error(%v)", err)
		return
	}
	p.Body = nil
	p.Operation = define.OP_AUTH_REPLY
	if err = server.writeTCPResponse(wr, p); err != nil {
		log.Error("[%s] server.sendTCPResponse() error(%v)", subKey, err)
	}
	return
}

// readRequest
func (server *Server) readTCPRequest(rr *bufio.Reader, proto *Proto) (err error) {
	var (
		packLen   int32
		headerLen int16
		bodyLen   int
	)
	if packLen, err = ioutil.ReadBigEndianInt32(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("packLen: %d", packLen)
	}
	if packLen > maxPackLen {
		return ErrProtoPackLen
	}
	if headerLen, err = ioutil.ReadBigEndianInt16(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("headerLen: %d", headerLen)
	}
	if headerLen != rawHeaderLen {
		return ErrProtoHeaderLen
	}
	if proto.Ver, err = ioutil.ReadBigEndianInt16(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("protoVer: %d", proto.Ver)
	}
	if proto.Operation, err = ioutil.ReadBigEndianInt32(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("operation: %d", proto.Operation)
	}
	if proto.SeqId, err = ioutil.ReadBigEndianInt32(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("seqId: %d", proto.SeqId)
	}
	bodyLen = int(packLen - int32(headerLen))
	if Conf.Debug {
		log.Debug("read body len: %d", bodyLen)
	}
	if bodyLen > 0 {
		proto.Body = proto.Buf[0:bodyLen]
		if err = ioutil.ReadAll(rr, proto.Body); err != nil {
			log.Error("body: ReadAll() error(%v)", err)
			return
		}
	} else {
		proto.Body = nil
	}
	if Conf.Debug {
		log.Debug("read proto: %v", proto)
	}
	return
}

// sendResponse send resp to client, sendResponse must be goroutine safe.
func (server *Server) writeTCPResponse(wr *bufio.Writer, proto *Proto) (err error) {
	if Conf.Debug {
		log.Debug("write proto: %v", proto)
	}
	if err = ioutil.WriteBigEndianInt32(wr, int32(rawHeaderLen)+int32(len(proto.Body))); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt16(wr, rawHeaderLen); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt16(wr, proto.Ver); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt32(wr, proto.Operation); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt32(wr, proto.SeqId); err != nil {
		return
	}
	if proto.Body != nil {
		if _, err = wr.Write(proto.Body); err != nil {
			return
		}
	}
	if err = wr.Flush(); err != nil {
		log.Error("tcp wr.Flush() error(%v)", err)
	}
	proto.Reset()
	return
}
<commit_msg>improve performance: batch write tcp response<commit_after>package main

import (
	"bufio"
	log "code.google.com/p/log4go"
	"github.com/Terry-Mao/goim/define"
	"github.com/Terry-Mao/goim/libs/io/ioutil"
	"net"
	"sync"
	"time"
)

// InitTCP listen all tcp.bind and start accept connections.
func InitTCP() (err error) {
	var (
		listener *net.TCPListener
		addr     *net.TCPAddr
	)
	for _, bind := range Conf.TCPBind {
		if addr, err = net.ResolveTCPAddr("tcp4", bind); err != nil {
			log.Error("net.ResolveTCPAddr(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		if listener, err = net.ListenTCP("tcp4", addr); err != nil {
			log.Error("net.ListenTCP(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		log.Debug("start tcp listen: \"%s\"", bind)
		// split N core accept
		for i := 0; i < Conf.MaxProc; i++ {
			go acceptTCP(DefaultServer, listener)
		}
	}
	return
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func acceptTCP(server *Server, lis *net.TCPListener) {
	var (
		conn *net.TCPConn
		err  error
		r    int
	)
	for {
		if conn, err = lis.AcceptTCP(); err != nil {
			// if listener close then return
			log.Error("listener.Accept(\"%s\") error(%v)", lis.Addr().String(), err)
			return
		}
		if err = conn.SetKeepAlive(Conf.TCPKeepalive); err != nil {
			log.Error("conn.SetKeepAlive() error(%v)", err)
			return
		}
		if err = conn.SetReadBuffer(Conf.TCPSndbuf); err != nil {
			log.Error("conn.SetReadBuffer() error(%v)", err)
			return
		}
		if err = conn.SetWriteBuffer(Conf.TCPRcvbuf); err != nil {
			log.Error("conn.SetWriteBuffer() error(%v)", err)
			return
		}
		go serveTCP(server, conn, r)
		if r++; r == maxInt {
			r = 0
		}
	}
}

func serveTCP(server *Server, conn *net.TCPConn, r int) {
	var (
		// bufpool
		rrp = server.round.Reader(r) // reader
		wrp = server.round.Writer(r) // writer
		// timer
		tr = server.round.Timer(r)
		// buf
		rr = NewBufioReaderSize(rrp, conn, Conf.ReadBufSize)  // reader buf
		wr = NewBufioWriterSize(wrp, conn, Conf.WriteBufSize) // writer buf
		// ip addr
		lAddr = conn.LocalAddr().String()
		rAddr = conn.RemoteAddr().String()
	)
	log.Debug("start tcp serve \"%s\" with \"%s\"", lAddr, rAddr)
	server.serveTCP(conn, rrp, wrp, rr, wr, tr)
}

func (server *Server) serveTCP(conn *net.TCPConn, rrp, wrp *sync.Pool, rr *bufio.Reader, wr *bufio.Writer, tr *Timer) {
	var (
		b   *Bucket
		p   *Proto
		key string
		hb  time.Duration // heartbeat
		err error
		trd *TimerData
		ch  = NewChannel(Conf.CliProto, Conf.SvrProto, define.NoRoom)
	)
	// auth
	if trd, err = tr.Add(Conf.HandshakeTimeout, conn); err != nil {
		log.Error("handshake: timer.Add() error(%v)", err)
	} else {
		if key, hb, err = server.authTCP(rr, wr, ch); err != nil {
			log.Error("handshake: server.auth error(%v)", err)
		}
		//deltimer
		tr.Del(trd)
	}
	// failed
	if err != nil {
		if err = conn.Close(); err != nil {
			log.Error("handshake: conn.Close() error(%v)", err)
		}
		PutBufioReader(rrp, rr)
		return
	}
	// register key->channel
	b = server.Bucket(key)
	b.Put(key, ch)
	// hanshake ok start dispatch goroutine
	go server.dispatchTCP(conn, wrp, wr, ch, hb, tr)
	for {
		// fetch a proto from channel free list
		if p, err = ch.CliProto.Set(); err != nil {
			// if full, simply close connection
			log.Error("%s fetch client proto error(%v)", key, err)
			goto failed
		}
		// parse request protocol
		if err = server.readTCPRequest(rr, p); err != nil {
			log.Error("%s read client request error(%v)", key, err)
			goto failed
		}
		// send to writer
		ch.CliProto.SetAdv()
		ch.Signal()
	}
failed:
	// dialog finish
	// may call twice
	if err = conn.Close(); err != nil {
		log.Error("reader: conn.Close() error(%v)", err)
	}
	PutBufioReader(rrp, rr)
	b.Del(key)
	log.Debug("wake up dispatch goroutine")
	ch.Finish()
	if err = server.operator.Disconnect(key, ch.RoomId); err != nil {
		log.Error("%s operator do disconnect error(%v)", key, err)
	}
	log.Debug("%s serverconn goroutine exit", key)
	return
}

// dispatch accepts connections on the listener and serves requests
// for each incoming connection.  dispatch blocks; the caller typically
// invokes it in a go statement.
func (server *Server) dispatchTCP(conn *net.TCPConn, wrp *sync.Pool, wr *bufio.Writer, ch *Channel, hb time.Duration, tr *Timer) {
	var (
		p   *Proto
		err error
		trd *TimerData
	)
	log.Debug("start dispatch goroutine")
	if trd, err = tr.Add(hb, conn); err != nil {
		log.Error("dispatch: timer.Add() error(%v)", err)
		goto failed
	}
	for {
		if !ch.Ready() {
			goto failed
		}
		// fetch message from clibox(client send)
		for {
			if p, err = ch.CliProto.Get(); err != nil {
				// must be empty error
				break
			}
			if p.Operation == define.OP_HEARTBEAT {
				// Use a previous timer value if difference between it and a new
				// value is less than TIMER_LAZY_DELAY milliseconds: this allows
				// to minimize the minheap operations for fast connections.
				if !trd.Lazy(hb) {
					tr.Del(trd)
					if trd, err = tr.Add(hb, conn); err != nil {
						log.Error("dispatch: timer.Add() error(%v)", err)
						goto failed
					}
				}
				// heartbeat
				p.Body = nil
				p.Operation = define.OP_HEARTBEAT_REPLY
			} else {
				// process message
				if err = server.operator.Operate(p); err != nil {
					log.Error("operator.Operate() error(%v)", err)
					goto failed
				}
			}
			if err = server.writeTCPResponse(wr, p); err != nil {
				log.Error("server.writeTCPResponse() error(%v)", err)
				goto failed
			}
			ch.CliProto.GetAdv()
		}
		// fetch message from svrbox(server send)
		for {
			if p, err = ch.SvrProto.Get(); err != nil {
				// must be empty error
				break
			}
			// just forward the message
			if err = server.writeTCPResponse(wr, p); err != nil {
				log.Error("server.writeTCPResponse() error(%v)", err)
				goto failed
			}
			ch.SvrProto.GetAdv()
		}
		// only hungry flush response
		if err = wr.Flush(); err != nil {
			goto failed
		}
	}
failed:
	// wake reader up
	if err = conn.Close(); err != nil {
		log.Warn("conn.Close() error(%v)", err)
	}
	// deltimer
	tr.Del(trd)
	PutBufioWriter(wrp, wr)
	log.Debug("dispatch goroutine exit")
	return
}

// auth for goim handshake with client, use rsa & aes.
func (server *Server) authTCP(rr *bufio.Reader, wr *bufio.Writer, ch *Channel) (subKey string, heartbeat time.Duration, err error) {
	var p *Proto
	// WARN
	// don't adv the cli proto, after auth simply discard it.
	if p, err = ch.CliProto.Set(); err != nil {
		return
	}
	if err = server.readTCPRequest(rr, p); err != nil {
		return
	}
	if p.Operation != define.OP_AUTH {
		log.Warn("auth operation not valid: %d", p.Operation)
		err = ErrOperation
		return
	}
	if subKey, ch.RoomId, heartbeat, err = server.operator.Connect(p); err != nil {
		log.Error("operator.Connect error(%v)", err)
		return
	}
	p.Body = nil
	p.Operation = define.OP_AUTH_REPLY
	if err = server.writeTCPResponse(wr, p); err != nil {
		log.Error("[%s] server.sendTCPResponse() error(%v)", subKey, err)
	}
	err = wr.Flush()
	return
}

// readRequest
func (server *Server) readTCPRequest(rr *bufio.Reader, proto *Proto) (err error) {
	var (
		packLen   int32
		headerLen int16
		bodyLen   int
	)
	if packLen, err = ioutil.ReadBigEndianInt32(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("packLen: %d", packLen)
	}
	if packLen > maxPackLen {
		return ErrProtoPackLen
	}
	if headerLen, err = ioutil.ReadBigEndianInt16(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("headerLen: %d", headerLen)
	}
	if headerLen != rawHeaderLen {
		return ErrProtoHeaderLen
	}
	if proto.Ver, err = ioutil.ReadBigEndianInt16(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("protoVer: %d", proto.Ver)
	}
	if proto.Operation, err = ioutil.ReadBigEndianInt32(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("operation: %d", proto.Operation)
	}
	if proto.SeqId, err = ioutil.ReadBigEndianInt32(rr); err != nil {
		return
	}
	if Conf.Debug {
		log.Debug("seqId: %d", proto.SeqId)
	}
	bodyLen = int(packLen - int32(headerLen))
	if Conf.Debug {
		log.Debug("read body len: %d", bodyLen)
	}
	if bodyLen > 0 {
		proto.Body = proto.Buf[0:bodyLen]
		if err = ioutil.ReadAll(rr, proto.Body); err != nil {
			log.Error("body: ReadAll() error(%v)", err)
			return
		}
	} else {
		proto.Body = nil
	}
	if Conf.Debug {
		log.Debug("read proto: %v", proto)
	}
	return
}

// sendResponse send resp to client, sendResponse must be goroutine safe.
func (server *Server) writeTCPResponse(wr *bufio.Writer, proto *Proto) (err error) {
	if Conf.Debug {
		log.Debug("write proto: %v", proto)
	}
	var packLen = int32(rawHeaderLen) + int32(len(proto.Body))
	// no available memory flush response
	if wr.Available() < int(packLen) {
		if err = wr.Flush(); err != nil {
			return
		}
	}
	if err = ioutil.WriteBigEndianInt32(wr, packLen); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt16(wr, rawHeaderLen); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt16(wr, proto.Ver); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt32(wr, proto.Operation); err != nil {
		return
	}
	if err = ioutil.WriteBigEndianInt32(wr, proto.SeqId); err != nil {
		return
	}
	if proto.Body != nil {
		if _, err = wr.Write(proto.Body); err != nil {
			return
		}
	}
	proto.Reset()
	return
}
<|endoftext|>


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

package buildkite

import (
	"fmt"
	"time"
)

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Author of a commit (used in CreateBuild)
type Author struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
}

// Create a build.
type CreateBuild struct {
	Commit  string `json:"commit"`
	Branch  string `json:"branch"`
	Message string `json:"message"`

	// Optional fields
	Author                      Author            `json:"author,omitempty"`
	Env                         map[string]string `json:"env,omitempty"`
	MetaData                    map[string]string `json:"meta_data,omitempty"`
	IgnorePipelineBranchFilters bool              `json:"ignore_pipeline_branch_filters,omitempty"`
}

// Creator represents who created a build
type Creator struct {
	AvatarURL string     `json:"avatar_url"`
	CreatedAt *Timestamp `json:"created_at"`
	Email     string     `json:"email"`
	ID        string     `json:"id"`
	Name      string     `json:"name"`
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string                `json:"id,omitempty"`
	URL         *string                `json:"url,omitempty"`
	WebURL      *string                `json:"web_url,omitempty"`
	Number      *int                   `json:"number,omitempty"`
	State       *string                `json:"state,omitempty"`
	Message     *string                `json:"message,omitempty"`
	Commit      *string                `json:"commit,omitempty"`
	Branch      *string                `json:"branch,omitempty"`
	Env         map[string]interface{} `json:"env,omitempty"`
	CreatedAt   *Timestamp             `json:"created_at,omitempty"`
	ScheduledAt *Timestamp             `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp             `json:"started_at,omitempty"`
	FinishedAt  *Timestamp             `json:"finished_at,omitempty"`
	MetaData    interface{}            `json:"meta_data,omitempty"`
	Creator     *Creator               `json:"creator,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the pipeline this build is associated with
	Pipeline *Pipeline `json:"pipeline,omitempty"`
}

// Job represents a job run during a build in buildkite
type Job struct {
	ID              *string    `json:"id,omitempty"`
	Type            *string    `json:"type,omitempty"`
	Name            *string    `json:"name,omitempty"`
	State           *string    `json:"state,omitempty"`
	LogsURL         *string    `json:"logs_url,omitempty"`
	RawLogsURL      *string    `json:"raw_log_url,omitempty"`
	Command         *string    `json:"command,omitempty"`
	ExitStatus      *int       `json:"exit_status,omitempty"`
	ArtifactPaths   *string    `json:"artifact_paths,omitempty"`
	CreatedAt       *Timestamp `json:"created_at,omitempty"`
	ScheduledAt     *Timestamp `json:"scheduled_at,omitempty"`
	RunnableAt      *Timestamp `json:"runnable_at,omitempty"`
	StartedAt       *Timestamp `json:"started_at,omitempty"`
	FinishedAt      *Timestamp `json:"finished_at,omitempty"`
	Agent           Agent      `json:"agent,omitempty"`
	AgentQueryRules []string   `json:"agent_query_rules,omitempty"`
	WebURL          string     `json:"web_url"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// Filters the results by the user who created the build
	Creator string `url:"creator,omitempty"`

	// Filters the results by builds created on or after the given time
	CreatedFrom time.Time `url:"created_from,omitempty"`

	// Filters the results by builds created before the given time
	CreatedTo time.Time `url:"created_to,omitempty"`

	// Filters the results by builds finished on or after the given time
	FinishedFrom time.Time `url:"finished_from,omitempty"`

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State []string `url:"state,brackets,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	ListOptions
}

func (as *BuildsService) Create(org string, pipeline string, b *CreateBuild) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	req, err := as.client.NewRequest("POST", u, b)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := as.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (as *BuildsService) Get(org string, pipeline string, id string) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s", org, pipeline, id)

	req, err := as.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := as.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-all-builds
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByPipeline lists the builds for a pipeline within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-pipeline
func (bs *BuildsService) ListByPipeline(org string, pipeline string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}
<commit_msg>Added pull request information<commit_after>// Copyright 2014 Mark Wolfe. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildkite

import (
	"fmt"
	"time"
)

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Author of a commit (used in CreateBuild)
type Author struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
}

// Create a build.
type CreateBuild struct {
	Commit  string `json:"commit"`
	Branch  string `json:"branch"`
	Message string `json:"message"`

	// Optional fields
	Author                      Author            `json:"author,omitempty"`
	Env                         map[string]string `json:"env,omitempty"`
	MetaData                    map[string]string `json:"meta_data,omitempty"`
	IgnorePipelineBranchFilters bool              `json:"ignore_pipeline_branch_filters,omitempty"`
}

// Creator represents who created a build
type Creator struct {
	AvatarURL string     `json:"avatar_url"`
	CreatedAt *Timestamp `json:"created_at"`
	Email     string     `json:"email"`
	ID        string     `json:"id"`
	Name      string     `json:"name"`
}

// PullRequest represents a Github PR
type PullRequest struct {
	ID          *string    `json:"id,omitempty"`
	Base        *string    `json:"base,omitempty"`
	Repository  *string    `json:"repository,omitempty"`
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string                `json:"id,omitempty"`
	URL         *string                `json:"url,omitempty"`
	WebURL      *string                `json:"web_url,omitempty"`
	Number      *int                   `json:"number,omitempty"`
	State       *string                `json:"state,omitempty"`
	Message     *string                `json:"message,omitempty"`
	Commit      *string                `json:"commit,omitempty"`
	Branch      *string                `json:"branch,omitempty"`
	Env         map[string]interface{} `json:"env,omitempty"`
	CreatedAt   *Timestamp             `json:"created_at,omitempty"`
	ScheduledAt *Timestamp             `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp             `json:"started_at,omitempty"`
	FinishedAt  *Timestamp             `json:"finished_at,omitempty"`
	MetaData    interface{}            `json:"meta_data,omitempty"`
	Creator     *Creator               `json:"creator,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the pipeline this build is associated with
	Pipeline *Pipeline `json:"pipeline,omitempty"`

	// the pull request this build is associated with
	PullRequest *PullRequest `json:"pull_request,omitempty"`
}

// Job represents a job run during a build in buildkite
type Job struct {
	ID              *string    `json:"id,omitempty"`
	Type            *string    `json:"type,omitempty"`
	Name            *string    `json:"name,omitempty"`
	State           *string    `json:"state,omitempty"`
	LogsURL         *string    `json:"logs_url,omitempty"`
	RawLogsURL      *string    `json:"raw_log_url,omitempty"`
	Command         *string    `json:"command,omitempty"`
	ExitStatus      *int       `json:"exit_status,omitempty"`
	ArtifactPaths   *string    `json:"artifact_paths,omitempty"`
	CreatedAt       *Timestamp `json:"created_at,omitempty"`
	ScheduledAt     *Timestamp `json:"scheduled_at,omitempty"`
	RunnableAt      *Timestamp `json:"runnable_at,omitempty"`
	StartedAt       *Timestamp `json:"started_at,omitempty"`
	FinishedAt      *Timestamp `json:"finished_at,omitempty"`
	Agent           Agent      `json:"agent,omitempty"`
	AgentQueryRules []string   `json:"agent_query_rules,omitempty"`
	WebURL          string     `json:"web_url"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// Filters the results by the user who created the build
	Creator string `url:"creator,omitempty"`

	// Filters the results by builds created on or after the given time
	CreatedFrom time.Time `url:"created_from,omitempty"`

	// Filters the results by builds created before the given time
	CreatedTo time.Time `url:"created_to,omitempty"`

	// Filters the results by builds finished on or after the given time
	FinishedFrom time.Time `url:"finished_from,omitempty"`

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State []string `url:"state,brackets,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	ListOptions
}

func (as *BuildsService) Create(org string, pipeline string, b *CreateBuild) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	req, err := as.client.NewRequest("POST", u, b)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := as.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (as *BuildsService) Get(org string, pipeline string, id string) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s", org, pipeline, id)

	req, err := as.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := as.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-all-builds
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByPipeline lists the builds for a pipeline within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-pipeline
func (bs *BuildsService) ListByPipeline(org string, pipeline string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}
<|endoftext|>


<commit_before>package cache

import (
	"encoding/gob"
	"fmt"
	_ "gnd.la/cache/driver/memcache"
	_ "gnd.la/cache/driver/redis"
	"gnd.la/config"
	_ "gnd.la/encoding/codec/msgpack"
	"gnd.la/log"
	"net"
	"reflect"
	"testing"
	"time"
)

type T interface {
	Error(...interface{})
	Errorf(string, ...interface{})
	Logf(string, ...interface{})
}

var (
	tests = []func(T, *Cache){
		testSetGet,
		testSetGetBasic,
		testSetGetMulti,
		testSetExpires,
		testDelete,
		testBytes,
	}
	benchmarks = []func(T, *Cache){
		testSetGet,
	}
)

func newCache(url string) (*Cache, error) {
	return New(config.MustParseURL(url))
}

func testPort(port int) bool {
	conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

func deepEqual(a interface{}, b interface{}) bool {
	return reflect.DeepEqual(a, b)
}

type simple struct {
	A int
	B int
	C int
}

func testSetGet(t T, c *Cache) {
	// Set object
	s1 := simple{1, 2, 3}
	if err := c.Set("s", s1, 0); err != nil {
		t.Error(err)
	}
	// Set pointer
	if err := c.Set("sp", &s1, 0); err != nil {
		t.Error(err)
	}
	// Get object from object
	var s2 simple
	err := c.Get("s", &s2)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, s2) {
			t.Errorf("different items (object from object): %v and %v", s1, s2)
		}
	}
	// Get object from pointer
	var s3 simple
	err = c.Get("sp", &s3)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, s3) {
			t.Errorf("different items (object from pointer): %v and %v", s1, s3)
		}
	}
	// Get pointer from object
	var s4 *simple
	err = c.Get("s", &s4)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, *s4) {
			t.Errorf("different items (pointer from object): %v and %v", s1, *s4)
		}
	}
	// Get pointer from pointer
	var s5 *simple
	err = c.Get("sp", &s5)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, *s5) {
			t.Errorf("different items (pointer from pointer): %v and %v", s1, *s5)
		}
	}
}

func testSetGetBasic(t T, c *Cache) {
	a1 := 42
	if err := c.Set("a", a1, 0); err != nil {
		t.Error(err)
	}
	var a2 int
	if err := c.Get("a", &a2); err != nil {
		t.Error(err)
	}
	if !deepEqual(a1, a2) {
		t.Errorf("%v != %v", a1, a2)
	}
	s1 := "fortytwo"
	if err := c.Set("s", s1, 0); err != nil {
		t.Error(err)
	}
	var s2 string
	if err := c.Get("s", &s2); err != nil {
		t.Error(err)
	}
	if !deepEqual(s1, s2) {
		t.Errorf("%q != %q", s1, s2)
	}
}

func testSetGetMulti(t T, c *Cache) {
	items := [][]int{
		[]int{0},
		[]int{1},
		[]int{2},
		[]int{3},
		[]int{4},
	}
	keys := make([]string, len(items))
	for ii, v := range items {
		key := fmt.Sprintf("i:%d", ii)
		if err := c.Set(key, v, 0); err != nil {
			t.Error(err)
		}
		keys[ii] = key
	}
	objs, err := c.GetMulti(keys, (*[]int)(nil))
	//objs, err := c.GetMulti(keys, nil)
	t.Logf("result from multi-get %v", objs)
	if err != nil {
		t.Error(err)
	}
	if len(objs) != len(items) {
		t.Errorf("expecting %d items in multi get, got %d", len(items), len(objs))
	}
	for ii, v := range items {
		key := fmt.Sprintf("i:%d", ii)
		if !deepEqual(v, objs[key]) {
			t.Errorf("object in multi get different, %v (%T) and %v (%T)", v, v, objs[key], objs[key])
		}
	}
}

func testSetExpires(t T, c *Cache) {
	s := simple{1, 2, 3}
	if err := c.Set("s", s, 1); err != nil {
		t.Error(err)
	}
	time.Sleep(2 * time.Second)
	err := c.Get("s", nil)
	if err != ErrNotFound {
		t.Errorf("expecting ErrNotFound, got %v", err)
	}
}

func testDelete(t T, c *Cache) {
	s := simple{1, 2, 3}
	if err := c.Set("s", s, 0); err != nil {
		t.Error(err)
	}
	if err := c.Delete("s"); err != nil {
		t.Error(err)
	}
	err := c.Get("s", nil)
	if err != ErrNotFound {
		t.Errorf("expecting ErrNotFound, got %v", err)
	}
}

func testBytes(t T, c *Cache) {
	b := make([]byte, 1000)
	for ii := range b {
		b[ii] = byte(ii)
	}
	if err := c.SetBytes("b", b, 0); err != nil {
		t.Error(err)
	}
	cb, err := c.GetBytes("b")
	if err != nil {
		t.Error(err)
	} else if !deepEqual(b, cb) {
		t.Error("cached bytes differ")
	}
}

func testCache(t *testing.T, url string) {
	if testing.Verbose() {
		log.SetLevel(log.LDebug)
	}
	c, err := newCache(url)
	if err != nil {
		t.Fatal(err)
	}
	for _, v := range tests {
		v(t, c)
	}
}

func TestGobCodec(t *testing.T) {
	testCache(t, "memory://?codec=gob")
}

func TestJsonCodec(t *testing.T) {
	testCache(t, "memory://?codec=json")
}

func TestMsgpackCodec(t *testing.T) {
	testCache(t, "memory://?codec=msgpack")
}

func TestCompress(t *testing.T) {
	testCache(t, "memory://?min_compress=0&compress_level=9")
}

func TestPrefix(t *testing.T) {
	prefix := "foo"
	c1, err := newCache("memory://?prefix=" + prefix)
	if err != nil {
		t.Fatal(err)
	}
	c2, err := newCache("memory://")
	if err != nil {
		t.Fatal(err)
	}
	s1 := simple{1, 2, 3}
	key := "spre"
	if err := c1.Set(key, s1, 0); err != nil {
		t.Fatal(err)
	}
	if err := c2.Get(key, nil); err != ErrNotFound {
		t.Errorf("expecting ErrNotFound, got %v", err)
	}
	var s2 simple
	err = c2.Get(prefix+key, &s2)
	if err != nil {
		t.Error(err)
	} else if !deepEqual(s1, s2) {
		t.Errorf("different objects %v and %v", s1, s2)
	}
}

func TestMemcache(t *testing.T) {
	if !testPort(11211) {
		t.Skip("memcache is not running. start memcache on localhost to run this test")
	}
	testCache(t, "memcache://127.0.0.1")
}

func TestRedis(t *testing.T) {
	if !testPort(6379) {
		t.Skip("redis is not running. start redis on localhost to run this test")
	}
	testCache(t, "redis://127.0.0.1")
}

func TestMemoryCacheMaxSize(t *testing.T) {
	c, err := newCache("memory://?max_size=1K")
	if err != nil {
		t.Fatal(err)
	}
	data1 := make([]byte, 256)
	data2 := make([]byte, 512)
	c.SetBytes("k1", data1, 0)
	c.SetBytes("k2", data2, 0)
	c.SetBytes("k3", data2, 0)
	// Sleep for 0.1s so the goroutine has time
	// to remove items.
	time.Sleep(100 * time.Millisecond)
	// Should have evicted k2 or k3
	k1d, err := c.GetBytes("k1")
	if err != nil {
		t.Error(err)
	}
	if len(k1d) != len(data1) {
		t.Errorf("bad data for key k1")
	}
	_, err2 := c.GetBytes("k2")
	_, err3 := c.GetBytes("k3")
	if err2 == nil && err3 == nil {
		t.Errorf("should have evicted k2 or k3")
	}
}

func benchmarkCache(b *testing.B, config string) {
	c, err := newCache(config)
	if err != nil {
		b.Fatal(err)
	}
	log.SetLevel(log.LInfo)
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for _, v := range benchmarks {
			v(b, c)
		}
	}
}

func BenchmarkGobCache(b *testing.B) {
	benchmarkCache(b, "memory://?codec=gob")
}

func BenchmarkJsonCache(b *testing.B) {
	benchmarkCache(b, "memory://?codec=json")
}

func BenchmarkMemcacheCache(b *testing.B) {
	if !testPort(11211) {
		b.Skip("memcache is not running. start memcache on localhost to run this test")
	}
	benchmarkCache(b, "memcache://?codec=json")
}

func BenchmarkRedisCache(b *testing.B) {
	if !testPort(6379) {
		b.Skip("redis is not running. start redis on localhost to run this test")
	}
	benchmarkCache(b, "redis://?codec=json")
}

func BenchmarkMsgpackCache(b *testing.B) {
	benchmarkCache(b, "memory://?codec=msgpack")
}

func BenchmarkPrefixCache(b *testing.B) {
	benchmarkCache(b, "memory://?prefix=foo&codec=gob")
}

func init() {
	gob.Register((*simple)(nil))
}
<commit_msg>Add a benchmark for msgpack with memory cache<commit_after>package cache

import (
	"encoding/gob"
	"fmt"
	_ "gnd.la/cache/driver/memcache"
	_ "gnd.la/cache/driver/redis"
	"gnd.la/config"
	_ "gnd.la/encoding/codec/msgpack"
	"gnd.la/log"
	"net"
	"reflect"
	"testing"
	"time"
)

type T interface {
	Error(...interface{})
	Errorf(string, ...interface{})
	Logf(string, ...interface{})
}

var (
	tests = []func(T, *Cache){
		testSetGet,
		testSetGetBasic,
		testSetGetMulti,
		testSetExpires,
		testDelete,
		testBytes,
	}
	benchmarks = []func(T, *Cache){
		testSetGet,
	}
)

func newCache(url string) (*Cache, error) {
	return New(config.MustParseURL(url))
}

func testPort(port int) bool {
	conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

func deepEqual(a interface{}, b interface{}) bool {
	return reflect.DeepEqual(a, b)
}

type simple struct {
	A int
	B int
	C int
}

func testSetGet(t T, c *Cache) {
	// Set object
	s1 := simple{1, 2, 3}
	if err := c.Set("s", s1, 0); err != nil {
		t.Error(err)
	}
	// Set pointer
	if err := c.Set("sp", &s1, 0); err != nil {
		t.Error(err)
	}
	// Get object from object
	var s2 simple
	err := c.Get("s", &s2)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, s2) {
			t.Errorf("different items (object from object): %v and %v", s1, s2)
		}
	}
	// Get object from pointer
	var s3 simple
	err = c.Get("sp", &s3)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, s3) {
			t.Errorf("different items (object from pointer): %v and %v", s1, s3)
		}
	}
	// Get pointer from object
	var s4 *simple
	err = c.Get("s", &s4)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, *s4) {
			t.Errorf("different items (pointer from object): %v and %v", s1, *s4)
		}
	}
	// Get pointer from pointer
	var s5 *simple
	err = c.Get("sp", &s5)
	if err != nil {
		t.Error(err)
	} else {
		if !deepEqual(s1, *s5) {
			t.Errorf("different items (pointer from pointer): %v and %v", s1, *s5)
		}
	}
}

func testSetGetBasic(t T, c *Cache) {
	a1 := 42
	if err := c.Set("a", a1, 0); err != nil {
		t.Error(err)
	}
	var a2 int
	if err := c.Get("a", &a2); err != nil {
		t.Error(err)
	}
	if !deepEqual(a1, a2) {
		t.Errorf("%v != %v", a1, a2)
	}
	s1 := "fortytwo"
	if err := c.Set("s", s1, 0); err != nil {
		t.Error(err)
	}
	var s2 string
	if err := c.Get("s", &s2); err != nil {
		t.Error(err)
	}
	if !deepEqual(s1, s2) {
		t.Errorf("%q != %q", s1, s2)
	}
}

func testSetGetMulti(t T, c *Cache) {
	items := [][]int{
		[]int{0},
		[]int{1},
		[]int{2},
		[]int{3},
		[]int{4},
	}
	keys := make([]string, len(items))
	for ii, v := range items {
		key := fmt.Sprintf("i:%d", ii)
		if err := c.Set(key, v, 0); err != nil {
			t.Error(err)
		}
		keys[ii] = key
	}
	objs, err := c.GetMulti(keys, (*[]int)(nil))
	//objs, err := c.GetMulti(keys, nil)
	t.Logf("result from multi-get %v", objs)
	if err != nil {
		t.Error(err)
	}
	if len(objs) != len(items) {
		t.Errorf("expecting %d items in multi get, got %d", len(items), len(objs))
	}
	for ii, v := range items {
		key := fmt.Sprintf("i:%d", ii)
		if !deepEqual(v, objs[key]) {
			t.Errorf("object in multi get different, %v (%T) and %v (%T)", v, v, objs[key], objs[key])
		}
	}
}

func testSetExpires(t T, c *Cache) {
	s := simple{1, 2, 3}
	if err := c.Set("s", s, 1); err != nil {
		t.Error(err)
	}
	time.Sleep(2 * time.Second)
	err := c.Get("s", nil)
	if err != ErrNotFound {
		t.Errorf("expecting ErrNotFound, got %v", err)
	}
}

func testDelete(t T, c *Cache) {
	s := simple{1, 2, 3}
	if err := c.Set("s", s, 0); err != nil {
		t.Error(err)
	}
	if err := c.Delete("s"); err != nil {
		t.Error(err)
	}
	err := c.Get("s", nil)
	if err != ErrNotFound {
		t.Errorf("expecting ErrNotFound, got %v", err)
	}
}

func testBytes(t T, c *Cache) {
	b := make([]byte, 1000)
	for ii := range b {
		b[ii] = byte(ii)
	}
	if err := c.SetBytes("b", b, 0); err != nil {
		t.Error(err)
	}
	cb, err := c.GetBytes("b")
	if err != nil {
		t.Error(err)
	} else if !deepEqual(b, cb) {
		t.Error("cached bytes differ")
	}
}

func testCache(t *testing.T, url string) {
	if testing.Verbose() {
		log.SetLevel(log.LDebug)
	}
	c, err := newCache(url)
	if err != nil {
		t.Fatal(err)
	}
	for _, v := range tests {
		v(t, c)
	}
}

func TestGobCodec(t *testing.T) {
	testCache(t, "memory://?codec=gob")
}

func TestJsonCodec(t *testing.T) {
	testCache(t, "memory://?codec=json")
}

func TestMsgpackCodec(t *testing.T) {
	testCache(t, "memory://?codec=msgpack")
}

func TestCompress(t *testing.T) {
	testCache(t, "memory://?min_compress=0&compress_level=9")
}

func TestPrefix(t *testing.T) {
	prefix := "foo"
	c1, err := newCache("memory://?prefix=" + prefix)
	if err != nil {
		t.Fatal(err)
	}
	c2, err := newCache("memory://")
	if err != nil {
		t.Fatal(err)
	}
	s1 := simple{1, 2, 3}
	key := "spre"
	if err := c1.Set(key, s1, 0); err != nil {
		t.Fatal(err)
	}
	if err := c2.Get(key, nil); err != ErrNotFound {
		t.Errorf("expecting ErrNotFound, got %v", err)
	}
	var s2 simple
	err = c2.Get(prefix+key, &s2)
	if err != nil {
		t.Error(err)
	} else if !deepEqual(s1, s2) {
		t.Errorf("different objects %v and %v", s1, s2)
	}
}

func TestMemcache(t *testing.T) {
	if !testPort(11211) {
		t.Skip("memcache is not running. start memcache on localhost to run this test")
	}
	testCache(t, "memcache://127.0.0.1")
}

func TestRedis(t *testing.T) {
	if !testPort(6379) {
		t.Skip("redis is not running. start redis on localhost to run this test")
	}
	testCache(t, "redis://127.0.0.1")
}

func TestMemoryCacheMaxSize(t *testing.T) {
	c, err := newCache("memory://?max_size=1K")
	if err != nil {
		t.Fatal(err)
	}
	data1 := make([]byte, 256)
	data2 := make([]byte, 512)
	c.SetBytes("k1", data1, 0)
	c.SetBytes("k2", data2, 0)
	c.SetBytes("k3", data2, 0)
	// Sleep for 0.1s so the goroutine has time
	// to remove items.
	time.Sleep(100 * time.Millisecond)
	// Should have evicted k2 or k3
	k1d, err := c.GetBytes("k1")
	if err != nil {
		t.Error(err)
	}
	if len(k1d) != len(data1) {
		t.Errorf("bad data for key k1")
	}
	_, err2 := c.GetBytes("k2")
	_, err3 := c.GetBytes("k3")
	if err2 == nil && err3 == nil {
		t.Errorf("should have evicted k2 or k3")
	}
}

func benchmarkCache(b *testing.B, config string) {
	c, err := newCache(config)
	if err != nil {
		b.Fatal(err)
	}
	log.SetLevel(log.LInfo)
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for _, v := range benchmarks {
			v(b, c)
		}
	}
}

func BenchmarkGobCache(b *testing.B) {
	benchmarkCache(b, "memory://?codec=gob")
}

func BenchmarkJsonCache(b *testing.B) {
	benchmarkCache(b, "memory://?codec=json")
}

func BenchmarkMemcacheCache(b *testing.B) {
	if !testPort(11211) {
		b.Skip("memcache is not running. start memcache on localhost to run this test")
	}
	benchmarkCache(b, "memcache://?codec=json")
}

func BenchmarkRedisCache(b *testing.B) {
	if !testPort(6379) {
		b.Skip("redis is not running. start redis on localhost to run this test")
	}
	benchmarkCache(b, "redis://?codec=json")
}

func BenchmarkMemcacheMsgpackCache(b *testing.B) {
	if !testPort(11211) {
		b.Skip("memcache is not running. start memcache on localhost to run this test")
	}
	benchmarkCache(b, "memcache://?codec=msgpack")
}

func BenchmarkMsgpackCache(b *testing.B) {
	benchmarkCache(b, "memory://?codec=msgpack")
}

func BenchmarkPrefixCache(b *testing.B) {
	benchmarkCache(b, "memory://?prefix=foo&codec=gob")
}

func init() {
	gob.Register((*simple)(nil))
}
<|endoftext|>


<commit_before>package sms // import "github.com/mndrix/sms-over-xmpp"

import (
	"crypto/rand"
	"encoding/base32"
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"

	xco "github.com/mndrix/go-xco"
	"github.com/pkg/errors"
)

// ErrIgnoreMessage should be returned to indicate that a message
// should be ignored; as if it never happened.
var ErrIgnoreMessage = errors.New("ignore this message")

// Component represents an SMS-over-XMPP component
type Component struct {
	config Config

	// xmpp is the XMPP component which handles all interactions
	// with an XMPP server.
	xmpp *xco.Component
}

func Main(config Config) {
	sc := &Component{config: config}

	// start goroutine for handling XMPP
	xmppErr, err := sc.runXmppComponent()
	if err != nil {
		panic(err)
	}

	// start goroutine for handling HTTP
	httpErr := sc.runHttpServer()

	select {
	case err := <-httpErr:
		log.Printf("ERROR HTTP: %s", err)
	case err := <-xmppErr:
		log.Printf("ERROR XMPP: %s", err)
	}
}

func (sc *Component) runHttpServer() <-chan error {
	config := sc.config
	addr := fmt.Sprintf("%s:%d", config.HttpHost(), config.HttpPort())
	errCh := make(chan error)
	go func() {
		errCh <- http.ListenAndServe(addr, sc)
		close(errCh)
	}()
	return errCh
}

func (sc *Component) runXmppComponent() (<-chan error, error) {
	config := sc.config
	opts := xco.Options{
		Name:         config.ComponentName(),
		SharedSecret: config.SharedSecret(),
		Address:      fmt.Sprintf("%s:%d", config.XmppHost(), config.XmppPort()),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}
	c, err := xco.NewComponent(opts)
	if err != nil {
		return nil, err
	}

	c.MessageHandler = sc.onMessage
	c.PresenceHandler = sc.onPresence
	c.IqHandler = sc.onIq
	c.UnknownHandler = sc.onUnknown
	sc.xmpp = c

	errCh := make(chan error)
	go func() {
		errCh <- c.Run()
		close(errCh)
	}()
	return errCh, nil
}

func (sc *Component) onMessage(c *xco.Component, m *xco.Message) error {
	log.Printf("Message: %+v", m)
	if m.Body == "" {
		log.Printf("  ignoring message with empty body")
		return nil
	}

	// convert recipient address into a phone number
	toPhone, err := sc.config.AddressToPhone(m.To)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'to' address to phone")
	}

	// convert author's address into a phone number
	fromPhone, err := sc.config.AddressToPhone(m.From)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'from' address to phone")
	}

	// choose an SMS provider
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "choosing an SMS provider")
	}

	// send the message
	err = provider.SendSms(fromPhone, toPhone, m.Body)
	return errors.Wrap(err, "sending SMS")
}

func (sc *Component) onPresence(c *xco.Component, p *xco.Presence) error {
	log.Printf("Presence: %+v", p)
	return nil
}

func (sc *Component) onIq(c *xco.Component, iq *xco.Iq) error {
	log.Printf("Iq: %+v", iq)
	return nil
}

func (sc *Component) onUnknown(c *xco.Component, x *xml.StartElement) error {
	log.Printf("Unknown: %+v", x)
	return nil
}

func (sc *Component) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	msgSid := r.FormValue("MessageSid")
	log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

	// which SMS provider is applicable?
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		msg := "ignored during provider selection"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	fromPhone, toPhone, body, err := provider.ReceiveSms(r)

	// convert author's phone number into XMPP address
	from, err := sc.config.PhoneToAddress(fromPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on From address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: From address %s: %s", fromPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// convert recipient's phone number into XMPP address
	to, err := sc.config.PhoneToAddress(toPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on To address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: To address %s: %s", toPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// deliver message over XMPP
	msg := &xco.Message{
		XMLName: xml.Name{
			Local: "message",
			Space: "jabber:component:accept",
		},

		Header: xco.Header{
			From: from,
			To:   to,
			ID:   NewId(),
		},
		Type: "chat",
		Body: body,
	}
	err = sc.xmpp.Send(msg)
	if err != nil {
		log.Printf("ERROR: can't send message: %s", err)
	}
}

// NewId generates a random string which is suitable as an XMPP stanza
// ID.  The string contains enough entropy to be universally unique.
func NewId() string {
	// generate 128 random bits (6 more than standard UUID)
	bytes := make([]byte, 16)
	_, err := rand.Read(bytes)
	if err != nil {
		panic(err)
	}

	// convert them to base 32 encoding
	s := base32.StdEncoding.EncodeToString(bytes)
	return strings.ToLower(strings.TrimRight(s, "="))
}
<commit_msg>Try to maintain connection to XMPP server<commit_after>package sms // import "github.com/mndrix/sms-over-xmpp"

import (
	"crypto/rand"
	"encoding/base32"
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	xco "github.com/mndrix/go-xco"
	"github.com/pkg/errors"
)

// ErrIgnoreMessage should be returned to indicate that a message
// should be ignored; as if it never happened.
var ErrIgnoreMessage = errors.New("ignore this message")

// Component represents an SMS-over-XMPP component
type Component struct {
	config Config

	// xmpp is the XMPP component which handles all interactions
	// with an XMPP server.
	xmpp *xco.Component
}

func Main(config Config) {
	sc := &Component{config: config}

	// start goroutine for handling XMPP
	xmppErr, err := sc.runXmppComponent()
	if err != nil {
		panic(err)
	}

	// start goroutine for handling HTTP
	httpErr := sc.runHttpServer()

	select {
	case err := <-httpErr:
		log.Printf("ERROR HTTP: %s", err)
	case err := <-xmppErr:
		log.Printf("ERROR XMPP: %s", err)
	}
}

func (sc *Component) runHttpServer() <-chan error {
	config := sc.config
	addr := fmt.Sprintf("%s:%d", config.HttpHost(), config.HttpPort())
	errCh := make(chan error)
	go func() {
		errCh <- http.ListenAndServe(addr, sc)
		close(errCh)
	}()
	return errCh
}

func (sc *Component) runXmppComponent() (<-chan error, error) {
	config := sc.config
	opts := xco.Options{
		Name:         config.ComponentName(),
		SharedSecret: config.SharedSecret(),
		Address:      fmt.Sprintf("%s:%d", config.XmppHost(), config.XmppPort()),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}
	c, err := xco.NewComponent(opts)
	if err != nil {
		return nil, err
	}

	c.MessageHandler = sc.onMessage
	c.PresenceHandler = sc.onPresence
	c.IqHandler = sc.onIq
	c.UnknownHandler = sc.onUnknown
	sc.xmpp = c

	errCh := make(chan error)
	go func() {
		defer func() { close(errCh) }()
		for {
			errCh <- c.Run()
			log.Printf("lost XMPP connection. Reconnecting")
			time.Sleep(1 * time.Second)
		}
	}()
	return errCh, nil
}

func (sc *Component) onMessage(c *xco.Component, m *xco.Message) error {
	log.Printf("Message: %+v", m)
	if m.Body == "" {
		log.Printf("  ignoring message with empty body")
		return nil
	}

	// convert recipient address into a phone number
	toPhone, err := sc.config.AddressToPhone(m.To)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'to' address to phone")
	}

	// convert author's address into a phone number
	fromPhone, err := sc.config.AddressToPhone(m.From)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'from' address to phone")
	}

	// choose an SMS provider
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "choosing an SMS provider")
	}

	// send the message
	err = provider.SendSms(fromPhone, toPhone, m.Body)
	return errors.Wrap(err, "sending SMS")
}

func (sc *Component) onPresence(c *xco.Component, p *xco.Presence) error {
	log.Printf("Presence: %+v", p)
	return nil
}

func (sc *Component) onIq(c *xco.Component, iq *xco.Iq) error {
	log.Printf("Iq: %+v", iq)
	return nil
}

func (sc *Component) onUnknown(c *xco.Component, x *xml.StartElement) error {
	log.Printf("Unknown: %+v", x)
	return nil
}

func (sc *Component) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	msgSid := r.FormValue("MessageSid")
	log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

	// which SMS provider is applicable?
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		msg := "ignored during provider selection"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	fromPhone, toPhone, body, err := provider.ReceiveSms(r)

	// convert author's phone number into XMPP address
	from, err := sc.config.PhoneToAddress(fromPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on From address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: From address %s: %s", fromPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// convert recipient's phone number into XMPP address
	to, err := sc.config.PhoneToAddress(toPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on To address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: To address %s: %s", toPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// deliver message over XMPP
	msg := &xco.Message{
		XMLName: xml.Name{
			Local: "message",
			Space: "jabber:component:accept",
		},

		Header: xco.Header{
			From: from,
			To:   to,
			ID:   NewId(),
		},
		Type: "chat",
		Body: body,
	}
	err = sc.xmpp.Send(msg)
	if err != nil {
		log.Printf("ERROR: can't send message: %s", err)
	}
}

// NewId generates a random string which is suitable as an XMPP stanza
// ID.  The string contains enough entropy to be universally unique.
func NewId() string {
	// generate 128 random bits (6 more than standard UUID)
	bytes := make([]byte, 16)
	_, err := rand.Read(bytes)
	if err != nil {
		panic(err)
	}

	// convert them to base 32 encoding
	s := base32.StdEncoding.EncodeToString(bytes)
	return strings.ToLower(strings.TrimRight(s, "="))
}
<|endoftext|>


<commit_before>package containers

import (
	"fmt"
	"strings"
	"time"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"

	"github.com/mitchellh/mapstructure"
)

// Container represents a container resource.
type Container struct {
	// The total number of bytes stored in the container.
	Bytes int `json:"bytes" mapstructure:"bytes"`

	// The total number of objects stored in the container.
	Count int `json:"count" mapstructure:"count"`

	// The name of the container.
	Name string `json:"name" mapstructure:"name"`
}

// ContainerPage is the page returned by a pager when traversing over a
// collection of containers.
type ContainerPage struct {
	pagination.MarkerPageBase
}

// IsEmpty returns true if a ListResult contains no container names.
func (r ContainerPage) IsEmpty() (bool, error) {
	names, err := ExtractNames(r)
	if err != nil {
		return true, err
	}
	return len(names) == 0, nil
}

// LastMarker returns the last container name in a ListResult.
func (r ContainerPage) LastMarker() (string, error) {
	names, err := ExtractNames(r)
	if err != nil {
		return "", err
	}
	if len(names) == 0 {
		return "", nil
	}
	return names[len(names)-1], nil
}

// ExtractInfo is a function that takes a ListResult and returns the containers' information.
func ExtractInfo(page pagination.Page) ([]Container, error) {
	untyped := page.(ContainerPage).Body.([]interface{})
	results := make([]Container, len(untyped))
	for index, each := range untyped {
		container := each.(map[string]interface{})
		err := mapstructure.Decode(container, &results[index])
		if err != nil {
			return results, err
		}
	}
	return results, nil
}

// ExtractNames is a function that takes a ListResult and returns the containers' names.
func ExtractNames(page pagination.Page) ([]string, error) {
	casted := page.(ContainerPage)
	ct := casted.Header.Get("Content-Type")

	switch {
	case strings.HasPrefix(ct, "application/json"):
		parsed, err := ExtractInfo(page)
		if err != nil {
			return nil, err
		}

		names := make([]string, 0, len(parsed))
		for _, container := range parsed {
			names = append(names, container.Name)
		}
		return names, nil
	case strings.HasPrefix(ct, "text/plain"):
		names := make([]string, 0, 50)

		body := string(page.(ContainerPage).Body.([]uint8))
		for _, name := range strings.Split(body, "\n") {
			if len(name) > 0 {
				names = append(names, name)
			}
		}

		return names, nil
	default:
		return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
	}
}

// GetHeader represents the headers returned in the response from a Get request.
type GetHeader struct {
	BytesUsed     int64     `mapstructure:"X-Account-Bytes-Used"`
	ContentLength int64     `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	ObjectCount   int64     `mapstructure:"X-Account-Object-Count"`
	TransID       string    `mapstructure:"X-Trans-Id"`
}

// GetResult represents the result of a get operation.
type GetResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Get. To obtain
// a map of headers, call the ExtractHeader method on the GetResult.
func (gr GetResult) Extract() (GetHeader, error) {
	var gh GetHeader
	if gr.Err != nil {
		return gh, gr.Err
	}

	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
		return gh, err
	}

	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
		if err != nil {
			return gh, err
		}
		gh.Date = t
	}

	return gh, nil
}

// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
// and returns the custom metadata associated with the container.
func (gr GetResult) ExtractMetadata() (map[string]string, error) {
	if gr.Err != nil {
		return nil, gr.Err
	}
	metadata := make(map[string]string)
	for k, v := range gr.Header {
		if strings.HasPrefix(k, "X-Container-Meta-") {
			key := strings.TrimPrefix(k, "X-Container-Meta-")
			metadata[key] = v[0]
		}
	}
	return metadata, nil
}

// CreateHeader represents the headers returned in the response from a Create request.
type CreateHeader struct {
	ContentLength int64     `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans-Id"`
}

// CreateResult represents the result of a create operation. To extract the
// the headers from the HTTP response, you can invoke the 'ExtractHeader'
// method on the result struct.
type CreateResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Create. To obtain
// a map of headers, call the ExtractHeader method on the CreateResult.
func (cr CreateResult) Extract() (CreateHeader, error) {
	var ch CreateHeader
	if cr.Err != nil {
		return ch, cr.Err
	}

	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
		return ch, err
	}

	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
		if err != nil {
			return ch, err
		}
		ch.Date = t
	}

	return ch, nil
}

// UpdateHeader represents the headers returned in the response from a Update request.
type UpdateHeader struct {
	ContentLength int64     `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans-Id"`
}

// UpdateResult represents the result of an update operation. To extract the
// the headers from the HTTP response, you can invoke the 'ExtractHeader'
// method on the result struct.
type UpdateResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Update. To obtain
// a map of headers, call the ExtractHeader method on the UpdateResult.
func (ur UpdateResult) Extract() (UpdateHeader, error) {
	var uh UpdateHeader
	if ur.Err != nil {
		return uh, ur.Err
	}

	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
		return uh, err
	}

	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
		if err != nil {
			return uh, err
		}
		uh.Date = t
	}

	return uh, nil
}

// DeleteHeader represents the headers returned in the response from a Delete request.
type DeleteHeader struct {
	ContentLength int64     `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans-Id"`
}

// DeleteResult represents the result of a delete operation. To extract the
// the headers from the HTTP response, you can invoke the 'ExtractHeader'
// method on the result struct.
type DeleteResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Delete. To obtain
// a map of headers, call the ExtractHeader method on the DeleteResult.
func (dr DeleteResult) Extract() (DeleteHeader, error) {
	var dh DeleteHeader
	if dr.Err != nil {
		return dh, dr.Err
	}

	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
		return dh, err
	}

	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, dr.Header["Date"][0])
		if err != nil {
			return dh, err
		}
		dh.Date = t
	}

	return dh, nil
}
<commit_msg>added struct fields<commit_after>package containers

import (
	"fmt"
	"strings"
	"time"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"

	"github.com/mitchellh/mapstructure"
)

// Container represents a container resource.
type Container struct {
	// The total number of bytes stored in the container.
	Bytes int `json:"bytes" mapstructure:"bytes"`

	// The total number of objects stored in the container.
	Count int `json:"count" mapstructure:"count"`

	// The name of the container.
	Name string `json:"name" mapstructure:"name"`
}

// ContainerPage is the page returned by a pager when traversing over a
// collection of containers.
type ContainerPage struct {
	pagination.MarkerPageBase
}

// IsEmpty returns true if a ListResult contains no container names.
func (r ContainerPage) IsEmpty() (bool, error) {
	names, err := ExtractNames(r)
	if err != nil {
		return true, err
	}
	return len(names) == 0, nil
}

// LastMarker returns the last container name in a ListResult.
func (r ContainerPage) LastMarker() (string, error) {
	names, err := ExtractNames(r)
	if err != nil {
		return "", err
	}
	if len(names) == 0 {
		return "", nil
	}
	return names[len(names)-1], nil
}

// ExtractInfo is a function that takes a ListResult and returns the containers' information.
func ExtractInfo(page pagination.Page) ([]Container, error) {
	untyped := page.(ContainerPage).Body.([]interface{})
	results := make([]Container, len(untyped))
	for index, each := range untyped {
		container := each.(map[string]interface{})
		err := mapstructure.Decode(container, &results[index])
		if err != nil {
			return results, err
		}
	}
	return results, nil
}

// ExtractNames is a function that takes a ListResult and returns the containers' names.
func ExtractNames(page pagination.Page) ([]string, error) {
	casted := page.(ContainerPage)
	ct := casted.Header.Get("Content-Type")

	switch {
	case strings.HasPrefix(ct, "application/json"):
		parsed, err := ExtractInfo(page)
		if err != nil {
			return nil, err
		}

		names := make([]string, 0, len(parsed))
		for _, container := range parsed {
			names = append(names, container.Name)
		}
		return names, nil
	case strings.HasPrefix(ct, "text/plain"):
		names := make([]string, 0, 50)

		body := string(page.(ContainerPage).Body.([]uint8))
		for _, name := range strings.Split(body, "\n") {
			if len(name) > 0 {
				names = append(names, name)
			}
		}

		return names, nil
	default:
		return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
	}
}

// GetHeader represents the headers returned in the response from a Get request.
type GetHeader struct {
	AcceptRanges     string    `mapstructure:"Accept-Ranges"`
	BytesUsed        int64     `mapstructure:"X-Account-Bytes-Used"`
	ContentLength    int64     `mapstructure:"Content-Length"`
	ContentType      string    `mapstructure:"Content-Type"`
	Date             time.Time `mapstructure:"-"`
	ObjectCount      int64     `mapstructure:"X-Container-Object-Count"`
	Read             string    `mapstructure:"X-Container-Read"`
	TransID          string    `mapstructure:"X-Trans-Id"`
	VersionsLocation string    `mapstructure:"X-Versions-Location"`
	Write            string    `mapstructure:"X-Container-Write"`
}

// GetResult represents the result of a get operation.
type GetResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Get. To obtain
// a map of headers, call the ExtractHeader method on the GetResult.
func (gr GetResult) Extract() (GetHeader, error) {
	var gh GetHeader
	if gr.Err != nil {
		return gh, gr.Err
	}

	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
		return gh, err
	}

	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
		if err != nil {
			return gh, err
		}
		gh.Date = t
	}

	return gh, nil
}

// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
// and returns the custom metadata associated with the container.
func (gr GetResult) ExtractMetadata() (map[string]string, error) {
	if gr.Err != nil {
		return nil, gr.Err
	}
	metadata := make(map[string]string)
	for k, v := range gr.Header {
		if strings.HasPrefix(k, "X-Container-Meta-") {
			key := strings.TrimPrefix(k, "X-Container-Meta-")
			metadata[key] = v[0]
		}
	}
	return metadata, nil
}

// CreateHeader represents the headers returned in the response from a Create request.
type CreateHeader struct {
	ContentLength int64     `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans-Id"`
}

// CreateResult represents the result of a create operation. To extract the
// the headers from the HTTP response, you can invoke the 'ExtractHeader'
// method on the result struct.
type CreateResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Create. To obtain
// a map of headers, call the ExtractHeader method on the CreateResult.
func (cr CreateResult) Extract() (CreateHeader, error) {
	var ch CreateHeader
	if cr.Err != nil {
		return ch, cr.Err
	}

	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
		return ch, err
	}

	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
		if err != nil {
			return ch, err
		}
		ch.Date = t
	}

	return ch, nil
}

// UpdateHeader represents the headers returned in the response from a Update request.
type UpdateHeader struct {
	ContentLength int64     `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans-Id"`
}

// UpdateResult represents the result of an update operation. To extract the
// the headers from the HTTP response, you can invoke the 'ExtractHeader'
// method on the result struct.
type UpdateResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Update. To obtain
// a map of headers, call the ExtractHeader method on the UpdateResult.
func (ur UpdateResult) Extract() (UpdateHeader, error) {
	var uh UpdateHeader
	if ur.Err != nil {
		return uh, ur.Err
	}

	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
		return uh, err
	}

	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
		if err != nil {
			return uh, err
		}
		uh.Date = t
	}

	return uh, nil
}

// DeleteHeader represents the headers returned in the response from a Delete request.
type DeleteHeader struct {
	ContentLength int64     `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans-Id"`
}

// DeleteResult represents the result of a delete operation. To extract the
// the headers from the HTTP response, you can invoke the 'ExtractHeader'
// method on the result struct.
type DeleteResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Delete. To obtain
// a map of headers, call the ExtractHeader method on the DeleteResult.
func (dr DeleteResult) Extract() (DeleteHeader, error) {
	var dh DeleteHeader
	if dr.Err != nil {
		return dh, dr.Err
	}

	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
		return dh, err
	}

	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, dr.Header["Date"][0])
		if err != nil {
			return dh, err
		}
		dh.Date = t
	}

	return dh, nil
}
<|endoftext|>