<commit_before>package raft

import (
  "net/http"
  "fmt"
  "time"
  "io/ioutil"
  "strconv"
  "encoding/json"
  "net/url"
  "bytes"
)

// Used to store any field to interact with the RPC
// functions
type RPC struct {
  Server *http.Server
  StateMachine *StateMachine
}

// Start the RPC server
func (rpc *RPC) Start() error {
  // TODO: test the server
  go func() {
  }()

  err := rpc.Server.ListenAndServe()

  return err
}

func (rpc *RPC) StartElection() {
  sm := rpc.StateMachine

  if sm.State.Is() != CANDIDATE {
    return
  }

  sm.State.SyncTerm <- struct{}{}
  defer func() {
    <- sm.State.SyncTerm
  }()

  sm.Timer.Stop();

  oldTerm := sm.State.CurrentTerm
  newTerm := sm.State.CurrentTerm + 1

  sm.State.CurrentTerm = newTerm
  sm.Exec("term::changed", oldTerm, newTerm)

  sm.State.VotedFor = sm.State.MyId

  timer := time.NewTimer(300 * time.Millisecond)

  done := make(chan struct{}, 0)
  go func() {
    for nodeName, _ := range sm.Cluster.Nodes {
      if nodeName == sm.State.MyId {
        continue
      }

      client := &http.Client{}
      form := url.Values{}
      form.Set("term", strconv.FormatUint(sm.State.CurrentTerm, 10))
      form.Set("candidateId", sm.State.MyId)
      req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/request-vote", nodeName), bytes.NewBufferString(form.Encode()))
      if err != nil {
        return
      }
      req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
      resp, err := client.Do(req)
      if err != nil {
        return
      }

      switch statusCode := resp.StatusCode; statusCode {
        case 200:
          done <- struct{}{}
        default:
          return
      }
    }
  }()

  LOOP: for {
    select {
      case <- timer.C:
        timer.Stop()
        return
      case <- done:
        break LOOP
    }
  }

  sm.Timer.Stop()
  sm.State.Switch(LEADER)
  go func() {
    defer func() {
      sm.Timer.Start()
    }()

    for state := sm.State.Is(); state == LEADER; state = sm.State.Is() {
      for nodeName, _ := range sm.Cluster.Nodes {
        if nodeName == sm.State.MyId {
          continue
        }

        client := &http.Client{}
        form := url.Values{}
        form.Set("term", strconv.FormatUint(sm.State.CurrentTerm, 10))
        req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/append-entry?%s", nodeName, form.Encode()), bytes.NewBufferString("[]"))
        if err != nil {
          return
        }
        req.Header.Add("Content-Type", "application/json")
        _, err = client.Do(req)
        if err != nil {
          return
        }
      }
      <- time.Tick(50 * time.Millisecond)
    }
  }()
}

// Create RPC connection
// TODO: create error object for http responses
func NewRPC(sm *StateMachine) (*RPC, error) {
  mux := http.NewServeMux()

  addr := fmt.Sprintf("%s:%d", sm.Configuration.RPCHost, sm.Configuration.RPCPort)

  server := &http.Server{
    Addr: addr,
    Handler: mux,
  }

  mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!")
  })
  mux.HandleFunc("/append-entry", func(w http.ResponseWriter, r *http.Request) {
    // Declare a variable that will be used to be the statusCode of the response
    // because WriteHeader actually writes header, it is not possible to play
    // with the header in a defer function's call
    statusCode := 200

    // Acquire the lock since we'll write and we don't want dirty reads
    sm.State.SyncTerm <- struct{}{}
    defer func() {
      // Send back the Current-Term in the response
      w.Header().Add("X-Current-Term", strconv.FormatUint(sm.State.CurrentTerm, 10))
      w.WriteHeader(statusCode)

      // Unlock
      <- sm.State.SyncTerm
    }()

    oldTerm := sm.State.CurrentTerm

    err := r.ParseForm()
    if err != nil {
      statusCode = 422
      return
    }

    // start parsing term
    term, ok := r.Form["term"]
    if !ok || term[0] == "" {
      statusCode = 422
      return
    }

    newTerm, err := strconv.ParseUint(term[0], 10, 0)
    if err != nil {
      statusCode = 422
      return
    }

    if newTerm < sm.State.CurrentTerm {
      statusCode = 422
      return
    }
    // end parsing term

    //// start parsing leaderCommit
    //leaderCommitValue, ok := r.Form["leaderCommit"]
    //if !ok || leaderCommitValue[0] == "" {
      //statusCode = 422
      //return
    //}

    //leaderCommit, err := strconv.ParseInt(leaderCommitValue[0], 10, 0)
    //if err != nil {
      //statusCode = 422
      //return
    //}
    //// end parsing leaderCommit

    //// start parsing prevLogTerm
    //prevLogTerm, ok := r.Form["prevLogTerm"]
    //if !ok || prevLogTerm[0] == "" {
      //statusCode = 422
      //return
    //}

    //prevTerm, err := strconv.ParseUint(prevLogTerm[0], 10, 0)
    //if err != nil {
      //statusCode = 422
      //return
    //}
    //// end parsing prevLogTerm

    //// start parsing prevLogIndex
    //prevLogIndex, ok := r.Form["prevLogIndex"]
    //if !ok || prevLogIndex[0] == "" {
      //statusCode = 422
      //return
    //}

    //prevIndex, err := strconv.ParseInt(prevLogIndex[0], 10, 0)
    //if err != nil {
      //statusCode = 422
      //return
    //}

    //// Acquire the lock
    //sm.Storage.C <- struct{}{}
    //defer func() {
      //<- sm.Storage.C
    //}()

    //// end parsing prevLogTerm

    // start parsing body
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
      statusCode = 400
      return
    }

    switch contentType := r.Header.Get("Content-Type"); contentType {
      case "application/json":
        var logs Logs

        err := json.Unmarshal(body, &logs)
        if err != nil {
          statusCode = 400
          return
        }

        if sm.State.Is() != FOLLOWER {
          sm.State.Switch(FOLLOWER)
        }

        if len(logs) == 0 && newTerm != sm.State.CurrentTerm {
          sm.State.CurrentTerm = newTerm
          sm.Exec("term::changed", oldTerm, newTerm)
        }

        // TODO: stop the timer after added all logs
        sm.Timer.Stop();
        for ok := false; !ok; {
          ok = sm.Timer.Start()
        }
      default:
        statusCode = 415
        return
    }
    // end parsing body

  })

  mux.HandleFunc("/request-vote", func(w http.ResponseWriter, r *http.Request) {
    // Declare a variable that will be used to be the statusCode of the response
    // because WriteHeader actually writes header, it is not possible to play
    // with the header in a defer function's call
    statusCode := 200

    // Acquire the lock since we'll write and we don't want dirty reads
    sm.State.SyncTerm <- struct{}{}
    defer func() {
      // Send back the Current-Term in the response
      w.Header().Add("X-Current-Term", strconv.FormatUint(sm.State.CurrentTerm, 10))
      w.WriteHeader(statusCode)

      // Unlock
      <- sm.State.SyncTerm
    }()

    err := r.ParseForm()
    if err != nil {
      statusCode = 422
      return
    }

    // start parsing term
    term, ok := r.Form["term"]
    if !ok || term[0] == "" {
      statusCode = 422
      return
    }

    newTerm, err := strconv.ParseUint(term[0], 10, 0)
    if err != nil {
      statusCode = 422
      return
    }

    if newTerm < sm.State.CurrentTerm {
      statusCode = 422
      return
    }
    // end parsing term

    // start parsing candidateId
    candidateId, ok := r.Form["candidateId"]
    if !ok || candidateId[0] == "" {
      statusCode = 422
      return
    }

    candidate := candidateId[0]

    // If the name of the candidate cannot be found in the
    // cluster configuration then tell him to gtfo
    if ok := sm.Cluster. Find(candidate); ok == false {
      statusCode = 422
      return
    }

    // If the candidate's term is the same as the CurrentTerm
    // We need to check if VotedFor is not null AND VotedFor is not the same candidate's ID
    // Otherwise since there is only one VotedFor per term and at start up the VotedFor is null
    // respond to false.
    fmt.Println("receive vote: ", r.Form, "votedFor :", sm.State.VotedFor, "myterm :", sm.State.CurrentTerm)
    if newTerm == sm.State.CurrentTerm {
      if sm.State.VotedFor != "" && sm.State.VotedFor != candidate {
        statusCode = 422
        return
      }
    }
    // end parsing candidateId

    // if Voted yes, convert to follower and vote for him
    if sm.State.Is() != FOLLOWER {
      sm.State.Switch(FOLLOWER)
      sm.Timer.Start()
    }

    sm.State.CurrentTerm = newTerm
    sm.State.VotedFor = candidate

  })

  rpc := &RPC{
    Server: server,
    StateMachine: sm,
  }

  return rpc, nil
}
<commit_msg>Modified fmt in favor of log<commit_after>package raft

import (
  "net/http"
  "fmt"
  "time"
  "io/ioutil"
  "strconv"
  "encoding/json"
  "net/url"
  "bytes"
  "log"
)

// Used to store any field to interact with the RPC
// functions
type RPC struct {
  Server *http.Server
  StateMachine *StateMachine
}

// Start the RPC server
func (rpc *RPC) Start() error {
  // TODO: test the server
  go func() {
  }()

  err := rpc.Server.ListenAndServe()

  return err
}

func (rpc *RPC) StartElection() {
  sm := rpc.StateMachine

  if sm.State.Is() != CANDIDATE {
    return
  }

  sm.State.SyncTerm <- struct{}{}
  defer func() {
    <- sm.State.SyncTerm
  }()

  sm.Timer.Stop();

  oldTerm := sm.State.CurrentTerm
  newTerm := sm.State.CurrentTerm + 1

  sm.State.CurrentTerm = newTerm
  sm.Exec("term::changed", oldTerm, newTerm)

  sm.State.VotedFor = sm.State.MyId

  timer := time.NewTimer(300 * time.Millisecond)

  done := make(chan struct{}, 0)
  go func() {
    for nodeName, _ := range sm.Cluster.Nodes {
      if nodeName == sm.State.MyId {
        continue
      }

      client := &http.Client{}
      form := url.Values{}
      form.Set("term", strconv.FormatUint(sm.State.CurrentTerm, 10))
      form.Set("candidateId", sm.State.MyId)
      req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/request-vote", nodeName), bytes.NewBufferString(form.Encode()))
      if err != nil {
        return
      }
      req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
      resp, err := client.Do(req)
      if err != nil {
        return
      }

      switch statusCode := resp.StatusCode; statusCode {
        case 200:
          done <- struct{}{}
        default:
          return
      }
    }
  }()

  LOOP: for {
    select {
      case <- timer.C:
        timer.Stop()
        return
      case <- done:
        break LOOP
    }
  }

  sm.Timer.Stop()
  sm.State.Switch(LEADER)
  go func() {
    defer func() {
      sm.Timer.Start()
    }()

    for state := sm.State.Is(); state == LEADER; state = sm.State.Is() {
      for nodeName, _ := range sm.Cluster.Nodes {
        if nodeName == sm.State.MyId {
          continue
        }

        log.Println("AppendEntry RPC started for: ", sm.State.MyId)
        client := &http.Client{}
        form := url.Values{}
        form.Set("term", strconv.FormatUint(sm.State.CurrentTerm, 10))
        req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/append-entry?%s", nodeName, form.Encode()), bytes.NewBufferString("[]"))
        if err != nil {
          return
        }
        req.Header.Add("Content-Type", "application/json")
        _, err = client.Do(req)
        if err != nil {
          return
        }
      }
      <- time.Tick(50 * time.Millisecond)
    }
  }()
}

