<commit_before>package janitor

import (
	"encoding/json"
	"time"
)

// Stats holds all of statistics data.
type Stats struct {
	Global *GlobalCounter `json:"global"` // global counter
	App    AppCounter     `json:"app"`    // app -> task -> counter

	inGlbCh  chan *deltaGlb // new global counter delta received
	inAppCh  chan *deltaApp // new app counter delta received
	delAppCh chan *deltaApp // removal signal app->task counter delta
}

// GlobalCounter hold current global statistics
type GlobalCounter struct {
	RxBytes  uint64 `json:"rx_bytes"`      // nb of received bytes
	TxBytes  uint64 `json:"tx_bytes"`      // nb of transmitted bytes
	Requests uint64 `json:"requests"`      // nb of client requests
	Fails    uint64 `json:"fails"`         // nb of failed requesets
	RxRate   uint   `json:"rx_rate"`       // received bytes / second
	TxRate   uint   `json:"tx_rate"`       // transmitted bytes / second
	ReqRate  uint   `json:"requests_rate"` // requests / second
	FailRate uint   `json:"fails_rate"`    // failed requests / second

	startedAt time.Time
}

type GlobalCounterAlias GlobalCounter

func (c *GlobalCounter) MarshalJSON() ([]byte, error) {
	var wrapper struct {
		GlobalCounterAlias
		Uptime string `json:"uptime"`
	}

	wrapper.GlobalCounterAlias = GlobalCounterAlias(*c)
	wrapper.Uptime = time.Now().Sub(c.startedAt).String()
	return json.Marshal(wrapper)
}

// AppCounter hold app current statistics
type AppCounter map[string]map[string]*TaskCounter

// TaskCounter hold one app-task's current statistics
type TaskCounter struct {
	ActiveClients uint   `json:"active_clients"` // active clients
	RxBytes       uint64 `json:"rx_bytes"`       // nb of received bytes
	TxBytes       uint64 `json:"tx_bytes"`       // nb of transmitted bytes
	Requests      uint64 `json:"requests"`       // nb of requests
	RxRate        uint   `json:"rx_rate"`        // received bytes / second
	TxRate        uint   `json:"tx_rate"`        // transmitted bytes / second
	ReqRate       uint   `json:"requests_rate"`  // requests / second

	startedAt time.Time
}

type TaskCounterAlias TaskCounter

func (c *TaskCounter) MarshalJSON() ([]byte, error) {
	var wrapper struct {
		TaskCounterAlias
		Uptime string `json:"uptime"`
	}

	wrapper.TaskCounterAlias = TaskCounterAlias(*c)
	wrapper.Uptime = time.Now().Sub(c.startedAt).String()
	return json.Marshal(wrapper)
}

type deltaApp struct {
	aid string
	tid string
	ac  int
	rx  uint64
	tx  uint64
	req uint64
}

type deltaGlb struct {
	rx   uint64
	tx   uint64
	req  uint64
	fail uint64
}

func newStats() *Stats {
	c := &Stats{
		Global: &GlobalCounter{
			startedAt: time.Now(),
		},
		App:      make(AppCounter),
		inGlbCh:  make(chan *deltaGlb, 1024),
		inAppCh:  make(chan *deltaApp, 1024),
		delAppCh: make(chan *deltaApp, 128),
	}

	go c.runCounters()
	return c
}

func (c *Stats) incr(dapp *deltaApp, dglb *deltaGlb) {
	if dapp != nil {
		c.inAppCh <- dapp
	}
	if dglb != nil {
		c.inGlbCh <- dglb
	}
}

func (c *Stats) del(aid, tid string) {
	c.delAppCh <- &deltaApp{aid: aid, tid: tid}
}

func (c *Stats) runCounters() {
	for {
		select {
		case d := <-c.inAppCh:
			c.updateApp(d)
		case d := <-c.inGlbCh:
			c.updateGlb(d)
		case d := <-c.delAppCh:
			c.removeApp(d)
		}
	}
}

func (c *Stats) updateGlb(d *deltaGlb) {
	c.Global.RxBytes += d.rx
	c.Global.TxBytes += d.tx
	c.Global.Requests += d.req
	c.Global.Fails += d.fail
}

func (c *Stats) updateApp(d *deltaApp) {
	if d.aid == "" || d.tid == "" {
		return
	}

	if _, ok := c.App[d.aid]; !ok {
		c.App[d.aid] = make(map[string]*TaskCounter)
	}
	app := c.App[d.aid]

	if _, ok := app[d.tid]; !ok {
		app[d.tid] = &TaskCounter{
			startedAt: time.Now(),
		}
	}
	task := app[d.tid]

	task.ActiveClients += uint(d.ac)
	if task.ActiveClients < 0 {
		task.ActiveClients = 0
	}

	if n := d.rx; n > 0 {
		task.RxBytes += n
	}
	if n := d.tx; n > 0 {
		task.TxBytes += n
	}
	if n := d.req; n > 0 {
		task.Requests += n
	}
}

func (c *Stats) removeApp(d *deltaApp) {
	if d.aid == "" || d.tid == "" {
		return
	}
	if _, ok := c.App[d.aid]; !ok {
		return
	}
	app := c.App[d.aid]

	if _, ok := app[d.tid]; ok {
		delete(app, d.tid)
	}

	if len(app) == 0 {
		delete(c.App, d.aid)
	}
}
<commit_msg>add proxy realtime traffic rate statistics<commit_after>package janitor

import (
	"encoding/json"
	"time"
)

var (
	rateFreshIntv = time.Second * 2 // rate calculation interval
)

// Stats holds all of statistics data.
type Stats struct {
	Global *GlobalCounter `json:"global"` // global counter
	App    AppCounter     `json:"app"`    // app -> task -> counter

	inGlbCh  chan *deltaGlb // new global counter delta received
	inAppCh  chan *deltaApp // new app counter delta received
	delAppCh chan *deltaApp // removal signal app->task counter delta
}

// GlobalCounter hold current global statistics
type GlobalCounter struct {
	RxBytes  uint64 `json:"rx_bytes"`      // nb of received bytes
	TxBytes  uint64 `json:"tx_bytes"`      // nb of transmitted bytes
	Requests uint64 `json:"requests"`      // nb of client requests
	Fails    uint64 `json:"fails"`         // nb of failed requesets
	RxRate   uint   `json:"rx_rate"`       // received bytes / second
	TxRate   uint   `json:"tx_rate"`       // transmitted bytes / second
	ReqRate  uint   `json:"requests_rate"` // requests / second
	FailRate uint   `json:"fails_rate"`    // failed requests / second

	lastRx   uint64 // used for calculate rate per second
	lastTx   uint64
	lastReq  uint64
	lastFail uint64
	freshed  bool

	startedAt time.Time
}

type GlobalCounterAlias GlobalCounter

func (c *GlobalCounter) MarshalJSON() ([]byte, error) {
	var wrapper struct {
		GlobalCounterAlias
		Uptime string `json:"uptime"`
	}

	wrapper.GlobalCounterAlias = GlobalCounterAlias(*c)
	wrapper.Uptime = time.Now().Sub(c.startedAt).String()
	return json.Marshal(wrapper)
}

// AppCounter hold app current statistics
type AppCounter map[string]map[string]*TaskCounter

// TaskCounter hold one app-task's current statistics
type TaskCounter struct {
	ActiveClients uint   `json:"active_clients"` // active clients
	RxBytes       uint64 `json:"rx_bytes"`       // nb of received bytes
	TxBytes       uint64 `json:"tx_bytes"`       // nb of transmitted bytes
	Requests      uint64 `json:"requests"`       // nb of requests
	RxRate        uint   `json:"rx_rate"`        // received bytes / second
	TxRate        uint   `json:"tx_rate"`        // transmitted bytes / second
	ReqRate       uint   `json:"requests_rate"`  // requests / second

	lastRx  uint64 // used for calculate rate per second
	lastTx  uint64
	lastReq uint64
	freshed bool

	startedAt time.Time
}

type TaskCounterAlias TaskCounter

func (c *TaskCounter) MarshalJSON() ([]byte, error) {
	var wrapper struct {
		TaskCounterAlias
		Uptime string `json:"uptime"`
	}

	wrapper.TaskCounterAlias = TaskCounterAlias(*c)
	wrapper.Uptime = time.Now().Sub(c.startedAt).String()
	return json.Marshal(wrapper)
}

type deltaApp struct {
	aid string
	tid string
	ac  int
	rx  uint64
	tx  uint64
	req uint64
}

type deltaGlb struct {
	rx   uint64
	tx   uint64
	req  uint64
	fail uint64
}

func newStats() *Stats {
	c := &Stats{
		Global: &GlobalCounter{
			startedAt: time.Now(),
		},
		App:      make(AppCounter),
		inGlbCh:  make(chan *deltaGlb, 1024),
		inAppCh:  make(chan *deltaApp, 1024),
		delAppCh: make(chan *deltaApp, 128),
	}

	go c.runCounters()
	return c
}

