<commit_before>/*
Copyright 2013 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kontrol

import (
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/coreos/etcd/config"
	ehttp "github.com/coreos/etcd/http"
	"github.com/coreos/etcd/metrics"
	"github.com/coreos/etcd/server"
	"github.com/coreos/etcd/store"
	"github.com/coreos/etcd/third_party/github.com/coreos/raft"
)

// This function is copied and modified from github.com/coreos/etcd/main.go file.
func (k *Kontrol) runEtcd(ready chan bool) {
	// Load default configuration.
	var config = config.New()
	config.Load(nil)

	// Load config values from kontrol.
	config.Name = k.name       // name of the etcd instance
	config.DataDir = k.dataDir // directory to store etcd log
	config.Peers = k.peers     // comma seperated values of other peers

	// By default etcd uses ports 4001 and 7001.
	// In Kontrol these ports depend on the kontrol's port.
	// Etcd http server port will be: kontrolPort + 1
	// Etcd peer server port will be: kontrolPort + 3001
	advertiseIP := k.ip
	if advertiseIP == "0.0.0.0" {
		advertiseIP = "127.0.0.1"
	}
	config.BindAddr = k.ip + ":" + strconv.Itoa(k.port+1)
	config.Addr = "http://" + advertiseIP + ":" + strconv.Itoa(k.port+1)
	config.Peer.BindAddr = k.ip + ":" + strconv.Itoa(k.port+3001)
	config.Peer.Addr = "http://" + advertiseIP + ":" + strconv.Itoa(k.port+3001)

	if config.DataDir == "" {
		log.Fatal("The data dir was not set and could not be guessed from machine name")
	}

	// Create data directory if it doesn't already exist.
	if err := os.MkdirAll(config.DataDir, 0744); err != nil {
		log.Fatal("Unable to create path: %s", err)
	}

	// Warn people if they have an info file
	info := filepath.Join(config.DataDir, "info")
	if _, err := os.Stat(info); err == nil {
		log.Warning("All cached configuration is now ignored. The file %s can be removed.", info)
	}

	var mbName string

	mb := metrics.NewBucket(mbName)

	// Retrieve CORS configuration
	corsInfo, err := ehttp.NewCORSInfo(config.CorsOrigins)
	if err != nil {
		log.Fatal("CORS:", err)
	}

	// Create etcd key-value store and registry.
	k.store = store.New()
	registry := server.NewRegistry(k.store)

	// Create stats objects
	followersStats := server.NewRaftFollowersStats(config.Name)
	serverStats := server.NewRaftServerStats(config.Name)

	// Calculate all of our timeouts
	heartbeatTimeout := time.Duration(config.Peer.HeartbeatTimeout) * time.Millisecond
	electionTimeout := time.Duration(config.Peer.ElectionTimeout) * time.Millisecond
	dialTimeout := (3 * heartbeatTimeout) + electionTimeout
	responseHeaderTimeout := (3 * heartbeatTimeout) + electionTimeout

	// Create peer server
	psConfig := server.PeerServerConfig{
		Name:           config.Name,
		Scheme:         config.PeerTLSInfo().Scheme(),
		URL:            config.Peer.Addr,
		SnapshotCount:  config.SnapshotCount,
		MaxClusterSize: config.MaxClusterSize,
		RetryTimes:     config.MaxRetryAttempts,
		RetryInterval:  config.RetryInterval,
	}
	ps := server.NewPeerServer(psConfig, registry, k.store, &mb, followersStats, serverStats)

	var psListener net.Listener = k.psListener
	if psConfig.Scheme == "https" {
		peerServerTLSConfig, err := config.PeerTLSInfo().ServerConfig()
		if err != nil {
			log.Fatal("peer server TLS error: ", err)
		}

		psListener, err = server.NewTLSListener(config.Peer.BindAddr, peerServerTLSConfig)
		if err != nil {
			log.Fatal("Failed to create peer listener: ", err)
		}
	} else {
		psListener, err = server.NewListener(config.Peer.BindAddr)
		if err != nil {
			log.Fatal("Failed to create peer listener: ", err)
		}
	}

	// Create raft transporter and server
	raftTransporter := server.NewTransporter(followersStats, serverStats, registry, heartbeatTimeout, dialTimeout, responseHeaderTimeout)
	if psConfig.Scheme == "https" {
		raftClientTLSConfig, err := config.PeerTLSInfo().ClientConfig()
		if err != nil {
			log.Fatal("raft client TLS error: ", err)
		}
		raftTransporter.SetTLSConfig(*raftClientTLSConfig)
	}
	raftServer, err := raft.NewServer(config.Name, config.DataDir, raftTransporter, k.store, ps, "")
	if err != nil {
		log.Fatal(err.Error())
	}
	raftServer.SetElectionTimeout(electionTimeout)
	raftServer.SetHeartbeatInterval(heartbeatTimeout)
	ps.SetRaftServer(raftServer)

	// Create etcd server
	s := server.New(config.Name, config.Addr, ps, registry, k.store, &mb)

	var sListener net.Listener = k.sListener
	if config.EtcdTLSInfo().Scheme() == "https" {
		etcdServerTLSConfig, err := config.EtcdTLSInfo().ServerConfig()
		if err != nil {
			log.Fatal("etcd TLS error: ", err)
		}

		sListener, err = server.NewTLSListener(config.BindAddr, etcdServerTLSConfig)
		if err != nil {
			log.Fatal("Failed to create TLS etcd listener: ", err)
		}
	} else {
		sListener, err = server.NewListener(config.BindAddr)
		if err != nil {
			log.Fatal("Failed to create etcd listener: ", err)
		}
	}

	ps.SetServer(s)
	ps.Start(config.Snapshot, config.Peers)

	go func() {
		log.Info("peer server [name %s, listen on %s, advertised url %s]", ps.Config.Name, psListener.Addr(), ps.Config.URL)
		sHTTP := &ehttp.CORSHandler{ps.HTTPHandler(), corsInfo}
		if err := http.Serve(psListener, sHTTP); err != nil {
			log.Fatal(err.Error())
		}
	}()

	log.Info("etcd server [name %s, listen on %s, advertised url %s]", s.Name, sListener.Addr(), s.URL())
	sHTTP := &ehttp.CORSHandler{s.HTTPHandler(), corsInfo}
	go func() {
		if err := http.Serve(sListener, sHTTP); err != nil {
			log.Fatal(err.Error())
		}
	}()

	close(ready)
}
<commit_msg>fix warning message about data dir<commit_after>/*
Copyright 2013 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kontrol

import (
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/coreos/etcd/config"
	ehttp "github.com/coreos/etcd/http"
	"github.com/coreos/etcd/metrics"
	"github.com/coreos/etcd/server"
	"github.com/coreos/etcd/store"
	"github.com/coreos/etcd/third_party/github.com/coreos/raft"
)

// This function is copied and modified from github.com/coreos/etcd/main.go file.
func (k *Kontrol) runEtcd(ready chan bool) {
	// Load config values from kontrol.
	var config = config.New()
	config.Name = k.name       // name of the etcd instance
	config.DataDir = k.dataDir // directory to store etcd log
	config.Peers = k.peers     // comma seperated values of other peers

	// Load other defaults.
	config.Load(nil)

	// By default etcd uses ports 4001 and 7001.
	// In Kontrol these ports depend on the kontrol's port.
	// Etcd http server port will be: kontrolPort + 1
	// Etcd peer server port will be: kontrolPort + 3001
	advertiseIP := k.ip
	if advertiseIP == "0.0.0.0" {
		advertiseIP = "127.0.0.1"
	}
	config.BindAddr = k.ip + ":" + strconv.Itoa(k.port+1)
	config.Addr = "http://" + advertiseIP + ":" + strconv.Itoa(k.port+1)
	config.Peer.BindAddr = k.ip + ":" + strconv.Itoa(k.port+3001)
	config.Peer.Addr = "http://" + advertiseIP + ":" + strconv.Itoa(k.port+3001)

	if config.DataDir == "" {
		log.Fatal("The data dir was not set and could not be guessed from machine name")
	}

	// Create data directory if it doesn't already exist.
	if err := os.MkdirAll(config.DataDir, 0744); err != nil {
		log.Fatal("Unable to create path: %s", err)
	}

	// Warn people if they have an info file
	info := filepath.Join(config.DataDir, "info")
	if _, err := os.Stat(info); err == nil {
		log.Warning("All cached configuration is now ignored. The file %s can be removed.", info)
	}

	var mbName string

	mb := metrics.NewBucket(mbName)

	// Retrieve CORS configuration
	corsInfo, err := ehttp.NewCORSInfo(config.CorsOrigins)
	if err != nil {
		log.Fatal("CORS:", err)
	}

	// Create etcd key-value store and registry.
	k.store = store.New()
	registry := server.NewRegistry(k.store)

	// Create stats objects
	followersStats := server.NewRaftFollowersStats(config.Name)
	serverStats := server.NewRaftServerStats(config.Name)

	// Calculate all of our timeouts
	heartbeatTimeout := time.Duration(config.Peer.HeartbeatTimeout) * time.Millisecond
	electionTimeout := time.Duration(config.Peer.ElectionTimeout) * time.Millisecond
	dialTimeout := (3 * heartbeatTimeout) + electionTimeout
	responseHeaderTimeout := (3 * heartbeatTimeout) + electionTimeout

	// Create peer server
	psConfig := server.PeerServerConfig{
		Name:           config.Name,
		Scheme:         config.PeerTLSInfo().Scheme(),
		URL:            config.Peer.Addr,
		SnapshotCount:  config.SnapshotCount,
		MaxClusterSize: config.MaxClusterSize,
		RetryTimes:     config.MaxRetryAttempts,
		RetryInterval:  config.RetryInterval,
	}
	ps := server.NewPeerServer(psConfig, registry, k.store, &mb, followersStats, serverStats)

	var psListener net.Listener = k.psListener
	if psConfig.Scheme == "https" {
		peerServerTLSConfig, err := config.PeerTLSInfo().ServerConfig()
		if err != nil {
			log.Fatal("peer server TLS error: ", err)
		}

		psListener, err = server.NewTLSListener(config.Peer.BindAddr, peerServerTLSConfig)
		if err != nil {
			log.Fatal("Failed to create peer listener: ", err)
		}
	} else {
		psListener, err = server.NewListener(config.Peer.BindAddr)
		if err != nil {
			log.Fatal("Failed to create peer listener: ", err)
		}
	}

	// Create raft transporter and server
	raftTransporter := server.NewTransporter(followersStats, serverStats, registry, heartbeatTimeout, dialTimeout, responseHeaderTimeout)
	if psConfig.Scheme == "https" {
		raftClientTLSConfig, err := config.PeerTLSInfo().ClientConfig()
		if err != nil {
			log.Fatal("raft client TLS error: ", err)
		}
		raftTransporter.SetTLSConfig(*raftClientTLSConfig)
	}
	raftServer, err := raft.NewServer(config.Name, config.DataDir, raftTransporter, k.store, ps, "")
	if err != nil {
		log.Fatal(err.Error())
	}
	raftServer.SetElectionTimeout(electionTimeout)
	raftServer.SetHeartbeatInterval(heartbeatTimeout)
	ps.SetRaftServer(raftServer)

	// Create etcd server
	s := server.New(config.Name, config.Addr, ps, registry, k.store, &mb)

	var sListener net.Listener = k.sListener
	if config.EtcdTLSInfo().Scheme() == "https" {
		etcdServerTLSConfig, err := config.EtcdTLSInfo().ServerConfig()
		if err != nil {
			log.Fatal("etcd TLS error: ", err)
		}

		sListener, err = server.NewTLSListener(config.BindAddr, etcdServerTLSConfig)
		if err != nil {
			log.Fatal("Failed to create TLS etcd listener: ", err)
		}
	} else {
		sListener, err = server.NewListener(config.BindAddr)
		if err != nil {
			log.Fatal("Failed to create etcd listener: ", err)
		}
	}

	ps.SetServer(s)
	ps.Start(config.Snapshot, config.Peers)

	go func() {
		log.Info("peer server [name %s, listen on %s, advertised url %s]", ps.Config.Name, psListener.Addr(), ps.Config.URL)
		sHTTP := &ehttp.CORSHandler{ps.HTTPHandler(), corsInfo}
		if err := http.Serve(psListener, sHTTP); err != nil {
			log.Fatal(err.Error())
		}
	}()

	log.Info("etcd server [name %s, listen on %s, advertised url %s]", s.Name, sListener.Addr(), s.URL())
	sHTTP := &ehttp.CORSHandler{s.HTTPHandler(), corsInfo}
	go func() {
		if err := http.Serve(sListener, sHTTP); err != nil {
			log.Fatal(err.Error())
		}
	}()

	close(ready)
}
<|endoftext|>


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

package mkl

/*
#cgo CFLAGS: -O2 -DMKL_ILP64 -I/opt/intel/mkl/include
#cgo LDFLAGS: -Wl,--start-group /opt/intel/mkl/lib/intel64/libmkl_intel_ilp64.a /opt/intel/mkl/lib/intel64/libmkl_intel_thread.a /opt/intel/mkl/lib/intel64/libmkl_core.a /opt/intel/lib/intel64/libiomp5.a -Wl,--end-group -lpthread -lm -ldl -L/opt/intel/lib/intel64
*/
import "C"
<commit_msg>Add flags to compile MKL on macos<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mkl