// Create RPC connection
// TODO: create error object for http responses
func NewRPC(sm *StateMachine) (*RPC, error) {
  mux := http.NewServeMux()

  addr := fmt.Sprintf("%s:%d", sm.Configuration.RPCHost, sm.Configuration.RPCPort)

  server := &http.Server{
    Addr: addr,
    Handler: mux,
  }

  mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!")
  })
  mux.HandleFunc("/append-entry", func(w http.ResponseWriter, r *http.Request) {
    // Declare a variable that will be used to be the statusCode of the response
    // because WriteHeader actually writes header, it is not possible to play
    // with the header in a defer function's call
    statusCode := 200

    // Acquire the lock since we'll write and we don't want dirty reads
    sm.State.SyncTerm <- struct{}{}
    defer func() {
      // Send back the Current-Term in the response
      w.Header().Add("X-Current-Term", strconv.FormatUint(sm.State.CurrentTerm, 10))
      w.WriteHeader(statusCode)

      // Unlock
      <- sm.State.SyncTerm
    }()

    oldTerm := sm.State.CurrentTerm

    err := r.ParseForm()
    if err != nil {
      statusCode = 422
      return
    }

    // start parsing term
    term, ok := r.Form["term"]
    if !ok || term[0] == "" {
      statusCode = 422
      return
    }

    newTerm, err := strconv.ParseUint(term[0], 10, 0)
    if err != nil {
      statusCode = 422
      return
    }

    if newTerm < sm.State.CurrentTerm {
      statusCode = 422
      return
    }
    // end parsing term

    //// start parsing leaderCommit
    //leaderCommitValue, ok := r.Form["leaderCommit"]
    //if !ok || leaderCommitValue[0] == "" {
      //statusCode = 422
      //return
    //}

    //leaderCommit, err := strconv.ParseInt(leaderCommitValue[0], 10, 0)
    //if err != nil {
      //statusCode = 422
      //return
    //}
    //// end parsing leaderCommit

    //// start parsing prevLogTerm
    //prevLogTerm, ok := r.Form["prevLogTerm"]
    //if !ok || prevLogTerm[0] == "" {
      //statusCode = 422
      //return
    //}

    //prevTerm, err := strconv.ParseUint(prevLogTerm[0], 10, 0)
    //if err != nil {
      //statusCode = 422
      //return
    //}
    //// end parsing prevLogTerm

    //// start parsing prevLogIndex
    //prevLogIndex, ok := r.Form["prevLogIndex"]
    //if !ok || prevLogIndex[0] == "" {
      //statusCode = 422
      //return
    //}

    //prevIndex, err := strconv.ParseInt(prevLogIndex[0], 10, 0)
    //if err != nil {
      //statusCode = 422
      //return
    //}

    //// Acquire the lock
    //sm.Storage.C <- struct{}{}
    //defer func() {
      //<- sm.Storage.C
    //}()

    //// end parsing prevLogTerm

    // start parsing body
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
      statusCode = 400
      return
    }

    switch contentType := r.Header.Get("Content-Type"); contentType {
      case "application/json":
        var logs Logs

        err := json.Unmarshal(body, &logs)
        if err != nil {
          statusCode = 400
          return
        }

        if sm.State.Is() != FOLLOWER {
          sm.State.Switch(FOLLOWER)
        }

        if len(logs) == 0 && newTerm != sm.State.CurrentTerm {
          sm.State.CurrentTerm = newTerm
          sm.Exec("term::changed", oldTerm, newTerm)
        }

        // TODO: stop the timer after added all logs
        sm.Timer.Stop();
        for ok := false; !ok; {
          ok = sm.Timer.Start()
        }
      default:
        statusCode = 415
        return
    }
    // end parsing body

  })

  mux.HandleFunc("/request-vote", func(w http.ResponseWriter, r *http.Request) {
    // Declare a variable that will be used to be the statusCode of the response
    // because WriteHeader actually writes header, it is not possible to play
    // with the header in a defer function's call
    statusCode := 200

    // Acquire the lock since we'll write and we don't want dirty reads
    sm.State.SyncTerm <- struct{}{}

    defer func() {
      // Send back the Current-Term in the response
      w.Header().Add("X-Current-Term", strconv.FormatUint(sm.State.CurrentTerm, 10))
      w.WriteHeader(statusCode)

      // Unlock
      <- sm.State.SyncTerm
    }()

    err := r.ParseForm()
    if err != nil {
      statusCode = 422
      return
    }

    // start parsing term
    term, ok := r.Form["term"]
    if !ok || term[0] == "" {
      statusCode = 422
      return
    }

    newTerm, err := strconv.ParseUint(term[0], 10, 0)
    if err != nil {
      statusCode = 422
      return
    }

    if newTerm < sm.State.CurrentTerm {
      statusCode = 422
      return
    }
    // end parsing term

    // start parsing candidateId
    candidateId, ok := r.Form["candidateId"]
    if !ok || candidateId[0] == "" {
      statusCode = 422
      return
    }

    candidate := candidateId[0]

    // If the name of the candidate cannot be found in the
    // cluster configuration then tell him to gtfo
    if ok := sm.Cluster. Find(candidate); ok == false {
      statusCode = 422
      return
    }

    // If the candidate's term is the same as the CurrentTerm
    // We need to check if VotedFor is not null AND VotedFor is not the same candidate's ID
    // Otherwise since there is only one VotedFor per term and at start up the VotedFor is null
    // respond to false.
    log.Println(sm.State.MyId, "receive vote: ", r.Form, "votedFor :", sm.State.VotedFor, "myterm :", sm.State.CurrentTerm)
    if newTerm == sm.State.CurrentTerm {
      if sm.State.VotedFor != "" && sm.State.VotedFor != candidate {
        statusCode = 422
        return
      }
    }
    // end parsing candidateId

    // if Voted yes, convert to follower and vote for him
    if sm.State.Is() != FOLLOWER {
      sm.State.Switch(FOLLOWER)
      sm.Timer.Start()
    }

    sm.State.CurrentTerm = newTerm
    sm.State.VotedFor = candidate

  })

  rpc := &RPC{
    Server: server,
    StateMachine: sm,
  }

  return rpc, nil
}
<|endoftext|>


<commit_before>// +build !windows

package plugin

import (
	"path/filepath"
)

type logData struct{}

func (p *plug) deinitLogger() error {
	return p.closeLog()
}

// initLogger opens and sets a log file on Unix platforms
func (p *plug) initLogger() error {
	pathArray := []string{"var", "log", p.params.Name + ".log"}
	defaultPath := filepath.Join(pathArray...)

	return p.openLog(defaultPath)
}
<commit_msg>path fix<commit_after>// +build !windows

package plugin

import (
	"path/filepath"
)

type logData struct{}

func (p *plug) deinitLogger() error {
	return p.closeLog()
}

// initLogger opens and sets a log file on Unix platforms
func (p *plug) initLogger() error {
	pathArray := []string{"/", "var", "log", p.params.Name + ".log"}
	defaultPath := filepath.Join(pathArray...)

	return p.openLog(defaultPath)
}
<|endoftext|>


<commit_before>// +build !windows

package plugin

import (
	"path/filepath"
)

type logData struct{}

func (p *plug) deinitLogger() error {
	return p.closeLog()
}

// initLogger opens and sets a log file on Unix platforms
func (p *plug) initLogger() error {
	pathArray := []string{"/", "var", "log", p.params.Name + ".log"}
	defaultPath := filepath.Join(pathArray...)

	return p.openLog(defaultPath)
}
<commit_msg>Log path fix<commit_after>// +build !windows

package plugin

import (
	"path/filepath"
)

type logData struct{}

func (p *plug) deinitLogger() error {
	return p.closeLog()
}

// initLogger opens and sets a log file on Unix platforms
func (p *plug) initLogger() error {
	pathArray := []string{"/", "var", "log", "openbaton", p.params.Type + "-plugin.log"}
	defaultPath := filepath.Join(pathArray...)

	return p.openLog(defaultPath)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 ENDOH takanao.
<https://github.com/MiCHiLU/go-lru-cache-stats>

Copyright 2012 Google Inc.
<https://github.com/golang/groupcache>

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 groupcache provides a data loading mechanism with caching
// and de-duplication that works across a set of peer processes.
//
// Each data Get first consults its local cache, otherwise delegates
// to the requested key's canonical owner, which then checks its cache
// or finally gets the data.  In the common case, many concurrent
// cache misses across a set of peers for the same key result in just
// one cache fill.
package lru

import (
	"errors"
	"strconv"
	"sync"
	"sync/atomic"

	"github.com/golang/groupcache/lru"
	"github.com/golang/groupcache/singleflight"
)

// A Getter loads data for a key.
type Getter interface {
	// Get returns the value identified by key, populating dest.
	//
	// The returned data must be unversioned. That is, key must
	// uniquely describe the loaded data, without an implicit
	// current time, and without relying on cache expiration
	// mechanisms.
	Get(ctx Context, key string, dest Sink) error
}

// A GetterFunc implements Getter with a function.
type GetterFunc func(ctx Context, key string, dest Sink) error

func (f GetterFunc) Get(ctx Context, key string, dest Sink) error {
	return f(ctx, key, dest)
}

var (
	mu     sync.RWMutex
	groups = make(map[string]*Group)
)

// GetGroup returns the named group previously created with NewGroup, or
// nil if there's no such group.
func GetGroup(name string) *Group {
	mu.RLock()
	g := groups[name]
	mu.RUnlock()
	return g
}

// NewGroup creates a coordinated group-aware Getter from a Getter.
//
// The returned Getter tries (but does not guarantee) to run only one
// Get call at once for a given key across an entire set of peer
// processes. Concurrent callers both in the local process and in
// other processes receive copies of the answer once the original Get
// completes.
//
// The group name must be unique for each getter.
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
	return newGroup(name, cacheBytes, getter)
}

// If peers is nil, the peerPicker is called via a sync.Once to initialize it.
func newGroup(name string, cacheBytes int64, getter Getter) *Group {
	if getter == nil {
		panic("nil Getter")
	}
	mu.Lock()
	defer mu.Unlock()
	if _, dup := groups[name]; dup {
		panic("duplicate registration of group " + name)
	}
	g := &Group{
		name:       name,
		getter:     getter,
		cacheBytes: cacheBytes,
	}
	if fn := newGroupHook; fn != nil {
		fn(g)
	}
	groups[name] = g
	return g
}

// newGroupHook, if non-nil, is called right after a new group is created.
var newGroupHook func(*Group)

// RegisterNewGroupHook registers a hook that is run each time
// a group is created.
func RegisterNewGroupHook(fn func(*Group)) {
	if newGroupHook != nil {
		panic("RegisterNewGroupHook called more than once")
	}
	newGroupHook = fn
}

// A Group is a cache namespace and associated data loaded spread over
// a group of 1 or more machines.
type Group struct {
	name       string
	getter     Getter
	cacheBytes int64 // limit for sum of mainCache

	// mainCache is a cache of the keys for which this process
	// (amongst its peers) is authorative. That is, this cache
	// contains keys which consistent hash on to this process's
	// peer number.
	mainCache cache

	// loadGroup ensures that each key is only fetched once
	// (either locally or remotely), regardless of the number of
	// concurrent callers.
	loadGroup singleflight.Group

	// Stats are statistics on the group.
	Stats Stats
}

// Stats are per-group statistics.
type Stats struct {
	Gets          AtomicInt // any Get request, including from peers
	CacheHits     AtomicInt // either cache was good
	Loads         AtomicInt // (gets - cacheHits)
	LoadsDeduped  AtomicInt // after singleflight
	LocalLoads    AtomicInt // total good local loads
	LocalLoadErrs AtomicInt // total bad local loads
}

// Name returns the name of the group.
func (g *Group) Name() string {
	return g.name
}

func (g *Group) Get(ctx Context, key string, dest Sink) error {
	g.Stats.Gets.Add(1)
	if dest == nil {
		return errors.New("groupcache: nil dest Sink")
	}
	value, cacheHit := g.lookupCache(key)

	if cacheHit {
		g.Stats.CacheHits.Add(1)
		return setSinkView(dest, value)
	}

	// Optimization to avoid double unmarshalling or copying: keep
	// track of whether the dest was already populated. One caller
	// (if local) will set this; the losers will not. The common
	// case will likely be one caller.
	destPopulated := false
	value, destPopulated, err := g.load(ctx, key, dest)
	if err != nil {
		return err
	}
	if destPopulated {
		return nil
	}
	return setSinkView(dest, value)
}

// load loads key either by invoking the getter locally or by sending it to another machine.
func (g *Group) load(ctx Context, key string, dest Sink) (value ByteView, destPopulated bool, err error) {
	g.Stats.Loads.Add(1)
	viewi, err := g.loadGroup.Do(key, func() (interface{}, error) {
		g.Stats.LoadsDeduped.Add(1)
		var value ByteView
		var err error
		value, err = g.getLocally(ctx, key, dest)
		if err != nil {
			g.Stats.LocalLoadErrs.Add(1)
			return nil, err
		}
		g.Stats.LocalLoads.Add(1)
		destPopulated = true // only one caller of load gets this return value
		g.populateCache(key, value, &g.mainCache)
		return value, nil
	})
	if err == nil {
		value = viewi.(ByteView)
	}
	return
}

