<commit_before>package gocbcore

import (
	"encoding/binary"
	"time"
)

func (c *Agent) handleServerNmv(s *memdPipeline, req *memdQRequest, resp *memdResponse) {
	// Try to parse the value as a bucket configuration
	bk, err := parseConfig(resp.Value, s.Hostname())
	if err == nil {
		c.updateConfig(bk)
	}

	// Redirect it!  This may actually come back to this server, but I won't tell
	//   if you don't ;)
	c.redispatchDirect(req)
}

func (c *Agent) handleServerDeath(s *memdPipeline) {
	// Refresh the routing data with the existing configuration, this has
	//   the effect of attempting to rebuild the dead server.
	c.updateConfig(nil)

	// TODO(brett19): We probably should actually try other ways of resolving
	//  the issue, like requesting a new configuration.
}

func appendFeatureCode(bytes []byte, feature HelloFeature) []byte {
	bytes = append(bytes, 0, 0)
	binary.BigEndian.PutUint16(bytes[len(bytes)-2:], uint16(FeatureSeqNo))
	return bytes
}

func (c *Agent) tryHello(pipeline *memdPipeline, deadline time.Time) error {
	var featuresBytes []byte

	if c.useMutationTokens {
		featuresBytes = appendFeatureCode(featuresBytes, FeatureSeqNo)
	}

	if featuresBytes == nil {
		// If we have no features, we don't need to HELLO at all
		return nil
	}

	_, err := pipeline.ExecuteRequest(&memdQRequest{
		memdRequest: memdRequest{
			Magic:  ReqMagic,
			Opcode: CmdHello,
			Key:    []byte("Go SDK"),
			Value:  featuresBytes,
		},
	}, deadline)

	return err
}

// Attempt to connect a server, this function must be called
//  in its own goroutine and will ensure that offline servers
//  are not spammed with connection attempts.
func (agent *Agent) connectServer(server *memdPipeline) {
	for {
		agent.serverFailuresLock.Lock()
		failureTime := agent.serverFailures[server.address]
		agent.serverFailuresLock.Unlock()

		if !failureTime.IsZero() {
			waitedTime := time.Since(failureTime)
			if waitedTime < agent.serverWaitTimeout {
				time.Sleep(agent.serverWaitTimeout - waitedTime)

				if !agent.checkPendingServer(server) {
					// Server is no longer pending.  Stop trying.
					break
				}
			}
		}

		err := agent.connectPipeline(server, time.Now().Add(agent.serverConnectTimeout))
		if err != nil {
			agent.serverFailuresLock.Lock()
			agent.serverFailures[server.address] = time.Now()
			agent.serverFailuresLock.Unlock()

			// Try to connect again
			continue
		}

		if !agent.activatePendingServer(server) {
			// If this is no longer a valid pending server, we should shut
			//   it down!
			server.Close()
		}

		// Shut down this goroutine as we were successful
		break
	}
}

func (c *Agent) connectPipeline(pipeline *memdPipeline, deadline time.Time) error {
	logDebugf("Attempting to connect pipeline to %s", pipeline.address)
	memdConn, err := DialMemdConn(pipeline.address, c.tlsConfig, deadline)
	if err != nil {
		logDebugf("Failed to connect. %v", err)
		return err
	}

	logDebugf("Connected.")
	pipeline.conn = memdConn
	go pipeline.Run()

	logDebugf("Authenticating...")
	err = c.initFn(pipeline, deadline)
	if err != nil {
		logDebugf("Failed to authenticate. %v", err)
		memdConn.Close()
		return err
	}

	c.tryHello(pipeline, deadline)

	return nil
}

// Drains all the requests out of the queue for this server.  This must be
//   invoked only once this server no longer exists in the routing data or an
//   infinite loop will likely occur.
func (c *Agent) shutdownPipeline(s *memdPipeline) {
	s.Drain(func(req *memdQRequest) {
		c.redispatchDirect(req)
	})
}

func (agent *Agent) checkPendingServer(server *memdPipeline) bool {
	oldRouting := agent.routingInfo.get()
	if oldRouting == nil {
		return false
	}

	// Find the index of the pending server we want to swap
	var serverIdx int = -1
	for i, s := range oldRouting.pendingServers {
		if s == server {
			serverIdx = i
			break
		}
	}

	return serverIdx != -1
}

func (agent *Agent) activatePendingServer(server *memdPipeline) bool {
	logDebugf("Activating Server...")

	var oldRouting *routeData
	for {
		oldRouting = agent.routingInfo.get()
		if oldRouting == nil {
			return false
		}

		// Find the index of the pending server we want to swap
		var serverIdx int = -1
		for i, s := range oldRouting.pendingServers {
			if s == server {
				serverIdx = i
				break
			}
		}

		if serverIdx == -1 {
			// This server is no longer in the list
			return false
		}

		var newRouting *routeData = &routeData{
			revId:      oldRouting.revId,
			capiEpList: oldRouting.capiEpList,
			mgmtEpList: oldRouting.mgmtEpList,
			n1qlEpList: oldRouting.n1qlEpList,
			vbMap:      oldRouting.vbMap,
			source:     oldRouting.source,
			deadQueue:  oldRouting.deadQueue,
		}

		// Copy the lists
		newRouting.queues = append(newRouting.queues, oldRouting.queues...)
		newRouting.servers = append(newRouting.servers, oldRouting.servers...)
		newRouting.pendingServers = append(newRouting.pendingServers, oldRouting.pendingServers...)

		// Swap around the pending server to being an active one
		newRouting.servers = append(newRouting.servers, server)
		newRouting.queues[serverIdx] = server.queue
		newRouting.pendingServers[serverIdx] = nil

		// Update to the new waitQueue
		for i, q := range newRouting.queues {
			if q == oldRouting.waitQueue {
				if newRouting.waitQueue == nil {
					newRouting.waitQueue = createMemdQueue()
				}
				newRouting.queues[i] = newRouting.waitQueue
			}
		}

		// Double-check the queue to make sure its still big enough.
		if len(newRouting.queues) != len(oldRouting.queues) {
			panic("Pending server swap corrupted the queue list.")
		}

		// Attempt to atomically update the routing data
		if !agent.routingInfo.update(oldRouting, newRouting) {
			// Someone preempted us, let's restart and try again...
			continue
		}

		server.SetHandlers(agent.handleServerNmv, agent.handleServerDeath)

		logDebugf("Switching routing data (server activation %d)...", serverIdx)
		oldRouting.logDebug()
		logDebugf("To new data...")
		newRouting.logDebug()

		// We've successfully swapped to the new config, lets finish building the
		//   new routing data's connections and destroy/draining old connections.
		break
	}

	oldWaitQueue := oldRouting.waitQueue
	if oldWaitQueue != nil {
		oldWaitQueue.Drain(func(req *memdQRequest) {
			agent.redispatchDirect(req)
		}, nil)
	}

	return true
}

// Accepts a cfgBucket object representing a cluster configuration and rebuilds the server list
//  along with any routing information for the Client.  Passing no config will refresh the existing one.
//  This method MUST NEVER BLOCK due to its use from various contention points.
func (agent *Agent) applyConfig(cfg *routeConfig) {
	// Check some basic things to ensure consistency!
	if len(cfg.vbMap) != agent.numVbuckets {
		panic("Received a configuration with a different number of vbuckets.")
	}

	var oldRouting *routeData
	var newRouting *routeData = &routeData{
		revId:      cfg.revId,
		capiEpList: cfg.capiEpList,
		mgmtEpList: cfg.mgmtEpList,
		n1qlEpList: cfg.n1qlEpList,
		vbMap:      cfg.vbMap,
		source:     cfg,
	}

	var needsDeadQueue bool
	for _, replicaList := range cfg.vbMap {
		for _, srvIdx := range replicaList {
			if srvIdx == -1 {
				needsDeadQueue = true
				break
			}
		}
	}
	if needsDeadQueue {
		newRouting.deadQueue = createMemdQueue()
	}

	var createdServers []*memdPipeline
	for {
		oldRouting = agent.routingInfo.get()
		if oldRouting == nil {
			return
		}

		if newRouting.revId < oldRouting.revId {
			logDebugf("Ignoring new configuration as it has an older revision id.")
			return
		}

		// Reset the lists before each iteration
		newRouting.queues = nil
		newRouting.servers = nil
		newRouting.pendingServers = nil

		for _, hostPort := range cfg.kvServerList {
			var thisServer *memdPipeline

			// See if this server exists in the old routing data and is still alive
			for _, oldServer := range oldRouting.servers {
				if oldServer.Address() == hostPort && !oldServer.IsClosed() {
					thisServer = oldServer
					break
				}
			}

			// If we found a still active connection in our old routing table,
			//   we just need to copy it over to the new routing table.
			if thisServer != nil {
				newRouting.pendingServers = append(newRouting.pendingServers, nil)
				newRouting.servers = append(newRouting.servers, thisServer)
				newRouting.queues = append(newRouting.queues, thisServer.queue)
				continue
			}

			// Search for any servers we are already trying to connect with.
			for _, oldServer := range oldRouting.pendingServers {
				if oldServer != nil && oldServer.Address() == hostPort {
					thisServer = oldServer
					break
				}
			}

			// If we do not already have a pending server we are trying to
			//   connect to, we should build one!
			if thisServer == nil {
				thisServer = CreateMemdPipeline(hostPort)
				createdServers = append(createdServers, thisServer)
			}

			if newRouting.waitQueue == nil {
				newRouting.waitQueue = createMemdQueue()
			}

			newRouting.pendingServers = append(newRouting.pendingServers, thisServer)
			newRouting.queues = append(newRouting.queues, newRouting.waitQueue)
		}

		// Check everything is sane
		if len(newRouting.queues) < len(cfg.kvServerList) {
			panic("Failed to generate a queues list that matches the config server list.")
		}

		// Attempt to atomically update the routing data
		if !agent.routingInfo.update(oldRouting, newRouting) {
			// Someone preempted us, let's restart and try again...
			continue
		}

		// We've successfully swapped to the new config, lets finish building the
		//   new routing data's connections and destroy/draining old connections.
		break
	}

	logDebugf("Switching routing data (update)...")
	oldRouting.logDebug()
	logDebugf("To new data...")
	newRouting.logDebug()

	// Launch all the new servers
	for _, newServer := range createdServers {
		go agent.connectServer(newServer)
	}

	// Identify all the dead servers and drain their requests
	for _, oldServer := range oldRouting.servers {
		found := false
		for _, newServer := range newRouting.servers {
			if newServer == oldServer {
				found = true
				break
			}
		}
		if !found {
			go agent.shutdownPipeline(oldServer)
		}
	}

	oldWaitQueue := oldRouting.waitQueue
	if oldWaitQueue != nil {
		oldWaitQueue.Drain(func(req *memdQRequest) {
			agent.redispatchDirect(req)
		}, nil)
	}

	oldDeadQueue := oldRouting.deadQueue
	if oldDeadQueue != nil {
		oldDeadQueue.Drain(func(req *memdQRequest) {
			agent.redispatchDirect(req)
		}, nil)
	}
}

func (agent *Agent) updateConfig(bk *cfgBucket) {
	if bk == nil {
		// Use the existing config if none was passed.
		oldRouting := agent.routingInfo.get()
		if oldRouting == nil {
			// If there is no previous config, we can't do anything
			return
		}

		agent.applyConfig(oldRouting.source)
	} else {
		// Normalize the cfgBucket to a routeConfig and apply it.
		routeCfg := buildRouteConfig(bk, agent.IsSecure())
		if !routeCfg.IsValid() {
			// We received an invalid configuration, lets shutdown.
			agent.Close()
			return
		}

		agent.applyConfig(routeCfg)
	}
}

func (agent *Agent) routeRequest(req *memdQRequest) (*memdQueue, error) {
	routingInfo := agent.routingInfo.get()
	if routingInfo == nil {
		return nil, nil
	}

	var srvIdx int

	repId := req.ReplicaIdx
	if repId < 0 {
		srvIdx = -repId - 1

		if srvIdx >= len(routingInfo.queues) {
			return nil, ErrInvalidServer
		}
	} else {
		if req.Key != nil {
			req.Vbucket = uint16(cbCrc(req.Key) % uint32(len(routingInfo.vbMap)))
		}

		if int(req.Vbucket) >= len(routingInfo.vbMap) {
			return nil, ErrInvalidVBucket
		}

		vBucketNodes := routingInfo.vbMap[req.Vbucket]

		if repId >= len(vBucketNodes) {
			return nil, ErrInvalidReplica
		}

		srvIdx = vBucketNodes[repId]
	}

	if srvIdx == -1 {
		return routingInfo.deadQueue, nil
	}

	return routingInfo.queues[srvIdx], nil
}