/*
#cgo linux CFLAGS: -O2 -DMKL_ILP64 -I/opt/intel/mkl/include
#cgo linux LDFLAGS: -Wl,--start-group /opt/intel/mkl/lib/intel64/libmkl_intel_ilp64.a /opt/intel/mkl/lib/intel64/libmkl_intel_thread.a /opt/intel/mkl/lib/intel64/libmkl_core.a /opt/intel/lib/intel64/libiomp5.a -Wl,--end-group -lpthread -lm -ldl -L/opt/intel/lib/intel64

#cgo darwin CFLAGS: -O2 -DMKL_ILP64 -m64 -I/opt/intel/mkl/include
#cgo darwin LDFLAGS: -L/opt/intel/lib /opt/intel/mkl/lib/libmkl_intel_ilp64.a /opt/intel/mkl/lib/libmkl_intel_thread.a /opt/intel/mkl/lib/libmkl_core.a /opt/intel/lib/libiomp5.a -lpthread -lm -ldl
*/
import "C"
<|endoftext|>


<commit_before>// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"code.google.com/p/gopacket"
	"encoding/binary"
	"errors"
)

// PFLog provides the layer for 'pf' packet-filter logging, as described at
// http://www.freebsd.org/cgi/man.cgi?query=pflog&sektion=4
type PFLog struct {
	BaseLayer
	Length              uint8
	Family              ProtocolFamily
	Action, Reason      uint8
	IFName, Ruleset     []byte
	RuleNum, SubruleNum uint32
	// There's some other fields here that we currently don't pull out.
}

func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	pf.Length = data[0]
	pf.Family = ProtocolFamily(data[1])
	pf.Action = data[2]
	pf.Reason = data[3]
	pf.IFName = data[4:20]
	pf.Ruleset = data[20:36]
	pf.RuleNum = binary.BigEndian.Uint32(data[36:40])
	pf.SubruleNum = binary.BigEndian.Uint32(data[40:44])
	if pf.Length%4 != 1 {
		return errors.New("PFLog header length should be 3 less than multiple of 4")
	}
	actualLength := int(pf.Length) + 3
	pf.Contents = data[:actualLength]
	pf.Payload = data[actualLength:]
	return nil
}

// LayerType returns layers.LayerTypePFLog
func (pf *PFLog) LayerType() gopacket.LayerType { return LayerTypePFLog }

func (pf *PFLog) CanDecode() gopacket.LayerClass { return LayerTypePFLog }

func (pf *PFLog) NextLayerType() gopacket.LayerType {
	return pf.Family.LayerType()
}

func decodePFLog(data []byte, p gopacket.PacketBuilder) error {
	pf := &PFLog{}
	return decodingLayerDecoder(pf, data, p)
}
<commit_msg>Add more fields to PFLog.<commit_after>// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"encoding/binary"
	"errors"

	"code.google.com/p/gopacket"
)

type PFDirection uint8

const (
	PFDirectionInOut PFDirection = 0
	PFDirectionIn    PFDirection = 1
	PFDirectionOut   PFDirection = 2
)

// PFLog provides the layer for 'pf' packet-filter logging, as described at
// http://www.freebsd.org/cgi/man.cgi?query=pflog&sektion=4
type PFLog struct {
	BaseLayer
	Length              uint8
	Family              ProtocolFamily
	Action, Reason      uint8
	IFName, Ruleset     []byte
	RuleNum, SubruleNum uint32
	UID                 uint32
	PID                 int32
	RuleUID             uint32
	RulePID             int32
	Direction           PFDirection
	// The remainder is padding
}

func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	pf.Length = data[0]
	pf.Family = ProtocolFamily(data[1])
	pf.Action = data[2]
	pf.Reason = data[3]
	pf.IFName = data[4:20]
	pf.Ruleset = data[20:36]
	pf.RuleNum = binary.BigEndian.Uint32(data[36:40])
	pf.SubruleNum = binary.BigEndian.Uint32(data[40:44])
	pf.UID = binary.BigEndian.Uint32(data[44:48])
	pf.PID = int32(binary.BigEndian.Uint32(data[48:52]))
	pf.RuleUID = binary.BigEndian.Uint32(data[52:56])
	pf.RulePID = int32(binary.BigEndian.Uint32(data[56:60]))
	pf.Direction = PFDirection(data[60])
	if pf.Length%4 != 1 {
		return errors.New("PFLog header length should be 3 less than multiple of 4")
	}
	actualLength := int(pf.Length) + 3
	pf.Contents = data[:actualLength]
	pf.Payload = data[actualLength:]
	return nil
}

// LayerType returns layers.LayerTypePFLog
func (pf *PFLog) LayerType() gopacket.LayerType { return LayerTypePFLog }

func (pf *PFLog) CanDecode() gopacket.LayerClass { return LayerTypePFLog }

func (pf *PFLog) NextLayerType() gopacket.LayerType {
	return pf.Family.LayerType()
}

func decodePFLog(data []byte, p gopacket.PacketBuilder) error {
	pf := &PFLog{}
	return decodingLayerDecoder(pf, data, p)
}
<|endoftext|>


<commit_before>package ledger

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

const (
	markerPrefix = ";__ledger_file"
)

var includedFiles = make(map[string]bool)

func NewLedgerReader(filename string) (*bytes.Buffer, error) {
	var buf bytes.Buffer

	err := includeFile(filename, &buf)
	//fmt.Printf("Buffer: %q\n", buf)
	return &buf, err
}

// includeFile reads filename into buf, adding special marker comments
// when there are step changes in file location due to 'include' directive.
func includeFile(filename string, buf *bytes.Buffer) error {
	filename = filepath.Clean(filename)
	lineNum := 0

	// check for include cyles
	if includedFiles[filename] {
		return fmt.Errorf("include cycle: '%s'", filename)
	} else {
		includedFiles[filename] = true
	}

	defer delete(includedFiles, filename)

	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()
	s := bufio.NewScanner(f)

	// mark the start of this file
	fmt.Fprintln(buf, marker(filename, lineNum))

	for s.Scan() {
		line := s.Text()

		if strings.HasPrefix(line, "include") {
			pieces := strings.Split(line, " ")
			if len(pieces) != 2 {
				return fmt.Errorf("%s:%d: invalid include directive", filename, lineNum)
			}

			// Resolve filepaths
			includedPath := filepath.Join(filename, "..", pieces[1])
			includedPaths, err := filepath.Glob(includedPath)

			// Include all resolved filepaths
			for i := 0; i < len(includedPaths) && err == nil; i++ {
				if !includedFiles[includedPaths[i]] {
					err = includeFile(includedPaths[i], buf)
				}
			}
			if err != nil {
				return fmt.Errorf("%s:%d: %s", filename, lineNum, err.Error())
			}
			lineNum++

			// mark the resumption point for this file
			fmt.Fprintln(buf, marker(filename, lineNum))
		} else {
			fmt.Fprintln(buf, s.Text())
			lineNum++
		}
	}
	return nil
}

func marker(filename string, lineNum int) string {
	return fmt.Sprintf("%s*-*%s*-*%d", markerPrefix, filename, lineNum)
}

func parseMarker(s string) (string, int) {
	v := strings.Split(s, "*-*")
	lineNum, _ := strconv.Atoi(v[2])
	return v[1], lineNum
}
<commit_msg>Clean up old comment<commit_after>package ledger

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

const (
	markerPrefix = ";__ledger_file"
)

var includedFiles = make(map[string]bool)

func NewLedgerReader(filename string) (*bytes.Buffer, error) {
	var buf bytes.Buffer

	err := includeFile(filename, &buf)
	return &buf, err
}

// includeFile reads filename into buf, adding special marker comments
// when there are step changes in file location due to 'include' directive.
func includeFile(filename string, buf *bytes.Buffer) error {
	filename = filepath.Clean(filename)
	lineNum := 0

	// check for include cyles
	if includedFiles[filename] {
		return fmt.Errorf("include cycle: '%s'", filename)
	} else {
		includedFiles[filename] = true
	}

	defer delete(includedFiles, filename)

	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()
	s := bufio.NewScanner(f)

	// mark the start of this file
	fmt.Fprintln(buf, marker(filename, lineNum))

	for s.Scan() {
		line := s.Text()

		if strings.HasPrefix(line, "include") {
			pieces := strings.Split(line, " ")
			if len(pieces) != 2 {
				return fmt.Errorf("%s:%d: invalid include directive", filename, lineNum)
			}

			// Resolve filepaths
			includedPath := filepath.Join(filename, "..", pieces[1])
			includedPaths, err := filepath.Glob(includedPath)

			// Include all resolved filepaths
			for i := 0; i < len(includedPaths) && err == nil; i++ {
				if !includedFiles[includedPaths[i]] {
					err = includeFile(includedPaths[i], buf)
				}
			}
			if err != nil {
				return fmt.Errorf("%s:%d: %s", filename, lineNum, err.Error())
			}
			lineNum++

			// mark the resumption point for this file
			fmt.Fprintln(buf, marker(filename, lineNum))
		} else {
			fmt.Fprintln(buf, s.Text())
			lineNum++
		}
	}
	return nil
}

func marker(filename string, lineNum int) string {
	return fmt.Sprintf("%s*-*%s*-*%d", markerPrefix, filename, lineNum)
}

func parseMarker(s string) (string, int) {
	v := strings.Split(s, "*-*")
	lineNum, _ := strconv.Atoi(v[2])
	return v[1], lineNum
}
<|endoftext|>


<commit_before>package gnosis

// package file will contain MarkdownHandler and RawHandler to handle incoming requests
// Whole thing needs to be written

import (
	"errors"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/blevesearch/bleve"
	// bleveHttp "github.com/blevesearch/bleve/http"
)

type Page struct {
	Title    string
	ToC      template.HTML
	Body     template.HTML
	Topics   template.HTML
	Keywords template.HTML
}

func MarkdownHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	// break up the request parameters - for reference, regex is listed below
	//filteredRequest, err := wikiFilter.FindStringSubmatch(request.URL.Path)

	requestPath := strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	//if requestPath == "" || requestPath == "/" {
	if requestPath == "" {
		requestPath = serverConfig.Default
	log.Printf("replaced the request path - Request path is [%s] of length [%d], comparing against [%s] of length [%d]", requestPath, len(requestPath), "", len(""))
	} else {
	log.Printf("did not replace - Request path is [%s] of length [%d], comparing against [%s] of length [%d]", requestPath, len(requestPath), "", len(""))
}

	// If the request doesn't end in .md, add that
	if !strings.HasSuffix(requestPath, ".md") {
		requestPath = requestPath + ".md"
	}

	pdata := new(PageMetadata)
	err = pdata.LoadPage(serverConfig.Path + requestPath)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target [ %s ] sent to server %s", request.URL.Path, requestPath, serverConfig.Prefix)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	if pdata.MatchedTag(serverConfig.Restricted) {
		log.Printf("request [ %s ] was against a page [ %s ] with a restricted tag", request.URL.Path, requestPath)
		http.Error(responsePipe, err.Error(), 403)
		return
	}

	// parse any markdown in the input
	body := template.HTML(bodyParseMarkdown(pdata.Page))
	toc := template.HTML(tocParseMarkdown(pdata.Page))
	keywords := pdata.PrintKeywords()
	topics := pdata.PrintTopics(serverConfig.TopicURL)

	// ##TODO## put this template right in the function call
	// Then remove the Page Struct above
	response := Page{Title: "", ToC: toc, Body: body, Keywords: keywords, Topics: topics}
	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, response)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func FindExtension(s string) (string, error) {
	for i := len(s); i > 0; i-- {
		if string(s[i]) == "." {
			return s[i:], nil
		}
	}
	return "", errors.New("found no extension")
}

func RawHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	if request.URL.Path == "" || request.URL.Path == "/" {
		request.URL.Path = serverConfig.Default
	}

	// If the request is a blocked restriction, shut it down.
	//extension, err := FindExtension(request.URL.Path)
	for _, restricted := range serverConfig.Restricted {
		if strings.HasSuffix(request.URL.Path, restricted) {
			log.Printf("request %s was improperly routed to the file handler with an disallowed extension %s", request.URL.Path, restricted)
			http.Error(responsePipe, "Request not allowed", 403)
			return
		}
	}

	// Load the file - 404 on failure.
	contents, err := ioutil.ReadFile(serverConfig.Path + request.URL.Path)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target sent to server %s - %v", request.URL.Path, serverConfig.Prefix, err)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	_, err = responsePipe.Write([]byte(contents))
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
	return
}

func SearchHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// if err = request.ParseForm(); err != nil {
	// 	log.Printf("error parsing search request, %v", err)
	// 	http.Error(responsePipe, err.Error(), 500)
	// 	return
	// }

	queryArgs := request.URL.Query()

	// debugging information
	for k, v := range queryArgs {
		log.Println("key:", k)
		log.Println("val:", strings.Join(v, ""))
	}

	query := bleve.NewQueryStringQuery(queryArgs["s"][0])
	searchRequest := bleve.NewSearchRequest(query)

	// validate the query
	err = searchRequest.Query.Validate()
	if err != nil {
		log.Printf("Error validating query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	log.Println("validated query")

	// log.Println(bleveHttp.IndexNames())

	return

	// index := bleveHttp.IndexByName(serverConfig.Default)
	index, err := bleve.Open(serverConfig.Path)
	defer index.Close()
	if index == nil {
		log.Printf("no such index '%s'", serverConfig.Default)
		http.Error(responsePipe, err.Error(), 404)
		return
	} else if err != nil {
		log.Printf("no such index '%s'", serverConfig.Path)
		http.Error(responsePipe, err.Error(), 404)
		log.Printf("problem opening index '%s' - %v", serverConfig.Path, err)
		return
	}

	log.Println("opened index")

	// execute the query
	searchResponse, err := index.Search(searchRequest)
	if err != nil {
		log.Printf("Error executing query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	log.Println("ran query")

	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, searchResponse)
	// err = RenderTemplate(responsePipe, serverConfig.Template, searchResponse)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}

	log.Println("Responded")
}

func MakeHandler(handlerConfig ServerSection) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		switch handlerConfig.ServerType {
		case "markdown":
			MarkdownHandler(w, r, handlerConfig)
		case "raw":
			RawHandler(w, r, handlerConfig)
		case "simpleSearch":
			SearchHandler(w, r, handlerConfig)
		default:
			log.Printf("Bad server type [%s]", handlerConfig.ServerType)
		}
	}
}
<commit_msg>cleaned up a lot of clutter in handlers.go<commit_after>package gnosis

// package file will contain MarkdownHandler and RawHandler to handle incoming requests
// Whole thing needs to be written

import (
	"errors"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/blevesearch/bleve"
	// bleveHttp "github.com/blevesearch/bleve/http"
)

type Page struct {
	Title    string
	ToC      template.HTML
	Body     template.HTML
	Topics   template.HTML
	Keywords template.HTML
}

func MarkdownHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	requestPath := strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	if requestPath == "" {
		requestPath = serverConfig.Default
	log.Printf("replaced the request path - Request path is [%s] of length [%d], comparing against [%s] of length [%d]", requestPath, len(requestPath), "", len(""))
	}

	// If the request doesn't end in .md, add that
	if !strings.HasSuffix(requestPath, ".md") {
		requestPath = requestPath + ".md"
	}

	pdata := new(PageMetadata)
	err = pdata.LoadPage(serverConfig.Path + requestPath)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target [ %s ] sent to server %s", request.URL.Path, requestPath, serverConfig.Prefix)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	if pdata.MatchedTag(serverConfig.Restricted) {
		log.Printf("request [ %s ] was against a page [ %s ] with a restricted tag", request.URL.Path, requestPath)
		http.Error(responsePipe, err.Error(), 403)
		return
	}

	// parse any markdown in the input
	body := template.HTML(bodyParseMarkdown(pdata.Page))
	toc := template.HTML(tocParseMarkdown(pdata.Page))
	keywords := pdata.PrintKeywords()
	topics := pdata.PrintTopics(serverConfig.TopicURL)

	// ##TODO## put this template right in the function call
	// Then remove the Page Struct above
	response := Page{Title: "", ToC: toc, Body: body, Keywords: keywords, Topics: topics}
	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, response)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func RawHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	if request.URL.Path == "" || request.URL.Path == "/" {
		request.URL.Path = serverConfig.Default
	}

	// If the request is a blocked restriction, shut it down.
	for _, restricted := range serverConfig.Restricted {
		if strings.HasSuffix(request.URL.Path, restricted) {
			log.Printf("request %s was improperly routed to the file handler with an disallowed extension %s", request.URL.Path, restricted)
			http.Error(responsePipe, "Request not allowed", 403)
			return
		}
	}

	// Load the file - 404 on failure.
	contents, err := ioutil.ReadFile(serverConfig.Path + request.URL.Path)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target sent to server %s - %v", request.URL.Path, serverConfig.Prefix, err)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	_, err = responsePipe.Write([]byte(contents))
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
	return
}

func SearchHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	queryArgs := request.URL.Query()

	query := bleve.NewQueryStringQuery(queryArgs["s"][0])
	searchRequest := bleve.NewSearchRequest(query)

	// validate the query
	err = searchRequest.Query.Validate()
	if err != nil {
		log.Printf("Error validating query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	index, err := bleve.Open(serverConfig.Path)
	defer index.Close()
	if index == nil {
		log.Printf("no such index '%s'", serverConfig.Default)
		http.Error(responsePipe, err.Error(), 404)
		return
	} else if err != nil {
		log.Printf("no such index '%s'", serverConfig.Path)
		http.Error(responsePipe, err.Error(), 404)
		log.Printf("problem opening index '%s' - %v", serverConfig.Path, err)
		return
	}

	// execute the query
	searchResponse, err := index.Search(searchRequest)
	if err != nil {
		log.Printf("Error executing query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, searchResponse)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func MakeHandler(handlerConfig ServerSection) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		switch handlerConfig.ServerType {
		case "markdown":
			MarkdownHandler(w, r, handlerConfig)
		case "raw":
			RawHandler(w, r, handlerConfig)
		case "simpleSearch":
			SearchHandler(w, r, handlerConfig)
		default:
			log.Printf("Bad server type [%s]", handlerConfig.ServerType)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

// StaticServeMux wraps ServeMux but allows for the interception of errors.
type StaticServeMux struct {
	*http.ServeMux
	errors map[int]http.Handler
}

// NewStaticServeMux allocates and returns a new StaticServeMux
func NewStaticServeMux() *StaticServeMux {
	return &StaticServeMux{
		ServeMux: http.NewServeMux(),
		errors:   make(map[int]http.Handler),
	}
}

// HandleError registers a handler for the given response code.
func (s *StaticServeMux) HandleError(status int, handler http.Handler) {
	if s.errors[status] != nil {
		panic("Handler for error already registered")
	}
	s.errors[status] = handler
}

func (s StaticServeMux) intercept(status int, w http.ResponseWriter, req *http.Request) bool {
	// Get error handler if there is one
	if h, f := s.errors[status]; f {
		h.ServeHTTP(statusResponseWriter{w, status}, req)
		return true
	}
	// Ignore non-error status codes
	if status < 400 {
		return false
	}
	http.Error(w, http.StatusText(status), status)
	return true
}

func (s *StaticServeMux) interceptHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		irw := &InterceptResponseWriter{
			ResponseWriter: w,
			r:              r,
			m:              s,
		}

		// If intercept occurred, originating call would have been panic'd.
		// Recover here once error has been dealt with.
		defer func() {
			if p := recover(); p != nil {
				if p == irw {
					return
				}
				panic(p)
			}
		}()

		handler.ServeHTTP(irw, r)
	})
}

func (s *StaticServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	h, _ := s.Handler(r)
	h = s.interceptHandler(h)
	h.ServeHTTP(w, r)
}

// InterceptResponseWriter allows non-200 responses to be intercepted based
// on their status code.
type InterceptResponseWriter struct {
	http.ResponseWriter
	r *http.Request
	m *StaticServeMux
}

// WriteHeader panics if the response should be intercepted, otherwise it
// writes the response status.
func (h *InterceptResponseWriter) WriteHeader(status int) {
	if h.m.intercept(status, h.ResponseWriter, h.r) {
		panic(h)
	} else {
		h.ResponseWriter.WriteHeader(status)
	}
}

type statusResponseWriter struct {
	http.ResponseWriter
	Status int
}

func (h statusResponseWriter) WriteHeader(status int) {
	if h.Status < 0 {
		return
	}
	if h.Status > 0 {
		h.ResponseWriter.WriteHeader(h.Status)
		return
	}
	h.ResponseWriter.WriteHeader(status)
}

// PreventListingDir panics whenever a file open fails, allowing index
// requests to be intercepted.
type PreventListingDir struct {
	http.Dir
}

// Open panics whenever opening a file fails.
func (dir *PreventListingDir) Open(name string) (f http.File, err error) {
	f, err = dir.Dir.Open(name)
	if f == nil {
		panic(dir)
	}
	return
}

// SuppressListingHandler returns a FileServer handler that does not permit
// the listing of files.
func SuppressListingHandler(dir http.Dir) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		d := &PreventListingDir{dir}
		h := http.FileServer(d)
		defer func() {
			if p := recover(); p != nil {
				if p == d {
					http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
					return
				}
				panic(p)
			}
		}()
		h.ServeHTTP(w, r)
	})
}

// CustomHeadersHandler creates a new handler that includes the provided
// headers in each response.
func CustomHeadersHandler(h http.Handler, headers Headers) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		wh := w.Header()
		for k, v := range headers {
			if wh.Get(k) == "" {
				wh.Set(k, v)
			}
		}
		h.ServeHTTP(w, r)
	})
}

// GzipResponseWriter gzips content written to it
type GzipResponseWriter struct {
	io.Writer
	http.ResponseWriter
	gotContentType bool
}

func (w *GzipResponseWriter) Write(b []byte) (int, error) {
	if !w.gotContentType {
		if w.Header().Get("Content-Type") == "" {
			w.Header().Set("Content-Type", http.DetectContentType(b))
		}
		w.gotContentType = true
	}
	return w.Writer.Write(b)
}