func (g *Group) getLocally(ctx Context, key string, dest Sink) (ByteView, error) {
	err := g.getter.Get(ctx, key, dest)
	if err != nil {
		return ByteView{}, err
	}
	return dest.view()
}

func (g *Group) lookupCache(key string) (value ByteView, ok bool) {
	if g.cacheBytes <= 0 {
		return
	}
	value, ok = g.mainCache.get(key)
	return
}

func (g *Group) populateCache(key string, value ByteView, cache *cache) {
	if g.cacheBytes <= 0 {
		return
	}
	cache.add(key, value)

	// Evict items from cache(s) if necessary.
	for {
		mainBytes := g.mainCache.bytes()
		if mainBytes <= g.cacheBytes {
			return
		}

		// TODO(bradfitz): this is good-enough-for-now logic.
		// It should be something based on measurements and/or
		// respecting the costs of different resources.
		victim := &g.mainCache
		victim.removeOldest()
	}
}

// CacheType represents a type of cache.
type CacheType int

const (
	// The MainCache is the cache for items that this peer is the
	// owner for.
	MainCache CacheType = iota + 1
)

// CacheStats returns stats about the provided cache within the group.
func (g *Group) CacheStats(which CacheType) CacheStats {
	switch which {
	case MainCache:
		return g.mainCache.stats()
	default:
		return CacheStats{}
	}
}

// cache is a wrapper around an *lru.Cache that adds synchronization,
// makes values always be ByteView, and counts the size of all keys and
// values.
type cache struct {
	mu         sync.RWMutex
	nbytes     int64 // of all keys and values
	lru        *lru.Cache
	nhit, nget int64
	nevict     int64 // number of evictions
}

func (c *cache) stats() CacheStats {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return CacheStats{
		Bytes:     c.nbytes,
		Items:     c.itemsLocked(),
		Gets:      c.nget,
		Hits:      c.nhit,
		Evictions: c.nevict,
	}
}

func (c *cache) add(key string, value ByteView) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.lru == nil {
		c.lru = &lru.Cache{
			OnEvicted: func(key lru.Key, value interface{}) {
				val := value.(ByteView)
				c.nbytes -= int64(len(key.(string))) + int64(val.Len())
				c.nevict++
			},
		}
	}
	c.lru.Add(key, value)
	c.nbytes += int64(len(key)) + int64(value.Len())
}

func (c *cache) get(key string) (value ByteView, ok bool) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.nget++
	if c.lru == nil {
		return
	}
	vi, ok := c.lru.Get(key)
	if !ok {
		return
	}
	c.nhit++
	return vi.(ByteView), true
}

func (c *cache) removeOldest() {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.lru != nil {
		c.lru.RemoveOldest()
	}
}

func (c *cache) bytes() int64 {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.nbytes
}

func (c *cache) items() int64 {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.itemsLocked()
}

func (c *cache) itemsLocked() int64 {
	if c.lru == nil {
		return 0
	}
	return int64(c.lru.Len())
}

// An AtomicInt is an int64 to be accessed atomically.
type AtomicInt int64

// Add atomically adds n to i.
func (i *AtomicInt) Add(n int64) {
	atomic.AddInt64((*int64)(i), n)
}

// Get atomically gets the value of i.
func (i *AtomicInt) Get() int64 {
	return atomic.LoadInt64((*int64)(i))
}

func (i *AtomicInt) String() string {
	return strconv.FormatInt(i.Get(), 10)
}