func (c *Stats) incr(dapp *deltaApp, dglb *deltaGlb) {
	if dapp != nil {
		c.inAppCh <- dapp
	}
	if dglb != nil {
		c.inGlbCh <- dglb
	}
}

func (c *Stats) del(aid, tid string) {
	c.delAppCh <- &deltaApp{aid: aid, tid: tid}
}

func (c *Stats) runCounters() {
	ticker := time.NewTicker(rateFreshIntv)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			c.freshRate()
		case d := <-c.inAppCh:
			c.updateApp(d)
		case d := <-c.inGlbCh:
			c.updateGlb(d)
		case d := <-c.delAppCh:
			c.removeApp(d)
		}
	}
}
func (c *Stats) freshRate() {
	c.Global.freshRate()

	for _, app := range c.App {
		for _, task := range app {
			task.freshRate()
		}
	}
}

// fresh global counter
func (c *GlobalCounter) freshRate() {
	if !c.freshed {
		c.RxRate = 0
		c.TxRate = 0
		c.ReqRate = 0
		c.FailRate = 0
		return
	}

	var (
		nRx   = c.RxBytes - c.lastRx
		nTx   = c.TxBytes - c.lastTx
		nReq  = c.Requests - c.lastReq
		nFail = c.Fails - c.lastFail
		intv  = uint64(rateFreshIntv.Seconds())
	)

	c.RxRate = uint(nRx / intv)
	c.TxRate = uint(nTx / intv)
	c.ReqRate = uint(nReq / intv)
	c.FailRate = uint(nFail / intv)

	c.lastRx = c.RxBytes
	c.lastTx = c.TxBytes
	c.lastReq = c.Requests
	c.lastFail = c.Fails

	c.freshed = false // mark as consumed
}

// fresh task counter
func (c *TaskCounter) freshRate() {
	if !c.freshed {
		c.RxRate = 0
		c.TxRate = 0
		c.ReqRate = 0
		return
	}

	var (
		nRx  = c.RxBytes - c.lastRx
		nTx  = c.TxBytes - c.lastTx
		nReq = c.Requests - c.lastReq
		intv = uint64(rateFreshIntv.Seconds())
	)

	c.RxRate = uint(nRx / intv)
	c.TxRate = uint(nTx / intv)
	c.ReqRate = uint(nReq / intv)

	c.lastRx = c.RxBytes
	c.lastTx = c.TxBytes
	c.lastReq = c.Requests

	c.freshed = false // mark as consumed
}

func (c *Stats) updateGlb(d *deltaGlb) {
	c.Global.RxBytes += d.rx
	c.Global.TxBytes += d.tx
	c.Global.Requests += d.req
	c.Global.Fails += d.fail
	c.Global.freshed = true
}

func (c *Stats) updateApp(d *deltaApp) {
	if d.aid == "" || d.tid == "" {
		return
	}

	if _, ok := c.App[d.aid]; !ok {
		c.App[d.aid] = make(map[string]*TaskCounter)
	}
	app := c.App[d.aid]

	if _, ok := app[d.tid]; !ok {
		app[d.tid] = &TaskCounter{
			startedAt: time.Now(),
		}
	}
	task := app[d.tid]

	task.ActiveClients += uint(d.ac)
	if task.ActiveClients < 0 {
		task.ActiveClients = 0
	}

	if n := d.rx; n > 0 {
		task.RxBytes += n
	}
	if n := d.tx; n > 0 {
		task.TxBytes += n
	}
	if n := d.req; n > 0 {
		task.Requests += n
	}

	task.freshed = true
}

func (c *Stats) removeApp(d *deltaApp) {
	if d.aid == "" || d.tid == "" {
		return
	}
	if _, ok := c.App[d.aid]; !ok {
		return
	}
	app := c.App[d.aid]

	if _, ok := app[d.tid]; ok {
		delete(app, d.tid)
	}

	if len(app) == 0 {
		delete(c.App, d.aid)
	}
}
<|endoftext|>


<commit_before>package testlog

import (
	"testing"

	"github.com/getlantern/golog"
)

// Capture captures logs to the given testing.T's Log function.
// Returns a function that stops capturing logs.
//
// Typical usage:
//
//    func MyTest(t *testing.T) {
//        stopCapture := testlog.Capture(t)
//        defer stopCapture()
//        // do stuff
//    }
//
func Capture(t *testing.T) func() {
	w := &testLogWriter{t}
	return golog.SetOutputs(w, w)
}

type testLogWriter struct {
	*testing.T
}

func (w testLogWriter) Write(p []byte) (n int, err error) {
	w.Log((string)(p))
	return len(p), nil
}
<commit_msg>call T.Log in the same goroutine to avoid data race<commit_after>package testlog

import (
	"errors"
	"sync"
	"testing"

	"github.com/getlantern/golog"
)

// Capture captures logs to the given testing.T's Log function.
// Returns a function that stops capturing logs.
//
// Typical usage:
//
//    func MyTest(t *testing.T) {
//        stopCapture := testlog.Capture(t)
//        defer stopCapture()
//        // do stuff
//    }
//
func Capture(t *testing.T) func() {
	w := &testLogWriter{T: t, ch: make(chan []byte)}
	go w.run()
	reset := golog.SetOutputs(w, w)
	return func() {
		reset()
		w.stop()
	}
}

type testLogWriter struct {
	*testing.T
	mu      sync.Mutex
	stopped bool
	ch      chan []byte
}

func (w *testLogWriter) Write(p []byte) (n int, err error) {
	w.mu.Lock()
	defer w.mu.Unlock()
	if !w.stopped {
		w.ch <- p
		return len(p), nil
	}
	return 0, errors.New("writing to stopped testlog writer")
}

func (w *testLogWriter) stop() {
	w.mu.Lock()
	defer w.mu.Unlock()
	w.stopped = true
	close(w.ch)
}

func (w *testLogWriter) run() {
	for p := range w.ch {
		w.Log((string)(p))
	}
}
<|endoftext|>


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

// Primitive HTTP client. See RFC 2616.

package http

import (
	"encoding/base64"
	"fmt"
	"io"
	"os"
	"strings"
)

// A Client is an HTTP client. Its zero value (DefaultClient) is a usable client
// that uses DefaultTransport.
// Client is not yet very configurable.
type Client struct {
	Transport RoundTripper // if nil, DefaultTransport is used

	// If CheckRedirect is not nil, the client calls it before
	// following an HTTP redirect. The arguments req and via
	// are the upcoming request and the requests made already,
	// oldest first. If CheckRedirect returns an error, the client
	// returns that error instead of issue the Request req.
	//
	// If CheckRedirect is nil, the Client uses its default policy,
	// which is to stop after 10 consecutive requests.
	CheckRedirect func(req *Request, via []*Request) os.Error
}

// DefaultClient is the default Client and is used by Get, Head, and Post.
var DefaultClient = &Client{}

// RoundTripper is an interface representing the ability to execute a
// single HTTP transaction, obtaining the Response for a given Request.
type RoundTripper interface {
	// RoundTrip executes a single HTTP transaction, returning
	// the Response for the request req.  RoundTrip should not
	// attempt to interpret the response.  In particular,
	// RoundTrip must return err == nil if it obtained a response,
	// regardless of the response's HTTP status code.  A non-nil
	// err should be reserved for failure to obtain a response.
	// Similarly, RoundTrip should not attempt to handle
	// higher-level protocol details such as redirects,
	// authentication, or cookies.
	//
	// RoundTrip may modify the request. The request Headers field is
	// guaranteed to be initialized.
	RoundTrip(req *Request) (resp *Response, err os.Error)
}

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Used in Send to implement io.ReadCloser by bundling together the
// bufio.Reader through which we read the response, and the underlying
// network connection.
type readClose struct {
	io.Reader
	io.Closer
}

// Do sends an HTTP request and returns an HTTP response, following
// policy (e.g. redirects, cookies, auth) as configured on the client.
//
// Callers should close resp.Body when done reading from it.
//
// Generally Get, Post, or PostForm will be used instead of Do.
func (c *Client) Do(req *Request) (resp *Response, err os.Error) {
	if req.Method == "GET" || req.Method == "HEAD" {
		return c.doFollowingRedirects(req)
	}
	return send(req, c.Transport)
}

// send issues an HTTP request.  Caller should close resp.Body when done reading from it.
func send(req *Request, t RoundTripper) (resp *Response, err os.Error) {
	if t == nil {
		t = DefaultTransport
		if t == nil {
			err = os.NewError("no http.Client.Transport or http.DefaultTransport")
			return
		}
	}

	// Most the callers of send (Get, Post, et al) don't need
	// Headers, leaving it uninitialized.  We guarantee to the
	// Transport that this has been initialized, though.
	if req.Header == nil {
		req.Header = make(Header)
	}

	info := req.URL.RawUserinfo
	if len(info) > 0 {
		if req.Header == nil {
			req.Header = make(Header)
		}
		req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(info)))
	}
	return t.RoundTrip(req)
}

// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirect(statusCode int) bool {
	switch statusCode {
	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
		return true
	}
	return false
}

// Get issues a GET to the specified URL.  If the response is one of the following
// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// Caller should close r.Body when done reading from it.
//
// Get is a convenience wrapper around DefaultClient.Get.
func Get(url string) (r *Response, err os.Error) {
	return DefaultClient.Get(url)
}

// Get issues a GET to the specified URL.  If the response is one of the
// following redirect codes, Get follows the redirect after calling the
// Client's CheckRedirect function.
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// Caller should close r.Body when done reading from it.
func (c *Client) Get(url string) (r *Response, err os.Error) {
	req, err := NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	return c.doFollowingRedirects(req)
}

func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) {
	// TODO: if/when we add cookie support, the redirected request shouldn't
	// necessarily supply the same cookies as the original.
	var base *URL
	redirectChecker := c.CheckRedirect
	if redirectChecker == nil {
		redirectChecker = defaultCheckRedirect
	}
	var via []*Request

	req := ireq
	url := "" // next relative or absolute URL to fetch (after first request)
	for redirect := 0; ; redirect++ {
		if redirect != 0 {
			req = new(Request)
			req.Method = ireq.Method
			req.Header = make(Header)
			req.URL, err = base.ParseURL(url)
			if err != nil {
				break
			}
			if len(via) > 0 {
				// Add the Referer header.
				lastReq := via[len(via)-1]
				if lastReq.URL.Scheme != "https" {
					req.Header.Set("Referer", lastReq.URL.String())
				}

				err = redirectChecker(req, via)
				if err != nil {
					break
				}
			}
		}

		url = req.URL.String()
		if r, err = send(req, c.Transport); err != nil {
			break
		}
		if shouldRedirect(r.StatusCode) {
			r.Body.Close()
			if url = r.Header.Get("Location"); url == "" {
				err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode))
				break
			}
			base = req.URL
			via = append(via, req)
			continue
		}
		return
	}

	method := ireq.Method
	err = &URLError{method[0:1] + strings.ToLower(method[1:]), url, err}
	return
}

func defaultCheckRedirect(req *Request, via []*Request) os.Error {
	if len(via) >= 10 {
		return os.ErrorString("stopped after 10 redirects")
	}
	return nil
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading from it.
//
// Post is a wrapper around DefaultClient.Post
func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	return DefaultClient.Post(url, bodyType, body)
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading from it.
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	req, err := NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", bodyType)
	return send(req, c.Transport)
}

// PostForm issues a POST to the specified URL,
// with data's keys and values urlencoded as the request body.
//
// Caller should close r.Body when done reading from it.
//
// PostForm is a wrapper around DefaultClient.PostForm
func PostForm(url string, data Values) (r *Response, err os.Error) {
	return DefaultClient.PostForm(url, data)
}

// PostForm issues a POST to the specified URL,
// with data's keys and values urlencoded as the request body.
//
// Caller should close r.Body when done reading from it.
func (c *Client) PostForm(url string, data Values) (r *Response, err os.Error) {
	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

// Head issues a HEAD to the specified URL.  If the response is one of the
// following redirect codes, Head follows the redirect after calling the
// Client's CheckRedirect function.
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// Head is a wrapper around DefaultClient.Head
func Head(url string) (r *Response, err os.Error) {
	return DefaultClient.Head(url)
}

// Head issues a HEAD to the specified URL.  If the response is one of the
// following redirect codes, Head follows the redirect after calling the
// Client's CheckRedirect function.
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
func (c *Client) Head(url string) (r *Response, err os.Error) {
	req, err := NewRequest("HEAD", url, nil)
	if err != nil {
		return nil, err
	}
	return c.doFollowingRedirects(req)
}
<commit_msg>http: document http client/transport thread safety<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Primitive HTTP client. See RFC 2616.

package http

import (
	"encoding/base64"
	"fmt"
	"io"
	"os"
	"strings"
)

// A Client is an HTTP client. Its zero value (DefaultClient) is a usable client
// that uses DefaultTransport.
//
// The Client's Transport typically has internal state (cached
// TCP connections), so Clients should be reused instead of created as
// needed. Clients are safe for concurrent use by multiple goroutines.
//
// Client is not yet very configurable.
type Client struct {
	Transport RoundTripper // if nil, DefaultTransport is used

	// If CheckRedirect is not nil, the client calls it before
	// following an HTTP redirect. The arguments req and via
	// are the upcoming request and the requests made already,
	// oldest first. If CheckRedirect returns an error, the client
	// returns that error instead of issue the Request req.
	//
	// If CheckRedirect is nil, the Client uses its default policy,
	// which is to stop after 10 consecutive requests.
	CheckRedirect func(req *Request, via []*Request) os.Error
}

// DefaultClient is the default Client and is used by Get, Head, and Post.
var DefaultClient = &Client{}

// RoundTripper is an interface representing the ability to execute a
// single HTTP transaction, obtaining the Response for a given Request.
//
// A RoundTripper must be safe for concurrent use by multiple
// goroutines.
type RoundTripper interface {
	// RoundTrip executes a single HTTP transaction, returning
	// the Response for the request req.  RoundTrip should not
	// attempt to interpret the response.  In particular,
	// RoundTrip must return err == nil if it obtained a response,
	// regardless of the response's HTTP status code.  A non-nil
	// err should be reserved for failure to obtain a response.
	// Similarly, RoundTrip should not attempt to handle
	// higher-level protocol details such as redirects,
	// authentication, or cookies.
	//
	// RoundTrip may modify the request. The request Headers field is
	// guaranteed to be initialized.
	RoundTrip(req *Request) (resp *Response, err os.Error)
}

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Used in Send to implement io.ReadCloser by bundling together the
// bufio.Reader through which we read the response, and the underlying
// network connection.
type readClose struct {
	io.Reader
	io.Closer
}

// Do sends an HTTP request and returns an HTTP response, following
// policy (e.g. redirects, cookies, auth) as configured on the client.
//
// Callers should close resp.Body when done reading from it.
//
// Generally Get, Post, or PostForm will be used instead of Do.
func (c *Client) Do(req *Request) (resp *Response, err os.Error) {
	if req.Method == "GET" || req.Method == "HEAD" {
		return c.doFollowingRedirects(req)
	}
	return send(req, c.Transport)
}

// send issues an HTTP request.  Caller should close resp.Body when done reading from it.
func send(req *Request, t RoundTripper) (resp *Response, err os.Error) {
	if t == nil {
		t = DefaultTransport
		if t == nil {
			err = os.NewError("no http.Client.Transport or http.DefaultTransport")
			return
		}
	}

	// Most the callers of send (Get, Post, et al) don't need
	// Headers, leaving it uninitialized.  We guarantee to the
	// Transport that this has been initialized, though.
	if req.Header == nil {
		req.Header = make(Header)
	}

	info := req.URL.RawUserinfo
	if len(info) > 0 {
		if req.Header == nil {
			req.Header = make(Header)
		}
		req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(info)))
	}
	return t.RoundTrip(req)
}

// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirect(statusCode int) bool {
	switch statusCode {
	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
		return true
	}
	return false
}

// Get issues a GET to the specified URL.  If the response is one of the following
// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// Caller should close r.Body when done reading from it.
//
// Get is a convenience wrapper around DefaultClient.Get.
func Get(url string) (r *Response, err os.Error) {
	return DefaultClient.Get(url)
}

// Get issues a GET to the specified URL.  If the response is one of the
// following redirect codes, Get follows the redirect after calling the
// Client's CheckRedirect function.
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// Caller should close r.Body when done reading from it.
func (c *Client) Get(url string) (r *Response, err os.Error) {
	req, err := NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	return c.doFollowingRedirects(req)
}

func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) {
	// TODO: if/when we add cookie support, the redirected request shouldn't
	// necessarily supply the same cookies as the original.
	var base *URL
	redirectChecker := c.CheckRedirect
	if redirectChecker == nil {
		redirectChecker = defaultCheckRedirect
	}
	var via []*Request

	req := ireq
	url := "" // next relative or absolute URL to fetch (after first request)
	for redirect := 0; ; redirect++ {
		if redirect != 0 {
			req = new(Request)
			req.Method = ireq.Method
			req.Header = make(Header)
			req.URL, err = base.ParseURL(url)
			if err != nil {
				break
			}
			if len(via) > 0 {
				// Add the Referer header.
				lastReq := via[len(via)-1]
				if lastReq.URL.Scheme != "https" {
					req.Header.Set("Referer", lastReq.URL.String())
				}

				err = redirectChecker(req, via)
				if err != nil {
					break
				}
			}
		}

		url = req.URL.String()
		if r, err = send(req, c.Transport); err != nil {
			break
		}
		if shouldRedirect(r.StatusCode) {
			r.Body.Close()
			if url = r.Header.Get("Location"); url == "" {
				err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode))
				break
			}
			base = req.URL
			via = append(via, req)
			continue
		}
		return
	}

	method := ireq.Method
	err = &URLError{method[0:1] + strings.ToLower(method[1:]), url, err}
	return
}