// This immediately dispatches a request to the appropriate server based on the
//  currently available routing data.
func (c *Agent) dispatchDirect(req *memdQRequest) error {
	for {
		pipeline, err := c.routeRequest(req)
		if err != nil {
			return err
		}

		if pipeline == nil {
			// If no routing data exists this indicates that this Agent
			//   has been shut down!
			return ErrShutdown
		}

		if !pipeline.QueueRequest(req) {
			continue
		}

		break
	}
	return nil
}

// This function is meant to be used when a memdRequest is internally shuffled
//   around.  It currently simply calls dispatchDirect.
func (c *Agent) redispatchDirect(req *memdQRequest) {
	// Reschedule the operation
	c.dispatchDirect(req)
}
<commit_msg>Redispatching ignores dispatch errors which could loose ops.<commit_after>package gocbcore

import (
	"encoding/binary"
	"time"
)

func (c *Agent) handleServerNmv(s *memdPipeline, req *memdQRequest, resp *memdResponse) {
	// Try to parse the value as a bucket configuration
	bk, err := parseConfig(resp.Value, s.Hostname())
	if err == nil {
		c.updateConfig(bk)
	}

	// Redirect it!  This may actually come back to this server, but I won't tell
	//   if you don't ;)
	c.redispatchDirect(req)
}

func (c *Agent) handleServerDeath(s *memdPipeline) {
	// Refresh the routing data with the existing configuration, this has
	//   the effect of attempting to rebuild the dead server.
	c.updateConfig(nil)

	// TODO(brett19): We probably should actually try other ways of resolving
	//  the issue, like requesting a new configuration.
}

func appendFeatureCode(bytes []byte, feature HelloFeature) []byte {
	bytes = append(bytes, 0, 0)
	binary.BigEndian.PutUint16(bytes[len(bytes)-2:], uint16(FeatureSeqNo))
	return bytes
}

func (c *Agent) tryHello(pipeline *memdPipeline, deadline time.Time) error {
	var featuresBytes []byte

	if c.useMutationTokens {
		featuresBytes = appendFeatureCode(featuresBytes, FeatureSeqNo)
	}

	if featuresBytes == nil {
		// If we have no features, we don't need to HELLO at all
		return nil
	}

	_, err := pipeline.ExecuteRequest(&memdQRequest{
		memdRequest: memdRequest{
			Magic:  ReqMagic,
			Opcode: CmdHello,
			Key:    []byte("Go SDK"),
			Value:  featuresBytes,
		},
	}, deadline)

	return err
}

// Attempt to connect a server, this function must be called
//  in its own goroutine and will ensure that offline servers
//  are not spammed with connection attempts.
func (agent *Agent) connectServer(server *memdPipeline) {
	for {
		agent.serverFailuresLock.Lock()
		failureTime := agent.serverFailures[server.address]
		agent.serverFailuresLock.Unlock()

		if !failureTime.IsZero() {
			waitedTime := time.Since(failureTime)
			if waitedTime < agent.serverWaitTimeout {
				time.Sleep(agent.serverWaitTimeout - waitedTime)

				if !agent.checkPendingServer(server) {
					// Server is no longer pending.  Stop trying.
					break
				}
			}
		}

		err := agent.connectPipeline(server, time.Now().Add(agent.serverConnectTimeout))
		if err != nil {
			agent.serverFailuresLock.Lock()
			agent.serverFailures[server.address] = time.Now()
			agent.serverFailuresLock.Unlock()

			// Try to connect again
			continue
		}

		if !agent.activatePendingServer(server) {
			// If this is no longer a valid pending server, we should shut
			//   it down!
			server.Close()
		}

		// Shut down this goroutine as we were successful
		break
	}
}

func (c *Agent) connectPipeline(pipeline *memdPipeline, deadline time.Time) error {
	logDebugf("Attempting to connect pipeline to %s", pipeline.address)
	memdConn, err := DialMemdConn(pipeline.address, c.tlsConfig, deadline)
	if err != nil {
		logDebugf("Failed to connect. %v", err)
		return err
	}

	logDebugf("Connected.")
	pipeline.conn = memdConn
	go pipeline.Run()

	logDebugf("Authenticating...")
	err = c.initFn(pipeline, deadline)
	if err != nil {
		logDebugf("Failed to authenticate. %v", err)
		memdConn.Close()
		return err
	}

	c.tryHello(pipeline, deadline)

	return nil
}

// Drains all the requests out of the queue for this server.  This must be
//   invoked only once this server no longer exists in the routing data or an
//   infinite loop will likely occur.
func (c *Agent) shutdownPipeline(s *memdPipeline) {
	s.Drain(func(req *memdQRequest) {
		c.redispatchDirect(req)
	})
}

func (agent *Agent) checkPendingServer(server *memdPipeline) bool {
	oldRouting := agent.routingInfo.get()
	if oldRouting == nil {
		return false
	}

	// Find the index of the pending server we want to swap
	var serverIdx int = -1
	for i, s := range oldRouting.pendingServers {
		if s == server {
			serverIdx = i
			break
		}
	}

	return serverIdx != -1
}

func (agent *Agent) activatePendingServer(server *memdPipeline) bool {
	logDebugf("Activating Server...")

	var oldRouting *routeData
	for {
		oldRouting = agent.routingInfo.get()
		if oldRouting == nil {
			return false
		}

		// Find the index of the pending server we want to swap
		var serverIdx int = -1
		for i, s := range oldRouting.pendingServers {
			if s == server {
				serverIdx = i
				break
			}
		}

		if serverIdx == -1 {
			// This server is no longer in the list
			return false
		}

		var newRouting *routeData = &routeData{
			revId:      oldRouting.revId,
			capiEpList: oldRouting.capiEpList,
			mgmtEpList: oldRouting.mgmtEpList,
			n1qlEpList: oldRouting.n1qlEpList,
			vbMap:      oldRouting.vbMap,
			source:     oldRouting.source,
			deadQueue:  oldRouting.deadQueue,
		}

		// Copy the lists
		newRouting.queues = append(newRouting.queues, oldRouting.queues...)
		newRouting.servers = append(newRouting.servers, oldRouting.servers...)
		newRouting.pendingServers = append(newRouting.pendingServers, oldRouting.pendingServers...)

		// Swap around the pending server to being an active one
		newRouting.servers = append(newRouting.servers, server)
		newRouting.queues[serverIdx] = server.queue
		newRouting.pendingServers[serverIdx] = nil

		// Update to the new waitQueue
		for i, q := range newRouting.queues {
			if q == oldRouting.waitQueue {
				if newRouting.waitQueue == nil {
					newRouting.waitQueue = createMemdQueue()
				}
				newRouting.queues[i] = newRouting.waitQueue
			}
		}

		// Double-check the queue to make sure its still big enough.
		if len(newRouting.queues) != len(oldRouting.queues) {
			panic("Pending server swap corrupted the queue list.")
		}

		// Attempt to atomically update the routing data
		if !agent.routingInfo.update(oldRouting, newRouting) {
			// Someone preempted us, let's restart and try again...
			continue
		}

		server.SetHandlers(agent.handleServerNmv, agent.handleServerDeath)

		logDebugf("Switching routing data (server activation %d)...", serverIdx)
		oldRouting.logDebug()
		logDebugf("To new data...")
		newRouting.logDebug()

		// We've successfully swapped to the new config, lets finish building the
		//   new routing data's connections and destroy/draining old connections.
		break
	}

	oldWaitQueue := oldRouting.waitQueue
	if oldWaitQueue != nil {
		oldWaitQueue.Drain(func(req *memdQRequest) {
			agent.redispatchDirect(req)
		}, nil)
	}

	return true
}

// Accepts a cfgBucket object representing a cluster configuration and rebuilds the server list
//  along with any routing information for the Client.  Passing no config will refresh the existing one.
//  This method MUST NEVER BLOCK due to its use from various contention points.
func (agent *Agent) applyConfig(cfg *routeConfig) {
	// Check some basic things to ensure consistency!
	if len(cfg.vbMap) != agent.numVbuckets {
		panic("Received a configuration with a different number of vbuckets.")
	}

	var oldRouting *routeData
	var newRouting *routeData = &routeData{
		revId:      cfg.revId,
		capiEpList: cfg.capiEpList,
		mgmtEpList: cfg.mgmtEpList,
		n1qlEpList: cfg.n1qlEpList,
		vbMap:      cfg.vbMap,
		source:     cfg,
	}

	var needsDeadQueue bool
	for _, replicaList := range cfg.vbMap {
		for _, srvIdx := range replicaList {
			if srvIdx == -1 {
				needsDeadQueue = true
				break
			}
		}
	}
	if needsDeadQueue {
		newRouting.deadQueue = createMemdQueue()
	}

	var createdServers []*memdPipeline
	for {
		oldRouting = agent.routingInfo.get()
		if oldRouting == nil {
			return
		}

		if newRouting.revId < oldRouting.revId {
			logDebugf("Ignoring new configuration as it has an older revision id.")
			return
		}

		// Reset the lists before each iteration
		newRouting.queues = nil
		newRouting.servers = nil
		newRouting.pendingServers = nil

		for _, hostPort := range cfg.kvServerList {
			var thisServer *memdPipeline

			// See if this server exists in the old routing data and is still alive
			for _, oldServer := range oldRouting.servers {
				if oldServer.Address() == hostPort && !oldServer.IsClosed() {
					thisServer = oldServer
					break
				}
			}

			// If we found a still active connection in our old routing table,
			//   we just need to copy it over to the new routing table.
			if thisServer != nil {
				newRouting.pendingServers = append(newRouting.pendingServers, nil)
				newRouting.servers = append(newRouting.servers, thisServer)
				newRouting.queues = append(newRouting.queues, thisServer.queue)
				continue
			}

			// Search for any servers we are already trying to connect with.
			for _, oldServer := range oldRouting.pendingServers {
				if oldServer != nil && oldServer.Address() == hostPort {
					thisServer = oldServer
					break
				}
			}

			// If we do not already have a pending server we are trying to
			//   connect to, we should build one!
			if thisServer == nil {
				thisServer = CreateMemdPipeline(hostPort)
				createdServers = append(createdServers, thisServer)
			}

			if newRouting.waitQueue == nil {
				newRouting.waitQueue = createMemdQueue()
			}

			newRouting.pendingServers = append(newRouting.pendingServers, thisServer)
			newRouting.queues = append(newRouting.queues, newRouting.waitQueue)
		}

		// Check everything is sane
		if len(newRouting.queues) < len(cfg.kvServerList) {
			panic("Failed to generate a queues list that matches the config server list.")
		}

		// Attempt to atomically update the routing data
		if !agent.routingInfo.update(oldRouting, newRouting) {
			// Someone preempted us, let's restart and try again...
			continue
		}

		// We've successfully swapped to the new config, lets finish building the
		//   new routing data's connections and destroy/draining old connections.
		break
	}

	logDebugf("Switching routing data (update)...")
	oldRouting.logDebug()
	logDebugf("To new data...")
	newRouting.logDebug()

	// Launch all the new servers
	for _, newServer := range createdServers {
		go agent.connectServer(newServer)
	}

	// Identify all the dead servers and drain their requests
	for _, oldServer := range oldRouting.servers {
		found := false
		for _, newServer := range newRouting.servers {
			if newServer == oldServer {
				found = true
				break
			}
		}
		if !found {
			go agent.shutdownPipeline(oldServer)
		}
	}

	oldWaitQueue := oldRouting.waitQueue
	if oldWaitQueue != nil {
		oldWaitQueue.Drain(func(req *memdQRequest) {
			agent.redispatchDirect(req)
		}, nil)
	}

	oldDeadQueue := oldRouting.deadQueue
	if oldDeadQueue != nil {
		oldDeadQueue.Drain(func(req *memdQRequest) {
			agent.redispatchDirect(req)
		}, nil)
	}
}

func (agent *Agent) updateConfig(bk *cfgBucket) {
	if bk == nil {
		// Use the existing config if none was passed.
		oldRouting := agent.routingInfo.get()
		if oldRouting == nil {
			// If there is no previous config, we can't do anything
			return
		}

		agent.applyConfig(oldRouting.source)
	} else {
		// Normalize the cfgBucket to a routeConfig and apply it.
		routeCfg := buildRouteConfig(bk, agent.IsSecure())
		if !routeCfg.IsValid() {
			// We received an invalid configuration, lets shutdown.
			agent.Close()
			return
		}

		agent.applyConfig(routeCfg)
	}
}

func (agent *Agent) routeRequest(req *memdQRequest) (*memdQueue, error) {
	routingInfo := agent.routingInfo.get()
	if routingInfo == nil {
		return nil, nil
	}

	var srvIdx int

	repId := req.ReplicaIdx
	if repId < 0 {
		srvIdx = -repId - 1

		if srvIdx >= len(routingInfo.queues) {
			return nil, ErrInvalidServer
		}
	} else {
		if req.Key != nil {
			req.Vbucket = uint16(cbCrc(req.Key) % uint32(len(routingInfo.vbMap)))
		}

		if int(req.Vbucket) >= len(routingInfo.vbMap) {
			return nil, ErrInvalidVBucket
		}

		vBucketNodes := routingInfo.vbMap[req.Vbucket]

		if repId >= len(vBucketNodes) {
			return nil, ErrInvalidReplica
		}

		srvIdx = vBucketNodes[repId]
	}

	if srvIdx == -1 {
		return routingInfo.deadQueue, nil
	}

	return routingInfo.queues[srvIdx], nil
}