// CacheStats are returned by stats accessors on Group.
type CacheStats struct {
	Bytes     int64
	Items     int64
	Gets      int64
	Hits      int64
	Evictions int64
}
<commit_msg>add GroupInterface<commit_after>/*
Copyright 2015 ENDOH takanao.
<https://github.com/MiCHiLU/go-lru-cache-stats>

Copyright 2012 Google Inc.
<https://github.com/golang/groupcache>

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 groupcache provides a data loading mechanism with caching
// and de-duplication that works across a set of peer processes.
//
// Each data Get first consults its local cache, otherwise delegates
// to the requested key's canonical owner, which then checks its cache
// or finally gets the data.  In the common case, many concurrent
// cache misses across a set of peers for the same key result in just
// one cache fill.
package lru

import (
	"errors"
	"strconv"
	"sync"
	"sync/atomic"

	"github.com/golang/groupcache/lru"
	"github.com/golang/groupcache/singleflight"
)

// A Getter loads data for a key.
type Getter interface {
	// Get returns the value identified by key, populating dest.
	//
	// The returned data must be unversioned. That is, key must
	// uniquely describe the loaded data, without an implicit
	// current time, and without relying on cache expiration
	// mechanisms.
	Get(ctx Context, key string, dest Sink) error
}

// A GetterFunc implements Getter with a function.
type GetterFunc func(ctx Context, key string, dest Sink) error

func (f GetterFunc) Get(ctx Context, key string, dest Sink) error {
	return f(ctx, key, dest)
}

var (
	mu     sync.RWMutex
	groups = make(map[string]*GroupInterface)
)

// GetGroup returns the named group previously created with NewGroup, or
// nil if there's no such group.
func GetGroup(name string) *GroupInterface {
	mu.RLock()
	g := groups[name]
	mu.RUnlock()
	return g
}

// NewGroup creates a coordinated group-aware Getter from a Getter.
//
// The returned Getter tries (but does not guarantee) to run only one
// Get call at once for a given key across an entire set of peer
// processes. Concurrent callers both in the local process and in
// other processes receive copies of the answer once the original Get
// completes.
//
// The group name must be unique for each getter.
func NewGroup(name string, cacheBytes int64, getter Getter, stats bool) *GroupInterface {
	return newGroup(name, cacheBytes, getter, stats)
}

// If peers is nil, the peerPicker is called via a sync.Once to initialize it.
func newGroup(name string, cacheBytes int64, getter Getter, stats bool) *GroupInterface {
	if getter == nil {
		panic("nil Getter")
	}
	mu.Lock()
	defer mu.Unlock()
	if _, dup := groups[name]; dup {
		panic("duplicate registration of group " + name)
	}
	g := &Group{
		name:       name,
		getter:     getter,
		cacheBytes: cacheBytes,
	}
	var gi GroupInterface
	if stats {
		gi = &GroupWithStats{
			Group: g,
		}
	} else {
		gi = g
	}
	if fn := newGroupHook; fn != nil {
		fn(&gi)
	}
	groups[name] = &gi
	return &gi
}

// newGroupHook, if non-nil, is called right after a new group is created.
var newGroupHook func(*GroupInterface)

// RegisterNewGroupHook registers a hook that is run each time
// a group is created.
func RegisterNewGroupHook(fn func(*GroupInterface)) {
	if newGroupHook != nil {
		panic("RegisterNewGroupHook called more than once")
	}
	newGroupHook = fn
}

type GroupInterface interface {
	Name() string
	Get(ctx Context, key string, dest Sink) error
	load(ctx Context, key string, dest Sink) (value ByteView, destPopulated bool, err error)
	getLocally(ctx Context, key string, dest Sink) (ByteView, error)
	lookupCache(key string) (value ByteView, ok bool)
	populateCache(key string, value ByteView, cache *cache)
}

// A Group is a cache namespace and associated data loaded spread over
// a group of 1 or more machines.
type Group struct {
	name       string
	getter     Getter
	cacheBytes int64 // limit for sum of mainCache

	// mainCache is a cache of the keys for which this process
	// (amongst its peers) is authorative. That is, this cache
	// contains keys which consistent hash on to this process's
	// peer number.
	mainCache cache

	// loadGroup ensures that each key is only fetched once
	// (either locally or remotely), regardless of the number of
	// concurrent callers.
	loadGroup singleflight.Group
}

type GroupWithStats struct {
	*Group

	// Stats are statistics on the group.
	Stats Stats
}

// Stats are per-group statistics.
type Stats struct {
	Gets          AtomicInt // any Get request, including from peers
	CacheHits     AtomicInt // either cache was good
	Loads         AtomicInt // (gets - cacheHits)
	LoadsDeduped  AtomicInt // after singleflight
	LocalLoads    AtomicInt // total good local loads
	LocalLoadErrs AtomicInt // total bad local loads
}

// Name returns the name of the group.
func (g *Group) Name() string {
	return g.name
}

func (g *Group) Get(ctx Context, key string, dest Sink) error {
	g.Stats.Gets.Add(1)
	if dest == nil {
		return errors.New("groupcache: nil dest Sink")
	}
	value, cacheHit := g.lookupCache(key)

	if cacheHit {
		g.Stats.CacheHits.Add(1)
		return setSinkView(dest, value)
	}

	// Optimization to avoid double unmarshalling or copying: keep
	// track of whether the dest was already populated. One caller
	// (if local) will set this; the losers will not. The common
	// case will likely be one caller.
	destPopulated := false
	value, destPopulated, err := g.load(ctx, key, dest)
	if err != nil {
		return err
	}
	if destPopulated {
		return nil
	}
	return setSinkView(dest, value)
}

// load loads key either by invoking the getter locally or by sending it to another machine.
func (g *Group) load(ctx Context, key string, dest Sink) (value ByteView, destPopulated bool, err error) {
	g.Stats.Loads.Add(1)
	viewi, err := g.loadGroup.Do(key, func() (interface{}, error) {
		g.Stats.LoadsDeduped.Add(1)
		var value ByteView
		var err error
		value, err = g.getLocally(ctx, key, dest)
		if err != nil {
			g.Stats.LocalLoadErrs.Add(1)
			return nil, err
		}
		g.Stats.LocalLoads.Add(1)
		destPopulated = true // only one caller of load gets this return value
		g.populateCache(key, value, &g.mainCache)
		return value, nil
	})
	if err == nil {
		value = viewi.(ByteView)
	}
	return
}

func (g *Group) getLocally(ctx Context, key string, dest Sink) (ByteView, error) {
	err := g.getter.Get(ctx, key, dest)
	if err != nil {
		return ByteView{}, err
	}
	return dest.view()
}

func (g *Group) lookupCache(key string) (value ByteView, ok bool) {
	if g.cacheBytes <= 0 {
		return
	}
	value, ok = g.mainCache.get(key)
	return
}

func (g *Group) populateCache(key string, value ByteView, cache *cache) {
	if g.cacheBytes <= 0 {
		return
	}
	cache.add(key, value)

	// Evict items from cache(s) if necessary.
	for {
		mainBytes := g.mainCache.bytes()
		if mainBytes <= g.cacheBytes {
			return
		}

		// TODO(bradfitz): this is good-enough-for-now logic.
		// It should be something based on measurements and/or
		// respecting the costs of different resources.
		victim := &g.mainCache
		victim.removeOldest()
	}
}

// CacheType represents a type of cache.
type CacheType int

const (
	// The MainCache is the cache for items that this peer is the
	// owner for.
	MainCache CacheType = iota + 1
)

// CacheStats returns stats about the provided cache within the group.
func (g *Group) CacheStats(which CacheType) CacheStats {
	switch which {
	case MainCache:
		return g.mainCache.stats()
	default:
		return CacheStats{}
	}
}

// cache is a wrapper around an *lru.Cache that adds synchronization,
// makes values always be ByteView, and counts the size of all keys and
// values.
type cache struct {
	mu         sync.RWMutex
	nbytes     int64 // of all keys and values
	lru        *lru.Cache
	nhit, nget int64
	nevict     int64 // number of evictions
}

func (c *cache) stats() CacheStats {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return CacheStats{
		Bytes:     c.nbytes,
		Items:     c.itemsLocked(),
		Gets:      c.nget,
		Hits:      c.nhit,
		Evictions: c.nevict,
	}
}

func (c *cache) add(key string, value ByteView) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.lru == nil {
		c.lru = &lru.Cache{
			OnEvicted: func(key lru.Key, value interface{}) {
				val := value.(ByteView)
				c.nbytes -= int64(len(key.(string))) + int64(val.Len())
				c.nevict++
			},
		}
	}
	c.lru.Add(key, value)
	c.nbytes += int64(len(key)) + int64(value.Len())
}

func (c *cache) get(key string) (value ByteView, ok bool) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.nget++
	if c.lru == nil {
		return
	}
	vi, ok := c.lru.Get(key)
	if !ok {
		return
	}
	c.nhit++
	return vi.(ByteView), true
}

func (c *cache) removeOldest() {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.lru != nil {
		c.lru.RemoveOldest()
	}
}

func (c *cache) bytes() int64 {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.nbytes
}

func (c *cache) items() int64 {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.itemsLocked()
}

func (c *cache) itemsLocked() int64 {
	if c.lru == nil {
		return 0
	}
	return int64(c.lru.Len())
}

// An AtomicInt is an int64 to be accessed atomically.
type AtomicInt int64

// Add atomically adds n to i.
func (i *AtomicInt) Add(n int64) {
	atomic.AddInt64((*int64)(i), n)
}

// Get atomically gets the value of i.
func (i *AtomicInt) Get() int64 {
	return atomic.LoadInt64((*int64)(i))
}

func (i *AtomicInt) String() string {
	return strconv.FormatInt(i.Get(), 10)
}

// CacheStats are returned by stats accessors on Group.
type CacheStats struct {
	Bytes     int64
	Items     int64
	Gets      int64
	Hits      int64
	Evictions int64
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"

	"crypto/rand"
	"database/sql"
	"encoding/hex"

	"github.com/go-martini/martini"
)

const errDatabase = "😧 The database is having some trouble... try again?"

var app *martini.ClassicMartini

func getRandomID() (string, error) {
	newID := make([]byte, 16)
	_, err := rand.Read(newID)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(newID), nil
}

func init() {

	app = martini.Classic()
	app.Post("/new", func() (int, string) {
		id, err := getRandomID()
		if err != nil {
			log.Printf("Error: %s", err)
			return 500, "😞 Something bad happened... try again?"
		}
		db, err := GetDatabase()
		if err != nil {
			log.Printf("Database error: %s", err)
			return 500, errDatabase
		}

		_, err = db.Exec("INSERT into counters (id, count) VALUES ($1, 0)", id)
		if err != nil {
			log.Printf("Insert error: %s", err)
			return 500, errDatabase
		}
		return 201, id
	})

	app.Put("/(?P<token>[a-zA-Z0-9]{32})", func(params martini.Params) (int, string) {
		db, err := GetDatabase()
		if err != nil {
			return 500, errDatabase
		}
		defer db.Close()

		tx, err := db.Begin()
		if err != nil {
			defer tx.Rollback()
			return 500, errDatabase
		}

		result := tx.QueryRow("SELECT count from counters WHERE id = $1 FOR UPDATE", params["token"])
		var count uint64
		err = result.Scan(&count)
		if err == sql.ErrNoRows {
			return 404, "404 page not found"
		}
		if err != nil {
			defer tx.Rollback()
			log.Printf("%s", err)
			return 500, errDatabase
		}

		_, err = tx.Exec("UPDATE counters SET count = count + 1 WHERE id = $1", params["token"])
		if err != nil {
			defer tx.Rollback()
			log.Printf("%s", err)
			return 500, errDatabase
		}

		tx.Commit()
		return 200, fmt.Sprintf("%d", count+1)
	})
}

func main() {
	db, err := GetDatabase()
	if err != nil {
		log.Fatal(err)
	}

	err = CreateDatabaseSchema(db)
	if err != nil {
		log.Fatal(err)
	}

	app.Run()
}
<commit_msg>Add a healthcheck endpoint<commit_after>package main

import (
	"fmt"
	"log"

	"crypto/rand"
	"database/sql"
	"encoding/hex"

	"github.com/go-martini/martini"
)

const errDatabase = "😧 The database is having some trouble... try again?"

var app *martini.ClassicMartini

func getRandomID() (string, error) {
	newID := make([]byte, 16)
	_, err := rand.Read(newID)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(newID), nil
}

func init() {

	app = martini.Classic()

	app.Get("/_healthcheck", func() string {
		return "OK"
	})

	app.Post("/new", func() (int, string) {
		id, err := getRandomID()
		if err != nil {
			log.Printf("Error: %s", err)
			return 500, "😞 Something bad happened... try again?"
		}
		db, err := GetDatabase()
		if err != nil {
			log.Printf("Database error: %s", err)
			return 500, errDatabase
		}

		_, err = db.Exec("INSERT into counters (id, count) VALUES ($1, 0)", id)
		if err != nil {
			log.Printf("Insert error: %s", err)
			return 500, errDatabase
		}
		return 201, id
	})

	app.Put("/(?P<token>[a-zA-Z0-9]{32})", func(params martini.Params) (int, string) {
		db, err := GetDatabase()
		if err != nil {
			return 500, errDatabase
		}
		defer db.Close()

		tx, err := db.Begin()
		if err != nil {
			defer tx.Rollback()
			return 500, errDatabase
		}

		result := tx.QueryRow("SELECT count from counters WHERE id = $1 FOR UPDATE", params["token"])
		var count uint64
		err = result.Scan(&count)
		if err == sql.ErrNoRows {
			return 404, "404 page not found"
		}
		if err != nil {
			defer tx.Rollback()
			log.Printf("%s", err)
			return 500, errDatabase
		}

		_, err = tx.Exec("UPDATE counters SET count = count + 1 WHERE id = $1", params["token"])
		if err != nil {
			defer tx.Rollback()
			log.Printf("%s", err)
			return 500, errDatabase
		}

		tx.Commit()
		return 200, fmt.Sprintf("%d", count+1)
	})
}

func main() {
	db, err := GetDatabase()
	if err != nil {
		log.Fatal(err)
	}

	err = CreateDatabaseSchema(db)
	if err != nil {
		log.Fatal(err)
	}

	app.Run()
}
<|endoftext|>


<commit_before>package iniflag

import (
	"os"
	"regexp"

	"github.com/Thomasdezeeuw/ini"
)

// Variables in a ${NAME} form inside configuration file are
// expected to be treated as ENV vars.
var envVar = regexp.MustCompile(`\${([A-Za-z0-9._\-]+)}`)

// config represents an INI configuration file.
type config struct {
	body map[string]map[string]string
}

// newConfig allocates and returns a new config.
func newConfig() *config {
	return &config{
		body: map[string]map[string]string{},
	}
}

// parse opens and parses the requested configuration file.
// It may be called multiple times, the files will be joined.
func (c *config) parse(file string) error {
	// Trying to open the configuration file.
	f, err := os.Open(file)
	if err != nil {
		return err
	}

	// Parsing the openned file.
	m, err := ini.Parse(f)
	if err != nil {
		return err
	}

	// Join the newly parsed file with the previous one.
	c.join(m)
	return nil
}

// join gets a maps of maps, and joins it with c.body.
// The input map has a priority (it overrides values of c.body).
func (c *config) join(m map[string]map[string]string) {
	// Iterating over all available sections in the config.
	for section := range m {
		// Make sure such section exists in the source map.
		if _, ok := c.body[section]; !ok {
			c.body[section] = map[string]string{}
		}

		// Iterating over all available keys of the section.
		for key := range m[section] {
			c.body[section][key] = m[section][key]
		}
	}
}

// get receives a key name as input and returns associated
// value in the configuration file, if any.
// Otherwise, false is returned as the second argument.
func (c *config) get(section, key string) (string, bool) {
	// Make sure such section exists.
	if _, ok := c.body[section]; !ok {
		return "", false
	}

	// Make sure such key exist.
	v, ok := c.body[section][key]
	if !ok {
		return "", false
	}

	// Replace environment variables in the received value
	// and return it.
	v = envVar.ReplaceAllStringFunc(v, func(k string) string {
		return os.Getenv(envVar.ReplaceAllString(k, "$1"))
	})
	return v, true
}
<commit_msg>Reflect the latest changes in INI package<commit_after>package iniflag

import (
	"os"
	"regexp"

	"github.com/Thomasdezeeuw/ini"
)

// Variables in a ${NAME} form inside configuration file are
// expected to be treated as ENV vars.
var envVar = regexp.MustCompile(`\${([A-Za-z0-9._\-]+)}`)

// config represents an INI configuration file.
type config struct {
	body map[string]map[string]string
}

// newConfig allocates and returns a new config.
func newConfig() *config {
	return &config{
		body: map[string]map[string]string{},
	}
}

// parse opens and parses the requested configuration file.
// It may be called multiple times, the files will be joined.
func (c *config) parse(file string) error {
	// Trying to open the configuration file.
	f, err := os.Open(file)
	if err != nil {
		return err
	}

	// Parsing the openned file.
	m, err := ini.Parse(f)
	if err != nil {
		return err
	}

	// Join the newly parsed file with the previous one.
	c.join(m)
	return nil
}

// join gets a maps of maps, and joins it with c.body.
// The input map has a priority (it overrides values of c.body).
func (c *config) join(m ini.Config) {
	// Iterating over all available sections in the config.
	for section := range m {
		// Make sure such section exists in the source map.
		if _, ok := c.body[section]; !ok {
			c.body[section] = map[string]string{}
		}

		// Iterating over all available keys of the section.
		for key := range m[section] {
			c.body[section][key] = m[section][key]
		}
	}
}

// get receives a key name as input and returns associated
// value in the configuration file, if any.
// Otherwise, false is returned as the second argument.
func (c *config) get(section, key string) (string, bool) {
	// Make sure such section exists.
	if _, ok := c.body[section]; !ok {
		return "", false
	}

	// Make sure such key exist.
	v, ok := c.body[section][key]
	if !ok {
		return "", false
	}

	// Replace environment variables in the received value
	// and return it.
	v = envVar.ReplaceAllStringFunc(v, func(k string) string {
		return os.Getenv(envVar.ReplaceAllString(k, "$1"))
	})
	return v, true
}
<|endoftext|>


<commit_before>// Copyright 2009 Thomas Jager <mail@jager.no>  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
This package provides an event based IRC client library. It allows to
register callbacks for the events you need to handle. Its features
include handling standard CTCP, reconnecting on errors and detecting
stones servers.
Details of the IRC protocol can be found in the following RFCs:
https://tools.ietf.org/html/rfc1459
https://tools.ietf.org/html/rfc2810
https://tools.ietf.org/html/rfc2811
https://tools.ietf.org/html/rfc2812
https://tools.ietf.org/html/rfc2813
The details of the client-to-client protocol (CTCP) can be found here: http://www.irchelp.org/irchelp/rfc/ctcpspec.html
*/

package irc

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	VERSION = "go-ircevent v2.1"
)

var ErrDisconnected = errors.New("Disconnect Called")

// Read data from a connection. To be used as a goroutine.
func (irc *Connection) readLoop() {
	defer irc.Done()
	br := bufio.NewReaderSize(irc.socket, 512)

	errChan := irc.ErrorChan()

	for {
		select {
		case <-irc.end:
			return
		default:
			// Set a read deadline based on the combined timeout and ping frequency
			// We should ALWAYS have received a response from the server within the timeout
			// after our own pings
			if irc.socket != nil {
				irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq))
			}

			msg, err := br.ReadString('\n')

			// We got past our blocking read, so bin timeout
			if irc.socket != nil {
				var zero time.Time
				irc.socket.SetReadDeadline(zero)
			}

			if err != nil {
				errChan <- err
				break
			}

			if irc.Debug {
				irc.Log.Printf("<-- %s\n", strings.TrimSpace(msg))
			}

			irc.lastMessage = time.Now()
			msg = msg[:len(msg)-2] //Remove \r\n
			event := &Event{Raw: msg}
			if msg[0] == ':' {
				if i := strings.Index(msg, " "); i > -1 {
					event.Source = msg[1:i]
					msg = msg[i+1 : len(msg)]

				} else {
					irc.Log.Printf("Misformed msg from server: %#s\n", msg)
				}

				if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 {
					event.Nick = event.Source[0:i]
					event.User = event.Source[i+1 : j]
					event.Host = event.Source[j+1 : len(event.Source)]
				}
			}

			split := strings.SplitN(msg, " :", 2)
			args := strings.Split(split[0], " ")
			event.Code = strings.ToUpper(args[0])
			event.Arguments = args[1:]
			if len(split) > 1 {
				event.Arguments = append(event.Arguments, split[1])
			}

			/* XXX: len(args) == 0: args should be empty */
			irc.RunCallbacks(event)
		}
	}
	return
}

// Loop to write to a connection. To be used as a goroutine.
func (irc *Connection) writeLoop() {
	defer irc.Done()
	errChan := irc.ErrorChan()
	for {
		select {
		case <-irc.end:
			return
		default:
			b, ok := <-irc.pwrite
			if !ok || b == "" || irc.socket == nil {
				return
			}

			if irc.Debug {
				irc.Log.Printf("--> %s\n", strings.TrimSpace(b))
			}

			// Set a write deadline based on the time out
			irc.socket.SetWriteDeadline(time.Now().Add(irc.Timeout))

			_, err := irc.socket.Write([]byte(b))

			// Past blocking write, bin timeout
			var zero time.Time
			irc.socket.SetWriteDeadline(zero)

			if err != nil {
				errChan <- err
				return
			}
		}
	}
	return
}