func defaultCheckRedirect(req *Request, via []*Request) os.Error {
	if len(via) >= 10 {
		return os.ErrorString("stopped after 10 redirects")
	}
	return nil
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading from it.
//
// Post is a wrapper around DefaultClient.Post
func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	return DefaultClient.Post(url, bodyType, body)
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading from it.
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	req, err := NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", bodyType)
	return send(req, c.Transport)
}

// PostForm issues a POST to the specified URL,
// with data's keys and values urlencoded as the request body.
//
// Caller should close r.Body when done reading from it.
//
// PostForm is a wrapper around DefaultClient.PostForm
func PostForm(url string, data Values) (r *Response, err os.Error) {
	return DefaultClient.PostForm(url, data)
}

// PostForm issues a POST to the specified URL,
// with data's keys and values urlencoded as the request body.
//
// Caller should close r.Body when done reading from it.
func (c *Client) PostForm(url string, data Values) (r *Response, err os.Error) {
	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

// Head issues a HEAD to the specified URL.  If the response is one of the
// following redirect codes, Head follows the redirect after calling the
// Client's CheckRedirect function.
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// Head is a wrapper around DefaultClient.Head
func Head(url string) (r *Response, err os.Error) {
	return DefaultClient.Head(url)
}

// Head issues a HEAD to the specified URL.  If the response is one of the
// following redirect codes, Head follows the redirect after calling the
// Client's CheckRedirect function.
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
func (c *Client) Head(url string) (r *Response, err os.Error) {
	req, err := NewRequest("HEAD", url, nil)
	if err != nil {
		return nil, err
	}
	return c.doFollowingRedirects(req)
}
<|endoftext|>


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

package server

import (
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"html/template"
	"net/http"
)

// デバッグログにリクエストボディを記録するかどうか。
var Debug = false

// WrapPage や WrapApi に渡す処理。
type HandlerFunc func(http.ResponseWriter, *http.Request) error

// 処理がパニックやエラーで終わったら、適当なレスポンスを HTML で返す。
func WrapPage(stopper *Stopper, f HandlerFunc, errTmpl *template.Template) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		// panic 対策。
		defer func() {
			if rcv := recover(); rcv != nil {
				RespondErrorHtml(w, r, erro.New(rcv), errTmpl, ParseSender(r)+":")
				return
			}
		}()

		if stopper != nil {
			stopper.Stop()
			defer stopper.Unstop()
		}

		//////////////////////////////
		LogRequest(level.DEBUG, r, Debug)
		//////////////////////////////

		if err := f(w, r); err != nil {
			RespondErrorHtml(w, r, erro.Wrap(err), errTmpl, ParseSender(r)+":")
			return
		}
	}
}

// 処理がパニックやエラーで終わったら、適当なレスポンスを JSON で返す。
func WrapApi(stopper *Stopper, f HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		// panic 対策。
		defer func() {
			if rcv := recover(); rcv != nil {
				RespondErrorJson(w, r, erro.New(rcv), ParseSender(r)+":")
				return
			}
		}()

		if stopper != nil {
			stopper.Stop()
			defer stopper.Unstop()
		}

		//////////////////////////////
		LogRequest(level.DEBUG, r, Debug)
		//////////////////////////////

		if err := f(w, r); err != nil {
			RespondErrorJson(w, r, erro.Wrap(err), ParseSender(r)+":")
			return
		}
	}
}
<commit_msg>リクエストログに送り元を付記するようにした<commit_after>// Copyright 2015 realglobe, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"html/template"
	"net/http"
)

// デバッグログにリクエストボディを記録するかどうか。
var Debug = false

// WrapPage や WrapApi に渡す処理。
type HandlerFunc func(http.ResponseWriter, *http.Request) error

// 処理がパニックやエラーで終わったら、適当なレスポンスを HTML で返す。
func WrapPage(stopper *Stopper, f HandlerFunc, errTmpl *template.Template) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var logPref string

		// panic 対策。
		defer func() {
			if rcv := recover(); rcv != nil {
				RespondErrorHtml(w, r, erro.New(rcv), errTmpl, logPref)
				return
			}
		}()

		if stopper != nil {
			stopper.Stop()
			defer stopper.Unstop()
		}

		logPref = ParseSender(r) + ": "

		LogRequest(level.DEBUG, r, Debug, logPref)

		if err := f(w, r); err != nil {
			RespondErrorHtml(w, r, erro.Wrap(err), errTmpl, logPref)
			return
		}
	}
}

// 処理がパニックやエラーで終わったら、適当なレスポンスを JSON で返す。
func WrapApi(stopper *Stopper, f HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var logPref string

		// panic 対策。
		defer func() {
			if rcv := recover(); rcv != nil {
				RespondErrorJson(w, r, erro.New(rcv), logPref)
				return
			}
		}()

		if stopper != nil {
			stopper.Stop()
			defer stopper.Unstop()
		}

		logPref = ParseSender(r) + ": "

		LogRequest(level.DEBUG, r, Debug, logPref)

		if err := f(w, r); err != nil {
			RespondErrorJson(w, r, erro.Wrap(err), logPref)
			return
		}
	}
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"github.com/olivere/elastic"
	"log"
	"reflect"
	"strings"
)

const (
	TYPE string = "irc"
)

type ElasticHistory struct {
	host             string
	client           *elastic.Client
	index            string
	incomingMessages chan *Message
}

func NewElasticHistory(host string, index string) *ElasticHistory {
	var err error

	history := new(ElasticHistory)
	history.client, err = elastic.NewClient(elastic.SetURL(host))
	history.incomingMessages = make(chan *Message)
	history.index = strings.Replace(index, "#", "", -1)

	if err != nil {
		log.Panicf("Failed to connect to elasticsearch : %e", err)
	}

	go history.incomingLoop()

	return history
}

func (history *ElasticHistory) GetLastMessages(count int) ([]byte, error) {
	result, err := history.client.Search().
		Index(history.index).
		Type(TYPE).
		Sort("Date", false).
		From(0).Size(count).
		Do()

	if err != nil {
		return nil, err
	}

	log.Printf("Fetched %d entries\n", result.TotalHits())

	resultSlice := make([]Message, result.TotalHits(), result.TotalHits())
	var tmpMessage Message

	for i, m := range result.Each(reflect.TypeOf(tmpMessage)) {
		resultSlice[i] = m.(Message)
	}

	return json.Marshal(resultSlice)
}

func (history *ElasticHistory) GetChannel() chan *Message {
	return history.incomingMessages
}

func (history *ElasticHistory) incomingLoop() {
	for {
		message := <-history.incomingMessages

		_, err := history.client.Index().
			Index(history.index).
			Type(TYPE).
			BodyJson(message).
			Do()

		if err != nil {
			log.Printf("Error : %e when inserting in elasticsearch\n", err)
		}
	}
}
<commit_msg>Fixed - correct historic message ordering<commit_after>package server

import (
	"encoding/json"
	"github.com/olivere/elastic"
	"log"
	"reflect"
	"strings"
)

const (
	TYPE string = "irc"
)

type ElasticHistory struct {
	host             string
	client           *elastic.Client
	index            string
	incomingMessages chan *Message
}

func NewElasticHistory(host string, index string) *ElasticHistory {
	var err error

	history := new(ElasticHistory)
	history.client, err = elastic.NewClient(elastic.SetURL(host))
	history.incomingMessages = make(chan *Message)
	history.index = strings.Replace(index, "#", "", -1)

	if err != nil {
		log.Panicf("Failed to connect to elasticsearch : %e", err)
	}

	go history.incomingLoop()

	return history
}

func (history *ElasticHistory) GetLastMessages(count int) ([]byte, error) {
	result, err := history.client.Search().
		Index(history.index).
		Type(TYPE).
		Sort("date", true).
		From(0).Size(count).
		Do()

	if err != nil {
		return nil, err
	}

	log.Printf("Fetched %d entries\n", result.TotalHits())

	resultSlice := make([]Message, result.TotalHits(), result.TotalHits())
	var tmpMessage Message

	for i, m := range result.Each(reflect.TypeOf(tmpMessage)) {
		resultSlice[i] = m.(Message)
	}

	return json.Marshal(resultSlice)
}

func (history *ElasticHistory) GetChannel() chan *Message {
	return history.incomingMessages
}

func (history *ElasticHistory) incomingLoop() {
	for {
		message := <-history.incomingMessages

		_, err := history.client.Index().
			Index(history.index).
			Type(TYPE).
			BodyJson(message).
			Do()

		if err != nil {
			log.Printf("Error : %e when inserting in elasticsearch\n", err)
		}
	}
}
<|endoftext|>