// GzipHandler gzips the HTTP response if supported by the client. Based on
// the implementation of `go.httpgzip`
func GzipHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Serve normally to clients that don't express gzip support
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			h.ServeHTTP(w, r)
			return
		}

		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(&GzipResponseWriter{Writer: gz, ResponseWriter: w}, r)
	})
}

// LogHandler wraps with a LoggingResponseWriter for the purpose of logging
// accesses and errors.
func LogHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		rw := NewLoggingResponseWriter(w)
		h.ServeHTTP(rw, r)
		rw.log(r)
	})
}

// LoggingResponseWriter intercepts the request and stores the status.
type LoggingResponseWriter struct {
	http.ResponseWriter
	status *int
	size   *int
}

// NewLoggingResponseWriter creates a new LoggingResponseWriter that wraps
// the given ResponseWriter. It will log 4xx/5xx responses to stderr, and
// everything else to stdout.
func NewLoggingResponseWriter(w http.ResponseWriter) LoggingResponseWriter {
	lrw := LoggingResponseWriter{
		ResponseWriter: w,
		status:         new(int),
		size:           new(int),
	}
	*lrw.status = 200 // as WriteHeader normally isn't called
	*lrw.size = 0
	return lrw
}

// WriteHeader records the status written in the response.
func (w LoggingResponseWriter) WriteHeader(status int) {
	w.ResponseWriter.WriteHeader(status)
	*w.status = status
}

func (w LoggingResponseWriter) Write(b []byte) (c int, e error) {
	c, e = w.ResponseWriter.Write(b)
	*w.size += c
	return
}

func (w LoggingResponseWriter) log(req *http.Request) {
	out := os.Stdout
	if *w.status >= 400 && *w.status < 600 {
		// direct all errors to stderr
		out = os.Stderr
	}

	t := time.Now().Format(time.RFC3339)
	remoteAddr := strings.Split(req.RemoteAddr, ":")[0]
	localAddr := strings.Split(req.Host, ":")[0]
	requestLine := req.Method + " " + req.RequestURI

	fmt.Fprintf(out, "%s [%s] %s %s %d %d\n", remoteAddr, t, localAddr,
		strconv.Quote(requestLine), *w.status, *w.size)
}
<commit_msg>Only panic when listing index.html<commit_after>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

// StaticServeMux wraps ServeMux but allows for the interception of errors.
type StaticServeMux struct {
	*http.ServeMux
	errors map[int]http.Handler
}

// NewStaticServeMux allocates and returns a new StaticServeMux
func NewStaticServeMux() *StaticServeMux {
	return &StaticServeMux{
		ServeMux: http.NewServeMux(),
		errors:   make(map[int]http.Handler),
	}
}

// HandleError registers a handler for the given response code.
func (s *StaticServeMux) HandleError(status int, handler http.Handler) {
	if s.errors[status] != nil {
		panic("Handler for error already registered")
	}
	s.errors[status] = handler
}

func (s StaticServeMux) intercept(status int, w http.ResponseWriter, req *http.Request) bool {
	// Get error handler if there is one
	if h, f := s.errors[status]; f {
		h.ServeHTTP(statusResponseWriter{w, status}, req)
		return true
	}
	// Ignore non-error status codes
	if status < 400 {
		return false
	}
	http.Error(w, http.StatusText(status), status)
	return true
}

func (s *StaticServeMux) interceptHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		irw := &InterceptResponseWriter{
			ResponseWriter: w,
			r:              r,
			m:              s,
		}

		// If intercept occurred, originating call would have been panic'd.
		// Recover here once error has been dealt with.
		defer func() {
			if p := recover(); p != nil {
				if p == irw {
					return
				}
				panic(p)
			}
		}()

		handler.ServeHTTP(irw, r)
	})
}

func (s *StaticServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	h, _ := s.Handler(r)
	h = s.interceptHandler(h)
	h.ServeHTTP(w, r)
}

// InterceptResponseWriter allows non-200 responses to be intercepted based
// on their status code.
type InterceptResponseWriter struct {
	http.ResponseWriter
	r *http.Request
	m *StaticServeMux
}

// WriteHeader panics if the response should be intercepted, otherwise it
// writes the response status.
func (h *InterceptResponseWriter) WriteHeader(status int) {
	if h.m.intercept(status, h.ResponseWriter, h.r) {
		panic(h)
	} else {
		h.ResponseWriter.WriteHeader(status)
	}
}

type statusResponseWriter struct {
	http.ResponseWriter
	Status int
}

func (h statusResponseWriter) WriteHeader(status int) {
	if h.Status < 0 {
		return
	}
	if h.Status > 0 {
		h.ResponseWriter.WriteHeader(h.Status)
		return
	}
	h.ResponseWriter.WriteHeader(status)
}

// PreventListingDir panics whenever a file open fails, allowing index
// requests to be intercepted.
type PreventListingDir struct {
	http.Dir
}

// Open panics whenever opening an index file fails.
func (dir *PreventListingDir) Open(name string) (f http.File, err error) {
	f, err = dir.Dir.Open(name)
	if f == nil && strings.HasSuffix(name, "/index.html") {
		panic(dir)
	}
	return
}

// SuppressListingHandler returns a FileServer handler that does not permit
// the listing of files.
func SuppressListingHandler(dir http.Dir) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		d := &PreventListingDir{dir}
		h := http.FileServer(d)
		defer func() {
			if p := recover(); p != nil {
				if p == d {
					http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
					return
				}
				panic(p)
			}
		}()
		h.ServeHTTP(w, r)
	})
}

// CustomHeadersHandler creates a new handler that includes the provided
// headers in each response.
func CustomHeadersHandler(h http.Handler, headers Headers) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		wh := w.Header()
		for k, v := range headers {
			if wh.Get(k) == "" {
				wh.Set(k, v)
			}
		}
		h.ServeHTTP(w, r)
	})
}

// GzipResponseWriter gzips content written to it
type GzipResponseWriter struct {
	io.Writer
	http.ResponseWriter
	gotContentType bool
}

func (w *GzipResponseWriter) Write(b []byte) (int, error) {
	if !w.gotContentType {
		if w.Header().Get("Content-Type") == "" {
			w.Header().Set("Content-Type", http.DetectContentType(b))
		}
		w.gotContentType = true
	}
	return w.Writer.Write(b)
}

// GzipHandler gzips the HTTP response if supported by the client. Based on
// the implementation of `go.httpgzip`
func GzipHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Serve normally to clients that don't express gzip support
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			h.ServeHTTP(w, r)
			return
		}

		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(&GzipResponseWriter{Writer: gz, ResponseWriter: w}, r)
	})
}

// LogHandler wraps with a LoggingResponseWriter for the purpose of logging
// accesses and errors.
func LogHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		rw := NewLoggingResponseWriter(w)
		h.ServeHTTP(rw, r)
		rw.log(r)
	})
}

// LoggingResponseWriter intercepts the request and stores the status.
type LoggingResponseWriter struct {
	http.ResponseWriter
	status *int
	size   *int
}

// NewLoggingResponseWriter creates a new LoggingResponseWriter that wraps
// the given ResponseWriter. It will log 4xx/5xx responses to stderr, and
// everything else to stdout.
func NewLoggingResponseWriter(w http.ResponseWriter) LoggingResponseWriter {
	lrw := LoggingResponseWriter{
		ResponseWriter: w,
		status:         new(int),
		size:           new(int),
	}
	*lrw.status = 200 // as WriteHeader normally isn't called
	*lrw.size = 0
	return lrw
}

// WriteHeader records the status written in the response.
func (w LoggingResponseWriter) WriteHeader(status int) {
	w.ResponseWriter.WriteHeader(status)
	*w.status = status
}

func (w LoggingResponseWriter) Write(b []byte) (c int, e error) {
	c, e = w.ResponseWriter.Write(b)
	*w.size += c
	return
}

func (w LoggingResponseWriter) log(req *http.Request) {
	out := os.Stdout
	if *w.status >= 400 && *w.status < 600 {
		// direct all errors to stderr
		out = os.Stderr
	}

	t := time.Now().Format(time.RFC3339)
	remoteAddr := strings.Split(req.RemoteAddr, ":")[0]
	localAddr := strings.Split(req.Host, ":")[0]
	requestLine := req.Method + " " + req.RequestURI

	fmt.Fprintf(out, "%s [%s] %s %s %d %d\n", remoteAddr, t, localAddr,
		strconv.Quote(requestLine), *w.status, *w.size)
}
<|endoftext|>


<commit_before>package gnosis

// package file will contain MarkdownHandler and RawHandler to handle incoming requests
// Whole thing needs to be written

import (
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"

	"github.com/JackKnifed/blackfriday"
)

type WikiPage struct {
	Title string
	//Body  string
	Body template.HTML
}

var templates = template.Must(template.ParseFiles("wiki.html"))

var wikiFilter = regexp.MustCompile("^/([a-zA-Z0-9_ /]+/)?([a-zA-Z0-9_ ]+)$")
var fileFIlter = regexp.MustCompile("^/([a-zA-Z0-9_ /]+/)?([a-zA-Z0-9_ ]+)?\\.([a-zA-Z0-9_ ]+)?")