// This immediately dispatches a request to the appropriate server based on the
//  currently available routing data.
func (c *Agent) dispatchDirect(req *memdQRequest) error {
	for {
		pipeline, err := c.routeRequest(req)
		if err != nil {
			return err
		}

		if pipeline == nil {
			// If no routing data exists this indicates that this Agent
			//   has been shut down!
			return ErrShutdown
		}

		if !pipeline.QueueRequest(req) {
			continue
		}

		break
	}
	return nil
}

// This function is meant to be used when a memdRequest is internally shuffled
//   around.  It currently simply calls dispatchDirect.
func (c *Agent) redispatchDirect(req *memdQRequest) {
	// Reschedule the operation
	err := c.dispatchDirect(req)
	if err != nil {
		panic("dispatchDirect errored during redispatch.")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 krepa098 (krepa098 at gmail dot com)
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose, including commercial applications,
// and to alter it and redistribute it freely, subject to the following restrictions:
// 	1.	The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
//		If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
// 	2. 	Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
// 	3. 	This notice may not be removed or altered from any source distribution.

package gosfml2

// #include <SFML/Graphics/Texture.h>
// #include <stdlib.h>
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

/////////////////////////////////////
///		STRUCTS
/////////////////////////////////////

type Texture struct {
	cptr *C.sfTexture
}

/////////////////////////////////////
///		FUNCS
/////////////////////////////////////

// Create a new texture
//
// 	width:  Texture width
// 	height: Texture height
func NewTexture(width, height uint) (texture *Texture, err error) {
	texture = &Texture{C.sfTexture_create(C.uint(width),C.uint(height))}
	runtime.SetFinalizer(texture, (*Texture).Destroy)

	if texture.cptr == nil {
		err = errors.New("NewTexture: Cannot create texture")
	}

	return
}

// Create a new texture from an image
//
// 	file: Path of the image file to load
// 	area: Area of the source image to load (nil to load the entire image)
func NewTextureFromFile(file string, area *IntRect) (texture *Texture, err error) {
	cFile := C.CString(file)
	defer C.free(unsafe.Pointer(cFile))
	texture = &Texture{C.sfTexture_createFromFile(cFile, area.toCPtr())}
	runtime.SetFinalizer(texture, (*Texture).Destroy)

	if texture.cptr == nil {
		err = errors.New("NewTextureFromFile: Cannot load texture " + file)
	}

	return
}

// Create a new texture from a file in memory
//
// 	data: Slice containing the file data
// 	area: Area of the source image to load (nil to load the entire image)
func NewTextureFromMemory(data []byte, area *IntRect) (texture *Texture, err error) {
	if len(data) > 0 {
		texture = &Texture{C.sfTexture_createFromMemory(unsafe.Pointer(&data[0]), C.size_t(len(data)), area.toCPtr())}
		runtime.SetFinalizer(texture, (*Texture).Destroy)
	}
	err = errors.New("NewTextureFromMemory: no data")
	return
}

// Create a new texture from an image
//
// 	image: Image to upload to the texture
// 	area:  Area of the source image to load (NULL to load the entire image)
func NewTextureFromImage(image *Image, area *IntRect) (texture *Texture, err error) {
	texture = &Texture{C.sfTexture_createFromImage(image.toCPtr(), area.toCPtr())}
	runtime.SetFinalizer(texture, (*Texture).Destroy)

	if texture.cptr == nil {
		err = errors.New("NewTextureFromFile: Cannot create texture from image")
	}

	return
}

// Copy an existing texture
func (this *Texture) Copy() *Texture {
	texture := &Texture{C.sfTexture_copy(this.cptr)}
	runtime.SetFinalizer(texture, (*Texture).Destroy)
	return texture
}

// Destroy an existing texture
func (this *Texture) Destroy() {
	C.sfTexture_destroy(this.cptr)
	this.cptr = nil
}

// Return the size of the texture
func (this *Texture) GetSize() (size Vector2u) {
	size.fromC(C.sfTexture_getSize(this.cptr))
	return
}

// Update a texture from the contents of a window
//
// 	window:  Window to copy to the texture
// 	x:       X offset in the texture where to copy the source pixels
// 	y:       Y offset in the texture where to copy the source pixels
func (this *Texture) UpdateFromWindow(window *Window, x, y uint) {
	C.sfTexture_updateFromWindow(this.cptr, window.cptr, C.uint(x), C.uint(y))
}

// Update a texture from the contents of a render-window
//
// 	renderWindow: Render-window to copy to the texture
// 	x:            X offset in the texture where to copy the source pixels
// 	y:            Y offset in the texture where to copy the source pixels
func (this *Texture) UpdateFromRenderWindow(window *RenderWindow, x, y uint) {
	C.sfTexture_updateFromRenderWindow(this.cptr, window.cptr, C.uint(x), C.uint(y))
}

// Update a texture from an image
//
// 	image:   Image to copy to the texture
// 	x:       X offset in the texture where to copy the source pixels
// 	y:       Y offset in the texture where to copy the source pixels
func (this *Texture) UpdateFromImage(image *Image, x, y uint) {
	C.sfTexture_updateFromImage(this.cptr, image.toCPtr(), C.uint(x), C.uint(y))
}

// Enable or disable the smooth filter on a texture
func (this *Texture) SetSmooth(smooth bool) {
	C.sfTexture_setSmooth(this.cptr, goBool2C(smooth))
}

// Tell whether the smooth filter is enabled or not for a texture
func (this *Texture) IsSmooth() bool {
	return sfBool2Go(C.sfTexture_isSmooth(this.cptr))
}

// Enable or disable repeating for a texture
//
// Repeating is involved when using texture coordinates
// outside the texture rectangle [0, 0, width, height].
// In this case, if repeat mode is enabled, the whole texture
// will be repeated as many times as needed to reach the
// coordinate (for example, if the X texture coordinate is
// 3 * width, the texture will be repeated 3 times).
// If repeat mode is disabled, the "extra space" will instead
// be filled with border pixels.
// Warning: on very old graphics cards, white pixels may appear
// when the texture is repeated. With such cards, repeat mode
// can be used reliably only if the texture has power-of-two
// dimensions (such as 256x128).
// Repeating is disabled by default.
func (this *Texture) SetRepeated(repeated bool) {
	C.sfTexture_setRepeated(this.cptr, goBool2C(repeated))
}

// Tell whether a texture is repeated or not
func (this *Texture) IsRepeated() bool {
	return sfBool2Go(C.sfTexture_isRepeated(this.cptr))
}

// Get the maximum texture size allowed
func TextureGetMaximumSize() uint {
	return uint(C.sfTexture_getMaximumSize())
}

// Bind a texture for rendering
//
// This function is not part of the graphics API, it mustn't be
// used when drawing SFML entities. It must be used only if you
// mix sfTexture with OpenGL code.
//
// 	texture: Pointer to the texture to bind, can be nil to use no texture
func BindTexture(texture *Texture) {
	C.sfTexture_bind(texture.toCPtr())
}

/////////////////////////////////////
///		GO <-> C
/////////////////////////////////////

func (this *Texture) toCPtr() *C.sfTexture {
	if this != nil {
		return this.cptr
	}
	return nil
}
<commit_msg>missing Texture.UpdateFromPixels<commit_after>// Copyright (c) 2012 krepa098 (krepa098 at gmail dot com)
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose, including commercial applications,
// and to alter it and redistribute it freely, subject to the following restrictions:
// 	1.	The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
//		If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
// 	2. 	Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
// 	3. 	This notice may not be removed or altered from any source distribution.

package gosfml2

// #include <SFML/Graphics/Texture.h>
// #include <stdlib.h>
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

/////////////////////////////////////
///		STRUCTS
/////////////////////////////////////

type Texture struct {
	cptr *C.sfTexture
}

/////////////////////////////////////
///		FUNCS
/////////////////////////////////////

// Create a new texture
//
// 	width:  Texture width
// 	height: Texture height
func NewTexture(width, height uint) (texture *Texture, err error) {
	texture = &Texture{C.sfTexture_create(C.uint(width),C.uint(height))}
	runtime.SetFinalizer(texture, (*Texture).Destroy)

	if texture.cptr == nil {
		err = errors.New("NewTexture: Cannot create texture")
	}

	return
}

// Create a new texture from an image
//
// 	file: Path of the image file to load
// 	area: Area of the source image to load (nil to load the entire image)
func NewTextureFromFile(file string, area *IntRect) (texture *Texture, err error) {
	cFile := C.CString(file)
	defer C.free(unsafe.Pointer(cFile))
	texture = &Texture{C.sfTexture_createFromFile(cFile, area.toCPtr())}
	runtime.SetFinalizer(texture, (*Texture).Destroy)

	if texture.cptr == nil {
		err = errors.New("NewTextureFromFile: Cannot load texture " + file)
	}

	return
}

// Create a new texture from a file in memory
//
// 	data: Slice containing the file data
// 	area: Area of the source image to load (nil to load the entire image)
func NewTextureFromMemory(data []byte, area *IntRect) (texture *Texture, err error) {
	if len(data) > 0 {
		texture = &Texture{C.sfTexture_createFromMemory(unsafe.Pointer(&data[0]), C.size_t(len(data)), area.toCPtr())}
		runtime.SetFinalizer(texture, (*Texture).Destroy)
	}
	err = errors.New("NewTextureFromMemory: no data")
	return
}

// Create a new texture from an image
//
// 	image: Image to upload to the texture
// 	area:  Area of the source image to load (NULL to load the entire image)
func NewTextureFromImage(image *Image, area *IntRect) (texture *Texture, err error) {
	texture = &Texture{C.sfTexture_createFromImage(image.toCPtr(), area.toCPtr())}
	runtime.SetFinalizer(texture, (*Texture).Destroy)

	if texture.cptr == nil {
		err = errors.New("NewTextureFromFile: Cannot create texture from image")
	}

	return
}

// Copy an existing texture
func (this *Texture) Copy() *Texture {
	texture := &Texture{C.sfTexture_copy(this.cptr)}
	runtime.SetFinalizer(texture, (*Texture).Destroy)
	return texture
}

// Destroy an existing texture
func (this *Texture) Destroy() {
	C.sfTexture_destroy(this.cptr)
	this.cptr = nil
}

// Return the size of the texture
func (this *Texture) GetSize() (size Vector2u) {
	size.fromC(C.sfTexture_getSize(this.cptr))
	return
}

// Update a texture from the contents of a window
//
// 	window:  Window to copy to the texture
// 	x:       X offset in the texture where to copy the source pixels
// 	y:       Y offset in the texture where to copy the source pixels
func (this *Texture) UpdateFromWindow(window *Window, x, y uint) {
	C.sfTexture_updateFromWindow(this.cptr, window.cptr, C.uint(x), C.uint(y))
}

// Update a texture from the contents of a render-window
//
// 	renderWindow: Render-window to copy to the texture
// 	x:            X offset in the texture where to copy the source pixels
// 	y:            Y offset in the texture where to copy the source pixels
func (this *Texture) UpdateFromRenderWindow(window *RenderWindow, x, y uint) {
	C.sfTexture_updateFromRenderWindow(this.cptr, window.cptr, C.uint(x), C.uint(y))
}

// Update a texture from an image
//
// 	image:   Image to copy to the texture
// 	x:       X offset in the texture where to copy the source pixels
// 	y:       Y offset in the texture where to copy the source pixels
func (this *Texture) UpdateFromImage(image *Image, x, y uint) {
	C.sfTexture_updateFromImage(this.cptr, image.toCPtr(), C.uint(x), C.uint(y))
}

// Update a texture from an array of pixels
//
// 	pixels:  Array of pixels to copy to the texture
// 	width:   Width of the pixel region contained in pixels
// 	height:  Height of the pixel region contained in pixels
// 	x:       X offset in the texture where to copy the source pixels
// 	y:       Y offset in the texture where to copy the source pixels
func (this *Texture) UpdateFromPixels(pixels []byte, width, height, x, y uint) {
	if len(pixels) != 0 {
		C.sfTexture_updateFromPixels(this.cptr, (*C.sfUint8)(unsafe.Pointer(&pixels[0])), C.uint(width), C.uint(height), C.uint(x), C.uint(y))
	}
}

// Enable or disable the smooth filter on a texture
func (this *Texture) SetSmooth(smooth bool) {
	C.sfTexture_setSmooth(this.cptr, goBool2C(smooth))
}

// Tell whether the smooth filter is enabled or not for a texture
func (this *Texture) IsSmooth() bool {
	return sfBool2Go(C.sfTexture_isSmooth(this.cptr))
}

// Enable or disable repeating for a texture
//
// Repeating is involved when using texture coordinates
// outside the texture rectangle [0, 0, width, height].
// In this case, if repeat mode is enabled, the whole texture
// will be repeated as many times as needed to reach the
// coordinate (for example, if the X texture coordinate is
// 3 * width, the texture will be repeated 3 times).
// If repeat mode is disabled, the "extra space" will instead
// be filled with border pixels.
// Warning: on very old graphics cards, white pixels may appear
// when the texture is repeated. With such cards, repeat mode
// can be used reliably only if the texture has power-of-two
// dimensions (such as 256x128).
// Repeating is disabled by default.
func (this *Texture) SetRepeated(repeated bool) {
	C.sfTexture_setRepeated(this.cptr, goBool2C(repeated))
}

// Tell whether a texture is repeated or not
func (this *Texture) IsRepeated() bool {
	return sfBool2Go(C.sfTexture_isRepeated(this.cptr))
}

// Get the maximum texture size allowed
func TextureGetMaximumSize() uint {
	return uint(C.sfTexture_getMaximumSize())
}

// Bind a texture for rendering
//
// This function is not part of the graphics API, it mustn't be
// used when drawing SFML entities. It must be used only if you
// mix sfTexture with OpenGL code.
//
// 	texture: Pointer to the texture to bind, can be nil to use no texture
func BindTexture(texture *Texture) {
	C.sfTexture_bind(texture.toCPtr())
}

/////////////////////////////////////
///		GO <-> C
/////////////////////////////////////

func (this *Texture) toCPtr() *C.sfTexture {
	if this != nil {
		return this.cptr
	}
	return nil
}
<|endoftext|>


<commit_before>package benchmarks

import (
	"testing"

	"github.com/yarf-framework/yarf"
)

// Yarf
type YarfSingle struct {
	yarf.Resource
}

func (y *YarfSingle) Get(c *yarf.Context) error {
	c.Render("Hello world!")

	return nil
}

func BenchmarkSingleYarf(b *testing.B) {
	y := yarf.New()
	y.Add("/", new(YarfSingle))

	responses, requests := generateSimpleRequests(b)
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		y.ServeHTTP(responses[i], requests[i])
	}
}
<commit_msg>Remove single test<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/codegangsta/negroni"
	"gitlab.mitre.org/intervention-engine/fhir/server"
)