<commit_before>package server

import (
	"time"

	"github.com/micro/go-micro/broker"
	"github.com/micro/go-micro/codec"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/server/debug"
	"github.com/micro/go-micro/transport"

	"golang.org/x/net/context"
)

type Options struct {
	Codecs       map[string]codec.NewCodec
	Broker       broker.Broker
	Registry     registry.Registry
	Transport    transport.Transport
	Metadata     map[string]string
	Name         string
	Address      string
	Advertise    string
	Id           string
	Version      string
	HdlrWrappers []HandlerWrapper
	SubWrappers  []SubscriberWrapper

	RegisterTTL time.Duration

	// Debug Handler which can be set by a user
	DebugHandler debug.DebugHandler

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

func newOptions(opt ...Option) Options {
	opts := Options{
		Codecs:   make(map[string]codec.NewCodec),
		Metadata: map[string]string{},
	}

	for _, o := range opt {
		o(&opts)
	}

	if opts.Broker == nil {
		opts.Broker = broker.DefaultBroker
	}

	if opts.Registry == nil {
		opts.Registry = registry.DefaultRegistry
	}

	if opts.Transport == nil {
		opts.Transport = transport.DefaultTransport
	}

	if opts.DebugHandler == nil {
		opts.DebugHandler = debug.DefaultDebugHandler
	}

	if len(opts.Address) == 0 {
		opts.Address = DefaultAddress
	}

	if len(opts.Name) == 0 {
		opts.Name = DefaultName
	}

	if len(opts.Id) == 0 {
		opts.Id = DefaultId
	}

	if len(opts.Version) == 0 {
		opts.Version = DefaultVersion
	}

	return opts
}

// Server name
func Name(n string) Option {
	return func(o *Options) {
		o.Name = n
	}
}

// Unique server id
func Id(id string) Option {
	return func(o *Options) {
		o.Id = id
	}
}

// Version of the service
func Version(v string) Option {
	return func(o *Options) {
		o.Version = v
	}
}

// Address to bind to - host:port
func Address(a string) Option {
	return func(o *Options) {
		o.Address = a
	}
}

// The address to advertise for discovery - host:port
func Advertise(a string) Option {
	return func(o *Options) {
		o.Advertise = a
	}
}

// Broker to use for pub/sub
func Broker(b broker.Broker) Option {
	return func(o *Options) {
		o.Broker = b
	}
}

// Codec to use to encode/decode requests for a given content type
func Codec(contentType string, c codec.NewCodec) Option {
	return func(o *Options) {
		o.Codecs[contentType] = c
	}
}

// Registry used for discovery
func Registry(r registry.Registry) Option {
	return func(o *Options) {
		o.Registry = r
	}
}

// Transport mechanism for communication e.g http, rabbitmq, etc
func Transport(t transport.Transport) Option {
	return func(o *Options) {
		o.Transport = t
	}
}

// DebugHandler for this server
func DebugHandler(d debug.DebugHandler) Option {
	return func(o *Options) {
		o.DebugHandler = d
	}
}

// Metadata associated with the server
func Metadata(md map[string]string) Option {
	return func(o *Options) {
		o.Metadata = md
	}
}

// Register the service with a TTL
func RegisterTTL(t time.Duration) Option {
	return func(o *Options) {
		o.RegisterTTL = t
	}
}

// Adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w HandlerWrapper) Option {
	return func(o *Options) {
		o.HdlrWrappers = append(o.HdlrWrappers, w)
	}
}

// Adds a subscriber Wrapper to a list of options passed into the server
func WrapSubscriber(w SubscriberWrapper) Option {
	return func(o *Options) {
		o.SubWrappers = append(o.SubWrappers, w)
	}
}
<commit_msg>add wait option<commit_after>package server

import (
	"time"

	"github.com/micro/go-micro/broker"
	"github.com/micro/go-micro/codec"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/server/debug"
	"github.com/micro/go-micro/transport"

	"golang.org/x/net/context"
)

type Options struct {
	Codecs       map[string]codec.NewCodec
	Broker       broker.Broker
	Registry     registry.Registry
	Transport    transport.Transport
	Metadata     map[string]string
	Name         string
	Address      string
	Advertise    string
	Id           string
	Version      string
	HdlrWrappers []HandlerWrapper
	SubWrappers  []SubscriberWrapper

	RegisterTTL time.Duration

	// Debug Handler which can be set by a user
	DebugHandler debug.DebugHandler

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

func newOptions(opt ...Option) Options {
	opts := Options{
		Codecs:   make(map[string]codec.NewCodec),
		Metadata: map[string]string{},
	}

	for _, o := range opt {
		o(&opts)
	}

	if opts.Broker == nil {
		opts.Broker = broker.DefaultBroker
	}

	if opts.Registry == nil {
		opts.Registry = registry.DefaultRegistry
	}

	if opts.Transport == nil {
		opts.Transport = transport.DefaultTransport
	}

	if opts.DebugHandler == nil {
		opts.DebugHandler = debug.DefaultDebugHandler
	}

	if len(opts.Address) == 0 {
		opts.Address = DefaultAddress
	}

	if len(opts.Name) == 0 {
		opts.Name = DefaultName
	}

	if len(opts.Id) == 0 {
		opts.Id = DefaultId
	}

	if len(opts.Version) == 0 {
		opts.Version = DefaultVersion
	}

	return opts
}

// Server name
func Name(n string) Option {
	return func(o *Options) {
		o.Name = n
	}
}

// Unique server id
func Id(id string) Option {
	return func(o *Options) {
		o.Id = id
	}
}

// Version of the service
func Version(v string) Option {
	return func(o *Options) {
		o.Version = v
	}
}

// Address to bind to - host:port
func Address(a string) Option {
	return func(o *Options) {
		o.Address = a
	}
}

// The address to advertise for discovery - host:port
func Advertise(a string) Option {
	return func(o *Options) {
		o.Advertise = a
	}
}

// Broker to use for pub/sub
func Broker(b broker.Broker) Option {
	return func(o *Options) {
		o.Broker = b
	}
}

// Codec to use to encode/decode requests for a given content type
func Codec(contentType string, c codec.NewCodec) Option {
	return func(o *Options) {
		o.Codecs[contentType] = c
	}
}

// Registry used for discovery
func Registry(r registry.Registry) Option {
	return func(o *Options) {
		o.Registry = r
	}
}

// Transport mechanism for communication e.g http, rabbitmq, etc
func Transport(t transport.Transport) Option {
	return func(o *Options) {
		o.Transport = t
	}
}

// DebugHandler for this server
func DebugHandler(d debug.DebugHandler) Option {
	return func(o *Options) {
		o.DebugHandler = d
	}
}

// Metadata associated with the server
func Metadata(md map[string]string) Option {
	return func(o *Options) {
		o.Metadata = md
	}
}

// Register the service with a TTL
func RegisterTTL(t time.Duration) Option {
	return func(o *Options) {
		o.RegisterTTL = t
	}
}

// Wait tells the server to wait for requests to finish before exiting
func Wait(b bool) Option {
	return func(o *Options) {
		if o.Context == nil {
			o.Context = context.Background()
		}
		o.Context = context.WithValue(o.Context, "wait", b)
	}
}

// Adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w HandlerWrapper) Option {
	return func(o *Options) {
		o.HdlrWrappers = append(o.HdlrWrappers, w)
	}
}