func MarkdownHandler(responsePipe http.ResponseWriter, request *http.Request) {
	var err error

	filteredRequest := wikiFilter.FindStringSubmatch(request.URL.Path)
	config := GetConfig()

	if filteredRequest == nil {
		log.Printf("null request improperly routed to wiki handler %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, "Request not allowed", 403)
	}

	if filteredRequest[1] != config.Mainserver.Prefix {
		log.Printf("request %s was improperly routed to wiki handler %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, err.Error(), 500)
	}

	contents, err := ioutil.ReadFile(config.Mainserver.Prefix + filteredRequest[2] + ".md")
	if err != nil {
		log.Printf("request %s points to an bad file target sent to server %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, err.Error(), 403)
	}
	// parse any markdown in the input
	body := template.HTML(blackfriday.MarkdownCommon(contents))

	response := WikiPage{Title: filteredRequest[2], Body: body}
	err = templates.ExecuteTemplate(responsePipe, "wiki.html", response)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func RawHandler(responsePipe http.ResponseWriter, request *http.Request) {
	var err error

	filteredRequest := wikiFilter.FindStringSubmatch(request.URL.Path)
	config := GetConfig()

	if filteredRequest == nil {
		log.Printf("null request improperly routed to wiki handler %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, "Request not allowed", 403)
	}

	if filteredRequest[1] != config.Mainserver.Prefix {
		log.Printf("request %s was improperly routed to wiki handler %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, err.Error(), 500)
	}

	contents, err := ioutil.ReadFile(config.Mainserver.Prefix + filteredRequest[2] + ".md")
	if err != nil {
		log.Printf("request %s points to an bad file target sent to server %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, err.Error(), 403)
	}

	_, err = responsePipe.Write([]byte(contents))
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}
<commit_msg>changed some things around to avoid out of index errors, and got webserver.go working (running) in test enviroment<commit_after>package gnosis

// package file will contain MarkdownHandler and RawHandler to handle incoming requests
// Whole thing needs to be written

import (
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"

	"github.com/JackKnifed/blackfriday"
)

type WikiPage struct {
	Title string
	//Body  string
	Body template.HTML
}

var templates = template.Must(template.ParseFiles("wiki.html"))

var wikiFilter = regexp.MustCompile("^(/([a-zA-Z0-9_ /]+/)?)([a-zA-Z0-9_ ]+)$")
var fileFIlter = regexp.MustCompile("^(/([a-zA-Z0-9_ /]+/)?)([a-zA-Z0-9_ ]+)?(\\.)?([a-zA-Z0-9_ ]+)?")

func MarkdownHandler(responsePipe http.ResponseWriter, request *http.Request) {
	var err error

	filteredRequest := wikiFilter.FindStringSubmatch(request.URL.Path)
	config := GetConfig()

	if filteredRequest == nil {
		log.Printf("null request [ %s ] improperly routed to wiki handler [ %s ]", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, "Request not allowed", 403)
	} else {
		if filteredRequest[1] != config.Mainserver.Prefix {
			log.Printf("request %s was improperly routed to wiki handler %s", request.URL.Path, config.Mainserver.Prefix)
			http.Error(responsePipe, err.Error(), 500)
		}

		contents, err := ioutil.ReadFile(config.Mainserver.Path + filteredRequest[3] + ".md")
		if err != nil {
			log.Printf("request [ %s ] points to an bad file target [ %s ]sent to server %s", request.URL.Path, filteredRequest[3], config.Mainserver.Prefix)
			http.Error(responsePipe, err.Error(), 403)
		}
		// parse any markdown in the input
		body := template.HTML(blackfriday.MarkdownCommon(contents))

		response := WikiPage{Title: filteredRequest[3], Body: body}
		err = templates.ExecuteTemplate(responsePipe, "wiki.html", response)
		if err != nil {
			http.Error(responsePipe, err.Error(), 500)
		}
	}
}

func RawHandler(responsePipe http.ResponseWriter, request *http.Request) {
	var err error

	filteredRequest := wikiFilter.FindStringSubmatch(request.URL.Path)
	config := GetConfig()

	if filteredRequest == nil {
		log.Printf("null request improperly routed to wiki handler %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, "Request not allowed", 403)
	}

	if filteredRequest[1] != config.Mainserver.Prefix {
		log.Printf("request %s was improperly routed to wiki handler %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, err.Error(), 500)
	}

	contents, err := ioutil.ReadFile(config.Mainserver.Prefix + filteredRequest[2] + ".md")
	if err != nil {
		log.Printf("request %s points to an bad file target sent to server %s", request.URL.Path, config.Mainserver.Prefix)
		http.Error(responsePipe, err.Error(), 403)
	}

	_, err = responsePipe.Write([]byte(contents))
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}
<|endoftext|>


<commit_before>package hashring

import (
	"crypto/md5"
	"fmt"
	"math"
	"sort"
)

type HashKey uint32

type HashKeyOrder []HashKey

func (h HashKeyOrder) Len() int           { return len(h) }
func (h HashKeyOrder) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h HashKeyOrder) Less(i, j int) bool { return h[i] < h[j] }

type HashRing struct {
	ring       map[HashKey]string
	sortedKeys []HashKey
	nodes      []string
	weights    map[string]int
}

func New(nodes []string) *HashRing {
	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    make(map[string]int),
	}
	hashRing.generateCircle()
	return hashRing
}

func NewWithWeights(weights map[string]int) *HashRing {
	nodes := make([]string, 0, len(weights))
	for node, _ := range weights {
		nodes = append(nodes, node)
	}
	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    weights,
	}
	hashRing.generateCircle()
	return hashRing
}

func hashVal(bKey []byte, entryFn func(int) int) HashKey {
	return ((HashKey(bKey[entryFn(3)]) << 24) |
		(HashKey(bKey[entryFn(2)]) << 16) |
		(HashKey(bKey[entryFn(1)]) << 8) |
		(HashKey(bKey[entryFn(0)])))
}

func hashDigest(key string) []byte {
	m := md5.New()
	m.Write([]byte(key))
	return m.Sum(nil)
}

func (h *HashRing) generateCircle() {
	totalWeight := 0
	for _, node := range h.nodes {
		if weight, ok := h.weights[node]; ok {
			totalWeight += weight
		} else {
			totalWeight += 1
		}
	}

	for _, node := range h.nodes {
		weight := 1

		if _, ok := h.weights[node]; ok {
			weight = h.weights[node]
		}

		factor := math.Floor(float64(40*len(h.nodes)*weight) / float64(totalWeight))

		for j := 0; j < int(factor); j++ {
			nodeKey := fmt.Sprintf("%s-%d", node, j)
			bKey := hashDigest(nodeKey)

			for i := 0; i < 3; i++ {
				key := hashVal(bKey, func(x int) int { return x + i*4 })
				h.ring[key] = node
				h.sortedKeys = append(h.sortedKeys, key)
			}
		}
	}

	sort.Sort(HashKeyOrder(h.sortedKeys))
}

func (h *HashRing) GetNode(stringKey string) (node string, ok bool) {
	pos, ok := h.GetNodePos(stringKey)
	if !ok {
		return "", false
	}
	return h.ring[h.sortedKeys[pos]], true
}

func (h *HashRing) GetNodePos(stringKey string) (pos int, ok bool) {
	if len(h.ring) == 0 {
		return 0, false
	}

	key := h.GenKey(stringKey)

	nodes := h.sortedKeys
	pos = sort.Search(len(nodes), func(i int) bool { return nodes[i] > key })

	if pos == len(nodes) {
		// Wrap the search, should return first node
		return 0, true
	} else {
		return pos, true
	}
}

func (h *HashRing) GenKey(key string) HashKey {
	bKey := hashDigest(key)
	return hashVal(bKey, func(x int) int { return x })
}

func (h *HashRing) AddNode(node string) *HashRing {
	return h.AddWeightedNode(node, 1)
}

func (h *HashRing) AddWeightedNode(node string, weight int) *HashRing {
	if weight <= 0 {
		return h
	}

	for _, eNode := range h.nodes {
		if eNode == node {
			return h
		}
	}

	nodes := make([]string, len(h.nodes), len(h.nodes)+1)
	copy(nodes, h.nodes)
	nodes = append(nodes, node)

	weights := make(map[string]int)
	for eNode, eWeight := range h.weights {
		weights[eNode] = eWeight
	}
	weights[node] = weight

	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    weights,
	}
	hashRing.generateCircle()
	return hashRing
}

func (h *HashRing) RemoveNode(node string) *HashRing {
	nodes := make([]string, 0)
	for _, eNode := range h.nodes {
		if eNode != node {
			nodes = append(nodes, eNode)
		}
	}

	weights := make(map[string]int)
	for eNode, eWeight := range h.weights {
		if eNode != node {
			weights[eNode] = eWeight
		}
	}

	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    weights,
	}
	hashRing.generateCircle()
	return hashRing
}
<commit_msg>Change function locations<commit_after>package hashring

import (
	"crypto/md5"
	"fmt"
	"math"
	"sort"
)

type HashKey uint32
type HashKeyOrder []HashKey

func (h HashKeyOrder) Len() int           { return len(h) }
func (h HashKeyOrder) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h HashKeyOrder) Less(i, j int) bool { return h[i] < h[j] }

type HashRing struct {
	ring       map[HashKey]string
	sortedKeys []HashKey
	nodes      []string
	weights    map[string]int
}

func New(nodes []string) *HashRing {
	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    make(map[string]int),
	}
	hashRing.generateCircle()
	return hashRing
}

func NewWithWeights(weights map[string]int) *HashRing {
	nodes := make([]string, 0, len(weights))
	for node, _ := range weights {
		nodes = append(nodes, node)
	}
	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    weights,
	}
	hashRing.generateCircle()
	return hashRing
}

func (h *HashRing) generateCircle() {
	totalWeight := 0
	for _, node := range h.nodes {
		if weight, ok := h.weights[node]; ok {
			totalWeight += weight
		} else {
			totalWeight += 1
		}
	}

	for _, node := range h.nodes {
		weight := 1

		if _, ok := h.weights[node]; ok {
			weight = h.weights[node]
		}

		factor := math.Floor(float64(40*len(h.nodes)*weight) / float64(totalWeight))

		for j := 0; j < int(factor); j++ {
			nodeKey := fmt.Sprintf("%s-%d", node, j)
			bKey := hashDigest(nodeKey)

			for i := 0; i < 3; i++ {
				key := hashVal(bKey, func(x int) int { return x + i*4 })
				h.ring[key] = node
				h.sortedKeys = append(h.sortedKeys, key)
			}
		}
	}

	sort.Sort(HashKeyOrder(h.sortedKeys))
}

func (h *HashRing) GetNode(stringKey string) (node string, ok bool) {
	pos, ok := h.GetNodePos(stringKey)
	if !ok {
		return "", false
	}
	return h.ring[h.sortedKeys[pos]], true
}

func (h *HashRing) GetNodePos(stringKey string) (pos int, ok bool) {
	if len(h.ring) == 0 {
		return 0, false
	}

	key := h.GenKey(stringKey)

	nodes := h.sortedKeys
	pos = sort.Search(len(nodes), func(i int) bool { return nodes[i] > key })

	if pos == len(nodes) {
		// Wrap the search, should return first node
		return 0, true
	} else {
		return pos, true
	}
}

func (h *HashRing) GenKey(key string) HashKey {
	bKey := hashDigest(key)
	return hashVal(bKey, func(x int) int { return x })
}

func (h *HashRing) AddNode(node string) *HashRing {
	return h.AddWeightedNode(node, 1)
}

func (h *HashRing) AddWeightedNode(node string, weight int) *HashRing {
	if weight <= 0 {
		return h
	}

	for _, eNode := range h.nodes {
		if eNode == node {
			return h
		}
	}

	nodes := make([]string, len(h.nodes), len(h.nodes)+1)
	copy(nodes, h.nodes)
	nodes = append(nodes, node)

	weights := make(map[string]int)
	for eNode, eWeight := range h.weights {
		weights[eNode] = eWeight
	}
	weights[node] = weight

	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    weights,
	}
	hashRing.generateCircle()
	return hashRing
}

func (h *HashRing) RemoveNode(node string) *HashRing {
	nodes := make([]string, 0)
	for _, eNode := range h.nodes {
		if eNode != node {
			nodes = append(nodes, eNode)
		}
	}

	weights := make(map[string]int)
	for eNode, eWeight := range h.weights {
		if eNode != node {
			weights[eNode] = eWeight
		}
	}

	hashRing := &HashRing{
		ring:       make(map[HashKey]string),
		sortedKeys: make([]HashKey, 0),
		nodes:      nodes,
		weights:    weights,
	}
	hashRing.generateCircle()
	return hashRing
}

func hashVal(bKey []byte, entryFn func(int) int) HashKey {
	return ((HashKey(bKey[entryFn(3)]) << 24) |
		(HashKey(bKey[entryFn(2)]) << 16) |
		(HashKey(bKey[entryFn(1)]) << 8) |
		(HashKey(bKey[entryFn(0)])))
}

func hashDigest(key string) []byte {
	m := md5.New()
	m.Write([]byte(key))
	return m.Sum(nil)
}
<|endoftext|>


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

package reddit

import (
	"encoding/json"
	"fmt"
)

// Headline represents an individual post from the perspective
// of a subreddit.
type Headline struct {
	Author       string  `json:"author"`
	Title        string  `json:"title"`
	URL          string  `json:"url"`
	Domain       string  `json:"domain"`
	Subreddit    string  `json:"subreddit"`
	SubredditId  string  `json:"subreddit_id"`
	FullId       string  `json:"name"`
	Id           string  `json:"id"`
	Permalink    string  `json:"permalink"`
	Selftext     string  `json:"selftext"`
	ThumbnailURL string  `json:"thumbnail"`
	DateCreated  float32 `json:"created_utc"`
	NumComments  int     `json:"num_comments"`
	Score        int     `json:"score"`
	// Ups and downs are fake to trick spammers
	Ups        int     `json:"ups"`
	Downs      int     `json:"downs"`
	IsNSFW     bool    `json:"over_18"`
	IsSelf     bool    `json:"is_self"`
	WasClicked bool    `json:"clicked"`
	IsSaved    bool    `json:"saved"`
	BannedBy   *string `json:"banned_by"`
}

// Sort headlines by popularity
type PopularitySort string

const (
	DefaultPopularity      PopularitySort = ""
	HotHeadlines                          = "hot"
	NewHeadlines                          = "new"
	RisingHeadlines                       = "rising"
	TopHeadlines                          = "top"
	ControversialHeadlines                = "controversial"
)

// Sort headlines by age
type AgeSort string

const (
	DefaultAge AgeSort = ""
	ThisHour           = "hour"
	ThisMonth          = "month"
	ThisYear           = "year"
	AllTime            = "all"
)

type Headlines []*Headline