// Pings the server if we have not received any messages for 5 minutes
// to keep the connection alive. To be used as a goroutine.
func (irc *Connection) pingLoop() {
	defer irc.Done()
	ticker := time.NewTicker(1 * time.Minute) // Tick every minute for monitoring
	ticker2 := time.NewTicker(irc.PingFreq)   // Tick at the ping frequency.
	for {
		select {
		case <-ticker.C:
			//Ping if we haven't received anything from the server within the keep alive period
			if time.Since(irc.lastMessage) >= irc.KeepAlive {
				irc.SendRawf("PING %d", time.Now().UnixNano())
			}
		case <-ticker2.C:
			//Ping at the ping frequency
			irc.SendRawf("PING %d", time.Now().UnixNano())
			//Try to recapture nickname if it's not as configured.
			if irc.nick != irc.nickcurrent {
				irc.nickcurrent = irc.nick
				irc.SendRawf("NICK %s", irc.nick)
			}
		case <-irc.end:
			ticker.Stop()
			ticker2.Stop()
			return
		}
	}
}

// Main loop to control the connection.
func (irc *Connection) Loop() {
	errChan := irc.ErrorChan()
	for !irc.stopped {
		err := <-errChan
		if irc.stopped {
			break
		}
		irc.Log.Printf("Error, disconnected: %s\n", err)
		for !irc.stopped {
			if err = irc.Reconnect(); err != nil {
				irc.Log.Printf("Error while reconnecting: %s\n", err)
				time.Sleep(1 * time.Second)
			} else {
				break
			}
		}
	}
}

// Quit the current connection and disconnect from the server
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.6
func (irc *Connection) Quit() {
	irc.SendRaw("QUIT")
	irc.stopped = true
}

// Use the connection to join a given channel.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.1
func (irc *Connection) Join(channel string) {
	irc.pwrite <- fmt.Sprintf("JOIN %s\r\n", channel)
}

// Leave a given channel.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.2
func (irc *Connection) Part(channel string) {
	irc.pwrite <- fmt.Sprintf("PART %s\r\n", channel)
}

// Send a notification to a nickname. This is similar to Privmsg but must not receive replies.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2
func (irc *Connection) Notice(target, message string) {
	irc.pwrite <- fmt.Sprintf("NOTICE %s :%s\r\n", target, message)
}

// Send a formated notification to a nickname.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2
func (irc *Connection) Noticef(target, format string, a ...interface{}) {
	irc.Notice(target, fmt.Sprintf(format, a...))
}

// Send (action) message to a target (channel or nickname).
// No clear RFC on this one...
func (irc *Connection) Action(target, message string) {
	irc.pwrite <- fmt.Sprintf("PRIVMSG %s :\001ACTION %s\001\r\n", target, message)
}

// Send formatted (action) message to a target (channel or nickname).
func (irc *Connection) Actionf(target, format string, a ...interface{}) {
	irc.Action(target, fmt.Sprintf(format, a...))
}

// Send (private) message to a target (channel or nickname).
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.1
func (irc *Connection) Privmsg(target, message string) {
	irc.pwrite <- fmt.Sprintf("PRIVMSG %s :%s\r\n", target, message)
}

// Send formated string to specified target (channel or nickname).
func (irc *Connection) Privmsgf(target, format string, a ...interface{}) {
	irc.Privmsg(target, fmt.Sprintf(format, a...))
}

// Send raw string.
func (irc *Connection) SendRaw(message string) {
	irc.pwrite <- message + "\r\n"
}

// Send raw formated string.
func (irc *Connection) SendRawf(format string, a ...interface{}) {
	irc.SendRaw(fmt.Sprintf(format, a...))
}

// Set (new) nickname.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.2
func (irc *Connection) Nick(n string) {
	irc.nick = n
	irc.SendRawf("NICK %s", n)
}

// Determine nick currently used with the connection.
func (irc *Connection) GetNick() string {
	return irc.nickcurrent
}

// Query information about a particular nickname.
// RFC 1459: https://tools.ietf.org/html/rfc1459#section-4.5.2
func (irc *Connection) Whois(nick string) {
	irc.SendRawf("WHOIS %s", nick)
}

// Query information about a given nickname in the server.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.5.1
func (irc *Connection) Who(nick string) {
	irc.SendRawf("WHO %s", nick)
}

// Set different modes for a target (channel or nickname).
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.3
func (irc *Connection) Mode(target string, modestring ...string) {
	if len(modestring) > 0 {
		mode := strings.Join(modestring, " ")
		irc.SendRawf("MODE %s %s", target, mode)
		return
	}
	irc.SendRawf("MODE %s", target)
}

func (irc *Connection) ErrorChan() chan error {
	return irc.Error
}

// A disconnect sends all buffered messages (if possible),
// stops all goroutines and then closes the socket.
func (irc *Connection) Disconnect() {
	for event := range irc.events {
		irc.ClearCallback(event)
	}

	close(irc.end)
	close(irc.pwrite)

	irc.Wait()
	irc.socket.Close()
	irc.socket = nil
	irc.ErrorChan() <- ErrDisconnected
}

// Reconnect to a server using the current connection.
func (irc *Connection) Reconnect() error {
	irc.end = make(chan struct{})
	return irc.Connect(irc.server)
}

// Connect to a given server using the current connection configuration.
// This function also takes care of identification if a password is provided.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1
func (irc *Connection) Connect(server string) error {
	irc.server = server
	irc.stopped = false

	// make sure everything is ready for connection
	if len(irc.server) == 0 {
		return errors.New("empty 'server'")
	}
	if strings.Count(irc.server, ":") != 1 {
		return errors.New("wrong number of ':' in address")
	}
	if strings.Index(irc.server, ":") == 0 {
		return errors.New("hostname is missing")
	}
	if strings.Index(irc.server, ":") == len(irc.server)-1 {
		return errors.New("port missing")
	}
	// check for valid range
	ports := strings.Split(irc.server, ":")[1]
	port, err := strconv.Atoi(ports)
	if err != nil {
		return errors.New("extracting port failed")
	}
	if !((port >= 0) && (port <= 65535)) {
		return errors.New("port number outside valid range")
	}
	if irc.Log == nil {
		return errors.New("'Log' points to nil")
	}
	if len(irc.nick) == 0 {
		return errors.New("empty 'nick'")
	}
	if len(irc.user) == 0 {
		return errors.New("empty 'user'")
	}

	if irc.UseTLS {
		dialer := &net.Dialer{Timeout: irc.Timeout}
		irc.socket, err = tls.DialWithDialer(dialer, "tcp", irc.server, irc.TLSConfig)
	} else {
		irc.socket, err = net.DialTimeout("tcp", irc.server, irc.Timeout)
	}
	if err != nil {
		return err
	}
	irc.Log.Printf("Connected to %s (%s)\n", irc.server, irc.socket.RemoteAddr())

	irc.pwrite = make(chan string, 10)
	irc.Error = make(chan error, 2)
	irc.Add(3)
	go irc.readLoop()
	go irc.writeLoop()
	go irc.pingLoop()
	if len(irc.Password) > 0 {
		irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password)
	}
	irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick)
	irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user)
	return nil
}

// Create a connection with the (publicly visible) nickname and username.
// The nickname is later used to address the user. Returns nil if nick
// or user are empty.
func IRC(nick, user string) *Connection {
	// catch invalid values
	if len(nick) == 0 {
		return nil
	}
	if len(user) == 0 {
		return nil
	}

	irc := &Connection{
		nick:      nick,
		user:      user,
		Log:       log.New(os.Stdout, "", log.LstdFlags),
		end:       make(chan struct{}),
		Version:   VERSION,
		KeepAlive: 4 * time.Minute,
		Timeout:   1 * time.Minute,
		PingFreq:  15 * time.Minute,
	}
	irc.setupCallbacks()
	return irc
}
<commit_msg>Added Connected method<commit_after>// Copyright 2009 Thomas Jager <mail@jager.no>  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
This package provides an event based IRC client library. It allows to
register callbacks for the events you need to handle. Its features
include handling standard CTCP, reconnecting on errors and detecting
stones servers.
Details of the IRC protocol can be found in the following RFCs:
https://tools.ietf.org/html/rfc1459
https://tools.ietf.org/html/rfc2810
https://tools.ietf.org/html/rfc2811
https://tools.ietf.org/html/rfc2812
https://tools.ietf.org/html/rfc2813
The details of the client-to-client protocol (CTCP) can be found here: http://www.irchelp.org/irchelp/rfc/ctcpspec.html
*/

package irc

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	VERSION = "go-ircevent v2.1"
)

var ErrDisconnected = errors.New("Disconnect Called")

// Read data from a connection. To be used as a goroutine.
func (irc *Connection) readLoop() {
	defer irc.Done()
	br := bufio.NewReaderSize(irc.socket, 512)

	errChan := irc.ErrorChan()

	for {
		select {
		case <-irc.end:
			return
		default:
			// Set a read deadline based on the combined timeout and ping frequency
			// We should ALWAYS have received a response from the server within the timeout
			// after our own pings
			if irc.socket != nil {
				irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq))
			}

			msg, err := br.ReadString('\n')

			// We got past our blocking read, so bin timeout
			if irc.socket != nil {
				var zero time.Time
				irc.socket.SetReadDeadline(zero)
			}

			if err != nil {
				errChan <- err
				break
			}

			if irc.Debug {
				irc.Log.Printf("<-- %s\n", strings.TrimSpace(msg))
			}

			irc.lastMessage = time.Now()
			msg = msg[:len(msg)-2] //Remove \r\n
			event := &Event{Raw: msg}
			if msg[0] == ':' {
				if i := strings.Index(msg, " "); i > -1 {
					event.Source = msg[1:i]
					msg = msg[i+1 : len(msg)]

				} else {
					irc.Log.Printf("Misformed msg from server: %#s\n", msg)
				}

				if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 {
					event.Nick = event.Source[0:i]
					event.User = event.Source[i+1 : j]
					event.Host = event.Source[j+1 : len(event.Source)]
				}
			}

			split := strings.SplitN(msg, " :", 2)
			args := strings.Split(split[0], " ")
			event.Code = strings.ToUpper(args[0])
			event.Arguments = args[1:]
			if len(split) > 1 {
				event.Arguments = append(event.Arguments, split[1])
			}

			/* XXX: len(args) == 0: args should be empty */
			irc.RunCallbacks(event)
		}
	}
	return
}

// Loop to write to a connection. To be used as a goroutine.
func (irc *Connection) writeLoop() {
	defer irc.Done()
	errChan := irc.ErrorChan()
	for {
		select {
		case <-irc.end:
			return
		default:
			b, ok := <-irc.pwrite
			if !ok || b == "" || irc.socket == nil {
				return
			}

			if irc.Debug {
				irc.Log.Printf("--> %s\n", strings.TrimSpace(b))
			}

			// Set a write deadline based on the time out
			irc.socket.SetWriteDeadline(time.Now().Add(irc.Timeout))

			_, err := irc.socket.Write([]byte(b))

			// Past blocking write, bin timeout
			var zero time.Time
			irc.socket.SetWriteDeadline(zero)

			if err != nil {
				errChan <- err
				return
			}
		}
	}
	return
}

// Pings the server if we have not received any messages for 5 minutes
// to keep the connection alive. To be used as a goroutine.
func (irc *Connection) pingLoop() {
	defer irc.Done()
	ticker := time.NewTicker(1 * time.Minute) // Tick every minute for monitoring
	ticker2 := time.NewTicker(irc.PingFreq)   // Tick at the ping frequency.
	for {
		select {
		case <-ticker.C:
			//Ping if we haven't received anything from the server within the keep alive period
			if time.Since(irc.lastMessage) >= irc.KeepAlive {
				irc.SendRawf("PING %d", time.Now().UnixNano())
			}
		case <-ticker2.C:
			//Ping at the ping frequency
			irc.SendRawf("PING %d", time.Now().UnixNano())
			//Try to recapture nickname if it's not as configured.
			if irc.nick != irc.nickcurrent {
				irc.nickcurrent = irc.nick
				irc.SendRawf("NICK %s", irc.nick)
			}
		case <-irc.end:
			ticker.Stop()
			ticker2.Stop()
			return
		}
	}
}