func main() {
	s := server.FHIRServer{DatabaseHost: "localhost", Middleware: make([]negroni.Handler, 0)}

	s.Run()
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(rw, "FHIR Server Yay! \\o/")
}
<commit_msg>fixed server.go FHIRServer constructor to match change in middleware config handling<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/codegangsta/negroni"
	"gitlab.mitre.org/intervention-engine/fhir/server"
)

func main() {
	s := server.FHIRServer{DatabaseHost: "localhost", MiddlewareConfig: make(map[string][]negroni.Handler)}

	s.Run()
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(rw, "FHIR Server Yay! \\o/")
}
<|endoftext|>


<commit_before>package sqrl

import (
	"code.google.com/p/rsc/qr"
//	"time"
//	"net"
	// "fmt"
//	"io"
	"net/http"
)

type Server struct {
	nonce *Nonce
}

func NewServer() *Server {
	server := new(Server)
	server.nonce = NewNonce()
	return server
}

func (s *Server) QRHandler(path string) http.Handler {
	handler := func(w http.ResponseWriter, r *http.Request) {
		url := ""

		if r.TLS == nil {
			url += "qrl://"
		}

		url += r.Host
		url += "/" + path + "?"
		url += r.URL.RawQuery
		url += "&nut="
		url += s.nonce.Generate(r.RemoteAddr)

		// w.Header().Add("Content-Type", "text/html")
		// io.WriteString(w, fmt.Sprintf("%#v

", url))
		// io.WriteString(w, fmt.Sprintf("%#v

", *r.URL))
		// io.WriteString(w, fmt.Sprintf("%#v

", *r))

		w.Header().Add("Content-Type", "image/png")
		qrcode, err := qr.Encode(url, qr.M)

		if err != nil {
			return
		}

		w.Write(qrcode.PNG())
	}

	return http.HandlerFunc(handler)
}<commit_msg>Added Auth handler to the server package to handle request from Android client available at https://github.com/geir54/android-sqrl<commit_after>package sqrl

import (
	"code.google.com/p/rsc/qr"
	"github.com/kalaspuffar/base64url"
	"github.com/dustyburwell/ed25519"
	"crypto/subtle"
//	"time"
//	"net"
	"fmt"
	"io"
	"net/http"
)

type Server struct {
	nonce *Nonce
}

func NewServer() *Server {
	server := new(Server)
	server.nonce = NewNonce()
	return server
}

func (s *Server) AuthHandler() http.Handler {
	handler := func(w http.ResponseWriter, r *http.Request) {
		/*
			Parse form data and handle error messages. Might not be the most visible to the end user.
		*/
		err := r.ParseForm()
		if(err != nil) {
			fmt.Println(err)
			return
		}

		message := r.FormValue("message")
		signature := r.FormValue("signature")
		publicKey := r.FormValue("publicKey")

		/*
			Decode publicKey and signature to a byte array using base64url package
		*/
		pubBytes, pubErr := base64url.Decode(publicKey)
		if pubErr != nil {
			fmt.Println(pubErr)
		}
		signBytes, signErr := base64url.Decode(signature)
		if signErr != nil {
			fmt.Println(signErr)
		}

		/*
			Change the byte array to an object with the correct sizes used by the ed25519 implementation
		*/
		var pk *[ed25519.PublicKeySize]byte
		pk = new([ed25519.PublicKeySize]byte)
		subtle.ConstantTimeCopy(1, pk[:32], pubBytes)
		var sig *[ed25519.SignatureSize]byte
		sig = new([ed25519.SignatureSize]byte)
		subtle.ConstantTimeCopy(1, sig[:64], signBytes)

		/*
			Verify the signature and return verified or not depending on the result.
		*/
		w.Header().Add("Content-Type", "text/html")
		if ed25519.Verify(pk, []byte(message), sig) {
			io.WriteString(w, "{result:true}Verified")
		} else {
			io.WriteString(w, "{result:false}Not Verified")
		}
	}
	return http.HandlerFunc(handler)
}

func (s *Server) QRHandler(path string) http.Handler {
	handler := func(w http.ResponseWriter, r *http.Request) {
		url := ""

		if r.TLS == nil {
			url += "qrl://"
		} else {
			url += "sqrl://"
		}

		url += r.Host
		url += "/" + path + "?"
		url += r.URL.RawQuery
		url += "&nut="
		url += s.nonce.Generate(r.RemoteAddr)

		// w.Header().Add("Content-Type", "text/html")
		// io.WriteString(w, fmt.Sprintf("%#v

", url))
		// io.WriteString(w, fmt.Sprintf("%#v

", *r.URL))
		// io.WriteString(w, fmt.Sprintf("%#v

", *r))

		w.Header().Add("Content-Type", "image/png")
		qrcode, err := qr.Encode(url, qr.M)

		if err != nil {
			return
		}

		w.Write(qrcode.PNG())
	}

	return http.HandlerFunc(handler)
}<|endoftext|>


<commit_before>package air

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"strings"
	"sync"

	"golang.org/x/crypto/acme"
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
)

// server is an HTTP server.
type server struct {
	a            *Air
	server       *http.Server
	requestPool  *sync.Pool
	responsePool *sync.Pool
}

// newServer returns a new instance of the `server` with the a.
func newServer(a *Air) *server {
	return &server{
		a:      a,
		server: &http.Server{},
		requestPool: &sync.Pool{
			New: func() interface{} {
				return &Request{}
			},
		},
		responsePool: &sync.Pool{
			New: func() interface{} {
				return &Response{}
			},
		},
	}
}

// serve starts the s.
func (s *server) serve() error {
	host, port, err := net.SplitHostPort(s.a.Address)
	if err != nil {
		return err
	}

	host = strings.ToLower(host)
	port = strings.ToLower(port)

	s.server.Addr = host + ":" + port
	s.server.Handler = s
	s.server.ReadTimeout = s.a.ReadTimeout
	s.server.ReadHeaderTimeout = s.a.ReadHeaderTimeout
	s.server.WriteTimeout = s.a.WriteTimeout
	s.server.IdleTimeout = s.a.IdleTimeout
	s.server.MaxHeaderBytes = s.a.MaxHeaderBytes
	s.server.ErrorLog = s.a.errorLogger

	idleTimeout := s.a.IdleTimeout
	if idleTimeout == 0 {
		idleTimeout = s.a.ReadTimeout
	}

	h2ch := h2c.NewHandler(s, &http2.Server{
		IdleTimeout: idleTimeout,
	})

	var hh http.Handler
	if s.a.HTTPSEnforced && port != "80" && port != "https" {
		hh = http.HandlerFunc(func(
			rw http.ResponseWriter,
			r *http.Request,
		) {
			host, _, _ := net.SplitHostPort(r.Host)
			if host == "" {
				host = r.Host
			}

			if port != "443" && port != "https" {
				host = fmt.Sprint(host, ":", port)
			}

			http.Redirect(
				rw,
				r,
				"https://"+host+r.RequestURI,
				http.StatusMovedPermanently,
			)
		})
	}

	if s.a.DebugMode {
		fmt.Println("air: serving in debug mode")
	}

	if s.a.TLSCertFile != "" && s.a.TLSKeyFile != "" {
		c, err := tls.LoadX509KeyPair(s.a.TLSCertFile, s.a.TLSKeyFile)
		if err != nil {
			return err
		}

		s.server.TLSConfig = &tls.Config{
			GetCertificate: func(
				chi *tls.ClientHelloInfo,
			) (*tls.Certificate, error) {
				if !s.allowedHost(chi.ServerName) {
					return nil, chi.Conn.Close()
				}

				return &c, nil
			},
		}
	} else if !s.a.DebugMode && s.a.ACMEEnabled {
		acm := &autocert.Manager{
			Prompt: autocert.AcceptTOS,
			Cache:  autocert.DirCache(s.a.ACMECertRoot),
			Client: &acme.Client{
				DirectoryURL: s.a.ACMEDirectoryURL,
			},
			Email: s.a.MaintainerEmail,
		}

		if hh != nil {
			hh = acm.HTTPHandler(hh)
		} else {
			hh = acm.HTTPHandler(h2ch)
		}

		s.server.Addr = host + ":443"
		s.server.TLSConfig = acm.TLSConfig()
		s.server.TLSConfig.GetCertificate = func(
			chi *tls.ClientHelloInfo,
		) (*tls.Certificate, error) {
			chi.ServerName = strings.ToLower(chi.ServerName)
			if !s.allowedHost(chi.ServerName) {
				return nil, chi.Conn.Close()
			}

			return acm.GetCertificate(chi)
		}
	} else {
		s.server.Handler = http.HandlerFunc(func(
			rw http.ResponseWriter,
			r *http.Request,
		) {
			if s.allowedHost(r.Host) {
				h2ch.ServeHTTP(rw, r)
			} else if h, ok := rw.(http.Hijacker); ok {
				if c, _, _ := h.Hijack(); c != nil {
					c.Close()
				}
			}
		})

		return s.server.ListenAndServe()
	}

	if hh != nil {
		hs := &http.Server{
			Addr: host + ":80",
			Handler: http.HandlerFunc(func(
				rw http.ResponseWriter,
				r *http.Request,
			) {
				if s.allowedHost(r.Host) {
					hh.ServeHTTP(rw, r)
				} else if h, ok := rw.(http.Hijacker); ok {
					if c, _, _ := h.Hijack(); c != nil {
						c.Close()
					}
				}
			}),
			ReadTimeout:       s.a.ReadTimeout,
			ReadHeaderTimeout: s.a.ReadHeaderTimeout,
			WriteTimeout:      s.a.WriteTimeout,
			IdleTimeout:       s.a.IdleTimeout,
			MaxHeaderBytes:    s.a.MaxHeaderBytes,
			ErrorLog:          s.a.errorLogger,
		}

		go hs.ListenAndServe()
		defer hs.Close()
	}

	return s.server.ListenAndServeTLS("", "")
}

// close closes the s immediately.
func (s *server) close() error {
	return s.server.Close()
}

// shutdown gracefully shuts down the s without interrupting any active
// connections.
func (s *server) shutdown(ctx context.Context) error {
	return s.server.Shutdown(ctx)
}

// allowedHost reports whether the host is allowed.
func (s *server) allowedHost(host string) bool {
	return s.a.DebugMode || len(s.a.HostWhitelist) == 0 ||
		stringSliceContainsCIly(s.a.HostWhitelist, host)
}