// FullPermalink returns the full URL of a headline.
func (h *Headline) FullPermalink() string {
	return "http://reddit.com" + h.Permalink
}

// String returns the string representation of a headline.
func (h *Headline) String() string {
	plural := ""
	if h.NumComments != 1 {
		plural = "s"
	}
	comments := fmt.Sprintf("%d comment%s", h.NumComments, plural)
	/*var comments string
	switch h.NumComments {
	case 0:
		comments = "0 comments"
	case 1:
		comments = "1 comment"
	default:
		comments = fmt.Sprintf("%d comments", h.NumComments)
	}*/
	return fmt.Sprintf("%d - %s (%s)", h.Score, h.Title, comments)
}

// DefaultHeadlines returns a slice of headlines on the default reddit frontpage.
func DefaultHeadlines() (Headlines, error) {
	url := "http://www.reddit.com/.json"
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}

// SubredditHeadlines returns a slice of headlines on the given subreddit.
func SubredditHeadlines(subreddit string) (Headlines, error) {
	url := fmt.Sprintf("http://www.reddit.com/r/%s.json", subreddit)
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}

// SortedHeadlines will return headlines from a subreddit (or homepage if "") by popularity and age
func SortedHeadlines(subreddit string, popularity PopularitySort, age AgeSort) (Headlines, error) {
	if age != DefaultAge {
		switch popularity {
		case NewHeadlines, RisingHeadlines, HotHeadlines:
			return nil, fmt.Errorf("Cannot sort %s by %s", popularity, age)
		}
	}

	url := "http://reddit.com/"

	if subreddit != "" {
		url = fmt.Sprintf("http://%s.reddit.com/", subreddit)
	}

	if popularity != DefaultPopularity {
		if popularity == NewHeadlines || popularity == RisingHeadlines {
			url = fmt.Sprintf("%s.json?sort=%s", url, popularity)
		} else {
			url = fmt.Sprintf("%s%s.json?sort=%s", url, popularity, popularity)
		}
	} else {
		url = fmt.Sprintf("%s.json", url)
	}

	if age != DefaultAge {
		if popularity != DefaultPopularity {
			url = fmt.Sprintf("%s&t=%s", url, age)
		} else {
			url = fmt.Sprintf("%s?t=%s", url, age)
		}
	}

	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil

}
<commit_msg>Fixed sorting for new and rising<commit_after>// Copyright 2012 Jimmy Zelinskie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package reddit

import (
	"encoding/json"
	"fmt"
)

// Headline represents an individual post from the perspective
// of a subreddit.
type Headline struct {
	Author       string  `json:"author"`
	Title        string  `json:"title"`
	URL          string  `json:"url"`
	Domain       string  `json:"domain"`
	Subreddit    string  `json:"subreddit"`
	SubredditId  string  `json:"subreddit_id"`
	FullId       string  `json:"name"`
	Id           string  `json:"id"`
	Permalink    string  `json:"permalink"`
	Selftext     string  `json:"selftext"`
	ThumbnailURL string  `json:"thumbnail"`
	DateCreated  float32 `json:"created_utc"`
	NumComments  int     `json:"num_comments"`
	Score        int     `json:"score"`
	// Ups and downs are fake to trick spammers
	Ups        int     `json:"ups"`
	Downs      int     `json:"downs"`
	IsNSFW     bool    `json:"over_18"`
	IsSelf     bool    `json:"is_self"`
	WasClicked bool    `json:"clicked"`
	IsSaved    bool    `json:"saved"`
	BannedBy   *string `json:"banned_by"`
}

// Sort headlines by popularity
type PopularitySort string

const (
	DefaultPopularity      PopularitySort = ""
	HotHeadlines                          = "hot"
	NewHeadlines                          = "new"
	RisingHeadlines                       = "rising"
	TopHeadlines                          = "top"
	ControversialHeadlines                = "controversial"
)

// Sort headlines by age
type AgeSort string

const (
	DefaultAge AgeSort = ""
	ThisHour           = "hour"
	ThisMonth          = "month"
	ThisYear           = "year"
	AllTime            = "all"
)

type Headlines []*Headline

// FullPermalink returns the full URL of a headline.
func (h *Headline) FullPermalink() string {
	return "http://reddit.com" + h.Permalink
}

// String returns the string representation of a headline.
func (h *Headline) String() string {
	plural := ""
	if h.NumComments != 1 {
		plural = "s"
	}
	comments := fmt.Sprintf("%d comment%s", h.NumComments, plural)
	return fmt.Sprintf("%d - %s (%s)", h.Score, h.Title, comments)
}

// DefaultHeadlines returns a slice of headlines on the default reddit frontpage.
func DefaultHeadlines() (Headlines, error) {
	url := "http://www.reddit.com/.json"
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}

// SubredditHeadlines returns a slice of headlines on the given subreddit.
func SubredditHeadlines(subreddit string) (Headlines, error) {
	url := fmt.Sprintf("http://www.reddit.com/r/%s.json", subreddit)
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}

// SortedHeadlines will return headlines from a subreddit (or homepage if "") by popularity and age
func SortedHeadlines(subreddit string, popularity PopularitySort, age AgeSort) (Headlines, error) {
	if age != DefaultAge {
		switch popularity {
		case NewHeadlines, RisingHeadlines, HotHeadlines:
			return nil, fmt.Errorf("Cannot sort %s by %s", popularity, age)
		}
	}

	url := "http://reddit.com/"

	if subreddit != "" {
		url = fmt.Sprintf("http://%s.reddit.com/", subreddit)
	}

	if popularity != DefaultPopularity {
		if popularity == NewHeadlines || popularity == RisingHeadlines {
			url = fmt.Sprintf("%snew.json?sort=%s", url, popularity)
		} else {
			url = fmt.Sprintf("%s%s.json?sort=%s", url, popularity, popularity)
		}
	} else {
		url = fmt.Sprintf("%s.json", url)
	}

	if age != DefaultAge {
		if popularity != DefaultPopularity {
			url = fmt.Sprintf("%s&t=%s", url, age)
		} else {
			url = fmt.Sprintf("%s?t=%s", url, age)
		}
	}

	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil

}
<|endoftext|>


<commit_before>package messagebus

import (
	"encoding/json"
	"fmt"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/route-registrar/config"
	"github.com/nats-io/nats"
)

//go:generate counterfeiter . MessageBus

type MessageBus interface {
	Connect(servers []config.MessageBusServer) error
	SendMessage(subject string, host string, route config.Route, privateInstanceId string) error
	Close()
}

type msgBus struct {
	natsConn *nats.Conn
	logger   lager.Logger
}

type Message struct {
	URIs              []string          `json:"uris"`
	Host              string            `json:"host"`
	Port              int               `json:"port"`
	Tags              map[string]string `json:"tags"`
	RouteServiceUrl   string            `json:"route_service_url,omitempty"`
	PrivateInstanceId string            `json:"private_instance_id"`
}

func NewMessageBus(logger lager.Logger) MessageBus {
	return &msgBus{
		logger: logger,
	}
}

func (m *msgBus) Connect(servers []config.MessageBusServer) error {
	m.logger.Debug("Connecting to nats", lager.Data{"servers": servers})

	var natsServers []string
	for _, server := range servers {
		m.logger.Info(
			"Adding NATS server",
			lager.Data{"server": server},
		)
		natsServers = append(
			natsServers,
			fmt.Sprintf("nats://%s:%s@%s", server.User, server.Password, server.Host),
		)
	}

	opts := nats.DefaultOptions
	opts.Servers = natsServers
	opts.PingInterval = 20 * time.Second
	natsConn, err := opts.Connect()

	if err != nil {
		return err
	}

	m.natsConn = natsConn

	return nil
}

func (m msgBus) SendMessage(subject string, host string, route config.Route, privateInstanceId string) error {
	m.logger.Debug("Creating message", lager.Data{"subject": subject, "host": host, "route": route, "privateInstanceId": privateInstanceId})

	msg := &Message{
		URIs:              route.URIs,
		Host:              host,
		Port:              route.Port,
		Tags:              route.Tags,
		RouteServiceUrl:   route.RouteServiceUrl,
		PrivateInstanceId: privateInstanceId,
	}

	json, err := json.Marshal(msg)
	if err != nil {
		// Untested as we cannot force json.Marshal to return error.
		return err
	}

	m.logger.Debug("Publishing message", lager.Data{"msg": string(json)})

	return m.natsConn.Publish(subject, json)
}

func (m msgBus) Close() {
	m.natsConn.Close()
}
<commit_msg>Avoid logging sensitive information<commit_after>package messagebus

import (
	"encoding/json"
	"fmt"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/route-registrar/config"
	"github.com/nats-io/nats"
)

//go:generate counterfeiter . MessageBus

type MessageBus interface {
	Connect(servers []config.MessageBusServer) error
	SendMessage(subject string, host string, route config.Route, privateInstanceId string) error
	Close()
}

type msgBus struct {
	natsConn *nats.Conn
	logger   lager.Logger
}

type Message struct {
	URIs              []string          `json:"uris"`
	Host              string            `json:"host"`
	Port              int               `json:"port"`
	Tags              map[string]string `json:"tags"`
	RouteServiceUrl   string            `json:"route_service_url,omitempty"`
	PrivateInstanceId string            `json:"private_instance_id"`
}

func NewMessageBus(logger lager.Logger) MessageBus {
	return &msgBus{
		logger: logger,
	}
}

func (m *msgBus) Connect(servers []config.MessageBusServer) error {

	var natsServers []string
	for _, server := range servers {
		natsServers = append(
			natsServers,
			fmt.Sprintf("nats://%s:%s@%s", server.User, server.Password, server.Host),
		)
	}

	opts := nats.DefaultOptions
	opts.Servers = natsServers
	opts.PingInterval = 20 * time.Second
	natsConn, err := opts.Connect()

	if err != nil {
		return err
	}

	m.natsConn = natsConn

	return nil
}

func (m msgBus) SendMessage(subject string, host string, route config.Route, privateInstanceId string) error {
	m.logger.Debug("Creating message", lager.Data{"subject": subject, "host": host, "route": route, "privateInstanceId": privateInstanceId})

	msg := &Message{
		URIs:              route.URIs,
		Host:              host,
		Port:              route.Port,
		Tags:              route.Tags,
		RouteServiceUrl:   route.RouteServiceUrl,
		PrivateInstanceId: privateInstanceId,
	}

	json, err := json.Marshal(msg)
	if err != nil {
		// Untested as we cannot force json.Marshal to return error.
		return err
	}

	m.logger.Debug("Publishing message", lager.Data{"msg": string(json)})

	return m.natsConn.Publish(subject, json)
}

func (m msgBus) Close() {
	m.natsConn.Close()
}
<|endoftext|>


<commit_before>/*
	Copyright (c) 2016 Christopher Young
	Distributable under the terms of The "BSD New"" License
	that can be found in the LICENSE file, herein included
	as part of this header.

	flowfast.go: Counts inputs from ADS1115, sends over a websocket.
*/

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	_ "github.com/mattn/go-sqlite3"
	"github.com/op/go-logging"
	"github.com/paulbellamy/ratecounter"
	"golang.org/x/net/websocket"
	"math"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	GALLONS_PER_CLICK = 1 / 68000.0 // FT-60 K-factor: 68,000.
	SQLITE_DB_FILE    = "./test.db"
	LISTEN_ADDR       = ":8081"
)

type FlowStats struct {
	EvaluatedTime time.Time // Time when the counters were evaluated.
	Flow_Total    float64
	// units=gallons.
	Flow_LastSecond   float64
	Flow_LastMinute   float64
	Flow_MaxPerMinute float64
	// units=GPH.
	Flow_LastSecond_GPH      float64
	Flow_LastMinute_GPH      float64
	Flow_MaxPerMinute_GPH    float64
	Flow_LastHour_Actual_GPH float64
	// Rate counters.
	flow_total_raw   uint64
	flow_last_second *ratecounter.RateCounter
	flow_last_minute *ratecounter.RateCounter
	flow_last_hour   *ratecounter.RateCounter

	mu *sync.Mutex
}