// Main loop to control the connection.
func (irc *Connection) Loop() {
	errChan := irc.ErrorChan()
	for !irc.stopped {
		err := <-errChan
		if irc.stopped {
			break
		}
		irc.Log.Printf("Error, disconnected: %s\n", err)
		for !irc.stopped {
			if err = irc.Reconnect(); err != nil {
				irc.Log.Printf("Error while reconnecting: %s\n", err)
				time.Sleep(1 * time.Second)
			} else {
				break
			}
		}
	}
}

// Quit the current connection and disconnect from the server
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.6
func (irc *Connection) Quit() {
	irc.SendRaw("QUIT")
	irc.stopped = true
}

// Use the connection to join a given channel.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.1
func (irc *Connection) Join(channel string) {
	irc.pwrite <- fmt.Sprintf("JOIN %s\r\n", channel)
}

// Leave a given channel.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.2
func (irc *Connection) Part(channel string) {
	irc.pwrite <- fmt.Sprintf("PART %s\r\n", channel)
}

// Send a notification to a nickname. This is similar to Privmsg but must not receive replies.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2
func (irc *Connection) Notice(target, message string) {
	irc.pwrite <- fmt.Sprintf("NOTICE %s :%s\r\n", target, message)
}

// Send a formated notification to a nickname.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2
func (irc *Connection) Noticef(target, format string, a ...interface{}) {
	irc.Notice(target, fmt.Sprintf(format, a...))
}

// Send (action) message to a target (channel or nickname).
// No clear RFC on this one...
func (irc *Connection) Action(target, message string) {
	irc.pwrite <- fmt.Sprintf("PRIVMSG %s :\001ACTION %s\001\r\n", target, message)
}

// Send formatted (action) message to a target (channel or nickname).
func (irc *Connection) Actionf(target, format string, a ...interface{}) {
	irc.Action(target, fmt.Sprintf(format, a...))
}

// Send (private) message to a target (channel or nickname).
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.1
func (irc *Connection) Privmsg(target, message string) {
	irc.pwrite <- fmt.Sprintf("PRIVMSG %s :%s\r\n", target, message)
}

// Send formated string to specified target (channel or nickname).
func (irc *Connection) Privmsgf(target, format string, a ...interface{}) {
	irc.Privmsg(target, fmt.Sprintf(format, a...))
}

// Send raw string.
func (irc *Connection) SendRaw(message string) {
	irc.pwrite <- message + "\r\n"
}

// Send raw formated string.
func (irc *Connection) SendRawf(format string, a ...interface{}) {
	irc.SendRaw(fmt.Sprintf(format, a...))
}

// Set (new) nickname.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.2
func (irc *Connection) Nick(n string) {
	irc.nick = n
	irc.SendRawf("NICK %s", n)
}

// Determine nick currently used with the connection.
func (irc *Connection) GetNick() string {
	return irc.nickcurrent
}

// Query information about a particular nickname.
// RFC 1459: https://tools.ietf.org/html/rfc1459#section-4.5.2
func (irc *Connection) Whois(nick string) {
	irc.SendRawf("WHOIS %s", nick)
}

// Query information about a given nickname in the server.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.5.1
func (irc *Connection) Who(nick string) {
	irc.SendRawf("WHO %s", nick)
}

// Set different modes for a target (channel or nickname).
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.3
func (irc *Connection) Mode(target string, modestring ...string) {
	if len(modestring) > 0 {
		mode := strings.Join(modestring, " ")
		irc.SendRawf("MODE %s %s", target, mode)
		return
	}
	irc.SendRawf("MODE %s", target)
}

func (irc *Connection) ErrorChan() chan error {
	return irc.Error
}

// Returns true if the connection is connected to an IRC server.
func (irc *Connection) Connected() bool {
	return !irc.stopped
}

// A disconnect sends all buffered messages (if possible),
// stops all goroutines and then closes the socket.
func (irc *Connection) Disconnect() {
	for event := range irc.events {
		irc.ClearCallback(event)
	}

	close(irc.end)
	close(irc.pwrite)

	irc.Wait()
	irc.socket.Close()
	irc.socket = nil
	irc.ErrorChan() <- ErrDisconnected
}

// Reconnect to a server using the current connection.
func (irc *Connection) Reconnect() error {
	irc.end = make(chan struct{})
	return irc.Connect(irc.server)
}

// Connect to a given server using the current connection configuration.
// This function also takes care of identification if a password is provided.
// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1
func (irc *Connection) Connect(server string) error {
	irc.server = server
	irc.stopped = false

	// make sure everything is ready for connection
	if len(irc.server) == 0 {
		return errors.New("empty 'server'")
	}
	if strings.Count(irc.server, ":") != 1 {
		return errors.New("wrong number of ':' in address")
	}
	if strings.Index(irc.server, ":") == 0 {
		return errors.New("hostname is missing")
	}
	if strings.Index(irc.server, ":") == len(irc.server)-1 {
		return errors.New("port missing")
	}
	// check for valid range
	ports := strings.Split(irc.server, ":")[1]
	port, err := strconv.Atoi(ports)
	if err != nil {
		return errors.New("extracting port failed")
	}
	if !((port >= 0) && (port <= 65535)) {
		return errors.New("port number outside valid range")
	}
	if irc.Log == nil {
		return errors.New("'Log' points to nil")
	}
	if len(irc.nick) == 0 {
		return errors.New("empty 'nick'")
	}
	if len(irc.user) == 0 {
		return errors.New("empty 'user'")
	}

	if irc.UseTLS {
		dialer := &net.Dialer{Timeout: irc.Timeout}
		irc.socket, err = tls.DialWithDialer(dialer, "tcp", irc.server, irc.TLSConfig)
	} else {
		irc.socket, err = net.DialTimeout("tcp", irc.server, irc.Timeout)
	}
	if err != nil {
		return err
	}
	irc.Log.Printf("Connected to %s (%s)\n", irc.server, irc.socket.RemoteAddr())

	irc.pwrite = make(chan string, 10)
	irc.Error = make(chan error, 2)
	irc.Add(3)
	go irc.readLoop()
	go irc.writeLoop()
	go irc.pingLoop()
	if len(irc.Password) > 0 {
		irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password)
	}
	irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick)
	irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user)
	return nil
}

// Create a connection with the (publicly visible) nickname and username.
// The nickname is later used to address the user. Returns nil if nick
// or user are empty.
func IRC(nick, user string) *Connection {
	// catch invalid values
	if len(nick) == 0 {
		return nil
	}
	if len(user) == 0 {
		return nil
	}

	irc := &Connection{
		nick:      nick,
		user:      user,
		Log:       log.New(os.Stdout, "", log.LstdFlags),
		end:       make(chan struct{}),
		Version:   VERSION,
		KeepAlive: 4 * time.Minute,
		Timeout:   1 * time.Minute,
		PingFreq:  15 * time.Minute,
	}
	irc.setupCallbacks()
	return irc
}
<|endoftext|>


<commit_before>package vars

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"strings"

	urlmatcher "github.com/azer/url-router"
	"github.com/jmartin82/mmock/definition"
)

type Request struct {
	Mock    *definition.Mock
	Request *definition.Request
}

func (rp Request) Fill(holders []string) map[string]string {

	vars := make(map[string]string)
	for _, tag := range holders {
		found := false
		s := ""
		if tag == "request.body" && rp.Request.Body != "" {
			s = rp.Request.Body
			found = true
		} else if tag == "request.scheme" {
			s, found = rp.Request.Scheme, true
		} else if tag == "request.port" {
			s, found = rp.Request.Port, true
		} else if tag == "request.url" {
			s, found = rp.getUrl()
		} else if tag == "request.authority" {
			s, found = rp.getAuthority()
		} else if tag == "request.hostname" {
			s, found = rp.Request.Host, true
		} else if tag == "request.path" {
			s, found = rp.Request.Path, true
		} else if tag == "request.fragment" {
			s, found = rp.Request.Fragment, true
		} else if strings.HasPrefix(tag, "request.body.") {
			s, found = rp.getBodyParam(tag[13:])
		} else if strings.HasPrefix(tag, "request.query.") {
			s, found = rp.getQueryStringParam(tag[14:])
		} else if strings.HasPrefix(tag, "request.path.") {
			s, found = rp.getPathParam(tag[13:])
		} else if strings.HasPrefix(tag, "request.cookie.") {
			s, found = rp.getCookieParam(tag[15:])
		}

		if found {
			vars[tag] = s
		}

	}
	return vars
}

func (rp Request) getAuthority() (string, bool) {
	if len(rp.Request.Port) == 0 || rp.Request.Port == "80" {
		return fmt.Sprintf("%s://%s", rp.Request.Scheme, rp.Request.Host), true
	}

	return fmt.Sprintf("%s://%s:%s", rp.Request.Scheme, rp.Request.Host, rp.Request.Port), true
}

func (rp Request) getUrl() (string, bool) {
	value, f := rp.getAuthority()

	if !f {
		return "", false
	}

	path := rp.Request.Path

	if len(path) != 0 {
		value += rp.Request.Path
	}

	queryStringParams := rp.Request.QueryStringParameters

	if len(queryStringParams) != 0 {
		queryVars := []string{}
		for key, values := range queryStringParams {
			for _, value := range values {
				queryVars = append(queryVars, fmt.Sprintf("%s=%s", key, value))
			}
		}
		value += "?" + strings.Join(queryVars, "&")
	}

	if len(rp.Request.Fragment) != 0 {
		value += "#" + rp.Request.Fragment
	}

	return value, true
}

func (rp Request) getPathParam(name string) (string, bool) {

	routes := urlmatcher.New(rp.Mock.Request.Path)
	mparm := routes.Match(rp.Request.Path)

	value, f := mparm.Params[name]
	if !f {
		return "", false
	}

	return value, true
}

func (rp Request) getQueryStringParam(name string) (string, bool) {

	if len(rp.Request.QueryStringParameters) == 0 {
		return "", false
	}
	value, f := rp.Request.QueryStringParameters[name]
	if !f {
		return "", false
	}

	return value[0], true
}

func (rp Request) getCookieParam(name string) (string, bool) {

	if len(rp.Request.Cookies) == 0 {
		return "", false
	}
	value, f := rp.Request.Cookies[name]
	if !f {
		return "", false
	}

	return value, true
}

func (rp Request) getBodyParam(name string) (string, bool) {

	contentType, found := rp.Request.Headers["Content-Type"]
	if !found {
		return "", false
	}

	if strings.HasPrefix(contentType[0], "application/x-www-form-urlencoded") {
		return rp.getUrlEncodedFormBodyParam(name)
	} else if strings.HasPrefix(contentType[0], "application/json") {
		return rp.getJsonBodyParam(name)
	}

	return "", false
}

func (rp Request) getUrlEncodedFormBodyParam(name string) (string, bool) {

	values, err := url.ParseQuery(rp.Request.Body)
	if err != nil {
		return "", false
	}

	value := values.Get(name)
	if value == "" {
		return "", false
	}

	return value, true
}

func (rp Request) getJsonBodyParam(name string) (string, bool) {

	hierarchy := strings.Split(name, ".")

	var payload interface{}
	if err := json.Unmarshal([]byte(rp.Request.Body), &payload); err != nil {
		return "", false
	}

	for _, value := range hierarchy {
		if mapper, ok := payload.(map[string]interface{}); ok {
			payload, ok = mapper[value]

			if !ok {
				return "", false
			}

			continue
		}

		if arrayMapper, ok := payload.([]interface{}); ok {
			index, err := strconv.Atoi(value)

			if err != nil || index >= len(arrayMapper) {
				return "", false
			}

			payload = arrayMapper[index]
			continue
		}
	}

	return rp.getJsonValue(payload)
}

func (rp Request) getJsonValue(object interface{}) (string, bool) {

	stringContent, ok := object.(string)

	if ok {
		return stringContent, true
	}

	genericContent, err := json.Marshal(object)

	if err == nil {
		return string(genericContent), true
	}

	return "", false
}
<commit_msg>make request url predictable<commit_after>package vars

import (
	"encoding/json"
	"fmt"
	"net/url"
	"sort"
	"strconv"
	"strings"

	urlmatcher "github.com/azer/url-router"
	"github.com/jmartin82/mmock/definition"
)

type Request struct {
	Mock    *definition.Mock
	Request *definition.Request
}