// ServeHTTP implements the `http.Handler`.
func (s *server) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	// Get request and response from the pool.

	req := s.requestPool.Get().(*Request)
	res := s.responsePool.Get().(*Response)

	// Tie the request body and the standard request body together.

	r.Body = &requestBody{
		r:  req,
		hr: r,
		rc: r.Body,
	}

	// Reset the request.

	req.Air = s.a
	req.SetHTTPRequest(r)
	req.res = res
	req.params = req.params[:0]
	req.routeParamNames = nil
	req.routeParamValues = nil
	req.parseRouteParamsOnce = &sync.Once{}
	req.parseOtherParamsOnce = &sync.Once{}
	req.localizedString = nil

	// Reset the response.

	res.Air = s.a
	res.SetHTTPResponseWriter(&responseWriter{
		r: res,
		w: rw,
	})
	res.Status = http.StatusOK
	res.ContentLength = -1
	res.Written = false
	res.Minified = false
	res.Gzipped = false
	res.req = req
	res.ohrw = rw
	res.servingContent = false
	res.serveContentError = nil
	res.reverseProxying = false
	res.deferredFuncs = res.deferredFuncs[:0]

	// Chain the gases stack.

	h := func(req *Request, res *Response) error {
		h := s.a.router.route(req)
		for i := len(s.a.Gases) - 1; i >= 0; i-- {
			h = s.a.Gases[i](h)
		}

		return h(req, res)
	}

	// Chain the pregases stack.

	for i := len(s.a.Pregases) - 1; i >= 0; i-- {
		h = s.a.Pregases[i](h)
	}

	// Execute the chain.

	if err := h(req, res); err != nil {
		if !res.Written && res.Status < http.StatusBadRequest {
			res.Status = http.StatusInternalServerError
		}

		s.a.ErrorHandler(err, req, res)
	}

	// Execute the deferred functions.

	for i := len(res.deferredFuncs) - 1; i >= 0; i-- {
		res.deferredFuncs[i]()
	}

	// Put the route param values back to the pool.

	if req.routeParamValues != nil {
		s.a.router.routeParamValuesPool.Put(req.routeParamValues)
	}

	// Put the request and response back to the pool.

	s.requestPool.Put(req)
	s.responsePool.Put(res)
}
<commit_msg>fix: correct `server.serve`<commit_after>package air

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"strings"
	"sync"

	"golang.org/x/crypto/acme"
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
)

// server is an HTTP server.
type server struct {
	a            *Air
	server       *http.Server
	requestPool  *sync.Pool
	responsePool *sync.Pool
}

// newServer returns a new instance of the `server` with the a.
func newServer(a *Air) *server {
	return &server{
		a:      a,
		server: &http.Server{},
		requestPool: &sync.Pool{
			New: func() interface{} {
				return &Request{}
			},
		},
		responsePool: &sync.Pool{
			New: func() interface{} {
				return &Response{}
			},
		},
	}
}

// serve starts the s.
func (s *server) serve() error {
	host, port, err := net.SplitHostPort(s.a.Address)
	if err != nil {
		return err
	}

	host = strings.ToLower(host)
	port = strings.ToLower(port)

	s.server.Addr = host + ":" + port
	s.server.ReadTimeout = s.a.ReadTimeout
	s.server.ReadHeaderTimeout = s.a.ReadHeaderTimeout
	s.server.WriteTimeout = s.a.WriteTimeout
	s.server.IdleTimeout = s.a.IdleTimeout
	s.server.MaxHeaderBytes = s.a.MaxHeaderBytes
	s.server.ErrorLog = s.a.errorLogger

	idleTimeout := s.a.IdleTimeout
	if idleTimeout == 0 {
		idleTimeout = s.a.ReadTimeout
	}

	h2ch := h2c.NewHandler(s, &http2.Server{
		IdleTimeout: idleTimeout,
	})

	var hh http.Handler
	if s.a.HTTPSEnforced && port != "80" && port != "http" {
		hh = http.HandlerFunc(func(
			rw http.ResponseWriter,
			r *http.Request,
		) {
			host, _, _ := net.SplitHostPort(r.Host)
			if host == "" {
				host = r.Host
			}

			if port != "443" && port != "https" {
				host = fmt.Sprint(host, ":", port)
			}

			http.Redirect(
				rw,
				r,
				"https://"+host+r.RequestURI,
				http.StatusMovedPermanently,
			)
		})
	}

	if s.a.DebugMode {
		fmt.Println("air: serving in debug mode")
	}

	if s.a.TLSCertFile != "" && s.a.TLSKeyFile != "" {
		c, err := tls.LoadX509KeyPair(s.a.TLSCertFile, s.a.TLSKeyFile)
		if err != nil {
			return err
		}

		s.server.TLSConfig = &tls.Config{
			Certificates: []tls.Certificate{c},
		}
	} else if !s.a.DebugMode && s.a.ACMEEnabled {
		acm := &autocert.Manager{
			Prompt: autocert.AcceptTOS,
			Cache:  autocert.DirCache(s.a.ACMECertRoot),
			HostPolicy: func(_ context.Context, host string) error {
				if !s.allowedHost(host) {
					return fmt.Errorf(
						"air: disallowed host: %s",
						host,
					)
				}

				return nil
			},
			Client: &acme.Client{
				DirectoryURL: s.a.ACMEDirectoryURL,
			},
			Email: s.a.MaintainerEmail,
		}

		if hh != nil {
			hh = acm.HTTPHandler(hh)
		} else {
			hh = acm.HTTPHandler(h2ch)
		}

		s.server.Addr = host + ":443"
		s.server.TLSConfig = acm.TLSConfig()
	} else {
		s.server.Handler = s.allowedHostCheckHandler(h2ch)
		return s.server.ListenAndServe()
	}

	if hh != nil {
		hs := &http.Server{
			Addr:              host + ":80",
			Handler:           s.allowedHostCheckHandler(hh),
			ReadTimeout:       s.a.ReadTimeout,
			ReadHeaderTimeout: s.a.ReadHeaderTimeout,
			WriteTimeout:      s.a.WriteTimeout,
			IdleTimeout:       s.a.IdleTimeout,
			MaxHeaderBytes:    s.a.MaxHeaderBytes,
			ErrorLog:          s.a.errorLogger,
		}

		go hs.ListenAndServe()
		defer hs.Close()
	}

	s.server.Handler = s.allowedHostCheckHandler(nil)

	return s.server.ListenAndServeTLS("", "")
}

// close closes the s immediately.
func (s *server) close() error {
	return s.server.Close()
}

// shutdown gracefully shuts down the s without interrupting any active
// connections.
func (s *server) shutdown(ctx context.Context) error {
	return s.server.Shutdown(ctx)
}

// allowedHost reports whether the host is allowed.
func (s *server) allowedHost(host string) bool {
	return s.a.DebugMode || len(s.a.HostWhitelist) == 0 ||
		stringSliceContainsCIly(s.a.HostWhitelist, host)
}

// allowedHostCheckHandler returns an `http.Handler` that checks if the
// request's host is allowed. If the request's host is allowed, then the h will
// be called.
//
// If the h is nil, the s is used.
func (s *server) allowedHostCheckHandler(h http.Handler) http.Handler {
	if h == nil {
		h = s
	}

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		if s.allowedHost(r.Host) {
			h.ServeHTTP(rw, r)
		} else if h, ok := rw.(http.Hijacker); ok {
			if c, _, _ := h.Hijack(); c != nil {
				c.Close()
			}
		}
	})
}

// ServeHTTP implements the `http.Handler`.
func (s *server) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	// Get request and response from the pool.

	req := s.requestPool.Get().(*Request)
	res := s.responsePool.Get().(*Response)

	// Tie the request body and the standard request body together.

	r.Body = &requestBody{
		r:  req,
		hr: r,
		rc: r.Body,
	}

	// Reset the request.

	req.Air = s.a
	req.SetHTTPRequest(r)
	req.res = res
	req.params = req.params[:0]
	req.routeParamNames = nil
	req.routeParamValues = nil
	req.parseRouteParamsOnce = &sync.Once{}
	req.parseOtherParamsOnce = &sync.Once{}
	req.localizedString = nil

	// Reset the response.

	res.Air = s.a
	res.SetHTTPResponseWriter(&responseWriter{
		r: res,
		w: rw,
	})
	res.Status = http.StatusOK
	res.ContentLength = -1
	res.Written = false
	res.Minified = false
	res.Gzipped = false
	res.req = req
	res.ohrw = rw
	res.servingContent = false
	res.serveContentError = nil
	res.reverseProxying = false
	res.deferredFuncs = res.deferredFuncs[:0]

	// Chain the gases stack.

	h := func(req *Request, res *Response) error {
		h := s.a.router.route(req)
		for i := len(s.a.Gases) - 1; i >= 0; i-- {
			h = s.a.Gases[i](h)
		}

		return h(req, res)
	}

	// Chain the pregases stack.

	for i := len(s.a.Pregases) - 1; i >= 0; i-- {
		h = s.a.Pregases[i](h)
	}

	// Execute the chain.

	if err := h(req, res); err != nil {
		if !res.Written && res.Status < http.StatusBadRequest {
			res.Status = http.StatusInternalServerError
		}

		s.a.ErrorHandler(err, req, res)
	}

	// Execute the deferred functions.

	for i := len(res.deferredFuncs) - 1; i >= 0; i-- {
		res.deferredFuncs[i]()
	}

	// Put the route param values back to the pool.

	if req.routeParamValues != nil {
		s.a.router.routeParamValuesPool.Put(req.routeParamValues)
	}

	// Put the request and response back to the pool.

	s.requestPool.Put(req)
	s.responsePool.Put(res)
}
<|endoftext|>


<commit_before>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package main

import (
	"fmt"
	"io"
	"net"
	"regexp"
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/paultag/sniff/parser"

	"k8s.io/client-go/1.4/kubernetes/typed/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/api"
	"k8s.io/client-go/1.4/pkg/apis/extensions"
	"k8s.io/client-go/1.4/pkg/watch"
	"k8s.io/client-go/1.4/tools/clientcmd"
)

const (
	// ingressClassKey picks a specific "class" for the Ingress. The controller
	// only processes Ingresses with this annotation either unset, or set
	// to either nginxIngressClass or the empty string.
	ingressClassKey = "kubernetes.io/ingress.class"
)

type ServerAndRegexp struct {
	Server *Server
	Regexp *regexp.Regexp
}

type Proxy struct {
	Lock       sync.RWMutex
	ServerList []ServerAndRegexp
	Default    *Server
}

func (c *Proxy) Get(host string) *Server {
	c.Lock.RLock()
	defer c.Lock.RUnlock()

	for _, tuple := range c.ServerList {
		if tuple.Regexp.MatchString(host) {
			return tuple.Server
		}
	}
	return c.Default
}

func (p *Proxy) Update(c *Config) error {
	servers := []ServerAndRegexp{}
	for i, server := range c.Servers {
		for _, hostname := range server.Names {
			var host_regexp *regexp.Regexp
			var err error
			if server.Regexp {
				host_regexp, err = regexp.Compile(hostname)
			} else {
				host_regexp, err = regexp.Compile("^" + regexp.QuoteMeta(hostname) + "$")
			}
			if err != nil {
				return fmt.Errorf("cannot update proxy due to invalid regex: %v", err)
			}
			tuple := ServerAndRegexp{&c.Servers[i], host_regexp}
			servers = append(servers, tuple)
		}
	}
	var def *Server
	for i, server := range c.Servers {
		if server.Default {
			def = &c.Servers[i]
			break
		}
	}

	p.Lock.Lock()
	defer p.Lock.Unlock()
	p.ServerList = servers
	p.Default = def

	return nil
}