type fuel_log struct {
	log_date_start time.Time
	log_date_end   time.Time
	flow           float64
}

var flow FlowStats

var logger = logging.MustGetLogger("flowfast")

func statusWebSocket(conn *websocket.Conn) {
	ticker := time.NewTicker(1 * time.Second)

	last_update := time.Now()
	for {
		<-ticker.C

		flow.mu.Lock()
		updateJSON, _ := json.Marshal(&flow) //TODO.
		flow.mu.Unlock()

		conn.Write(updateJSON)
		t := time.Now()
		logChan <- fuel_log{log_date_start: last_update, log_date_end: t, flow: flow.Flow_LastSecond}
		last_update = t
	}
}

func startWebListener() {
	http.HandleFunc("/",
		func(w http.ResponseWriter, req *http.Request) {
			s := websocket.Server{
				Handler: websocket.Handler(statusWebSocket)}
			s.ServeHTTP(w, req)
		})

	logger.Debugf("listening on %s.\n", LISTEN_ADDR)
	err := http.ListenAndServe(LISTEN_ADDR, nil)
	if err != nil {
		logger.Errorf("can't listen on socket: %s\n", err.Error())
		os.Exit(-1)
	}
}

var i2cbus embd.I2CBus

var inputChan chan float64

// Re-calculate stats every second.
func statsCalculator() {
	ticker := time.NewTicker(1 * time.Second)
	for {
		<-ticker.C
		flow.mu.Lock()

		flow.EvaluatedTime = time.Now()

		flow.Flow_Total = float64(flow.flow_total_raw) * GALLONS_PER_CLICK
		flow.Flow_LastSecond = float64(flow.flow_last_second.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastMinute = float64(flow.flow_last_minute.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastHour_Actual_GPH = float64(flow.flow_last_hour.Rate()) * GALLONS_PER_CLICK

		// Calculate maximums.
		if flow.Flow_LastMinute > flow.Flow_MaxPerMinute {
			flow.Flow_MaxPerMinute = flow.Flow_LastMinute
			flow.Flow_MaxPerMinute_GPH = flow.Flow_MaxPerMinute * float64(60.0) // Extrapolate.
		}

		// Extrapolate "GPH" numbers for the Second and Minute flow values.
		flow.Flow_LastSecond_GPH = flow.Flow_LastSecond * float64(3600.0)
		flow.Flow_LastMinute_GPH = flow.Flow_LastMinute * float64(60.0)

		flow.mu.Unlock()
	}
}

func processInput() {
	lastMeasurement := make([]float64, 0)
	lows := make([]float64, 0)
	highs := make([]float64, 0)
	calibrated := false

	var lowMean float64
	var lowStdev float64

	var highMean float64
	var highStdev float64

	for {
		mv := <-inputChan

		countCondition := false

		if len(lastMeasurement) == 0 {
			// This is the first measurement. Can't compare against anything.
			lastMeasurement = append(lastMeasurement, mv)
			continue
		}

		// Threshold is 1000mV of difference. Count only the leading edge.
		if !calibrated && mv > lastMeasurement[0] && (mv-lastMeasurement[0]) > float64(1000.0) {
			logger.Debugf("countCondition %f -> %f!\n", lastMeasurement[0], mv)
			countCondition = true

			// Calibrate functions.
			lows = append(lows, lastMeasurement[0])
			highs = append(highs, mv)
			if len(lows) >= 1000 && len(highs) >= 1000 {
				logger.Debugf("calibrating...\n")
				lowMean, lowStdev = removeOutliers(lows)
				logger.Debugf("lowMean=%f, lowStdev=%f\n", lowMean, lowStdev)
				highMean, highStdev = removeOutliers(highs)
				logger.Debugf("highMean=%f, highStdev=%f\n", highMean, highStdev)

				calibrated = true
			}
		}

		if calibrated && (math.Abs(lastMeasurement[0]-lowMean) < 2*lowStdev) && (math.Abs(mv-highMean) < 2*highStdev) {
			logger.Debugf("countCondition [calibrated] %f -> %f!\n", lastMeasurement[0], mv)
			countCondition = true
		}

		lastMeasurement[0] = mv
		if countCondition {
			flow.flow_total_raw++
			flow.flow_last_second.Incr(1)
			flow.flow_last_minute.Incr(1)
			flow.flow_last_hour.Incr(1)
		}
	}
}

// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.cpp
// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.h

func writeBitsW(bus embd.I2CBus, reg byte, bit_start, val_len uint, val uint16) {
	cur_val, err := bus.ReadWordFromReg(0x48, reg)
	if err != nil {
		logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		return
	}

	mask := uint16(((1 << val_len) - 1) << (bit_start - val_len + 1))
	val = val << (bit_start - val_len + 1)
	val &= mask
	cur_val &= ^(mask)
	cur_val |= val
	bus.WriteWordToReg(0x48, reg, cur_val)
}

func readADS1115() {
	inputChan = make(chan float64, 1024)

	go processInput()
	go statsCalculator()

	i2cbus = embd.NewI2CBus(1) //TODO: error checking.

	// Set up the device. ADS1115::setRate().
	writeBitsW(i2cbus, 0x01, 7, 3, 0x07)  // 860 samples/sec.
	writeBitsW(i2cbus, 0x01, 8, 1, 0)     // ADS1115_MODE_CONTINUOUS.
	writeBitsW(i2cbus, 0x01, 11, 3, 0x00) // +/-6.144V. ADS1115_PGA_6P144. ADS1115_MV_6P144. 0.187500 mV div.
	writeBitsW(i2cbus, 0x01, 14, 3, 0x00) // setMultiplexer(ADS1115_MUX_P0_N1).

	for {
		v, err := i2cbus.ReadWordFromReg(0x48, 0x00)

		cv := int16(v)
		mv := float64(cv) * float64(0.187500) // units=mV.

		if err != nil {
			logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		}

		inputChan <- mv
		time.Sleep(1 * time.Millisecond) // 1 kHz. Oversampling.
	}

	return
}

var logChan chan fuel_log

// Logs fuel data to an SQLite database.
func dbLogger() {
	logChan = make(chan fuel_log, 1024)

	// Check if we need to create a new database.
	createDatabase := false
	if _, err := os.Stat(SQLITE_DB_FILE); os.IsNotExist(err) {
		createDatabase = true
		logger.Debugf("creating new database '%s'.\n", SQLITE_DB_FILE)
	}

	db, err := sql.Open("sqlite3", SQLITE_DB_FILE)
	if err != nil {
		logger.Errorf("sql.Open(): %s\n", err.Error())
	}
	defer db.Close()

	if createDatabase {
		createSmt := `
			CREATE TABLE fuel_flow (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, log_date_start INTEGER, log_date_end INTEGER, flow REAL);
		`

		_, err = db.Exec(createSmt)
		if err != nil {
			logger.Errorf("%q: %s\n", err, createSmt)
			return
		}
	}

	for {
		f := <-logChan
		//FIXME: Timestamps here are a hack.
		q := fmt.Sprintf("INSERT INTO fuel_flow(log_date_start, log_date_end, flow) values(%d, %d, %f)", f.log_date_start.Unix(), f.log_date_end.Unix(), f.flow)
		_, err = db.Exec(q)
		if err != nil {
			logger.Errorf("stmt.Exec(): %s\n", err.Error())
		}
	}
}

func main() {
	// Set up logging for stdout (colors).
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logFormat := logging.MustStringFormatter(`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`)
	logBackendFormatter := logging.NewBackendFormatter(logBackend, logFormat)

	// Set up logging for file.
	logFileFp, err := os.OpenFile("/var/log/flowfast.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		logger.Errorf("Failed to open '%s': %s\n", "/var/log/flowfast.log", err.Error())
		return
	}
	defer logFileFp.Close()
	logFileBackend := logging.NewLogBackend(logFileFp, "", 0)
	logFileBackendFormatter := logging.NewBackendFormatter(logFileBackend, logFormat)
	logging.SetBackend(logBackendFormatter, logFileBackendFormatter)

	// Set up rate counters and mutex.
	flow.flow_last_second = ratecounter.NewRateCounter(1 * time.Second)
	flow.flow_last_minute = ratecounter.NewRateCounter(1 * time.Minute)
	flow.flow_last_hour = ratecounter.NewRateCounter(1 * time.Hour)
	flow.mu = &sync.Mutex{}

	go startWebListener()
	go dbLogger()
	go readADS1115()

	// Wait indefinitely.
	select {}
}
<commit_msg>Refactor detect code.<commit_after>/*
	Copyright (c) 2016 Christopher Young
	Distributable under the terms of The "BSD New"" License
	that can be found in the LICENSE file, herein included
	as part of this header.

	flowfast.go: Counts inputs from ADS1115, sends over a websocket.
*/

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	_ "github.com/mattn/go-sqlite3"
	"github.com/op/go-logging"
	"github.com/paulbellamy/ratecounter"
	"golang.org/x/net/websocket"
	"math"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	GALLONS_PER_CLICK = 1 / 68000.0 // FT-60 K-factor: 68,000.
	SQLITE_DB_FILE    = "./test.db"
	LISTEN_ADDR       = ":8081"
)

type FlowStats struct {
	EvaluatedTime time.Time // Time when the counters were evaluated.
	Flow_Total    float64
	// units=gallons.
	Flow_LastSecond   float64
	Flow_LastMinute   float64
	Flow_MaxPerMinute float64
	// units=GPH.
	Flow_LastSecond_GPH      float64
	Flow_LastMinute_GPH      float64
	Flow_MaxPerMinute_GPH    float64
	Flow_LastHour_Actual_GPH float64
	// Rate counters.
	flow_total_raw   uint64
	flow_last_second *ratecounter.RateCounter
	flow_last_minute *ratecounter.RateCounter
	flow_last_hour   *ratecounter.RateCounter

	mu *sync.Mutex
}

type fuel_log struct {
	log_date_start time.Time
	log_date_end   time.Time
	flow           float64
}

var flow FlowStats

var logger = logging.MustGetLogger("flowfast")

func statusWebSocket(conn *websocket.Conn) {
	ticker := time.NewTicker(1 * time.Second)

	last_update := time.Now()
	for {
		<-ticker.C

		flow.mu.Lock()
		updateJSON, _ := json.Marshal(&flow) //TODO.
		flow.mu.Unlock()

		conn.Write(updateJSON)
		t := time.Now()
		logChan <- fuel_log{log_date_start: last_update, log_date_end: t, flow: flow.Flow_LastSecond}
		last_update = t
	}
}

func startWebListener() {
	http.HandleFunc("/",
		func(w http.ResponseWriter, req *http.Request) {
			s := websocket.Server{
				Handler: websocket.Handler(statusWebSocket)}
			s.ServeHTTP(w, req)
		})

	logger.Debugf("listening on %s.\n", LISTEN_ADDR)
	err := http.ListenAndServe(LISTEN_ADDR, nil)
	if err != nil {
		logger.Errorf("can't listen on socket: %s\n", err.Error())
		os.Exit(-1)
	}
}

var i2cbus embd.I2CBus

var inputChan chan float64

// Re-calculate stats every second.
func statsCalculator() {
	ticker := time.NewTicker(1 * time.Second)
	for {
		<-ticker.C
		flow.mu.Lock()

		flow.EvaluatedTime = time.Now()

		flow.Flow_Total = float64(flow.flow_total_raw) * GALLONS_PER_CLICK
		flow.Flow_LastSecond = float64(flow.flow_last_second.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastMinute = float64(flow.flow_last_minute.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastHour_Actual_GPH = float64(flow.flow_last_hour.Rate()) * GALLONS_PER_CLICK

		// Calculate maximums.
		if flow.Flow_LastMinute > flow.Flow_MaxPerMinute {
			flow.Flow_MaxPerMinute = flow.Flow_LastMinute
			flow.Flow_MaxPerMinute_GPH = flow.Flow_MaxPerMinute * float64(60.0) // Extrapolate.
		}

		// Extrapolate "GPH" numbers for the Second and Minute flow values.
		flow.Flow_LastSecond_GPH = flow.Flow_LastSecond * float64(3600.0)
		flow.Flow_LastMinute_GPH = flow.Flow_LastMinute * float64(60.0)

		flow.mu.Unlock()
	}
}

func processInput() {

	inputHigh := false

	for {
		mv := <-inputChan

		countCondition := false

		// 0V low.
		if math.Abs(mv-0.0) <= float64(1000.0) { // Low.
			inputHigh = false
		}

		// 5V high.
		if !inputHigh && math.Abs(mv-5000.0) <= float64(1000.0) { // High.
			inputHigh = true
			countCondition = true
			logger.Debugf("count! %f %f\n", lastMeasurement, mv)
		}

		if countCondition {
			flow.flow_total_raw++
			flow.flow_last_second.Incr(1)
			flow.flow_last_minute.Incr(1)
			flow.flow_last_hour.Incr(1)
		}
	}
}

// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.cpp
// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.h

func writeBitsW(bus embd.I2CBus, reg byte, bit_start, val_len uint, val uint16) {
	cur_val, err := bus.ReadWordFromReg(0x48, reg)
	if err != nil {
		logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		return
	}

	mask := uint16(((1 << val_len) - 1) << (bit_start - val_len + 1))
	val = val << (bit_start - val_len + 1)
	val &= mask
	cur_val &= ^(mask)
	cur_val |= val
	bus.WriteWordToReg(0x48, reg, cur_val)
}

func readADS1115() {
	inputChan = make(chan float64, 1024)

	go processInput()
	go statsCalculator()

	i2cbus = embd.NewI2CBus(1) //TODO: error checking.

	// Set up the device. ADS1115::setRate().
	writeBitsW(i2cbus, 0x01, 7, 3, 0x07)  // 860 samples/sec.
	writeBitsW(i2cbus, 0x01, 8, 1, 0)     // ADS1115_MODE_CONTINUOUS.
	writeBitsW(i2cbus, 0x01, 11, 3, 0x00) // +/-6.144V. ADS1115_PGA_6P144. ADS1115_MV_6P144. 0.187500 mV div.
	writeBitsW(i2cbus, 0x01, 14, 3, 0x00) // setMultiplexer(ADS1115_MUX_P0_N1).

	for {
		v, err := i2cbus.ReadWordFromReg(0x48, 0x00)

		cv := int16(v)
		mv := float64(cv) * float64(0.187500) // units=mV.

		if err != nil {
			logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		}

		inputChan <- mv
		time.Sleep(1 * time.Millisecond) // 1 kHz. Oversampling.
	}

	return
}

var logChan chan fuel_log

// Logs fuel data to an SQLite database.
func dbLogger() {
	logChan = make(chan fuel_log, 1024)

	// Check if we need to create a new database.
	createDatabase := false
	if _, err := os.Stat(SQLITE_DB_FILE); os.IsNotExist(err) {
		createDatabase = true
		logger.Debugf("creating new database '%s'.\n", SQLITE_DB_FILE)
	}

	db, err := sql.Open("sqlite3", SQLITE_DB_FILE)
	if err != nil {
		logger.Errorf("sql.Open(): %s\n", err.Error())
	}
	defer db.Close()

	if createDatabase {
		createSmt := `
			CREATE TABLE fuel_flow (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, log_date_start INTEGER, log_date_end INTEGER, flow REAL);
		`

		_, err = db.Exec(createSmt)
		if err != nil {
			logger.Errorf("%q: %s\n", err, createSmt)
			return
		}
	}

	for {
		f := <-logChan
		//FIXME: Timestamps here are a hack.
		q := fmt.Sprintf("INSERT INTO fuel_flow(log_date_start, log_date_end, flow) values(%d, %d, %f)", f.log_date_start.Unix(), f.log_date_end.Unix(), f.flow)
		_, err = db.Exec(q)
		if err != nil {
			logger.Errorf("stmt.Exec(): %s\n", err.Error())
		}
	}
}

func main() {
	// Set up logging for stdout (colors).
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logFormat := logging.MustStringFormatter(`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`)
	logBackendFormatter := logging.NewBackendFormatter(logBackend, logFormat)

	// Set up logging for file.
	logFileFp, err := os.OpenFile("/var/log/flowfast.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		logger.Errorf("Failed to open '%s': %s\n", "/var/log/flowfast.log", err.Error())
		return
	}
	defer logFileFp.Close()
	logFileBackend := logging.NewLogBackend(logFileFp, "", 0)
	logFileBackendFormatter := logging.NewBackendFormatter(logFileBackend, logFormat)
	logging.SetBackend(logBackendFormatter, logFileBackendFormatter)

	// Set up rate counters and mutex.
	flow.flow_last_second = ratecounter.NewRateCounter(1 * time.Second)
	flow.flow_last_minute = ratecounter.NewRateCounter(1 * time.Minute)
	flow.flow_last_hour = ratecounter.NewRateCounter(1 * time.Hour)
	flow.mu = &sync.Mutex{}

	go startWebListener()
	go dbLogger()
	go readADS1115()

	// Wait indefinitely.
	select {}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"
)

const GAMETIME int = 60

// Positions
const QB int = 0
const RB int = 1
const WR int = 2
const OL int = 3
const DL int = 4
const LB int = 5
const DB int = 6

// Map #'s to positions
var offPos = map[int]int{
	0:  QB,
	1:  RB,
	2:  RB,
	3:  WR,
	4:  WR,
	5:  WR,
	6:  WR,
	7:  WR,
	8:  OL,
	9:  OL,
	10: OL,
}

var defPos = map[int]int{
	0:  DL,
	1:  DL,
	2:  DL,
	3:  DL,
	4:  LB,
	5:  LB,
	6:  LB,
	7:  DB,
	8:  DB,
	9:  DB,
	10: DB,
}

var offPlayerChan = make(chan Player)
var defPlayerChan = make(chan Player)

func main() {
	setupTeams()
	go handleOffPlayerChannel(offPlayerChan)
	go handleDefPlayerChannel(defPlayerChan)

	fmt.Println("Ready for kickoff!")
	fmt.Println("The", stallions.TeamName, "vs The", mustangs.TeamName, ". Should be a good one!")

	ticks := 0

	for ticks < GAMETIME {
		snapTheBall()
		time.Sleep(5 * time.Second)
		ticks++
	}

}

func handleOffPlayerChannel(players <-chan Player) {
	for player := range players {
		switch player.OffPos {
		case QB:
			fmt.Println("Quarter back!")
		case RB:
			fmt.Println("Running back!")
		case WR:
			fmt.Println("Wide receivers!")
		case OL:
			fmt.Println("Offensive lineman!")
		default:
			panic("I don't know what position this is!")

		}
	}
}

func handleDefPlayerChannel(players <-chan Player) {
	for player := range players {
		switch player.DefPos {
		case DL:
			fmt.Println("Defensive line!")
		case LB:
			fmt.Println("Line baker!")
		case DB:
			fmt.Println("Defensive back!")
		default:
			panic("I don't know what position this is!")

		}
	}
}

func snapTheBall() {
	go doOffense()
	go doDefense()
}

func doOffense() {
	for _, player := range stallions.Players {
		offPlayerChan <- player
	}
}

func doDefense() {
	for _, player := range mustangs.Players {
		defPlayerChan <- player
	}
}

func setupTeams() {
	teams := []*Team{&stallions, &mustangs}
	for _, team := range teams {
		for i := 0; i < 11; i++ {
			player := createPlayer(i, team)
			team.Players[i] = player
		}
	}
}

func createPlayer(num int, team *Team) Player {
	player := Player{Number: num}
	player.Name = fmt.Sprintf("%v %v", team.TeamName, num)
	player.OffPos = offPos[num]
	player.DefPos = defPos[num]
	return player
}

var stallions = Team{TeamName: "Stallions", Color: "White"}
var mustangs = Team{TeamName: "Mustangs", Color: "Black"}

type Team struct {
	TeamName string
	Color    string
	Players  [11]Player
}

type Player struct {
	Name   string
	Number int
	OffPos int
	DefPos int
}
<commit_msg>Building A Football Game Using Go (Part 5)<commit_after>package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

const GAMETIME int = 60

// Positions
const QB int = 0
const RB int = 1
const WR int = 2
const OL int = 3
const DL int = 4
const LB int = 5
const DB int = 6

// Map #'s to positions
var offPos = map[int]int{
	0:  QB,
	1:  RB,
	2:  WR,
	3:  WR,
	4:  WR,
	5:  WR,
	6:  OL,
	7:  OL,
	8:  OL,
	9:  OL,
	10: OL,
}

var defPos = map[int]int{
	0:  DL,
	1:  DL,
	2:  DL,
	3:  DL,
	4:  LB,
	5:  LB,
	6:  LB,
	7:  DB,
	8:  DB,
	9:  DB,
	10: DB,
}

var offPlayerChan = make(chan Player)
var defPlayerChan = make(chan Player)
var isHiked = false

func main() {
	setupTeams()
	go handleOffPlayerChannel(offPlayerChan)
	go handleDefPlayerChannel(defPlayerChan)

	fmt.Println("Ready for kickoff!")
	fmt.Println("The", stallions.TeamName, "vs The", mustangs.TeamName)

	ticks := 0

	for ticks < GAMETIME {
		snapTheBall()
		time.Sleep(5 * time.Second)
		ticks++
	}

}

func printOffAction(player *Player) {
	if isHiked {
		print("!")
	} else {
		print(".")
	}
}
func printDefAction(player *Player) {
	if isHiked {
		print("$")
	} else {
		print("-")
	}
}

func handleOffPlayerChannel(players <-chan Player) {
	for player := range players {
		duration, _ := time.ParseDuration("200ms")
		time.Sleep(duration)

		switch player.OffPos {
		case QB:
			print("Hike!")
			isHiked = true
		case RB:
			printOffAction(&player)
		case WR:
			printOffAction(&player)
		case OL:
			printOffAction(&player)
		default:
			panic("I don't know what position this is!")

		}
	}

}

func handleDefPlayerChannel(players <-chan Player) {
	for player := range players {
		go doPlayerDefense(&player)
	}
}

func doPlayerDefense(player *Player) {
	m := sync.Mutex{}
	m.Lock()

	for !isHiked {
		time.Sleep(time.Duration(10 * time.Millisecond))
	}
	m.Unlock()

	duration, _ := time.ParseDuration(fmt.Sprintf("%vms", rand.Intn(100)))
	time.Sleep(duration)

	switch player.DefPos {

	case DL:
		printDefAction(player)
	case LB:
		printDefAction(player)
	case DB:
		printDefAction(player)
	default:
		panic("I don't know what position this is!")

	}
}

func snapTheBall() {
	go doOffense()
	go doDefense()
}

func doOffense() {
	for _, player := range stallions.Players {
		offPlayerChan <- player
	}
}

func doDefense() {
	for _, player := range mustangs.Players {
		defPlayerChan <- player
	}
}

func setupTeams() {
	teams := []*Team{&stallions, &mustangs}
	for _, team := range teams {
		for i := 0; i < 11; i++ {
			player := createPlayer(i, team)
			team.Players[i] = player
		}
	}
}

func createPlayer(num int, team *Team) Player {
	player := Player{Number: num}
	player.Name = fmt.Sprintf("%v %v", team.TeamName, num)
	player.OffPos = offPos[num]
	player.DefPos = defPos[num]
	return player
}

var stallions = Team{TeamName: "Stallions", Color: "White"}
var mustangs = Team{TeamName: "Mustangs", Color: "Black"}

type Team struct {
	TeamName string
	Color    string
	Players  [11]Player
}

type Player struct {
	Name   string
	Number int
	OffPos int
	DefPos int
}
<|endoftext|>