func (rp Request) Fill(holders []string) map[string]string {

	vars := make(map[string]string)
	for _, tag := range holders {
		found := false
		s := ""
		if tag == "request.body" && rp.Request.Body != "" {
			s = rp.Request.Body
			found = true
		} else if tag == "request.scheme" {
			s, found = rp.Request.Scheme, true
		} else if tag == "request.port" {
			s, found = rp.Request.Port, true
		} else if tag == "request.url" {
			s, found = rp.getUrl()
		} else if tag == "request.authority" {
			s, found = rp.getAuthority()
		} else if tag == "request.hostname" {
			s, found = rp.Request.Host, true
		} else if tag == "request.path" {
			s, found = rp.Request.Path, true
		} else if tag == "request.fragment" {
			s, found = rp.Request.Fragment, true
		} else if strings.HasPrefix(tag, "request.body.") {
			s, found = rp.getBodyParam(tag[13:])
		} else if strings.HasPrefix(tag, "request.query.") {
			s, found = rp.getQueryStringParam(tag[14:])
		} else if strings.HasPrefix(tag, "request.path.") {
			s, found = rp.getPathParam(tag[13:])
		} else if strings.HasPrefix(tag, "request.cookie.") {
			s, found = rp.getCookieParam(tag[15:])
		}

		if found {
			vars[tag] = s
		}

	}
	return vars
}

func (rp Request) getAuthority() (string, bool) {
	if len(rp.Request.Port) == 0 || rp.Request.Port == "80" {
		return fmt.Sprintf("%s://%s", rp.Request.Scheme, rp.Request.Host), true
	}

	return fmt.Sprintf("%s://%s:%s", rp.Request.Scheme, rp.Request.Host, rp.Request.Port), true
}

func (rp Request) getUrl() (string, bool) {
	value, f := rp.getAuthority()

	if !f {
		return "", false
	}

	path := rp.Request.Path

	if len(path) != 0 {
		value += rp.Request.Path
	}

	queryStringParams := rp.Request.QueryStringParameters

	if len(queryStringParams) != 0 {
		queryKeys := []string{}
		queryVars := []string{}

		//make predictable
		for key := range queryStringParams {
			queryKeys = append(queryKeys, key)
		}
		sort.Strings(queryKeys)

		for _, key := range queryKeys {
			for _, value := range queryStringParams[key] {
				queryVars = append(queryVars, fmt.Sprintf("%s=%s", key, value))
			}
		}
		value += "?" + strings.Join(queryVars, "&")
	}

	if len(rp.Request.Fragment) != 0 {
		value += "#" + rp.Request.Fragment
	}

	return value, true
}

func (rp Request) getPathParam(name string) (string, bool) {

	routes := urlmatcher.New(rp.Mock.Request.Path)
	mparm := routes.Match(rp.Request.Path)

	value, f := mparm.Params[name]
	if !f {
		return "", false
	}

	return value, true
}

func (rp Request) getQueryStringParam(name string) (string, bool) {

	if len(rp.Request.QueryStringParameters) == 0 {
		return "", false
	}
	value, f := rp.Request.QueryStringParameters[name]
	if !f {
		return "", false
	}

	return value[0], true
}

func (rp Request) getCookieParam(name string) (string, bool) {

	if len(rp.Request.Cookies) == 0 {
		return "", false
	}
	value, f := rp.Request.Cookies[name]
	if !f {
		return "", false
	}

	return value, true
}

func (rp Request) getBodyParam(name string) (string, bool) {

	contentType, found := rp.Request.Headers["Content-Type"]
	if !found {
		return "", false
	}

	if strings.HasPrefix(contentType[0], "application/x-www-form-urlencoded") {
		return rp.getUrlEncodedFormBodyParam(name)
	} else if strings.HasPrefix(contentType[0], "application/json") {
		return rp.getJsonBodyParam(name)
	}

	return "", false
}

func (rp Request) getUrlEncodedFormBodyParam(name string) (string, bool) {

	values, err := url.ParseQuery(rp.Request.Body)
	if err != nil {
		return "", false
	}

	value := values.Get(name)
	if value == "" {
		return "", false
	}

	return value, true
}

func (rp Request) getJsonBodyParam(name string) (string, bool) {

	hierarchy := strings.Split(name, ".")

	var payload interface{}
	if err := json.Unmarshal([]byte(rp.Request.Body), &payload); err != nil {
		return "", false
	}

	for _, value := range hierarchy {
		if mapper, ok := payload.(map[string]interface{}); ok {
			payload, ok = mapper[value]

			if !ok {
				return "", false
			}

			continue
		}

		if arrayMapper, ok := payload.([]interface{}); ok {
			index, err := strconv.Atoi(value)

			if err != nil || index >= len(arrayMapper) {
				return "", false
			}

			payload = arrayMapper[index]
			continue
		}
	}

	return rp.getJsonValue(payload)
}