func (c *Config) Serve() error {
	listener, err := net.Listen("tcp", fmt.Sprintf(
		"%s:%d", c.Bind.Host, c.Bind.Port,
	))
	if err != nil {
		return err
	}

	proxy := Proxy{}
	err = proxy.Update(c)
	if err != nil {
		return err
	}

	if c.Kubernetes != nil {
		rules := clientcmd.NewDefaultClientConfigLoadingRules()
		if c.Kubernetes.Kubeconfig != "" {
			rules.ExplicitPath = c.Kubernetes.Kubeconfig
		}
		cmdcfg, err := rules.Load()
		if err != nil {
			return err
		}
		ccfg := clientcmd.NewDefaultClientConfig(*cmdcfg, nil)
		rcfg, err := ccfg.ClientConfig()
		if err != nil {
			return err
		}
		extclient := v1beta1.NewForConfigOrDie(rcfg)

		// watch ingresses
		updateTrigger := make(chan struct{}, 1)
		ingresses := map[string]*extensions.Ingress{}
		lock := sync.Mutex{}
		class := c.Kubernetes.IngressClass
		if class == "" {
			class = "sniff"
		}
		go func() {
			for {
				w, err := extclient.Ingresses("").Watch(api.ListOptions{})
				if err != nil {
					glog.Errorf("Ingress watch error: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
					continue
				}
				evs := w.ResultChan()

			EventLoop:
				for ev := range evs {
					i := ev.Object.(*extensions.Ingress)
					if i != nil && i.Annotations[ingressClassKey] != class {
						continue
					}
					switch ev.Type {
					case watch.Added, watch.Modified:
						lock.Lock()
						ingresses[i.Namespace+"/"+i.Name] = i
						lock.Unlock()
					case watch.Deleted:
						lock.Lock()
						delete(ingresses, i.Namespace+"/"+i.Name)
						lock.Unlock()
					case watch.Error:
						glog.Errorf("Ingress watch error event: %v", ev.Object)
						w.Stop()
						break EventLoop
					}
					select {
					case updateTrigger <- struct{}{}:
					default:
					}
				}

				// TODO: add backoff logic
				time.Sleep(time.Second)
			}
		}()

		go func() {
			for range updateTrigger {
				lock.Lock()
				for _, i := range ingresses {
					c.Servers = []Server{}
					if i.Spec.Backend != nil {
						c.Servers = append(c.Servers, Server{
							Default: true,
							Host:    i.Spec.Backend.ServiceName + "." + i.Namespace,
							// TODO: support string values:
							Port: int(i.Spec.Backend.ServicePort.IntVal),
						})
					}
					for _, r := range i.Spec.Rules {
						if r.HTTP == nil {
							continue
						}
						for _, p := range r.HTTP.Paths {
							if p.Path != "" {
								continue
							}
							c.Servers = append(c.Servers, Server{
								Names: []string{r.Host},
								Host:  i.Spec.Backend.ServiceName + "." + i.Namespace,
								// TODO: support string values:
								Port: int(i.Spec.Backend.ServicePort.IntVal),
							})
						}
					}
				}
				lock.Unlock()

				err := proxy.Update(c)
				if err != nil {
					glog.Errorf("Error updating proxy: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
				}
			}
		}()
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		go proxy.Handle(conn)
	}
}

func (s *Proxy) Handle(conn net.Conn) {
	data := make([]byte, 4096)

	length, err := conn.Read(data)
	if err != nil {
		glog.Errorf("Error reading the configuration: %s", err)
	}

	var proxy *Server
	hostname, hostname_err := parser.GetHostname(data[:])
	if hostname_err == nil {
		glog.V(6).Infof("Parsed hostname: %s", hostname)

		proxy = s.Get(hostname)
		if proxy == nil {
			glog.V(4).Infof("No proxy matched %s", hostname)
			conn.Close()
			return
		}
	} else {
		glog.V(6).Info("Parsed request without hostname")

		proxy = s.Default
		if proxy == nil {
			glog.V(4).Info("No default proxy")
			conn.Close()
			return
		}
	}

	clientConn, err := net.Dial("tcp", fmt.Sprintf(
		"%s:%d", proxy.Host, proxy.Port,
	))
	if err != nil {
		glog.Warningf("Error connecting to backend: %s", err)
		conn.Close()
		return
	}
	n, err := clientConn.Write(data[:length])
	glog.V(7).Infof("Wrote %d bytes", n)
	if err != nil {
		glog.V(7).Infof("Error sending data to backend: %s", err)
		conn.Close()
		clientConn.Close()
	}
	Copycat(clientConn, conn)
}

func Copycat(client, server net.Conn) {
	defer client.Close()
	defer server.Close()

	glog.V(6).Info("Entering copy routine")

	doCopy := func(s, c net.Conn, cancel chan<- bool) {
		io.Copy(s, c)
		cancel <- true
	}

	cancel := make(chan bool, 2)

	go doCopy(server, client, cancel)
	go doCopy(client, server, cancel)

	select {
	case <-cancel:
		glog.V(6).Info("Disconnected")
		return
	}

}

// vim: foldmethod=marker
<commit_msg>Add access log like logging<commit_after>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package main

import (
	"fmt"
	"io"
	"net"
	"regexp"
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/paultag/sniff/parser"

	"k8s.io/client-go/1.4/kubernetes/typed/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/api"
	"k8s.io/client-go/1.4/pkg/apis/extensions"
	"k8s.io/client-go/1.4/pkg/watch"
	"k8s.io/client-go/1.4/tools/clientcmd"
)

const (
	// ingressClassKey picks a specific "class" for the Ingress. The controller
	// only processes Ingresses with this annotation either unset, or set
	// to either nginxIngressClass or the empty string.
	ingressClassKey = "kubernetes.io/ingress.class"
)

type ServerAndRegexp struct {
	Server *Server
	Regexp *regexp.Regexp
}

type Proxy struct {
	Lock       sync.RWMutex
	ServerList []ServerAndRegexp
	Default    *Server
}

func (c *Proxy) Get(host string) *Server {
	c.Lock.RLock()
	defer c.Lock.RUnlock()

	for _, tuple := range c.ServerList {
		if tuple.Regexp.MatchString(host) {
			return tuple.Server
		}
	}
	return c.Default
}

func (p *Proxy) Update(c *Config) error {
	servers := []ServerAndRegexp{}
	for i, server := range c.Servers {
		for _, hostname := range server.Names {
			var host_regexp *regexp.Regexp
			var err error
			if server.Regexp {
				host_regexp, err = regexp.Compile(hostname)
			} else {
				host_regexp, err = regexp.Compile("^" + regexp.QuoteMeta(hostname) + "$")
			}
			if err != nil {
				return fmt.Errorf("cannot update proxy due to invalid regex: %v", err)
			}
			tuple := ServerAndRegexp{&c.Servers[i], host_regexp}
			servers = append(servers, tuple)
		}
	}
	var def *Server
	for i, server := range c.Servers {
		if server.Default {
			def = &c.Servers[i]
			break
		}
	}

	p.Lock.Lock()
	defer p.Lock.Unlock()
	p.ServerList = servers
	p.Default = def

	return nil
}

func (c *Config) Serve() error {
	glog.V(1).Infof("Listening on %s:%d", c.Bind.Host, c.Bind.Port)
	listener, err := net.Listen("tcp", fmt.Sprintf(
		"%s:%d", c.Bind.Host, c.Bind.Port,
	))
	if err != nil {
		return err
	}

	proxy := Proxy{}
	err = proxy.Update(c)
	if err != nil {
		return err
	}

	if c.Kubernetes != nil {
		rules := clientcmd.NewDefaultClientConfigLoadingRules()
		if c.Kubernetes.Kubeconfig != "" {
			rules.ExplicitPath = c.Kubernetes.Kubeconfig
		}
		cmdcfg, err := rules.Load()
		if err != nil {
			return err
		}
		ccfg := clientcmd.NewDefaultClientConfig(*cmdcfg, nil)
		rcfg, err := ccfg.ClientConfig()
		if err != nil {
			return err
		}
		extclient := v1beta1.NewForConfigOrDie(rcfg)

		// watch ingresses
		updateTrigger := make(chan struct{}, 1)
		ingresses := map[string]*extensions.Ingress{}
		lock := sync.Mutex{}
		class := c.Kubernetes.IngressClass
		if class == "" {
			class = "sniff"
		}
		go func() {
			for {
				w, err := extclient.Ingresses("").Watch(api.ListOptions{})
				if err != nil {
					glog.Errorf("Ingress watch error: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
					continue
				}
				evs := w.ResultChan()

			EventLoop:
				for ev := range evs {
					i := ev.Object.(*extensions.Ingress)
					if i != nil && i.Annotations[ingressClassKey] != class {
						continue
					}
					switch ev.Type {
					case watch.Added, watch.Modified:
						lock.Lock()
						ingresses[i.Namespace+"/"+i.Name] = i
						lock.Unlock()
					case watch.Deleted:
						lock.Lock()
						delete(ingresses, i.Namespace+"/"+i.Name)
						lock.Unlock()
					case watch.Error:
						glog.Errorf("Ingress watch error event: %v", ev.Object)
						w.Stop()
						break EventLoop
					}
					select {
					case updateTrigger <- struct{}{}:
					default:
					}
				}

				// TODO: add backoff logic
				time.Sleep(time.Second)
			}
		}()

		go func() {
			for range updateTrigger {
				lock.Lock()
				for _, i := range ingresses {
					c.Servers = []Server{}
					if i.Spec.Backend != nil {
						c.Servers = append(c.Servers, Server{
							Default: true,
							Host:    i.Spec.Backend.ServiceName + "." + i.Namespace,
							// TODO: support string values:
							Port: int(i.Spec.Backend.ServicePort.IntVal),
						})
					}
					for _, r := range i.Spec.Rules {
						if r.HTTP == nil {
							continue
						}
						for _, p := range r.HTTP.Paths {
							if p.Path != "" {
								continue
							}
							c.Servers = append(c.Servers, Server{
								Names: []string{r.Host},
								Host:  i.Spec.Backend.ServiceName + "." + i.Namespace,
								// TODO: support string values:
								Port: int(i.Spec.Backend.ServicePort.IntVal),
							})
						}
					}
				}
				lock.Unlock()

				err := proxy.Update(c)
				if err != nil {
					glog.Errorf("Error updating proxy: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
				}
			}
		}()
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		glog.V(3).Infof(
			"%s -> %s",
			conn.RemoteAddr(),
			conn.LocalAddr(),
			time.Now().String(),
		)
		go proxy.Handle(conn)
	}
}

func (s *Proxy) Handle(conn net.Conn) {
	data := make([]byte, 4096)

	length, err := conn.Read(data)
	if err != nil {
		glog.Errorf("Error reading the configuration: %s", err)
	}

	var proxy *Server
	hostname, hostname_err := parser.GetHostname(data[:])
	if hostname_err == nil {
		glog.V(6).Infof("Parsed hostname: %s", hostname)

		proxy = s.Get(hostname)
		if proxy == nil {
			glog.V(4).Infof("No proxy matched %s", hostname)
			conn.Close()
			return
		}
	} else {
		glog.V(6).Info("Parsed request without hostname")

		proxy = s.Default
		if proxy == nil {
			glog.V(4).Info("No default proxy")
			conn.Close()
			return
		}
	}

	clientConn, err := net.Dial("tcp", fmt.Sprintf(
		"%s:%d", proxy.Host, proxy.Port,
	))
	if err != nil {
		glog.Warningf("Error connecting to backend: %s", err)
		conn.Close()
		return
	}
	n, err := clientConn.Write(data[:length])
	glog.V(7).Infof("Wrote %d bytes", n)
	if err != nil {
		glog.V(7).Infof("Error sending data to backend: %s", err)
		conn.Close()
		clientConn.Close()
	}
	Copycat(clientConn, conn)
}

func Copycat(client, server net.Conn) {
	defer client.Close()
	defer server.Close()

	glog.V(6).Info("Entering copy routine")

	doCopy := func(s, c net.Conn, cancel chan<- bool) {
		io.Copy(s, c)
		cancel <- true
	}

	cancel := make(chan bool, 2)

	go doCopy(server, client, cancel)
	go doCopy(client, server, cancel)

	select {
	case <-cancel:
		glog.V(6).Info("Disconnected")
		return
	}

}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc"

	"github.com/atotto/clipboard"
	"github.com/pocke/go-iprange"
	"github.com/skratchdot/open-golang/open"
)

func (c *CLI) Server() int {
	ra, err := iprange.New(c.Allow)
	if err != nil {
		c.writeError(err)
		return RPCError
	}

	uri := &URI{}
	rpc.Register(uri)
	clipboard := &Clipboard{}
	rpc.Register(clipboard)

	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", c.Port))
	if err != nil {
		c.writeError(err)
		return RPCError
	}
	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		c.writeError(err)
		return RPCError
	}

	for {
		conn, err := l.Accept()
		if err != nil {
			log.Println(err)
		}
		log.Printf("Request from %s", conn.RemoteAddr())
		if !ra.InlucdeConn(conn) {
			continue
		}
		rpc.ServeConn(conn)
	}
	return Success
}

type URI struct{}

func (_ *URI) Open(url string, _ *struct{}) error {
	return open.Run(url)
}

type Clipboard struct{}

func (_ *Clipboard) Copy(text string, _ *struct{}) error {
	return clipboard.WriteAll(text)
}

func (_ *Clipboard) Paste(_ struct{}, resp *string) error {
	t, err := clipboard.ReadAll()
	*resp = t
	return err
}
<commit_msg>connCh<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc"

	"github.com/atotto/clipboard"
	"github.com/pocke/go-iprange"
	"github.com/skratchdot/open-golang/open"
)

var connCh = make(chan net.Conn, 1)

func (c *CLI) Server() int {
	ra, err := iprange.New(c.Allow)
	if err != nil {
		c.writeError(err)
		return RPCError
	}

	uri := &URI{}
	rpc.Register(uri)
	clipboard := &Clipboard{}
	rpc.Register(clipboard)

	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", c.Port))
	if err != nil {
		c.writeError(err)
		return RPCError
	}
	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		c.writeError(err)
		return RPCError
	}

	for {
		conn, err := l.Accept()
		if err != nil {
			log.Println(err)
		}
		log.Printf("Request from %s", conn.RemoteAddr())
		if !ra.InlucdeConn(conn) {
			continue
		}
		connCh <- conn
		rpc.ServeConn(conn)
	}
	return Success
}