// Adds a subscriber Wrapper to a list of options passed into the server
func WrapSubscriber(w SubscriberWrapper) Option {
	return func(o *Options) {
		o.SubWrappers = append(o.SubWrappers, w)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/stringid"
	"github.com/kubernetes-incubator/ocid/oci"
	"github.com/kubernetes-incubator/ocid/utils"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

type sandbox struct {
	id         string
	name       string
	logDir     string
	labels     map[string]string
	containers oci.Store
}

const (
	podInfraRootfs      = "/var/lib/ocid/graph/vfs/pause"
	podDefaultNamespace = "default"
)

func (s *sandbox) addContainer(c *oci.Container) {
	s.containers.Add(c.Name(), c)
}

func (s *sandbox) getContainer(name string) *oci.Container {
	return s.containers.Get(name)
}

func (s *sandbox) removeContainer(c *oci.Container) {
	s.containers.Delete(c.Name())
}

func (s *Server) generatePodIDandName(name, namespace string) (string, string, error) {
	var (
		err error
		id  = stringid.GenerateNonCryptoID()
	)
	if namespace == "" {
		namespace = podDefaultNamespace
	}
	if name, err = s.reservePodName(id, namespace+"-"+name); err != nil {
		return "", "", err
	}
	return id, name, err
}

// RunPodSandbox creates and runs a pod-level sandbox.
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (*pb.RunPodSandboxResponse, error) {
	// process req.Name
	name := req.GetConfig().GetMetadata().GetName()
	if name == "" {
		return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty")
	}

	namespace := req.GetConfig().GetMetadata().GetNamespace()

	var err error
	id, name, err := s.generatePodIDandName(name, namespace)
	if err != nil {
		return nil, err
	}
	podSandboxDir := filepath.Join(s.sandboxDir, id)
	if _, err = os.Stat(podSandboxDir); err == nil {
		return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir)
	}

	if err = os.MkdirAll(podSandboxDir, 0755); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := os.RemoveAll(podSandboxDir); err2 != nil {
				logrus.Warnf("couldn't cleanup podSandboxDir %s: %v", podSandboxDir, err2)
			}
		}
	}()

	// creates a spec Generator with the default spec.
	g := generate.New()

	podInfraRootfs := filepath.Join(s.root, "graph/vfs/pause")
	// setup defaults for the pod sandbox
	g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs"))
	g.SetRootReadonly(true)
	g.SetProcessArgs([]string{"/pause"})

	// set hostname
	hostname := req.GetConfig().GetHostname()
	if hostname != "" {
		g.SetHostname(hostname)
	}

	// set log directory
	logDir := req.GetConfig().GetLogDirectory()
	if logDir == "" {
		logDir = fmt.Sprintf("/var/log/ocid/pods/%s", id)
	}

	// set DNS options
	dnsServers := req.GetConfig().GetDnsOptions().GetServers()
	dnsSearches := req.GetConfig().GetDnsOptions().GetSearches()
	resolvPath := fmt.Sprintf("%s/resolv.conf", podSandboxDir)
	err = parseDNSOptions(dnsServers, dnsSearches, resolvPath)
	if err != nil {
		err1 := removeFile(resolvPath)
		if err1 != nil {
			err = err1
			return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1)
		}
		return nil, err
	}

	g.AddBindMount(resolvPath, "/etc/resolv.conf", "ro")

	// add labels
	labels := req.GetConfig().GetLabels()
	labelsJSON, err := json.Marshal(labels)
	if err != nil {
		return nil, err
	}
	g.AddAnnotation("ocid/labels", string(labelsJSON))
	g.AddAnnotation("ocid/log_path", logDir)
	g.AddAnnotation("ocid/name", name)
	containerName := name + "-infra"
	g.AddAnnotation("ocid/container_name", containerName)
	s.addSandbox(&sandbox{
		id:         id,
		name:       name,
		logDir:     logDir,
		labels:     labels,
		containers: oci.NewMemoryStore(),
	})

	annotations := req.GetConfig().GetAnnotations()
	for k, v := range annotations {
		g.AddAnnotation(k, v)
	}

	// setup cgroup settings
	cgroupParent := req.GetConfig().GetLinux().GetCgroupParent()
	if cgroupParent != "" {
		g.SetLinuxCgroupsPath(cgroupParent)
	}

	// set up namespaces
	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostNetwork() {
		err = g.RemoveLinuxNamespace("network")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostPid() {
		err = g.RemoveLinuxNamespace("pid")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostIpc() {
		err = g.RemoveLinuxNamespace("ipc")
		if err != nil {
			return nil, err
		}
	}

	err = g.SaveToFile(filepath.Join(podSandboxDir, "config.json"))
	if err != nil {
		return nil, err
	}

	if _, err = os.Stat(podInfraRootfs); err != nil {
		if os.IsNotExist(err) {
			// TODO: Replace by rootfs creation API when it is ready
			if err = utils.CreateFakeRootfs(podInfraRootfs, "docker://kubernetes/pause"); err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}

	container, err := oci.NewContainer(containerName, podSandboxDir, podSandboxDir, labels, id, false)
	if err != nil {
		return nil, err
	}

	if err = s.runtime.CreateContainer(container); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	// setup the network
	podNamespace := ""
	netnsPath, err := container.NetNsPath()
	if err != nil {
		return nil, err
	}
	if err = s.netPlugin.SetUpPod(netnsPath, podNamespace, id, containerName); err != nil {
		return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err)
	}

	if err = s.runtime.StartContainer(container); err != nil {
		return nil, err
	}

	s.addContainer(container)

	if err = s.podIDIndex.Add(id); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	return &pb.RunPodSandboxResponse{PodSandboxId: &id}, nil
}

// StopPodSandbox stops the sandbox. If there are any running containers in the
// sandbox, they should be force terminated.
func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) {
	sbID := req.PodSandboxId
	if *sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}
	sb := s.getSandbox(*sbID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", *sbID)
	}

	podInfraContainer := sb.name + "-infra"
	for _, c := range sb.containers.List() {
		if podInfraContainer == c.Name() {
			podNamespace := ""
			netnsPath, err := c.NetNsPath()
			if err != nil {
				return nil, err
			}
			if err := s.netPlugin.TearDownPod(netnsPath, podNamespace, *sbID, podInfraContainer); err != nil {
				return nil, fmt.Errorf("failed to destroy network for container %s in sandbox %s: %v", c.Name(), *sbID, err)
			}
		}
		cStatus := s.runtime.ContainerStatus(c)
		if cStatus.Status != "stopped" {
			if err := s.runtime.StopContainer(c); err != nil {
				return nil, fmt.Errorf("failed to stop container %s in sandbox %s: %v", c.Name(), *sbID, err)
			}
		}
	}

	return &pb.StopPodSandboxResponse{}, nil
}

// RemovePodSandbox deletes the sandbox. If there are any running containers in the
// sandbox, they should be force deleted.
func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) {
	sbID := req.PodSandboxId
	if *sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}
	sb := s.getSandbox(*sbID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", *sbID)
	}

	podInfraContainer := sb.name + "-infra"

	// Delete all the containers in the sandbox
	for _, c := range sb.containers.List() {
		if err := s.runtime.DeleteContainer(c); err != nil {
			return nil, fmt.Errorf("failed to delete container %s in sandbox %s: %v", c.Name(), *sbID, err)
		}
		if podInfraContainer == c.Name() {
			continue
		}
		containerDir := filepath.Join(s.runtime.ContainerDir(), c.Name())
		if err := os.RemoveAll(containerDir); err != nil {
			return nil, fmt.Errorf("failed to remove container %s directory: %v", c.Name(), err)
		}
	}

	// Remove the files related to the sandbox
	podSandboxDir := filepath.Join(s.sandboxDir, *sbID)
	if err := os.RemoveAll(podSandboxDir); err != nil {
		return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", *sbID, err)
	}

	return &pb.RemovePodSandboxResponse{}, nil
}

// PodSandboxStatus returns the Status of the PodSandbox.
func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) {
	sbID := req.PodSandboxId
	if *sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}
	sb := s.getSandbox(*sbID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", *sbID)
	}

	podInfraContainerName := sb.name + "-infra"
	podInfraContainer := sb.getContainer(podInfraContainerName)
	if err := s.runtime.UpdateStatus(podInfraContainer); err != nil {
		return nil, err
	}

	cState := s.runtime.ContainerStatus(podInfraContainer)
	created := cState.Created.Unix()

	netNsPath, err := podInfraContainer.NetNsPath()
	if err != nil {
		return nil, err
	}
	podNamespace := ""
	ip, err := s.netPlugin.GetContainerNetworkStatus(netNsPath, podNamespace, *sbID, podInfraContainerName)
	if err != nil {
		// ignore the error on network status
		ip = ""
	}

	rStatus := pb.PodSandBoxState_NOTREADY
	if cState.Status == ContainerStateRunning {
		rStatus = pb.PodSandBoxState_READY
	}

	return &pb.PodSandboxStatusResponse{
		Status: &pb.PodSandboxStatus{
			Id:        sbID,
			CreatedAt: int64Ptr(created),
			Linux: &pb.LinuxPodSandboxStatus{
				Namespaces: &pb.Namespace{
					Network: sPtr(netNsPath),
				},
			},
			Network: &pb.PodSandboxNetworkStatus{Ip: &ip},
			State:   &rStatus,
		},
	}, nil
}

// ListPodSandbox returns a list of SandBoxes.
func (s *Server) ListPodSandbox(context.Context, *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) {
	var pods []*pb.PodSandbox
	for _, sb := range s.state.sandboxes {
		podInfraContainerName := sb.name + "-infra"
		podInfraContainer := sb.getContainer(podInfraContainerName)
		if err := s.runtime.UpdateStatus(podInfraContainer); err != nil {
			return nil, err
		}
		cState := s.runtime.ContainerStatus(podInfraContainer)
		created := cState.Created.Unix()
		rStatus := pb.PodSandBoxState_NOTREADY
		if cState.Status == ContainerStateRunning {
			rStatus = pb.PodSandBoxState_READY
		}

		pod := &pb.PodSandbox{
			Id:        &sb.id,
			CreatedAt: int64Ptr(created),
			State:     &rStatus,
		}

		pods = append(pods, pod)
	}

	return &pb.ListPodSandboxResponse{
		Items: pods,
	}, nil
}
<commit_msg>Release pod name when pod is removed<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/stringid"
	"github.com/kubernetes-incubator/ocid/oci"
	"github.com/kubernetes-incubator/ocid/utils"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