func (rp Request) getJsonValue(object interface{}) (string, bool) {

	stringContent, ok := object.(string)

	if ok {
		return stringContent, true
	}

	genericContent, err := json.Marshal(object)

	if err == nil {
		return string(genericContent), true
	}

	return "", false
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten

import (
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten/internal/ui"
)

type runContext struct {
	isRunning       bool
	fps             float64
	newScreenWidth  int
	newScreenHeight int
	newScreenScale  int
	isRunningSlowly bool
	m               sync.RWMutex
}

var currentRunContext runContext

func (c *runContext) startRunning() {
	c.m.Lock()
	defer c.m.Unlock()
	c.isRunning = true
}

func (c *runContext) endRunning() {
	c.m.Lock()
	defer c.m.Unlock()
	c.isRunning = false
}

func (c *runContext) FPS() float64 {
	c.m.RLock()
	defer c.m.RUnlock()
	if !c.isRunning {
		// TODO: Should panic here?
		return 0
	}
	return c.fps
}

func (c *runContext) updateFPS(fps float64) {
	c.m.Lock()
	defer c.m.Unlock()
	c.fps = fps
}

func (c *runContext) IsRunningSlowly() bool {
	c.m.RLock()
	defer c.m.RUnlock()
	if !c.isRunning {
		// TODO: Should panic here?
		return false
	}
	return c.isRunningSlowly
}

func (c *runContext) setRunningSlowly(isRunningSlowly bool) {
	c.m.Lock()
	defer c.m.Unlock()
	c.isRunningSlowly = isRunningSlowly
}

func (c *runContext) updateScreenSize(g *graphicsContext) error {
	c.m.Lock()
	defer c.m.Unlock()
	if c.newScreenWidth == 0 && c.newScreenHeight == 0 && c.newScreenScale == 0 {
		return nil
	}
	changed := false
	if 0 < c.newScreenWidth || 0 < c.newScreenHeight {
		c := ui.CurrentUI().SetScreenSize(c.newScreenWidth, c.newScreenHeight)
		changed = changed || c
	}
	if 0 < c.newScreenScale {
		c := ui.CurrentUI().SetScreenScale(c.newScreenScale)
		changed = changed || c
	}
	if changed {
		w, h := c.newScreenWidth, c.newScreenHeight
		if err := g.setSize(w, h, ui.CurrentUI().ActualScreenScale()); err != nil {
			return err
		}
	}
	c.newScreenWidth = 0
	c.newScreenHeight = 0
	c.newScreenScale = 0
	return nil
}

func (c *runContext) SetScreenSize(width, height int) {
	c.m.Lock()
	defer c.m.Unlock()
	if !c.isRunning {
		panic("ebiten: SetScreenSize must be called during Run")
	}
	if width <= 0 || height <= 0 {
		panic("ebiten: width and height must be positive")
	}
	c.newScreenWidth = width
	c.newScreenHeight = height
}

func (c *runContext) SetScreenScale(scale int) {
	c.m.Lock()
	defer c.m.Unlock()
	if !c.isRunning {
		panic("ebiten: SetScreenScale must be called during Run")
	}
	if scale <= 0 {
		panic("ebiten: scale must be positive")
	}
	c.newScreenScale = scale
}

// FPS represents how many times game updating happens in a second.
const FPS = 60

// CurrentFPS returns the current number of frames per second of rendering.
//
// This function is concurrent-safe.
//
// This value represents how many times rendering happens in 1/60 second and
// NOT how many times logical game updating (a passed function to Run) happens.
// Note that logical game updating is assured to happen 60 times in a second
// as long as the screen is active.
func CurrentFPS() float64 {
	return currentRunContext.FPS()
}

// IsRunningSlowly returns true if the game is running too slowly to keep 60 FPS of rendering.
// The game screen is not updated when IsRunningSlowly is true.
// It is recommended to skip heavy processing, especially drawing, when IsRunningSlowly is true.
//
// This function is concurrent-safe.
func IsRunningSlowly() bool {
	return currentRunContext.IsRunningSlowly()
}

// Run runs the game.
// f is a function which is called at every frame.
// The argument (*Image) is the render target that represents the screen.
//
// This function must be called from the main thread.
// Note that ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
//
// The given function f is guaranteed to be called 60 times a second
// even if a rendering frame is skipped.
// f is not called when the screen is not shown.
func Run(f func(*Image) error, width, height, scale int, title string) error {
	currentRunContext.startRunning()
	defer currentRunContext.endRunning()

	if err := ui.CurrentUI().Start(width, height, scale, title); err != nil {
		return err
	}
	defer ui.CurrentUI().Terminate()

	glContext.Check()
	graphicsContext, err := newGraphicsContext(width, height, ui.CurrentUI().ActualScreenScale())
	if err != nil {
		return err
	}

	frames := 0
	n := ui.Now()
	beforeForUpdate := n
	beforeForFPS := n
	for {
		// TODO: setSize should be called after swapping buffers?
		if err := currentRunContext.updateScreenSize(graphicsContext); err != nil {
			return err
		}
		if err := ui.CurrentUI().DoEvents(); err != nil {
			return err
		}
		if ui.CurrentUI().IsClosed() {
			return nil
		}
		now := ui.Now()
		// If beforeForUpdate is too old, we assume that screen is not shown.
		if int64(5*time.Second/FPS) < now-beforeForUpdate {
			currentRunContext.setRunningSlowly(false)
			beforeForUpdate = now
		} else {
			t := now - beforeForUpdate
			currentRunContext.setRunningSlowly(t*FPS >= int64(time.Second*5/2))
			tt := int(t * FPS / int64(time.Second))
			for i := 0; i < tt; i++ {
				if err := ui.CurrentUI().DoEvents(); err != nil {
					return err
				}
				if ui.CurrentUI().IsClosed() {
					return nil
				}
				if err := graphicsContext.update(f); err != nil {
					return err
				}
			}
			ui.CurrentUI().SwapBuffers()
			beforeForUpdate += int64(tt) * int64(time.Second) / FPS
			frames++
		}

		// Calc the current FPS.
		if time.Second <= time.Duration(now-beforeForFPS) {
			currentRunContext.updateFPS(float64(frames) * float64(time.Second) / float64(now-beforeForFPS))
			beforeForFPS = now
			frames = 0
		}
	}
}

// SetScreenSize changes the (logical) size of the screen.
// This doesn't affect the current scale of the screen.
//
// This function is concurrent-safe.
func SetScreenSize(width, height int) {
	currentRunContext.SetScreenSize(width, height)
}

// SetScreenSize changes the scale of the screen.
//
// This function is concurrent-safe.
func SetScreenScale(scale int) {
	currentRunContext.SetScreenScale(scale)
}

// ScreenScale returns the current screen scale.
//
// This function is concurrent-safe.
func ScreenScale() int {
	return ui.CurrentUI().ScreenScale()
}
<commit_msg>ui: Make FPS more stable (#203)<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten

import (
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten/internal/ui"
)

type runContext struct {
	isRunning       bool
	fps             float64
	newScreenWidth  int
	newScreenHeight int
	newScreenScale  int
	isRunningSlowly bool
	m               sync.RWMutex
}

var currentRunContext runContext

func (c *runContext) startRunning() {
	c.m.Lock()
	defer c.m.Unlock()
	c.isRunning = true
}

func (c *runContext) endRunning() {
	c.m.Lock()
	defer c.m.Unlock()
	c.isRunning = false
}

func (c *runContext) FPS() float64 {
	c.m.RLock()
	defer c.m.RUnlock()
	if !c.isRunning {
		// TODO: Should panic here?
		return 0
	}
	return c.fps
}

func (c *runContext) updateFPS(fps float64) {
	c.m.Lock()
	defer c.m.Unlock()
	c.fps = fps
}

func (c *runContext) IsRunningSlowly() bool {
	c.m.RLock()
	defer c.m.RUnlock()
	if !c.isRunning {
		// TODO: Should panic here?
		return false
	}
	return c.isRunningSlowly
}

func (c *runContext) setRunningSlowly(isRunningSlowly bool) {
	c.m.Lock()
	defer c.m.Unlock()
	c.isRunningSlowly = isRunningSlowly
}

func (c *runContext) updateScreenSize(g *graphicsContext) error {
	c.m.Lock()
	defer c.m.Unlock()
	if c.newScreenWidth == 0 && c.newScreenHeight == 0 && c.newScreenScale == 0 {
		return nil
	}
	changed := false
	if 0 < c.newScreenWidth || 0 < c.newScreenHeight {
		c := ui.CurrentUI().SetScreenSize(c.newScreenWidth, c.newScreenHeight)
		changed = changed || c
	}
	if 0 < c.newScreenScale {
		c := ui.CurrentUI().SetScreenScale(c.newScreenScale)
		changed = changed || c
	}
	if changed {
		w, h := c.newScreenWidth, c.newScreenHeight
		if err := g.setSize(w, h, ui.CurrentUI().ActualScreenScale()); err != nil {
			return err
		}
	}
	c.newScreenWidth = 0
	c.newScreenHeight = 0
	c.newScreenScale = 0
	return nil
}

func (c *runContext) SetScreenSize(width, height int) {
	c.m.Lock()
	defer c.m.Unlock()
	if !c.isRunning {
		panic("ebiten: SetScreenSize must be called during Run")
	}
	if width <= 0 || height <= 0 {
		panic("ebiten: width and height must be positive")
	}
	c.newScreenWidth = width
	c.newScreenHeight = height
}

func (c *runContext) SetScreenScale(scale int) {
	c.m.Lock()
	defer c.m.Unlock()
	if !c.isRunning {
		panic("ebiten: SetScreenScale must be called during Run")
	}
	if scale <= 0 {
		panic("ebiten: scale must be positive")
	}
	c.newScreenScale = scale
}

// FPS represents how many times game updating happens in a second.
const FPS = 60

// CurrentFPS returns the current number of frames per second of rendering.
//
// This function is concurrent-safe.
//
// This value represents how many times rendering happens in 1/60 second and
// NOT how many times logical game updating (a passed function to Run) happens.
// Note that logical game updating is assured to happen 60 times in a second
// as long as the screen is active.
func CurrentFPS() float64 {
	return currentRunContext.FPS()
}

// IsRunningSlowly returns true if the game is running too slowly to keep 60 FPS of rendering.
// The game screen is not updated when IsRunningSlowly is true.
// It is recommended to skip heavy processing, especially drawing, when IsRunningSlowly is true.
//
// This function is concurrent-safe.
func IsRunningSlowly() bool {
	return currentRunContext.IsRunningSlowly()
}

// Run runs the game.
// f is a function which is called at every frame.
// The argument (*Image) is the render target that represents the screen.
//
// This function must be called from the main thread.
// Note that ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
//
// The given function f is guaranteed to be called 60 times a second
// even if a rendering frame is skipped.
// f is not called when the screen is not shown.
func Run(f func(*Image) error, width, height, scale int, title string) error {
	currentRunContext.startRunning()
	defer currentRunContext.endRunning()

	if err := ui.CurrentUI().Start(width, height, scale, title); err != nil {
		return err
	}
	defer ui.CurrentUI().Terminate()

	glContext.Check()
	graphicsContext, err := newGraphicsContext(width, height, ui.CurrentUI().ActualScreenScale())
	if err != nil {
		return err
	}

	frames := 0
	n := ui.Now()
	beforeForUpdate := n
	beforeForFPS := n
	for {
		// TODO: setSize should be called after swapping buffers?
		if err := currentRunContext.updateScreenSize(graphicsContext); err != nil {
			return err
		}
		if err := ui.CurrentUI().DoEvents(); err != nil {
			return err
		}
		if ui.CurrentUI().IsClosed() {
			return nil
		}
		now := ui.Now()
		// If beforeForUpdate is too old, we assume that screen is not shown.
		if int64(5*time.Second/FPS) < now-beforeForUpdate {
			currentRunContext.setRunningSlowly(false)
			beforeForUpdate = now
		} else {
			// Note that generally t is a little more than 1/60[sec].
			t := now - beforeForUpdate
			currentRunContext.setRunningSlowly(t*FPS >= int64(time.Second*5/2))
			tt := int(t * FPS / int64(time.Second))
			// As t is not accurate 1/60[sec], errors are accumulated
			// (generally, errors are positive values).
			// To make the FPS stable, set tt 1 if t is a little less than 1/60[sec].
			if tt == 0 && (int64(time.Second)/FPS-int64(10*time.Millisecond)) < t {
				tt = 1
			}
			for i := 0; i < tt; i++ {
				if err := ui.CurrentUI().DoEvents(); err != nil {
					return err
				}
				if ui.CurrentUI().IsClosed() {
					return nil
				}
				if err := graphicsContext.update(f); err != nil {
					return err
				}
			}
			ui.CurrentUI().SwapBuffers()
			d := int64(tt) * int64(time.Second) / FPS
			beforeForUpdate += d
			frames++
		}

		// Calc the current FPS.
		if time.Second <= time.Duration(now-beforeForFPS) {
			currentRunContext.updateFPS(float64(frames) * float64(time.Second) / float64(now-beforeForFPS))
			beforeForFPS = now
			frames = 0
		}
	}
}

// SetScreenSize changes the (logical) size of the screen.
// This doesn't affect the current scale of the screen.
//
// This function is concurrent-safe.
func SetScreenSize(width, height int) {
	currentRunContext.SetScreenSize(width, height)
}

// SetScreenSize changes the scale of the screen.
//
// This function is concurrent-safe.
func SetScreenScale(scale int) {
	currentRunContext.SetScreenScale(scale)
}

// ScreenScale returns the current screen scale.
//
// This function is concurrent-safe.
func ScreenScale() int {
	return ui.CurrentUI().ScreenScale()
}
<|endoftext|>


<commit_before>// +build darwin

package memory

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os/exec"
	"strconv"
	"strings"
)

// Get memory statistics
func Get() (*Memory, error) {
	cmd := exec.Command("vm_stat")
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	memory, err := collectMemoryStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	cmd = exec.Command("sysctl", "-n", "vm.swapusage")
	out, err = cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	swap, err := collectSwapStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	memory.SwapTotal = swap.total
	memory.SwapUsed = swap.used
	memory.SwapFree = swap.free
	return memory, nil
}

// Memory represents memory statistics for darwin
type Memory struct {
	Total, Used, Cached, Free, Active, Inactive, SwapTotal, SwapUsed, SwapFree uint64
}

// References:
//   - https://support.apple.com/en-us/HT201464#memory
//   - https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html
//   - https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/
func collectMemoryStats(out io.Reader) (*Memory, error) {
	scanner := bufio.NewScanner(out)
	if !scanner.Scan() { // skip the first line
		return nil, fmt.Errorf("failed to scan output of vm_stat")
	}

	var memory Memory
	var speculative, wired, purgeable, fileBacked, compressed uint64
	memStats := map[string]*uint64{
		"Pages free":                   &memory.Free,
		"Pages active":                 &memory.Active,
		"Pages inactive":               &memory.Inactive,
		"Pages speculative":            &speculative,
		"Pages wired down":             &wired,
		"Pages purgeable":              &purgeable,
		"File-backed pages":            &fileBacked,
		"Pages occupied by compressor": &compressed,
	}
	for scanner.Scan() {
		line := scanner.Text()
		i := strings.IndexRune(line, ':')
		if i < 0 {
			continue
		}
		if ptr := memStats[line[:i]]; ptr != nil {
			val := strings.TrimRight(strings.TrimSpace(line[i+1:]), ".")
			if v, err := strconv.ParseUint(val, 10, 64); err == nil {
				*ptr = v * 4096
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	memory.Cached = purgeable + fileBacked
	memory.Used = wired + compressed + memory.Active + memory.Inactive + speculative - memory.Cached
	memory.Total = memory.Used + memory.Cached + memory.Free
	return &memory, nil
}

type memorySwap struct {
	total, free, used uint64
}

func collectSwapStats(out io.Reader) (*memorySwap, error) {
	var total, used, free float64
	cnt, err := fmt.Fscanf(out, "total = %fM used = %fM free = %fM", &total, &used, &free)
	if err != nil {
		return nil, err
	}
	if cnt != 3 {
		return nil, errors.New("failed to parse output of 'sysctl -n vm.swapusage'")
	}
	return &memorySwap{
		total: uint64(total * 1024 * 1024),
		used:  uint64(used * 1024 * 1024),
		free:  uint64(free * 1024 * 1024),
	}, nil
}
<commit_msg>improve error handling for memory on darwin<commit_after>// +build darwin

package memory

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os/exec"
	"strconv"
	"strings"
)

// Get memory statistics
func Get() (*Memory, error) {
	cmd := exec.Command("vm_stat")
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	memory, err := collectMemoryStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	cmd = exec.Command("sysctl", "-n", "vm.swapusage")
	out, err = cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	swap, err := collectSwapStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	memory.SwapTotal = swap.total
	memory.SwapUsed = swap.used
	memory.SwapFree = swap.free
	return memory, nil
}

// Memory represents memory statistics for darwin
type Memory struct {
	Total, Used, Cached, Free, Active, Inactive, SwapTotal, SwapUsed, SwapFree uint64
}

// References:
//   - https://support.apple.com/en-us/HT201464#memory
//   - https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html
//   - https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/
func collectMemoryStats(out io.Reader) (*Memory, error) {
	scanner := bufio.NewScanner(out)
	if !scanner.Scan() { // skip the first line
		return nil, fmt.Errorf("failed to scan output of vm_stat")
	}

	var memory Memory
	var speculative, wired, purgeable, fileBacked, compressed uint64
	memStats := map[string]*uint64{
		"Pages free":                   &memory.Free,
		"Pages active":                 &memory.Active,
		"Pages inactive":               &memory.Inactive,
		"Pages speculative":            &speculative,
		"Pages wired down":             &wired,
		"Pages purgeable":              &purgeable,
		"File-backed pages":            &fileBacked,
		"Pages occupied by compressor": &compressed,
	}
	for scanner.Scan() {
		line := scanner.Text()
		i := strings.IndexRune(line, ':')
		if i < 0 {
			continue
		}
		if ptr := memStats[line[:i]]; ptr != nil {
			val := strings.TrimRight(strings.TrimSpace(line[i+1:]), ".")
			if v, err := strconv.ParseUint(val, 10, 64); err == nil {
				*ptr = v * 4096
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	memory.Cached = purgeable + fileBacked
	memory.Used = wired + compressed + memory.Active + memory.Inactive + speculative - memory.Cached
	memory.Total = memory.Used + memory.Cached + memory.Free
	return &memory, nil
}

type memorySwap struct {
	total, free, used uint64
}

func collectSwapStats(out io.Reader) (*memorySwap, error) {
	var total, used, free float64
	cnt, err := fmt.Fscanf(out, "total = %fM used = %fM free = %fM", &total, &used, &free)
	if err != nil {
		return nil, fmt.Errorf("failed to scan output of sysctl -n vmswapusage: %s", err)
	}
	if cnt != 3 {
		return nil, errors.New("failed to parse output of 'sysctl -n vm.swapusage'")
	}
	return &memorySwap{
		total: uint64(total * 1024 * 1024),
		used:  uint64(used * 1024 * 1024),
		free:  uint64(free * 1024 * 1024),
	}, nil
}
<|endoftext|>


<commit_before>// Go binding for nanomsg

package nanomsg

import (
	"testing"
)

func TestVersion(t *testing.T) {
	if Version.Current != 0 {
		t.Fatalf("Unexpected library version: %s", Version)
	}
}
<commit_msg>fix version test in nanomsg-0.3<commit_after>// Go binding for nanomsg

package nanomsg

import (
	"testing"
)

func TestVersion(t *testing.T) {
    // nanomsg-0.1 C:R:A(0:0:0)
    // nanomsg-0.2 C:R:A(0:1:0)
    // nanomsg-0.3 C:R:A(1:0:1)
    if Version.Current < 0 || Version.Revision < 0 || Version.Age < 0 {
        t.Fatalf("Unexpected library version: %s", Version)
    }
    // if change current, set revision=0
    if Version.Current > 0 {
        if Version.Revision != 0 {
            t.Fatalf("Unexpected library version: %s", Version)
        }
    }
}
<|endoftext|>