type URI struct{}

func (_ *URI) Open(url string, _ *struct{}) error {
	<-connCh
	return open.Run(url)
}

type Clipboard struct{}

func (_ *Clipboard) Copy(text string, _ *struct{}) error {
	<-connCh
	return clipboard.WriteAll(text)
}

func (_ *Clipboard) Paste(_ struct{}, resp *string) error {
	<-connCh
	t, err := clipboard.ReadAll()
	*resp = t
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"

	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
)

type chatHistory struct {
	History []chatMessage
}

type chatMessage struct { // A message structure to put in the chat buffer
	User    string
	Message string
}

var chatBuffer chatHistory // The chat buffer

func health(c web.C, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	fmt.Fprintf(w, "%s\n", "Uh, we had a slight weapons malfunction, but uh... everything's perfectly all right now. We're fine. We're all fine here now, thank you. How are you?")
}

func postMessage(c web.C, w http.ResponseWriter, r *http.Request) {
	// Spec-defined query parameters
	name := r.URL.Query().Get("name")
	line := r.URL.Query().Get("line")

	if name != "" && line != "" {
		message := chatMessage{User: name, Message: line}
		chatBuffer.History = append(chatBuffer.History, message)
	}

	history, err := json.Marshal(chatBuffer.History)
	if err != nil {
		panic(err)
	}

	w.Header().Set("Access-Control-Allow-Origin", "*")
	fmt.Fprintf(w, "%s\n", history)
}

func main() {
	goji.Get("/health", health)    // Service health
	goji.Get("/chat", postMessage) // This hurts me because this is definitely not how a RESTful endpoint should work but the project spec demanded it
	goji.Handle("/*", http.FileServer(http.Dir("content")))
	flag.Set("bind", ":9020") // Set the port that Goji listens on
	goji.Serve()              // Start listening
}
<commit_msg>Adding a POST route<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"

	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
)

type chatHistory struct {
	History []chatMessage
}

type chatMessage struct { // A message structure to put in the chat buffer
	User    string
	Message string
}

var chatBuffer chatHistory // The chat buffer

func health(c web.C, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	fmt.Fprintf(w, "%s\n", "Uh, we had a slight weapons malfunction, but uh... everything's perfectly all right now. We're fine. We're all fine here now, thank you. How are you?")
}

func postMessage(c web.C, w http.ResponseWriter, r *http.Request) {
	// Spec-defined query parameters
	name := r.URL.Query().Get("name")
	line := r.URL.Query().Get("line")

	if name != "" && line != "" {
		message := chatMessage{User: name, Message: line}
		chatBuffer.History = append(chatBuffer.History, message)
	}

	history, err := json.Marshal(chatBuffer.History)
	if err != nil {
		panic(err)
	}

	w.Header().Set("Access-Control-Allow-Origin", "*")
	fmt.Fprintf(w, "%s\n", history)
}

func main() {
	goji.Get("/health", health)     // Service health
	goji.Get("/chat", postMessage)  // This hurts me because this is definitely not how a RESTful endpoint should work but the project spec demanded it
	goji.Post("/chat", postMessage) // This hurts me because this is rendundant but #specs
	goji.Handle("/*", http.FileServer(http.Dir("content")))
	flag.Set("bind", ":9020") // Set the port that Goji listens on
	goji.Serve()              // Start listening
}
<|endoftext|>


<commit_before>package pilosa

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/pilosa/pilosa/internal"
)

// Default server settings.
const (
	DefaultAntiEntropyInterval = 10 * time.Minute
	DefaultPollingInterval     = 60 * time.Second
)

// Server represents a holder wrapped by a running HTTP server.
type Server struct {
	ln net.Listener

	// Close management.
	wg      sync.WaitGroup
	closing chan struct{}

	// Data storage and HTTP interface.
	Holder            *Holder
	Handler           *Handler
	Broadcaster       Broadcaster
	BroadcastReceiver BroadcastReceiver

	// Cluster configuration.
	// Host is replaced with actual host after opening if port is ":0".
	Host    string
	Cluster *Cluster

	// Background monitoring intervals.
	AntiEntropyInterval time.Duration
	PollingInterval     time.Duration

	LogOutput io.Writer
}

// NewServer returns a new instance of Server.
func NewServer() *Server {
	s := &Server{
		closing: make(chan struct{}),

		Holder:            NewHolder(),
		Handler:           NewHandler(),
		Broadcaster:       NopBroadcaster,
		BroadcastReceiver: NopBroadcastReceiver,

		AntiEntropyInterval: DefaultAntiEntropyInterval,
		PollingInterval:     DefaultPollingInterval,

		LogOutput: os.Stderr,
	}

	s.Handler.Holder = s.Holder

	return s
}

// Open opens and initializes the server.
func (s *Server) Open() error {
	// Require a port in the hostname.
	host, port, err := net.SplitHostPort(s.Host)
	if err != nil {
		return err
	} else if port == "" {
		port = DefaultPort
	}

	// Open HTTP listener to determine port (if specified as :0).
	ln, err := net.Listen("tcp", ":"+port)
	if err != nil {
		return err
	}
	s.ln = ln

	// Determine hostname based on listening port.
	s.Host = net.JoinHostPort(host, strconv.Itoa(s.ln.Addr().(*net.TCPAddr).Port))

	// Create local node if no cluster is specified.
	if len(s.Cluster.Nodes) == 0 {
		s.Cluster.Nodes = []*Node{{Host: s.Host}}
	}

	// Open holder.
	if err := s.Holder.Open(); err != nil {
		return err
	}

	if err := s.BroadcastReceiver.Start(s); err != nil {
		return err
	}

	// Open NodeSet communication
	if err := s.Cluster.NodeSet.Open(); err != nil {
		return err
	}

	// Create executor for executing queries.
	e := NewExecutor()
	e.Holder = s.Holder
	e.Host = s.Host
	e.Cluster = s.Cluster

	// Initialize HTTP handler.
	s.Handler.Broadcaster = s.Broadcaster
	s.Handler.StatusHandler = s
	s.Handler.Host = s.Host
	s.Handler.Cluster = s.Cluster
	s.Handler.Executor = e
	s.Handler.LogOutput = s.LogOutput

	// Initialize Holder.
	s.Holder.Broadcaster = s.Broadcaster
	s.Holder.LogOutput = s.LogOutput

	// Serve HTTP.
	go func() { http.Serve(ln, s.Handler) }()

	// Start background monitoring.
	s.wg.Add(2)
	go func() { defer s.wg.Done(); s.monitorAntiEntropy() }()
	go func() { defer s.wg.Done(); s.monitorMaxSlices() }()

	return nil
}

// Close closes the server and waits for it to shutdown.
func (s *Server) Close() error {
	// Notify goroutines to stop.
	close(s.closing)
	s.wg.Wait()

	if s.ln != nil {
		s.ln.Close()
	}
	if s.Holder != nil {
		s.Holder.Close()
	}

	return nil
}

// Addr returns the address of the listener.
func (s *Server) Addr() net.Addr {
	if s.ln == nil {
		return nil
	}
	return s.ln.Addr()
}

func (s *Server) logger() *log.Logger { return log.New(s.LogOutput, "", log.LstdFlags) }

func (s *Server) monitorAntiEntropy() {
	ticker := time.NewTicker(s.AntiEntropyInterval)
	defer ticker.Stop()

	s.logger().Printf("holder sync monitor initializing (%s interval)", s.AntiEntropyInterval)

	for {
		// Wait for tick or a close.
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		s.logger().Printf("holder sync beginning")

		// Initialize syncer with local holder and remote client.
		var syncer HolderSyncer
		syncer.Holder = s.Holder
		syncer.Host = s.Host
		syncer.Cluster = s.Cluster
		syncer.Closing = s.closing

		// Sync holders.
		if err := syncer.SyncHolder(); err != nil {
			s.logger().Printf("holder sync error: err=%s", err)
			continue
		}

		// Record successful sync in log.
		s.logger().Printf("holder sync complete")
	}
}

// monitorMaxSlices periodically pulls the highest slice from each node in the cluster.
func (s *Server) monitorMaxSlices() {
	// Ignore if only one node in the cluster.
	if len(s.Cluster.Nodes) <= 1 {
		return
	}

	ticker := time.NewTicker(s.PollingInterval)
	defer ticker.Stop()

	for {
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		oldmaxslices := s.Holder.MaxSlices()
		for _, node := range s.Cluster.Nodes {
			if s.Host != node.Host {
				maxSlices, _ := checkMaxSlices(node.Host)
				for index, newmax := range maxSlices {
					// if we don't know about an index locally, log an error because
					// indexes should be created and synced prior to slice creation
					if localIndex := s.Holder.Index(index); localIndex != nil {
						if newmax > oldmaxslices[index] {
							oldmaxslices[index] = newmax
							localIndex.SetRemoteMaxSlice(newmax)
						}
					} else {
						s.logger().Printf("Local Index not found: %s", index)
					}
				}
			}
		}
	}
}

// ReceiveMessage represents an implementation of BroadcastHandler.
func (s *Server) ReceiveMessage(pb proto.Message) error {
	switch obj := pb.(type) {
	case *internal.CreateSliceMessage:
		idx := s.Holder.Index(obj.Index)
		if idx == nil {
			return fmt.Errorf("Local Index not found: %s", obj.Index)
		}
		if obj.IsInverse {
			idx.SetRemoteMaxInverseSlice(obj.Slice)
		} else {
			idx.SetRemoteMaxSlice(obj.Slice)
		}
	case *internal.CreateIndexMessage:
		opt := IndexOptions{
			ColumnLabel: obj.Meta.ColumnLabel,
			TimeQuantum: TimeQuantum(obj.Meta.TimeQuantum),
		}
		_, err := s.Holder.CreateIndex(obj.Index, opt)
		if err != nil {
			return err
		}
	case *internal.DeleteIndexMessage:
		if err := s.Holder.DeleteIndex(obj.Index); err != nil {
			return err
		}
	case *internal.CreateFrameMessage:
		index := s.Holder.Index(obj.Index)
		opt := FrameOptions{
			RowLabel:       obj.Meta.RowLabel,
			InverseEnabled: obj.Meta.InverseEnabled,
			CacheType:      obj.Meta.CacheType,
			CacheSize:      obj.Meta.CacheSize,
			TimeQuantum:    TimeQuantum(obj.Meta.TimeQuantum),
		}
		_, err := index.CreateFrame(obj.Frame, opt)
		if err != nil {
			return err
		}
	case *internal.DeleteFrameMessage:
		index := s.Holder.Index(obj.Index)
		if err := index.DeleteFrame(obj.Frame); err != nil {
			return err
		}
	}
	return nil
}

// LocalStatus returns the state of the local node as well as the
// holder (indexes/frames) according to the local node.
// In a gossip implementation, memberlist.Delegate.LocalState() uses this.
// Server implements StatusHandler.
func (s *Server) LocalStatus() (proto.Message, error) {
	if s.Holder == nil {
		return nil, errors.New("Server.Holder is nil")
	}

	ns := internal.NodeStatus{
		Host:    s.Host,
		State:   NodeStateUp,
		Indexes: encodeIndexes(s.Holder.Indexes()),
	}

	// Append Slice list per this Node's indexes
	for _, index := range ns.Indexes {
		index.Slices = s.Cluster.OwnsSlices(index.Name, index.MaxSlice, s.Host)
	}

	return &ns, nil
}

// ClusterStatus returns the NodeState for all nodes in the cluster.
func (s *Server) ClusterStatus() (proto.Message, error) {
	// Update local Node.state.
	ns, err := s.LocalStatus()
	if err != nil {
		return nil, err
	}
	node := s.Cluster.NodeByHost(s.Host)
	node.SetStatus(ns.(*internal.NodeStatus))

	// Update NodeState for all nodes.
	for host, nodeState := range s.Cluster.NodeStates() {
		node := s.Cluster.NodeByHost(host)
		node.SetState(nodeState)
	}

	return s.Cluster.Status(), nil
}

// HandleRemoteStatus receives incoming NodeState from remote nodes.
func (s *Server) HandleRemoteStatus(pb proto.Message) error {
	return s.mergeRemoteStatus(pb.(*internal.NodeStatus))
}