type sandbox struct {
	id         string
	name       string
	logDir     string
	labels     map[string]string
	containers oci.Store
}

const (
	podInfraRootfs      = "/var/lib/ocid/graph/vfs/pause"
	podDefaultNamespace = "default"
)

func (s *sandbox) addContainer(c *oci.Container) {
	s.containers.Add(c.Name(), c)
}

func (s *sandbox) getContainer(name string) *oci.Container {
	return s.containers.Get(name)
}

func (s *sandbox) removeContainer(c *oci.Container) {
	s.containers.Delete(c.Name())
}

func (s *Server) generatePodIDandName(name, namespace string) (string, string, error) {
	var (
		err error
		id  = stringid.GenerateNonCryptoID()
	)
	if namespace == "" {
		namespace = podDefaultNamespace
	}
	if name, err = s.reservePodName(id, namespace+"-"+name); err != nil {
		return "", "", err
	}
	return id, name, err
}

// RunPodSandbox creates and runs a pod-level sandbox.
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (*pb.RunPodSandboxResponse, error) {
	// process req.Name
	name := req.GetConfig().GetMetadata().GetName()
	if name == "" {
		return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty")
	}

	namespace := req.GetConfig().GetMetadata().GetNamespace()

	var err error
	id, name, err := s.generatePodIDandName(name, namespace)
	if err != nil {
		return nil, err
	}
	podSandboxDir := filepath.Join(s.sandboxDir, id)
	if _, err = os.Stat(podSandboxDir); err == nil {
		return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir)
	}

	if err = os.MkdirAll(podSandboxDir, 0755); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := os.RemoveAll(podSandboxDir); err2 != nil {
				logrus.Warnf("couldn't cleanup podSandboxDir %s: %v", podSandboxDir, err2)
			}
		}
	}()

	// creates a spec Generator with the default spec.
	g := generate.New()

	podInfraRootfs := filepath.Join(s.root, "graph/vfs/pause")
	// setup defaults for the pod sandbox
	g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs"))
	g.SetRootReadonly(true)
	g.SetProcessArgs([]string{"/pause"})

	// set hostname
	hostname := req.GetConfig().GetHostname()
	if hostname != "" {
		g.SetHostname(hostname)
	}

	// set log directory
	logDir := req.GetConfig().GetLogDirectory()
	if logDir == "" {
		logDir = fmt.Sprintf("/var/log/ocid/pods/%s", id)
	}

	// set DNS options
	dnsServers := req.GetConfig().GetDnsOptions().GetServers()
	dnsSearches := req.GetConfig().GetDnsOptions().GetSearches()
	resolvPath := fmt.Sprintf("%s/resolv.conf", podSandboxDir)
	err = parseDNSOptions(dnsServers, dnsSearches, resolvPath)
	if err != nil {
		err1 := removeFile(resolvPath)
		if err1 != nil {
			err = err1
			return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1)
		}
		return nil, err
	}

	g.AddBindMount(resolvPath, "/etc/resolv.conf", "ro")

	// add labels
	labels := req.GetConfig().GetLabels()
	labelsJSON, err := json.Marshal(labels)
	if err != nil {
		return nil, err
	}
	g.AddAnnotation("ocid/labels", string(labelsJSON))
	g.AddAnnotation("ocid/log_path", logDir)
	g.AddAnnotation("ocid/name", name)
	containerName := name + "-infra"
	g.AddAnnotation("ocid/container_name", containerName)
	s.addSandbox(&sandbox{
		id:         id,
		name:       name,
		logDir:     logDir,
		labels:     labels,
		containers: oci.NewMemoryStore(),
	})

	annotations := req.GetConfig().GetAnnotations()
	for k, v := range annotations {
		g.AddAnnotation(k, v)
	}

	// setup cgroup settings
	cgroupParent := req.GetConfig().GetLinux().GetCgroupParent()
	if cgroupParent != "" {
		g.SetLinuxCgroupsPath(cgroupParent)
	}

	// set up namespaces
	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostNetwork() {
		err = g.RemoveLinuxNamespace("network")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostPid() {
		err = g.RemoveLinuxNamespace("pid")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostIpc() {
		err = g.RemoveLinuxNamespace("ipc")
		if err != nil {
			return nil, err
		}
	}

	err = g.SaveToFile(filepath.Join(podSandboxDir, "config.json"))
	if err != nil {
		return nil, err
	}

	if _, err = os.Stat(podInfraRootfs); err != nil {
		if os.IsNotExist(err) {
			// TODO: Replace by rootfs creation API when it is ready
			if err = utils.CreateFakeRootfs(podInfraRootfs, "docker://kubernetes/pause"); err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}

	container, err := oci.NewContainer(containerName, podSandboxDir, podSandboxDir, labels, id, false)
	if err != nil {
		return nil, err
	}

	if err = s.runtime.CreateContainer(container); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	// setup the network
	podNamespace := ""
	netnsPath, err := container.NetNsPath()
	if err != nil {
		return nil, err
	}
	if err = s.netPlugin.SetUpPod(netnsPath, podNamespace, id, containerName); err != nil {
		return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err)
	}

	if err = s.runtime.StartContainer(container); err != nil {
		return nil, err
	}

	s.addContainer(container)

	if err = s.podIDIndex.Add(id); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	return &pb.RunPodSandboxResponse{PodSandboxId: &id}, nil
}

// StopPodSandbox stops the sandbox. If there are any running containers in the
// sandbox, they should be force terminated.
func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) {
	sbID := req.PodSandboxId
	if *sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}
	sb := s.getSandbox(*sbID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", *sbID)
	}

	podInfraContainer := sb.name + "-infra"
	for _, c := range sb.containers.List() {
		if podInfraContainer == c.Name() {
			podNamespace := ""
			netnsPath, err := c.NetNsPath()
			if err != nil {
				return nil, err
			}
			if err := s.netPlugin.TearDownPod(netnsPath, podNamespace, *sbID, podInfraContainer); err != nil {
				return nil, fmt.Errorf("failed to destroy network for container %s in sandbox %s: %v", c.Name(), *sbID, err)
			}
		}
		cStatus := s.runtime.ContainerStatus(c)
		if cStatus.Status != "stopped" {
			if err := s.runtime.StopContainer(c); err != nil {
				return nil, fmt.Errorf("failed to stop container %s in sandbox %s: %v", c.Name(), *sbID, err)
			}
		}
	}

	return &pb.StopPodSandboxResponse{}, nil
}

// RemovePodSandbox deletes the sandbox. If there are any running containers in the
// sandbox, they should be force deleted.
func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) {
	sbID := req.PodSandboxId
	if *sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}
	sb := s.getSandbox(*sbID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", *sbID)
	}

	podInfraContainer := sb.name + "-infra"

	// Delete all the containers in the sandbox
	for _, c := range sb.containers.List() {
		if err := s.runtime.DeleteContainer(c); err != nil {
			return nil, fmt.Errorf("failed to delete container %s in sandbox %s: %v", c.Name(), *sbID, err)
		}
		if podInfraContainer == c.Name() {
			continue
		}
		containerDir := filepath.Join(s.runtime.ContainerDir(), c.Name())
		if err := os.RemoveAll(containerDir); err != nil {
			return nil, fmt.Errorf("failed to remove container %s directory: %v", c.Name(), err)
		}
	}

	// Remove the files related to the sandbox
	podSandboxDir := filepath.Join(s.sandboxDir, *sbID)
	if err := os.RemoveAll(podSandboxDir); err != nil {
		return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", *sbID, err)
	}

	s.releasePodName(sb.name)

	return &pb.RemovePodSandboxResponse{}, nil
}

// PodSandboxStatus returns the Status of the PodSandbox.
func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) {
	sbID := req.PodSandboxId
	if *sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}
	sb := s.getSandbox(*sbID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", *sbID)
	}

	podInfraContainerName := sb.name + "-infra"
	podInfraContainer := sb.getContainer(podInfraContainerName)
	if err := s.runtime.UpdateStatus(podInfraContainer); err != nil {
		return nil, err
	}

	cState := s.runtime.ContainerStatus(podInfraContainer)
	created := cState.Created.Unix()

	netNsPath, err := podInfraContainer.NetNsPath()
	if err != nil {
		return nil, err
	}
	podNamespace := ""
	ip, err := s.netPlugin.GetContainerNetworkStatus(netNsPath, podNamespace, *sbID, podInfraContainerName)
	if err != nil {
		// ignore the error on network status
		ip = ""
	}

	rStatus := pb.PodSandBoxState_NOTREADY
	if cState.Status == ContainerStateRunning {
		rStatus = pb.PodSandBoxState_READY
	}

	return &pb.PodSandboxStatusResponse{
		Status: &pb.PodSandboxStatus{
			Id:        sbID,
			CreatedAt: int64Ptr(created),
			Linux: &pb.LinuxPodSandboxStatus{
				Namespaces: &pb.Namespace{
					Network: sPtr(netNsPath),
				},
			},
			Network: &pb.PodSandboxNetworkStatus{Ip: &ip},
			State:   &rStatus,
		},
	}, nil
}

// ListPodSandbox returns a list of SandBoxes.
func (s *Server) ListPodSandbox(context.Context, *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) {
	var pods []*pb.PodSandbox
	for _, sb := range s.state.sandboxes {
		podInfraContainerName := sb.name + "-infra"
		podInfraContainer := sb.getContainer(podInfraContainerName)
		if err := s.runtime.UpdateStatus(podInfraContainer); err != nil {
			return nil, err
		}
		cState := s.runtime.ContainerStatus(podInfraContainer)
		created := cState.Created.Unix()
		rStatus := pb.PodSandBoxState_NOTREADY
		if cState.Status == ContainerStateRunning {
			rStatus = pb.PodSandBoxState_READY
		}

		pod := &pb.PodSandbox{
			Id:        &sb.id,
			CreatedAt: int64Ptr(created),
			State:     &rStatus,
		}

		pods = append(pods, pod)
	}

	return &pb.ListPodSandboxResponse{
		Items: pods,
	}, nil
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/smancke/guble/protocol"

	"fmt"
	"net/http"
	"reflect"
	"time"
)

// Startable interface for modules which provide a start mechanism
type Startable interface {
	Start() error
}

// Stopable interface for modules which provide a stop mechanism
type Stopable interface {
	Stop() error
}

// Endpoint adds a HTTP handler for the `GetPrefix()` to the webserver
type Endpoint interface {
	http.Handler
	GetPrefix() string
}

// Service is the main class for simple control of a server
type Service struct {
	webServer     *WebServer
	router        Router
	stopListener  []Stopable
	startListener []Startable
	// The time given to each Module on Stop()
	StopGracePeriod time.Duration
}

// NewService registers the Main Router, where other modules can subscribe for messages
func NewService(
	addr string,
	router Router) *Service {
	service := &Service{
		stopListener:    make([]Stopable, 0, 5),
		webServer:       NewWebServer(addr),
		router:          router,
		StopGracePeriod: time.Second * 2,
	}
	service.Register(service.webServer)
	service.Register(service.router)

	return service
}

// Register the supplied module on this service.
// This method checks the module for the following interfaces and
// does the expected registrations:
//   Stopable: notify when the service stops
//   Endpoint: Register the handler function of the Endpoint in the http service at prefix
//
// If the module does not have a HandlerFunc, the prefix parameter is ignored
func (service *Service) Register(module interface{}) {
	name := reflect.TypeOf(module).String()

	if m, ok := module.(Startable); ok {
		protocol.Info("register %v as StartListener", name)
		service.AddStartListener(m)
	}

	if m, ok := module.(Endpoint); ok {
		protocol.Info("register %v as Endpoint to %v", name, m.GetPrefix())
		service.AddHandler(m.GetPrefix(), m)
	}

	if m, ok := module.(Stopable); ok {
		protocol.Info("register %v as StopListener", name)
		service.AddStopListener(m)
	}
}

func (service *Service) AddHandler(prefix string, handler http.Handler) {
	service.webServer.mux.Handle(prefix, handler)
}

func (service *Service) Start() error {
	el := protocol.NewErrorList("Errors occured while startup the service: ")

	for _, startable := range service.startListener {
		name := reflect.TypeOf(startable).String()

		protocol.Debug("starting module %v", name)
		if err := startable.Start(); err != nil {
			protocol.Err("error on startup module %v", name)
			el.Add(err)
		}
	}
	return el.ErrorOrNil()
}

func (service *Service) AddStopListener(stopable Stopable) {
	service.stopListener = append(service.stopListener, stopable)
}

func (service *Service) AddStartListener(startable Startable) {
	service.startListener = append(service.startListener, startable)
}

func (service *Service) Stop() error {
	errors := make(map[string]error)
	for _, stopable := range service.stopListener {
		name := reflect.TypeOf(stopable).String()
		stoppedChan := make(chan bool)
		errorChan := make(chan error)
		protocol.Info("stopping %v ...", name)
		go func() {
			err := stopable.Stop()
			if err != nil {
				errorChan <- err
				return
			}
			stoppedChan <- true
		}()
		select {
		case err := <-errorChan:
			protocol.Err("error while stopping %v: %v", name, err.Error)
			errors[name] = err
		case <-stoppedChan:
			protocol.Info("stopped %v", name)
		case <-time.After(service.StopGracePeriod):
			errors[name] = fmt.Errorf("error while stopping %v: not returned after %v seconds", name, service.StopGracePeriod)
			protocol.Err(errors[name].Error())
		}
	}
	if len(errors) > 0 {
		return fmt.Errorf("Errors while stopping modules %q", errors)
	}
	return nil
}

func (service *Service) GetWebServer() *WebServer {
	return service.webServer
}
<commit_msg>renaming<commit_after>package server

import (
	"github.com/smancke/guble/protocol"

	"fmt"
	"net/http"
	"reflect"
	"time"
)

// Startable interface for modules which provide a start mechanism
type Startable interface {
	Start() error
}

// Stopable interface for modules which provide a stop mechanism
type Stopable interface {
	Stop() error
}

// Endpoint adds a HTTP handler for the `GetPrefix()` to the webserver
type Endpoint interface {
	http.Handler
	GetPrefix() string
}

// Service is the main class for simple control of a server
type Service struct {
	webServer  *WebServer
	router     Router
	stopables  []Stopable
	startables []Startable
	// The time given to each Module on Stop()
	StopGracePeriod time.Duration
}

// NewService registers the Main Router, where other modules can subscribe for messages
func NewService(
	addr string,
	router Router) *Service {
	service := &Service{
		stopables:       make([]Stopable, 0, 5),
		webServer:       NewWebServer(addr),
		router:          router,
		StopGracePeriod: time.Second * 2,
	}
	service.Register(service.webServer)
	service.Register(service.router)

	return service
}

// Register the supplied module on this service.
// This method checks the module for the following interfaces and
// does the expected registrations:
//   Stopable: notify when the service stops
//   Endpoint: Register the handler function of the Endpoint in the http service at prefix
//
// If the module does not have a HandlerFunc, the prefix parameter is ignored
func (service *Service) Register(module interface{}) {
	name := reflect.TypeOf(module).String()

	if m, ok := module.(Startable); ok {
		protocol.Info("register %v as StartListener", name)
		service.AddStartListener(m)
	}

	if m, ok := module.(Endpoint); ok {
		protocol.Info("register %v as Endpoint to %v", name, m.GetPrefix())
		service.AddHandler(m.GetPrefix(), m)
	}

	if m, ok := module.(Stopable); ok {
		protocol.Info("register %v as StopListener", name)
		service.AddStopListener(m)
	}
}

func (service *Service) AddHandler(prefix string, handler http.Handler) {
	service.webServer.mux.Handle(prefix, handler)
}

func (service *Service) Start() error {
	el := protocol.NewErrorList("Errors occured while startup the service: ")

	for _, startable := range service.startables {
		name := reflect.TypeOf(startable).String()

		protocol.Debug("starting module %v", name)
		if err := startable.Start(); err != nil {
			protocol.Err("error on startup module %v", name)
			el.Add(err)
		}
	}
	return el.ErrorOrNil()
}

func (service *Service) AddStopListener(stopable Stopable) {
	service.stopables = append(service.stopables, stopable)
}

func (service *Service) AddStartListener(startable Startable) {
	service.startables = append(service.startables, startable)
}

func (service *Service) Stop() error {
	errors := make(map[string]error)
	for _, stopable := range service.stopables {
		name := reflect.TypeOf(stopable).String()
		stoppedChan := make(chan bool)
		errorChan := make(chan error)
		protocol.Info("stopping %v ...", name)
		go func() {
			err := stopable.Stop()
			if err != nil {
				errorChan <- err
				return
			}
			stoppedChan <- true
		}()
		select {
		case err := <-errorChan:
			protocol.Err("error while stopping %v: %v", name, err.Error)
			errors[name] = err
		case <-stoppedChan:
			protocol.Info("stopped %v", name)
		case <-time.After(service.StopGracePeriod):
			errors[name] = fmt.Errorf("error while stopping %v: not returned after %v seconds", name, service.StopGracePeriod)
			protocol.Err(errors[name].Error())
		}
	}
	if len(errors) > 0 {
		return fmt.Errorf("Errors while stopping modules %q", errors)
	}
	return nil
}

func (service *Service) GetWebServer() *WebServer {
	return service.webServer
}
<|endoftext|>