func (s *Server) mergeRemoteStatus(ns *internal.NodeStatus) error {
	// Update Node.state.
	node := s.Cluster.NodeByHost(ns.Host)
	node.SetStatus(ns)

	// Create indexes that don't exist.
	for _, index := range ns.Indexes {
		opt := IndexOptions{
			ColumnLabel: index.Meta.ColumnLabel,
			TimeQuantum: TimeQuantum(index.Meta.TimeQuantum),
		}
		idx, err := s.Holder.CreateIndexIfNotExists(index.Name, opt)
		if err != nil {
			return err
		}
		// Create frames that don't exist.
		for _, f := range index.Frames {
			opt := FrameOptions{
				RowLabel:    f.Meta.RowLabel,
				TimeQuantum: TimeQuantum(f.Meta.TimeQuantum),
				CacheSize:   f.Meta.CacheSize,
			}
			_, err := idx.CreateFrameIfNotExists(f.Name, opt)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func checkMaxSlices(hostport string) (map[string]uint64, error) {
	// Create HTTP request.
	req, err := http.NewRequest("GET", (&url.URL{
		Scheme: "http",
		Host:   hostport,
		Path:   "/slices/max",
	}).String(), nil)

	if err != nil {
		return nil, err
	}

	// Require protobuf encoding.
	req.Header.Set("Accept", "application/x-protobuf")
	req.Header.Set("Content-Type", "application/x-protobuf")

	// Send request to remote node.
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Read response into buffer.
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Check status code.
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("invalid status: code=%d, err=%s", resp.StatusCode, body)
	}

	// Decode response object.
	pb := internal.MaxSlicesResponse{}

	if err = proto.Unmarshal(body, &pb); err != nil {
		return nil, err
	}

	return pb.MaxSlices, nil
}

// StatusHandler specifies two methods which an object must implement to share
// state in the cluster. These are used by the GossipNodeSet to implement the
// LocalState and MergeRemoteState methods of memberlist.Delegate
type StatusHandler interface {
	LocalStatus() (proto.Message, error)
	ClusterStatus() (proto.Message, error)
	HandleRemoteStatus(proto.Message) error
}
<commit_msg>set local node state to UP by default<commit_after>package pilosa

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/pilosa/pilosa/internal"
)

// Default server settings.
const (
	DefaultAntiEntropyInterval = 10 * time.Minute
	DefaultPollingInterval     = 60 * time.Second
)

// Server represents a holder wrapped by a running HTTP server.
type Server struct {
	ln net.Listener

	// Close management.
	wg      sync.WaitGroup
	closing chan struct{}

	// Data storage and HTTP interface.
	Holder            *Holder
	Handler           *Handler
	Broadcaster       Broadcaster
	BroadcastReceiver BroadcastReceiver

	// Cluster configuration.
	// Host is replaced with actual host after opening if port is ":0".
	Host    string
	Cluster *Cluster

	// Background monitoring intervals.
	AntiEntropyInterval time.Duration
	PollingInterval     time.Duration

	LogOutput io.Writer
}

// NewServer returns a new instance of Server.
func NewServer() *Server {
	s := &Server{
		closing: make(chan struct{}),

		Holder:            NewHolder(),
		Handler:           NewHandler(),
		Broadcaster:       NopBroadcaster,
		BroadcastReceiver: NopBroadcastReceiver,

		AntiEntropyInterval: DefaultAntiEntropyInterval,
		PollingInterval:     DefaultPollingInterval,

		LogOutput: os.Stderr,
	}

	s.Handler.Holder = s.Holder

	return s
}

// Open opens and initializes the server.
func (s *Server) Open() error {
	// Require a port in the hostname.
	host, port, err := net.SplitHostPort(s.Host)
	if err != nil {
		return err
	} else if port == "" {
		port = DefaultPort
	}

	// Open HTTP listener to determine port (if specified as :0).
	ln, err := net.Listen("tcp", ":"+port)
	if err != nil {
		return err
	}
	s.ln = ln

	// Determine hostname based on listening port.
	s.Host = net.JoinHostPort(host, strconv.Itoa(s.ln.Addr().(*net.TCPAddr).Port))

	// Create local node if no cluster is specified.
	if len(s.Cluster.Nodes) == 0 {
		s.Cluster.Nodes = []*Node{{Host: s.Host}}
	}

	// Open holder.
	if err := s.Holder.Open(); err != nil {
		return err
	}

	if err := s.BroadcastReceiver.Start(s); err != nil {
		return err
	}

	// Open NodeSet communication
	if err := s.Cluster.NodeSet.Open(); err != nil {
		return err
	}

	// Create executor for executing queries.
	e := NewExecutor()
	e.Holder = s.Holder
	e.Host = s.Host
	e.Cluster = s.Cluster

	// Initialize HTTP handler.
	s.Handler.Broadcaster = s.Broadcaster
	s.Handler.StatusHandler = s
	s.Handler.Host = s.Host
	s.Handler.Cluster = s.Cluster
	s.Handler.Executor = e
	s.Handler.LogOutput = s.LogOutput

	// Initialize Holder.
	s.Holder.Broadcaster = s.Broadcaster
	s.Holder.LogOutput = s.LogOutput

	// Serve HTTP.
	go func() { http.Serve(ln, s.Handler) }()

	// Start background monitoring.
	s.wg.Add(2)
	go func() { defer s.wg.Done(); s.monitorAntiEntropy() }()
	go func() { defer s.wg.Done(); s.monitorMaxSlices() }()

	return nil
}

// Close closes the server and waits for it to shutdown.
func (s *Server) Close() error {
	// Notify goroutines to stop.
	close(s.closing)
	s.wg.Wait()

	if s.ln != nil {
		s.ln.Close()
	}
	if s.Holder != nil {
		s.Holder.Close()
	}

	return nil
}

// Addr returns the address of the listener.
func (s *Server) Addr() net.Addr {
	if s.ln == nil {
		return nil
	}
	return s.ln.Addr()
}

func (s *Server) logger() *log.Logger { return log.New(s.LogOutput, "", log.LstdFlags) }

func (s *Server) monitorAntiEntropy() {
	ticker := time.NewTicker(s.AntiEntropyInterval)
	defer ticker.Stop()

	s.logger().Printf("holder sync monitor initializing (%s interval)", s.AntiEntropyInterval)

	for {
		// Wait for tick or a close.
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		s.logger().Printf("holder sync beginning")

		// Initialize syncer with local holder and remote client.
		var syncer HolderSyncer
		syncer.Holder = s.Holder
		syncer.Host = s.Host
		syncer.Cluster = s.Cluster
		syncer.Closing = s.closing

		// Sync holders.
		if err := syncer.SyncHolder(); err != nil {
			s.logger().Printf("holder sync error: err=%s", err)
			continue
		}

		// Record successful sync in log.
		s.logger().Printf("holder sync complete")
	}
}

// monitorMaxSlices periodically pulls the highest slice from each node in the cluster.
func (s *Server) monitorMaxSlices() {
	// Ignore if only one node in the cluster.
	if len(s.Cluster.Nodes) <= 1 {
		return
	}

	ticker := time.NewTicker(s.PollingInterval)
	defer ticker.Stop()

	for {
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		oldmaxslices := s.Holder.MaxSlices()
		for _, node := range s.Cluster.Nodes {
			if s.Host != node.Host {
				maxSlices, _ := checkMaxSlices(node.Host)
				for index, newmax := range maxSlices {
					// if we don't know about an index locally, log an error because
					// indexes should be created and synced prior to slice creation
					if localIndex := s.Holder.Index(index); localIndex != nil {
						if newmax > oldmaxslices[index] {
							oldmaxslices[index] = newmax
							localIndex.SetRemoteMaxSlice(newmax)
						}
					} else {
						s.logger().Printf("Local Index not found: %s", index)
					}
				}
			}
		}
	}
}

// ReceiveMessage represents an implementation of BroadcastHandler.
func (s *Server) ReceiveMessage(pb proto.Message) error {
	switch obj := pb.(type) {
	case *internal.CreateSliceMessage:
		idx := s.Holder.Index(obj.Index)
		if idx == nil {
			return fmt.Errorf("Local Index not found: %s", obj.Index)
		}
		if obj.IsInverse {
			idx.SetRemoteMaxInverseSlice(obj.Slice)
		} else {
			idx.SetRemoteMaxSlice(obj.Slice)
		}
	case *internal.CreateIndexMessage:
		opt := IndexOptions{
			ColumnLabel: obj.Meta.ColumnLabel,
			TimeQuantum: TimeQuantum(obj.Meta.TimeQuantum),
		}
		_, err := s.Holder.CreateIndex(obj.Index, opt)
		if err != nil {
			return err
		}
	case *internal.DeleteIndexMessage:
		if err := s.Holder.DeleteIndex(obj.Index); err != nil {
			return err
		}
	case *internal.CreateFrameMessage:
		index := s.Holder.Index(obj.Index)
		opt := FrameOptions{
			RowLabel:       obj.Meta.RowLabel,
			InverseEnabled: obj.Meta.InverseEnabled,
			CacheType:      obj.Meta.CacheType,
			CacheSize:      obj.Meta.CacheSize,
			TimeQuantum:    TimeQuantum(obj.Meta.TimeQuantum),
		}
		_, err := index.CreateFrame(obj.Frame, opt)
		if err != nil {
			return err
		}
	case *internal.DeleteFrameMessage:
		index := s.Holder.Index(obj.Index)
		if err := index.DeleteFrame(obj.Frame); err != nil {
			return err
		}
	}
	return nil
}

// LocalStatus returns the state of the local node as well as the
// holder (indexes/frames) according to the local node.
// In a gossip implementation, memberlist.Delegate.LocalState() uses this.
// Server implements StatusHandler.
func (s *Server) LocalStatus() (proto.Message, error) {
	if s.Holder == nil {
		return nil, errors.New("Server.Holder is nil")
	}

	ns := internal.NodeStatus{
		Host:    s.Host,
		State:   NodeStateUp,
		Indexes: encodeIndexes(s.Holder.Indexes()),
	}

	// Append Slice list per this Node's indexes
	for _, index := range ns.Indexes {
		index.Slices = s.Cluster.OwnsSlices(index.Name, index.MaxSlice, s.Host)
	}

	return &ns, nil
}

// ClusterStatus returns the NodeState for all nodes in the cluster.
func (s *Server) ClusterStatus() (proto.Message, error) {
	// Update local Node.state.
	ns, err := s.LocalStatus()
	if err != nil {
		return nil, err
	}
	node := s.Cluster.NodeByHost(s.Host)
	node.SetStatus(ns.(*internal.NodeStatus))

	// Update NodeState for all nodes.
	for host, nodeState := range s.Cluster.NodeStates() {
		// In a default configuration (or single-node) where a StaticNodeSet is used
		// then all nodes are marked as DOWN. At the very least, we should consider
		// the local node as UP.
		// TODO: we should be able to remove this check if/when cluster.Nodes and
		// cluster.NodeSet are unified.
		if host == s.Host {
			nodeState = NodeStateUp
		}
		node := s.Cluster.NodeByHost(host)
		node.SetState(nodeState)
	}

	return s.Cluster.Status(), nil
}

// HandleRemoteStatus receives incoming NodeState from remote nodes.
func (s *Server) HandleRemoteStatus(pb proto.Message) error {
	return s.mergeRemoteStatus(pb.(*internal.NodeStatus))
}

func (s *Server) mergeRemoteStatus(ns *internal.NodeStatus) error {
	// Update Node.state.
	node := s.Cluster.NodeByHost(ns.Host)
	node.SetStatus(ns)

	// Create indexes that don't exist.
	for _, index := range ns.Indexes {
		opt := IndexOptions{
			ColumnLabel: index.Meta.ColumnLabel,
			TimeQuantum: TimeQuantum(index.Meta.TimeQuantum),
		}
		idx, err := s.Holder.CreateIndexIfNotExists(index.Name, opt)
		if err != nil {
			return err
		}
		// Create frames that don't exist.
		for _, f := range index.Frames {
			opt := FrameOptions{
				RowLabel:    f.Meta.RowLabel,
				TimeQuantum: TimeQuantum(f.Meta.TimeQuantum),
				CacheSize:   f.Meta.CacheSize,
			}
			_, err := idx.CreateFrameIfNotExists(f.Name, opt)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func checkMaxSlices(hostport string) (map[string]uint64, error) {
	// Create HTTP request.
	req, err := http.NewRequest("GET", (&url.URL{
		Scheme: "http",
		Host:   hostport,
		Path:   "/slices/max",
	}).String(), nil)

	if err != nil {
		return nil, err
	}

	// Require protobuf encoding.
	req.Header.Set("Accept", "application/x-protobuf")
	req.Header.Set("Content-Type", "application/x-protobuf")

	// Send request to remote node.
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Read response into buffer.
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Check status code.
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("invalid status: code=%d, err=%s", resp.StatusCode, body)
	}

	// Decode response object.
	pb := internal.MaxSlicesResponse{}

	if err = proto.Unmarshal(body, &pb); err != nil {
		return nil, err
	}

	return pb.MaxSlices, nil
}

// StatusHandler specifies two methods which an object must implement to share
// state in the cluster. These are used by the GossipNodeSet to implement the
// LocalState and MergeRemoteState methods of memberlist.Delegate
type StatusHandler interface {
	LocalStatus() (proto.Message, error)
	ClusterStatus() (proto.Message, error)
	HandleRemoteStatus(proto.Message) error
}
<|endoftext|>