<commit_before>// Copyright 2014 The rkt Authors
//
// 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 common defines values shared by different parts
// of rkt (e.g. stage0 and stage1)
package common

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/aci"
	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
)

const (
	stage1Dir = "/stage1"
	stage2Dir = "/opt/stage2"

	EnvLockFd               = "RKT_LOCK_FD"
	Stage1IDFilename        = "stage1ID"
	OverlayPreparedFilename = "overlay-prepared"

	MetadataServicePort    = 2375
	MetadataServiceRegSock = "/run/rkt/metadata-svc.sock"

	DefaultLocalConfigDir  = "/etc/rkt"
	DefaultSystemConfigDir = "/usr/lib/rkt"
)

// Stage1ImagePath returns the path where the stage1 app image (unpacked ACI) is rooted,
// (i.e. where its contents are extracted during stage0).
func Stage1ImagePath(root string) string {
	return filepath.Join(root, stage1Dir)
}

// Stage1RootfsPath returns the path to the stage1 rootfs
func Stage1RootfsPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.RootfsDir)
}

// Stage1ManifestPath returns the path to the stage1's manifest file inside the expanded ACI.
func Stage1ManifestPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.ManifestFile)
}

// PodManifestPath returns the path in root to the Pod Manifest
func PodManifestPath(root string) string {
	return filepath.Join(root, "pod")
}

// AppsPath returns the path where the apps within a pod live.
func AppsPath(root string) string {
	return filepath.Join(Stage1RootfsPath(root), stage2Dir)
}

// AppPath returns the path to an app's rootfs.
func AppPath(root string, appName types.ACName) string {
	return filepath.Join(AppsPath(root), appName.String())
}

// AppRootfsPath returns the path to an app's rootfs.
func AppRootfsPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.RootfsDir)
}

// RelAppPath returns the path of an app relative to the stage1 chroot.
func RelAppPath(appName types.ACName) string {
	return filepath.Join(stage2Dir, appName.String())
}

// RelAppRootfsPath returns the path of an app's rootfs relative to the stage1 chroot.
func RelAppRootfsPath(appName types.ACName) string {
	return filepath.Join(RelAppPath(appName), aci.RootfsDir)
}

// ImageManifestPath returns the path to the app's manifest file inside a pod.
func ImageManifestPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.ManifestFile)
}

// MetadataServicePublicURL returns the public URL used to host the metadata service
func MetadataServicePublicURL(ip net.IP, token string) string {
	return fmt.Sprintf("http://%v:%v/%v", ip, MetadataServicePort, token)
}

func GetRktLockFD() (int, error) {
	if v := os.Getenv(EnvLockFd); v != "" {
		fd, err := strconv.ParseUint(v, 10, 32)
		if err != nil {
			return -1, err
		}
		return int(fd), nil
	}
	return -1, fmt.Errorf("%v env var is not set", EnvLockFd)
}

// SupportsOverlay returns whether the system supports overlay filesystem
func SupportsOverlay() bool {
	exec.Command("modprobe", "overlay").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		fmt.Println("error opening /proc/filesystems")
		return false
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if s.Text() == "nodev\toverlay" {
			return true
		}
	}
	return false
}

// PrivateNetList implements the flag.Value interface to allow specification
// of -private-net with and without values
type PrivateNetList struct {
	mapping map[string]bool
}

func (l *PrivateNetList) String() string {
	return strings.Join(l.Strings(), ",")
}

func (l *PrivateNetList) Set(value string) error {
	if l.mapping == nil {
		l.mapping = make(map[string]bool)
	}
	for _, s := range strings.Split(value, ",") {
		l.mapping[s] = true
	}
	return nil
}

func (l *PrivateNetList) Type() string {
	return "privateNetList"
}

func (l *PrivateNetList) Strings() []string {
	var list []string
	for k, _ := range l.mapping {
		list = append(list, k)
	}
	return list
}

func (l *PrivateNetList) Any() bool {
	return len(l.mapping) > 0
}

func (l *PrivateNetList) All() bool {
	return l.mapping["all"]
}

func (l *PrivateNetList) Specific(net string) bool {
	return l.mapping[net]
}
<commit_msg>rkt:common: add SupportsUserNS() to check for CONFIG_USER_NS<commit_after>// Copyright 2014 The rkt Authors
//
// 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 common defines values shared by different parts
// of rkt (e.g. stage0 and stage1)
package common

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/aci"
	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
)

const (
	stage1Dir = "/stage1"
	stage2Dir = "/opt/stage2"

	EnvLockFd               = "RKT_LOCK_FD"
	Stage1IDFilename        = "stage1ID"
	OverlayPreparedFilename = "overlay-prepared"

	MetadataServicePort    = 2375
	MetadataServiceRegSock = "/run/rkt/metadata-svc.sock"

	DefaultLocalConfigDir  = "/etc/rkt"
	DefaultSystemConfigDir = "/usr/lib/rkt"
)

// Stage1ImagePath returns the path where the stage1 app image (unpacked ACI) is rooted,
// (i.e. where its contents are extracted during stage0).
func Stage1ImagePath(root string) string {
	return filepath.Join(root, stage1Dir)
}

// Stage1RootfsPath returns the path to the stage1 rootfs
func Stage1RootfsPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.RootfsDir)
}

// Stage1ManifestPath returns the path to the stage1's manifest file inside the expanded ACI.
func Stage1ManifestPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.ManifestFile)
}

// PodManifestPath returns the path in root to the Pod Manifest
func PodManifestPath(root string) string {
	return filepath.Join(root, "pod")
}

// AppsPath returns the path where the apps within a pod live.
func AppsPath(root string) string {
	return filepath.Join(Stage1RootfsPath(root), stage2Dir)
}

// AppPath returns the path to an app's rootfs.
func AppPath(root string, appName types.ACName) string {
	return filepath.Join(AppsPath(root), appName.String())
}

// AppRootfsPath returns the path to an app's rootfs.
func AppRootfsPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.RootfsDir)
}

// RelAppPath returns the path of an app relative to the stage1 chroot.
func RelAppPath(appName types.ACName) string {
	return filepath.Join(stage2Dir, appName.String())
}

// RelAppRootfsPath returns the path of an app's rootfs relative to the stage1 chroot.
func RelAppRootfsPath(appName types.ACName) string {
	return filepath.Join(RelAppPath(appName), aci.RootfsDir)
}

// ImageManifestPath returns the path to the app's manifest file inside a pod.
func ImageManifestPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.ManifestFile)
}

// MetadataServicePublicURL returns the public URL used to host the metadata service
func MetadataServicePublicURL(ip net.IP, token string) string {
	return fmt.Sprintf("http://%v:%v/%v", ip, MetadataServicePort, token)
}

func GetRktLockFD() (int, error) {
	if v := os.Getenv(EnvLockFd); v != "" {
		fd, err := strconv.ParseUint(v, 10, 32)
		if err != nil {
			return -1, err
		}
		return int(fd), nil
	}
	return -1, fmt.Errorf("%v env var is not set", EnvLockFd)
}

// SupportsOverlay returns whether the system supports overlay filesystem
func SupportsOverlay() bool {
	exec.Command("modprobe", "overlay").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		fmt.Println("error opening /proc/filesystems")
		return false
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if s.Text() == "nodev\toverlay" {
			return true
		}
	}
	return false
}

// SupportsUserNS returns whether the kernel has CONFIG_USER_NS set
func SupportsUserNS() bool {
	if _, err := os.Stat("/proc/self/uid_map"); err == nil {
		return true
	}

	return false
}

// PrivateNetList implements the flag.Value interface to allow specification
// of -private-net with and without values
type PrivateNetList struct {
	mapping map[string]bool
}

func (l *PrivateNetList) String() string {
	return strings.Join(l.Strings(), ",")
}

func (l *PrivateNetList) Set(value string) error {
	if l.mapping == nil {
		l.mapping = make(map[string]bool)
	}
	for _, s := range strings.Split(value, ",") {
		l.mapping[s] = true
	}
	return nil
}

func (l *PrivateNetList) Type() string {
	return "privateNetList"
}

func (l *PrivateNetList) Strings() []string {
	var list []string
	for k, _ := range l.mapping {
		list = append(list, k)
	}
	return list
}

func (l *PrivateNetList) Any() bool {
	return len(l.mapping) > 0
}

func (l *PrivateNetList) All() bool {
	return l.mapping["all"]
}

func (l *PrivateNetList) Specific(net string) bool {
	return l.mapping[net]
}
<|endoftext|>


<commit_before>package stats

import (
	"net/http"
	"time"
)

// LogResponseWritter wraps the standard http.ResponseWritter allowing for more
// verbose logging
type LogResponseWritter struct {
	status int
	size   int
	http.ResponseWriter
}

// Status provides an easy way to retrieve the status code
func (w *LogResponseWritter) Status() int {
	return w.status
}

// Size provides an easy way to retrieve the response size in bytes
func (w *LogResponseWritter) Size() int {
	return w.size
}

// Header returns & satisfies the http.ResponseWriter interface
func (w *LogResponseWritter) Header() http.Header {
	return w.ResponseWriter.Header()
}

// Write satisfies the http.ResponseWriter interface and
// captures data written, in bytes
func (w *LogResponseWritter) Write(data []byte) (int, error) {

	written, err := w.ResponseWriter.Write(data)
	w.size += written

	return written, err
}

// WriteHeader satisfies the http.ResponseWriter interface and
// allows us to cach the status code
func (w *LogResponseWritter) WriteHeader(statusCode int) {

	w.status = statusCode
	w.ResponseWriter.WriteHeader(statusCode)
}

// HTTPRequest contains information about the life of an http request
type HTTPRequest struct {
	URL                   string        `json:"url"`
	Method                string        `json:"method"`
	RequestContentLength  int64         `json:"reqContent"`
	Headers               http.Header   `json:"headers"`
	Start                 time.Time     `json:"start"`
	End                   time.Time     `json:"end"`
	Duration              time.Duration `json:"duration"`
	ResponseContentLength int64         `json:"resContent"`
	StatusCode            int           `json:"status"`
	HasErrors             bool          `json:"hasErrs"`
	Error                 string        `json:"err"`
	writer                *LogResponseWritter
	clientStats           *ClientStats
}

// NewHTTPRequest creates a new HTTPRequest for monitoring which wraps the ResponseWriter in order
// to collect stats so you need to call the Writer() function from the HTTPRequest created by this call
func (s *ClientStats) NewHTTPRequest(w http.ResponseWriter, r *http.Request) *HTTPRequest {

	return &HTTPRequest{
		Start:                time.Now().UTC(),
		URL:                  r.URL.String(),
		Method:               r.Method,
		RequestContentLength: r.ContentLength,
		Headers:              r.Header,
		writer:               &LogResponseWritter{status: 200, ResponseWriter: w},
		clientStats:          s,
	}
}

// Writer returns a wrapped http.ResponseWriter for logging purposes
func (r *HTTPRequest) Writer() http.ResponseWriter {
	return r.writer
}

// Failure records an HTTP failure and automatically completes the request
func (r *HTTPRequest) Failure(err string) {
	r.HasErrors = true
	r.Error = err
	r.Complete()
}

// Complete finalizes an HTTPRequest and logs it.
func (r *HTTPRequest) Complete() {

	r.End = time.Now().UTC()
	r.Duration = r.End.Sub(r.Start)
	r.ResponseContentLength = int64(r.writer.Size())
	r.StatusCode = r.writer.Status()
	r.clientStats.httpStats.Add(r)
}
<commit_msg>Updated Duration to always be nanoseconds<commit_after>package stats

import (
	"net/http"
	"time"
)

// LogResponseWritter wraps the standard http.ResponseWritter allowing for more
// verbose logging
type LogResponseWritter struct {
	status int
	size   int
	http.ResponseWriter
}

// Status provides an easy way to retrieve the status code
func (w *LogResponseWritter) Status() int {
	return w.status
}

// Size provides an easy way to retrieve the response size in bytes
func (w *LogResponseWritter) Size() int {
	return w.size
}

// Header returns & satisfies the http.ResponseWriter interface
func (w *LogResponseWritter) Header() http.Header {
	return w.ResponseWriter.Header()
}

// Write satisfies the http.ResponseWriter interface and
// captures data written, in bytes
func (w *LogResponseWritter) Write(data []byte) (int, error) {

	written, err := w.ResponseWriter.Write(data)
	w.size += written

	return written, err
}

// WriteHeader satisfies the http.ResponseWriter interface and
// allows us to cach the status code
func (w *LogResponseWritter) WriteHeader(statusCode int) {

	w.status = statusCode
	w.ResponseWriter.WriteHeader(statusCode)
}

// HTTPRequest contains information about the life of an http request
type HTTPRequest struct {
	URL                   string      `json:"url"`
	Method                string      `json:"method"`
	RequestContentLength  int64       `json:"reqContent"`
	Headers               http.Header `json:"headers"`
	Start                 time.Time   `json:"start"`
	End                   time.Time   `json:"end"`
	Duration              int64       `json:"duration"`
	ResponseContentLength int64       `json:"resContent"`
	StatusCode            int         `json:"status"`
	HasErrors             bool        `json:"hasErrs"`
	Error                 string      `json:"err"`
	writer                *LogResponseWritter
	clientStats           *ClientStats
}

// NewHTTPRequest creates a new HTTPRequest for monitoring which wraps the ResponseWriter in order
// to collect stats so you need to call the Writer() function from the HTTPRequest created by this call
func (s *ClientStats) NewHTTPRequest(w http.ResponseWriter, r *http.Request) *HTTPRequest {

	return &HTTPRequest{
		Start:                time.Now().UTC(),
		URL:                  r.URL.String(),
		Method:               r.Method,
		RequestContentLength: r.ContentLength,
		Headers:              r.Header,
		writer:               &LogResponseWritter{status: 200, ResponseWriter: w},
		clientStats:          s,
	}
}

// Writer returns a wrapped http.ResponseWriter for logging purposes
func (r *HTTPRequest) Writer() http.ResponseWriter {
	return r.writer
}

// Failure records an HTTP failure and automatically completes the request
func (r *HTTPRequest) Failure(err string) {
	r.HasErrors = true
	r.Error = err
	r.Complete()
}

// Complete finalizes an HTTPRequest and logs it.
func (r *HTTPRequest) Complete() {

	r.End = time.Now().UTC()
	r.Duration = r.End.Sub(r.Start).Nanoseconds()
	r.ResponseContentLength = int64(r.writer.Size())
	r.StatusCode = r.writer.Status()
	r.clientStats.httpStats.Add(r)
}
<|endoftext|>


<commit_before>package gost

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

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

type httpConnector struct {
	User *url.Userinfo
}

// HTTPConnector creates a Connector for HTTP proxy client.
// It accepts an optional auth info for HTTP Basic Authentication.
func HTTPConnector(user *url.Userinfo) Connector {
	return &httpConnector{User: user}
}

func (c *httpConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
	req := &http.Request{
		Method:     http.MethodConnect,
		URL:        &url.URL{Host: addr},
		Host:       addr,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
	}
	req.Header.Set("User-Agent", DefaultUserAgent)
	req.Header.Set("Proxy-Connection", "keep-alive")

	if c.User != nil {
		u := c.User.Username()
		p, _ := c.User.Password()
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
	}

	if err := req.Write(conn); err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Log(string(dump))
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Log(string(dump))
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%s", resp.Status)
	}

	return conn, nil
}

type httpHandler struct {
	options *HandlerOptions
}

// HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler {
	h := &httpHandler{
		options: &HandlerOptions{},
	}
	for _, opt := range opts {
		opt(h.options)
	}
	return h
}

func (h *httpHandler) Handle(conn net.Conn) {
	defer conn.Close()

	req, err := http.ReadRequest(bufio.NewReader(conn))
	if err != nil {
		log.Logf("[http] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		return
	}

	h.handleRequest(conn, req)
}

func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
	if req == nil {
		return
	}
	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
	}

	if req.Method == "PRI" || (req.Method != http.MethodConnect && req.URL.Scheme != "http") {
		resp := "HTTP/1.1 400 Bad Request\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp)
		}
		return
	}

	if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
		log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
		b := []byte("HTTP/1.1 403 Forbidden\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		conn.Write(b)
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		return
	}

	u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if Debug && (u != "" || p != "") {
		log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
	}
	if !authenticate(u, p, h.options.Users...) {
		log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
		resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
			"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		return
	}

	req.Header.Del("Proxy-Authorization")
	// req.Header.Del("Proxy-Connection")

	// try to get the actual host.
	if v := req.Header.Get("Gost-Target"); v != "" {
		if host, err := decodeServerName(v); err == nil {
			req.Host = host
		}
	}

	// forward http request
	lastNode := h.options.Chain.LastNode()
	if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
		h.forwardRequest(conn, req)
		return
	}

	host := req.Host
	if !strings.Contains(host, ":") {
		host += ":80"
	}

	cc, err := h.options.Chain.Dial(host)
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if req.Method == http.MethodConnect {
		b := []byte("HTTP/1.1 200 Connection established\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(b))
		}
		conn.Write(b)
	} else {
		req.Header.Del("Proxy-Connection")

		if err = req.Write(cc); err != nil {
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
			return
		}
	}

	log.Logf("[http] %s <-> %s", cc.LocalAddr(), host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", cc.LocalAddr(), host)
}

func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
	if h.options.Chain.IsEmpty() {
		return
	}
	lastNode := h.options.Chain.LastNode()

	cc, err := h.options.Chain.Conn()
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), lastNode.Addr, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if lastNode.User != nil {
		s := lastNode.User.String()
		if _, set := lastNode.User.Password(); !set {
			s += ":"
		}
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
	}

	cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
	if !req.URL.IsAbs() {
		req.URL.Scheme = "http" // make sure that the URL is absolute
	}
	if err = req.WriteProxy(cc); err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
		return
	}
	cc.SetWriteDeadline(time.Time{})

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
	return
}

func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
	if proxyAuth == "" {
		return
	}

	if !strings.HasPrefix(proxyAuth, "Basic ") {
		return
	}
	c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
	if err != nil {
		return
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return
	}

	return cs[:s], cs[s+1:], true
}

func authenticate(username, password string, users ...*url.Userinfo) bool {
	if len(users) == 0 {
		return true
	}

	for _, user := range users {
		u := user.Username()
		p, _ := user.Password()
		if (u == username && p == password) ||
			(u == username && p == "") ||
			(u == "" && p == password) {
			return true
		}
	}
	return false
}
<commit_msg>Fix http Handle (#231)<commit_after>package gost

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

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

type httpConnector struct {
	User *url.Userinfo
}

// HTTPConnector creates a Connector for HTTP proxy client.
// It accepts an optional auth info for HTTP Basic Authentication.
func HTTPConnector(user *url.Userinfo) Connector {
	return &httpConnector{User: user}
}

func (c *httpConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
	req := &http.Request{
		Method:     http.MethodConnect,
		URL:        &url.URL{Host: addr},
		Host:       addr,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
	}
	req.Header.Set("User-Agent", DefaultUserAgent)
	req.Header.Set("Proxy-Connection", "keep-alive")

	if c.User != nil {
		u := c.User.Username()
		p, _ := c.User.Password()
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
	}

	if err := req.Write(conn); err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Log(string(dump))
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Log(string(dump))
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%s", resp.Status)
	}

	return conn, nil
}

type httpHandler struct {
	options *HandlerOptions
}

// HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler {
	h := &httpHandler{
		options: &HandlerOptions{},
	}
	for _, opt := range opts {
		opt(h.options)
	}
	return h
}

func (h *httpHandler) Handle(conn net.Conn) {
	defer conn.Close()

	req, err := http.ReadRequest(bufio.NewReader(conn))
	if err != nil {
		log.Logf("[http] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		return
	}
	defer req.Body.Close()

	h.handleRequest(conn, req)
}

func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
	if req == nil {
		return
	}
	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
	}

	if req.Method == "PRI" || (req.Method != http.MethodConnect && req.URL.Scheme != "http") {
		resp := "HTTP/1.1 400 Bad Request\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp)
		}
		return
	}

	if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
		log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
		b := []byte("HTTP/1.1 403 Forbidden\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		conn.Write(b)
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		return
	}

	u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if Debug && (u != "" || p != "") {
		log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
	}
	if !authenticate(u, p, h.options.Users...) {
		log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
		resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
			"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		return
	}

	req.Header.Del("Proxy-Authorization")
	// req.Header.Del("Proxy-Connection")

	// try to get the actual host.
	if v := req.Header.Get("Gost-Target"); v != "" {
		if host, err := decodeServerName(v); err == nil {
			req.Host = host
		}
	}

	// forward http request
	lastNode := h.options.Chain.LastNode()
	if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
		h.forwardRequest(conn, req)
		return
	}

	host := req.Host
	if !strings.Contains(host, ":") {
		host += ":80"
	}

	cc, err := h.options.Chain.Dial(host)
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if req.Method == http.MethodConnect {
		b := []byte("HTTP/1.1 200 Connection established\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(b))
		}
		conn.Write(b)
	} else {
		req.Header.Del("Proxy-Connection")

		if err = req.Write(cc); err != nil {
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
			return
		}
	}

	log.Logf("[http] %s <-> %s", cc.LocalAddr(), host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", cc.LocalAddr(), host)
}

func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
	if h.options.Chain.IsEmpty() {
		return
	}
	lastNode := h.options.Chain.LastNode()

	cc, err := h.options.Chain.Conn()
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), lastNode.Addr, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if lastNode.User != nil {
		s := lastNode.User.String()
		if _, set := lastNode.User.Password(); !set {
			s += ":"
		}
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
	}

	cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
	if !req.URL.IsAbs() {
		req.URL.Scheme = "http" // make sure that the URL is absolute
	}
	if err = req.WriteProxy(cc); err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
		return
	}
	cc.SetWriteDeadline(time.Time{})

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
	return
}

func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
	if proxyAuth == "" {
		return
	}

	if !strings.HasPrefix(proxyAuth, "Basic ") {
		return
	}
	c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
	if err != nil {
		return
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return
	}

	return cs[:s], cs[s+1:], true
}

func authenticate(username, password string, users ...*url.Userinfo) bool {
	if len(users) == 0 {
		return true
	}

	for _, user := range users {
		u := user.Username()
		p, _ := user.Password()
		if (u == username && p == password) ||
			(u == username && p == "") ||
			(u == "" && p == password) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"time"
)

// Request represents HTTP request.
//
// It is forbidden copying Request instances. Create new instances instead.
type Request struct {
	// Request header
	Header RequestHeader

	// Request body
	Body []byte

	// Request URI.
	// URI becomes available only after Request.ParseURI() call.
	URI       URI
	parsedURI bool

	// Arguments sent in POST.
	// PostArgs becomes available only after Request.ParsePostArgs() call.
	PostArgs       Args
	parsedPostArgs bool

	timeoutCh    chan error
	timeoutTimer *time.Timer
}

// Response represents HTTP response.
//
// It is forbidden copying Response instances. Create new instances instead.
type Response struct {
	// Response header
	Header ResponseHeader

	// Response body
	Body []byte

	// If set to true, Response.Read() skips reading body.
	// Use it for HEAD requests.
	SkipBody bool

	timeoutCh    chan error
	timeoutTimer *time.Timer
}

// ParseURI parses request uri and fills Request.URI.
func (req *Request) ParseURI() {
	if req.parsedURI {
		return
	}
	req.URI.Parse(req.Header.host, req.Header.RequestURI)
	req.parsedURI = true
}

// ParsePostArgs parses args sent in POST body and fills Request.PostArgs.
func (req *Request) ParsePostArgs() error {
	if req.parsedPostArgs {
		return nil
	}

	if !req.Header.IsMethodPost() {
		return fmt.Errorf("Cannot parse POST args for %q request", req.Header.Method)
	}
	if !bytes.Equal(req.Header.contentType, strPostArgsContentType) {
		return fmt.Errorf("Cannot parse POST args for %q Content-Type. Required %q Content-Type",
			req.Header.contentType, strPostArgsContentType)
	}
	req.PostArgs.ParseBytes(req.Body)
	req.parsedPostArgs = true
	return nil
}

// Clear clears request contents.
func (req *Request) Clear() {
	req.Header.Clear()
	req.Body = req.Body[:0]
	req.URI.Clear()
	req.parsedURI = false
	req.PostArgs.Clear()
	req.parsedPostArgs = false
}

// Clear clears response contents.
func (resp *Response) Clear() {
	resp.Header.Clear()
	resp.Body = resp.Body[:0]
}

// ErrReadTimeout may be returned by Request.ReadTimeout
// or Response.ReadTimeout on timeout.
var ErrReadTimeout = errors.New("read timeout")

// ReadTimeout reads request (including body) from the given r during
// the given timeout.
//
// If request couldn't be read during the given timeout,
// ErrReadTimeout is returned.
// Request can no longer be used after ErrReadTimeout error.
func (req *Request) ReadTimeout(r *bufio.Reader, timeout time.Duration) error {
	if timeout <= 0 {
		return req.Read(r)
	}

	ch := req.timeoutCh
	if ch == nil {
		ch = make(chan error, 1)
		req.timeoutCh = ch
	} else if len(ch) > 0 {
		panic("BUG: Request.timeoutCh must be empty!")
	}

	go func() {
		ch <- req.Read(r)
	}()

	var err error
	req.timeoutTimer = initTimer(req.timeoutTimer, timeout)
	select {
	case err = <-ch:
	case <-req.timeoutTimer.C:
		req.timeoutCh = nil
		err = ErrReadTimeout
	}
	stopTimer(req.timeoutTimer)
	return err
}

// ReadTimeout reads response (including body) from the given r during
// the given timeout.
//
// If response couldn't be read during the given timeout,
// ErrReadTimeout is returned.
// Request can no longer be used after ErrReadTimeout error.
func (resp *Response) ReadTimeout(r *bufio.Reader, timeout time.Duration) error {
	if timeout <= 0 {
		return resp.Read(r)
	}

	ch := resp.timeoutCh
	if ch == nil {
		ch = make(chan error, 1)
		resp.timeoutCh = ch
	} else if len(ch) > 0 {
		panic("BUG: Response.timeoutCh must be empty!")
	}

	go func() {
		ch <- resp.Read(r)
	}()

	var err error
	resp.timeoutTimer = initTimer(resp.timeoutTimer, timeout)
	select {
	case err = <-ch:
	case <-resp.timeoutTimer.C:
		resp.timeoutCh = nil
		err = ErrReadTimeout
	}
	stopTimer(resp.timeoutTimer)
	return err
}

// Read reads request (including body) from the given r.
func (req *Request) Read(r *bufio.Reader) error {
	req.Body = req.Body[:0]
	req.URI.Clear()
	req.parsedURI = false
	req.PostArgs.Clear()
	req.parsedPostArgs = false

	err := req.Header.Read(r)
	if err != nil {
		return err
	}

	if req.Header.IsMethodPost() {
		body, err := readBody(r, req.Header.ContentLength, req.Body)
		if err != nil {
			req.Clear()
			return err
		}
		req.Body = body
	}
	return nil
}

// Read reads response (including body) from the given r.
func (resp *Response) Read(r *bufio.Reader) error {
	resp.Body = resp.Body[:0]

	err := resp.Header.Read(r)
	if err != nil {
		return err
	}

	if isSkipResponseBody(resp.Header.StatusCode) || resp.SkipBody {
		resp.SkipBody = false
		return nil
	}

	body, err := readBody(r, resp.Header.ContentLength, resp.Body)
	if err != nil {
		resp.Clear()
		return err
	}
	resp.Body = body
	return nil
}

func isSkipResponseBody(statusCode int) bool {
	// From http/1.1 specs:
	// All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body
	if statusCode >= 100 && statusCode < 200 {
		return true
	}
	return statusCode == StatusNoContent || statusCode == StatusNotModified
}

// Write write request to w.
//
// Write doesn't flush request to w for performance reasons.
func (req *Request) Write(w *bufio.Writer) error {
	contentLengthOld := req.Header.ContentLength
	req.Header.ContentLength = len(req.Body)
	err := req.Header.Write(w)
	req.Header.ContentLength = contentLengthOld
	if err != nil {
		return err
	}
	if req.Header.IsMethodPost() {
		_, err = w.Write(req.Body)
	} else if len(req.Body) > 0 {
		return fmt.Errorf("Non-zero body for non-POST request. body=%q", req.Body)
	}
	return err
}

// Write writes response to w.
//
// Write doesn't flush response to w for performance reasons.
func (resp *Response) Write(w *bufio.Writer) error {
	contentLengthOld := resp.Header.ContentLength
	resp.Header.ContentLength = len(resp.Body)
	err := resp.Header.Write(w)
	resp.Header.ContentLength = contentLengthOld
	if err != nil {
		return err
	}
	_, err = w.Write(resp.Body)
	return err
}

func readBody(r *bufio.Reader, contentLength int, b []byte) ([]byte, error) {
	b = b[:0]
	if contentLength >= 0 {
		return readBodyFixedSize(r, contentLength, b)
	}
	return readBodyChunked(r, b)
}

func readBodyFixedSize(r *bufio.Reader, n int, buf []byte) ([]byte, error) {
	if n == 0 {
		return buf, nil
	}

	bufLen := len(buf)
	bufCap := bufLen + n
	if cap(buf) < bufCap {
		b := make([]byte, bufLen, round2(bufCap))
		copy(b, buf)
		buf = b
	}
	buf = buf[:bufCap]
	b := buf[bufLen:]

	for {
		nn, err := r.Read(b)
		if nn <= 0 {
			if err != nil {
				if err == io.EOF {
					err = io.ErrUnexpectedEOF
				}
				return nil, err
			}
			panic(fmt.Sprintf("BUF: bufio.Read() returned (%d, nil)", nn))
		}
		if nn == n {
			return buf, nil
		}
		if nn > n {
			panic(fmt.Sprintf("BUF: read more than requested: %d vs %d", nn, n))
		}
		n -= nn
		b = b[nn:]
	}
}

func readBodyChunked(r *bufio.Reader, b []byte) ([]byte, error) {
	if len(b) > 0 {
		panic("Expected zero-length buffer")
	}

	strCRLFLen := len(strCRLF)
	for {
		chunkSize, err := parseChunkSize(r)
		if err != nil {
			return nil, err
		}
		b, err = readBodyFixedSize(r, chunkSize+strCRLFLen, b)
		if err != nil {
			return nil, err
		}
		if !bytes.Equal(b[len(b)-strCRLFLen:], strCRLF) {
			return nil, fmt.Errorf("cannot find crlf at the end of chunk")
		}
		b = b[:len(b)-strCRLFLen]
		if chunkSize == 0 {
			return b, nil
		}
	}
}

func parseChunkSize(r *bufio.Reader) (int, error) {
	n, err := readHexInt(r)
	if err != nil {
		return -1, err
	}
	c, err := r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\r' char at the end of chunk size: %s", err)
	}
	if c != '\r' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\r')
	}
	c, err = r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\n' char at the end of chunk size: %s", err)
	}
	if c != '\n' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\n')
	}
	return n, nil
}

func round2(n int) int {
	if n <= 0 {
		return 0
	}
	n--
	x := uint(0)
	for n > 0 {
		n >>= 1
		x++
	}
	return 1 << x
}
<commit_msg>moved common code to clearSkipHeader()<commit_after>package fasthttp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"time"
)

// Request represents HTTP request.
//
// It is forbidden copying Request instances. Create new instances instead.
type Request struct {
	// Request header
	Header RequestHeader

	// Request body
	Body []byte

	// Request URI.
	// URI becomes available only after Request.ParseURI() call.
	URI       URI
	parsedURI bool

	// Arguments sent in POST.
	// PostArgs becomes available only after Request.ParsePostArgs() call.
	PostArgs       Args
	parsedPostArgs bool

	timeoutCh    chan error
	timeoutTimer *time.Timer
}

// Response represents HTTP response.
//
// It is forbidden copying Response instances. Create new instances instead.
type Response struct {
	// Response header
	Header ResponseHeader

	// Response body
	Body []byte

	// If set to true, Response.Read() skips reading body.
	// Use it for HEAD requests.
	SkipBody bool

	timeoutCh    chan error
	timeoutTimer *time.Timer
}

// ParseURI parses request uri and fills Request.URI.
func (req *Request) ParseURI() {
	if req.parsedURI {
		return
	}
	req.URI.Parse(req.Header.host, req.Header.RequestURI)
	req.parsedURI = true
}

// ParsePostArgs parses args sent in POST body and fills Request.PostArgs.
func (req *Request) ParsePostArgs() error {
	if req.parsedPostArgs {
		return nil
	}

	if !req.Header.IsMethodPost() {
		return fmt.Errorf("Cannot parse POST args for %q request", req.Header.Method)
	}
	if !bytes.Equal(req.Header.contentType, strPostArgsContentType) {
		return fmt.Errorf("Cannot parse POST args for %q Content-Type. Required %q Content-Type",
			req.Header.contentType, strPostArgsContentType)
	}
	req.PostArgs.ParseBytes(req.Body)
	req.parsedPostArgs = true
	return nil
}

// Clear clears request contents.
func (req *Request) Clear() {
	req.Header.Clear()
	req.clearSkipHeader()
}

func (req *Request) clearSkipHeader() {
	req.Body = req.Body[:0]
	req.URI.Clear()
	req.parsedURI = false
	req.PostArgs.Clear()
	req.parsedPostArgs = false
}

// Clear clears response contents.
func (resp *Response) Clear() {
	resp.Header.Clear()
	resp.clearSkipHeader()
}

func (resp *Response) clearSkipHeader() {
	resp.Body = resp.Body[:0]
}

// ErrReadTimeout may be returned by Request.ReadTimeout
// or Response.ReadTimeout on timeout.
var ErrReadTimeout = errors.New("read timeout")

// ReadTimeout reads request (including body) from the given r during
// the given timeout.
//
// If request couldn't be read during the given timeout,
// ErrReadTimeout is returned.
// Request can no longer be used after ErrReadTimeout error.
func (req *Request) ReadTimeout(r *bufio.Reader, timeout time.Duration) error {
	if timeout <= 0 {
		return req.Read(r)
	}

	ch := req.timeoutCh
	if ch == nil {
		ch = make(chan error, 1)
		req.timeoutCh = ch
	} else if len(ch) > 0 {
		panic("BUG: Request.timeoutCh must be empty!")
	}

	go func() {
		ch <- req.Read(r)
	}()

	var err error
	req.timeoutTimer = initTimer(req.timeoutTimer, timeout)
	select {
	case err = <-ch:
	case <-req.timeoutTimer.C:
		req.timeoutCh = nil
		err = ErrReadTimeout
	}
	stopTimer(req.timeoutTimer)
	return err
}

// ReadTimeout reads response (including body) from the given r during
// the given timeout.
//
// If response couldn't be read during the given timeout,
// ErrReadTimeout is returned.
// Request can no longer be used after ErrReadTimeout error.
func (resp *Response) ReadTimeout(r *bufio.Reader, timeout time.Duration) error {
	if timeout <= 0 {
		return resp.Read(r)
	}

	ch := resp.timeoutCh
	if ch == nil {
		ch = make(chan error, 1)
		resp.timeoutCh = ch
	} else if len(ch) > 0 {
		panic("BUG: Response.timeoutCh must be empty!")
	}

	go func() {
		ch <- resp.Read(r)
	}()

	var err error
	resp.timeoutTimer = initTimer(resp.timeoutTimer, timeout)
	select {
	case err = <-ch:
	case <-resp.timeoutTimer.C:
		resp.timeoutCh = nil
		err = ErrReadTimeout
	}
	stopTimer(resp.timeoutTimer)
	return err
}

// Read reads request (including body) from the given r.
func (req *Request) Read(r *bufio.Reader) error {
	req.clearSkipHeader()
	err := req.Header.Read(r)
	if err != nil {
		return err
	}

	if req.Header.IsMethodPost() {
		body, err := readBody(r, req.Header.ContentLength, req.Body)
		if err != nil {
			req.Clear()
			return err
		}
		req.Body = body
	}
	return nil
}

// Read reads response (including body) from the given r.
func (resp *Response) Read(r *bufio.Reader) error {
	resp.clearSkipHeader()
	err := resp.Header.Read(r)
	if err != nil {
		return err
	}

	if isSkipResponseBody(resp.Header.StatusCode) || resp.SkipBody {
		resp.SkipBody = false
		return nil
	}

	body, err := readBody(r, resp.Header.ContentLength, resp.Body)
	if err != nil {
		resp.Clear()
		return err
	}
	resp.Body = body
	return nil
}

func isSkipResponseBody(statusCode int) bool {
	// From http/1.1 specs:
	// All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body
	if statusCode >= 100 && statusCode < 200 {
		return true
	}
	return statusCode == StatusNoContent || statusCode == StatusNotModified
}

// Write write request to w.
//
// Write doesn't flush request to w for performance reasons.
func (req *Request) Write(w *bufio.Writer) error {
	contentLengthOld := req.Header.ContentLength
	req.Header.ContentLength = len(req.Body)
	err := req.Header.Write(w)
	req.Header.ContentLength = contentLengthOld
	if err != nil {
		return err
	}
	if req.Header.IsMethodPost() {
		_, err = w.Write(req.Body)
	} else if len(req.Body) > 0 {
		return fmt.Errorf("Non-zero body for non-POST request. body=%q", req.Body)
	}
	return err
}

// Write writes response to w.
//
// Write doesn't flush response to w for performance reasons.
func (resp *Response) Write(w *bufio.Writer) error {
	contentLengthOld := resp.Header.ContentLength
	resp.Header.ContentLength = len(resp.Body)
	err := resp.Header.Write(w)
	resp.Header.ContentLength = contentLengthOld
	if err != nil {
		return err
	}
	_, err = w.Write(resp.Body)
	return err
}

func readBody(r *bufio.Reader, contentLength int, b []byte) ([]byte, error) {
	b = b[:0]
	if contentLength >= 0 {
		return readBodyFixedSize(r, contentLength, b)
	}
	return readBodyChunked(r, b)
}

func readBodyFixedSize(r *bufio.Reader, n int, buf []byte) ([]byte, error) {
	if n == 0 {
		return buf, nil
	}

	bufLen := len(buf)
	bufCap := bufLen + n
	if cap(buf) < bufCap {
		b := make([]byte, bufLen, round2(bufCap))
		copy(b, buf)
		buf = b
	}
	buf = buf[:bufCap]
	b := buf[bufLen:]

	for {
		nn, err := r.Read(b)
		if nn <= 0 {
			if err != nil {
				if err == io.EOF {
					err = io.ErrUnexpectedEOF
				}
				return nil, err
			}
			panic(fmt.Sprintf("BUF: bufio.Read() returned (%d, nil)", nn))
		}
		if nn == n {
			return buf, nil
		}
		if nn > n {
			panic(fmt.Sprintf("BUF: read more than requested: %d vs %d", nn, n))
		}
		n -= nn
		b = b[nn:]
	}
}

func readBodyChunked(r *bufio.Reader, b []byte) ([]byte, error) {
	if len(b) > 0 {
		panic("Expected zero-length buffer")
	}

	strCRLFLen := len(strCRLF)
	for {
		chunkSize, err := parseChunkSize(r)
		if err != nil {
			return nil, err
		}
		b, err = readBodyFixedSize(r, chunkSize+strCRLFLen, b)
		if err != nil {
			return nil, err
		}
		if !bytes.Equal(b[len(b)-strCRLFLen:], strCRLF) {
			return nil, fmt.Errorf("cannot find crlf at the end of chunk")
		}
		b = b[:len(b)-strCRLFLen]
		if chunkSize == 0 {
			return b, nil
		}
	}
}

func parseChunkSize(r *bufio.Reader) (int, error) {
	n, err := readHexInt(r)
	if err != nil {
		return -1, err
	}
	c, err := r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\r' char at the end of chunk size: %s", err)
	}
	if c != '\r' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\r')
	}
	c, err = r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\n' char at the end of chunk size: %s", err)
	}
	if c != '\n' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\n')
	}
	return n, nil
}

func round2(n int) int {
	if n <= 0 {
		return 0
	}
	n--
	x := uint(0)
	for n > 0 {
		n >>= 1
		x++
	}
	return 1 << x
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	auth "github.com/heroku/lumbermill/Godeps/_workspace/src/github.com/heroku/authenticater"
	influx "github.com/heroku/lumbermill/Godeps/_workspace/src/github.com/influxdb/influxdb-go"
)

var influxDbStaleTimeout = 24 * time.Minute // Would be nice to make this smaller, but it lags due to continuous queries.
var influxDbSeriesCheckQueries = []string{
	"select * from dyno.load.%s limit 1",
	"select * from dyno.mem.%s limit 1",
}

var healthCheckClientsLock = new(sync.Mutex)
var healthCheckClients = make(map[string]*influx.Client)

type server struct {
	sync.WaitGroup
	connectionCloser chan struct{}
	hashRing         *hashRing
	http             *http.Server
	shutdownChan     shutdownChan
	isShuttingDown   bool
	credStore        map[string]string

	// scheduler based sampling lock for writing to recentTokens
	tokenLock        *int32
	recentTokensLock *sync.RWMutex
	recentTokens     map[string]string
}

func newServer(httpServer *http.Server, ath auth.Authenticater, hashRing *hashRing) *server {
	s := &server{
		connectionCloser: make(chan struct{}),
		shutdownChan:     make(chan struct{}),
		http:             httpServer,
		hashRing:         hashRing,
		credStore:        make(map[string]string),
		tokenLock:        new(int32),
		recentTokensLock: new(sync.RWMutex),
		recentTokens:     make(map[string]string),
	}

	mux := http.NewServeMux()

	mux.HandleFunc("/drain", auth.WrapAuth(ath,
		func(w http.ResponseWriter, r *http.Request) {
			s.serveDrain(w, r)
			s.recycleConnection(w)
		}))

	mux.HandleFunc("/health", s.serveHealth)
	mux.HandleFunc("/health/influxdb", s.serveInfluxDBHealth)
	mux.HandleFunc("/target/", auth.WrapAuth(ath, s.serveTarget))

	s.http.Handler = mux

	return s
}

func (s *server) Close() error {
	s.shutdownChan <- struct{}{}
	return nil
}

func (s *server) scheduleConnectionRecycling(after time.Duration) {
	for !s.isShuttingDown {
		time.Sleep(after)
		s.connectionCloser <- struct{}{}
	}
}

func (s *server) recycleConnection(w http.ResponseWriter) {
	select {
	case <-s.connectionCloser:
		w.Header().Set("Connection", "close")
	default:
		if s.isShuttingDown {
			w.Header().Set("Connection", "close")
		}
	}
}

func (s *server) Run(connRecycle time.Duration) {
	go s.awaitShutdown()
	go s.scheduleConnectionRecycling(connRecycle)

	if err := s.http.ListenAndServe(); err != nil {
		log.Fatalln("Unable to start HTTP server: ", err)
	}
}

// Serves a 200 OK, unless shutdown has been requested.
// Shutting down serves a 503 since that's how ELBs implement connection draining.
func (s *server) serveHealth(w http.ResponseWriter, r *http.Request) {
	if s.isShuttingDown {
		http.Error(w, "Shutting Down", 503)
	}

	w.WriteHeader(http.StatusOK)
}

func getHealthCheckClient(host string, f clientFunc) (*influx.Client, error) {
	healthCheckClientsLock.Lock()
	defer healthCheckClientsLock.Unlock()

	client, exists := healthCheckClients[host]
	if !exists {
		var err error
		clientConfig := createInfluxDBClient(host, f)
		client, err = influx.NewClient(&clientConfig)
		if err != nil {
			log.Printf("err=%q at=getHealthCheckClient host=%q", err, host)
			return nil, err
		}

		healthCheckClients[host] = client
	}

	return client, nil
}

func checkRecentToken(client *influx.Client, token, host string, errors chan error) {
	for _, qfmt := range influxDbSeriesCheckQueries {
		query := fmt.Sprintf(qfmt, token)
		results, err := client.Query(query, influx.Second)
		if err != nil || len(results) == 0 {
			errors <- fmt.Errorf("at=influxdb-health err=%q result_length=%d host=%q query=%q", err, len(results), host, query)
			continue
		}

		t, ok := results[0].Points[0][0].(float64)
		if !ok {
			errors <- fmt.Errorf("at=influxdb-health err=\"time column was not a number\" host=%q query=%q", host, query)
			continue
		}

		ts := time.Unix(int64(t), int64(0)).UTC()
		now := time.Now().UTC()
		if now.Sub(ts) > influxDbStaleTimeout {
			errors <- fmt.Errorf("at=influxdb-health err=\"stale data\" host=%q ts=%q now=%q query=%q", host, ts, now, query)
		}
	}
}

func (s *server) checkRecentTokens() []error {
	var errSlice []error

	wg := new(sync.WaitGroup)

	s.recentTokensLock.RLock()
	tokenMap := make(map[string]string)
	for host, token := range s.recentTokens {
		tokenMap[host] = token
	}
	s.recentTokensLock.RUnlock()

	errors := make(chan error, len(tokenMap)*len(influxDbSeriesCheckQueries))

	for host, token := range tokenMap {
		wg.Add(1)
		go func(token, host string) {
			client, err := getHealthCheckClient(host, newClientFunc)
			if err != nil {
				return
			}
			checkRecentToken(client, token, host, errors)
			wg.Done()
		}(token, host)
	}

	wg.Wait()
	close(errors)

	for err := range errors {
		errSlice = append(errSlice, err)
	}

	return errSlice
}

func (s *server) serveInfluxDBHealth(w http.ResponseWriter, r *http.Request) {
	errors := s.checkRecentTokens()

	if len(errors) > 0 {
		w.WriteHeader(http.StatusServiceUnavailable)
		for _, err := range errors {
			w.Write([]byte(err.Error() + "\n"))
			log.Println(err)
		}
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (s *server) awaitShutdown() {
	<-s.shutdownChan
	log.Printf("Shutting down.")
	s.isShuttingDown = true
}
<commit_msg>Protect /health/influxdb with Auth for DDoS avaoidance.<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	auth "github.com/heroku/lumbermill/Godeps/_workspace/src/github.com/heroku/authenticater"
	influx "github.com/heroku/lumbermill/Godeps/_workspace/src/github.com/influxdb/influxdb-go"
)

var influxDbStaleTimeout = 24 * time.Minute // Would be nice to make this smaller, but it lags due to continuous queries.
var influxDbSeriesCheckQueries = []string{
	"select * from dyno.load.%s limit 1",
	"select * from dyno.mem.%s limit 1",
}

var healthCheckClientsLock = new(sync.Mutex)
var healthCheckClients = make(map[string]*influx.Client)

type server struct {
	sync.WaitGroup
	connectionCloser chan struct{}
	hashRing         *hashRing
	http             *http.Server
	shutdownChan     shutdownChan
	isShuttingDown   bool
	credStore        map[string]string

	// scheduler based sampling lock for writing to recentTokens
	tokenLock        *int32
	recentTokensLock *sync.RWMutex
	recentTokens     map[string]string
}

func newServer(httpServer *http.Server, ath auth.Authenticater, hashRing *hashRing) *server {
	s := &server{
		connectionCloser: make(chan struct{}),
		shutdownChan:     make(chan struct{}),
		http:             httpServer,
		hashRing:         hashRing,
		credStore:        make(map[string]string),
		tokenLock:        new(int32),
		recentTokensLock: new(sync.RWMutex),
		recentTokens:     make(map[string]string),
	}

	mux := http.NewServeMux()

	mux.HandleFunc("/drain", auth.WrapAuth(ath,
		func(w http.ResponseWriter, r *http.Request) {
			s.serveDrain(w, r)
			s.recycleConnection(w)
		}))

	mux.HandleFunc("/health", s.serveHealth)
	mux.HandleFunc("/health/influxdb", auth.WrapAuth(ath, s.serveInfluxDBHealth))
	mux.HandleFunc("/target/", auth.WrapAuth(ath, s.serveTarget))

	s.http.Handler = mux

	return s
}

func (s *server) Close() error {
	s.shutdownChan <- struct{}{}
	return nil
}

func (s *server) scheduleConnectionRecycling(after time.Duration) {
	for !s.isShuttingDown {
		time.Sleep(after)
		s.connectionCloser <- struct{}{}
	}
}

func (s *server) recycleConnection(w http.ResponseWriter) {
	select {
	case <-s.connectionCloser:
		w.Header().Set("Connection", "close")
	default:
		if s.isShuttingDown {
			w.Header().Set("Connection", "close")
		}
	}
}

func (s *server) Run(connRecycle time.Duration) {
	go s.awaitShutdown()
	go s.scheduleConnectionRecycling(connRecycle)

	if err := s.http.ListenAndServe(); err != nil {
		log.Fatalln("Unable to start HTTP server: ", err)
	}
}

// Serves a 200 OK, unless shutdown has been requested.
// Shutting down serves a 503 since that's how ELBs implement connection draining.
func (s *server) serveHealth(w http.ResponseWriter, r *http.Request) {
	if s.isShuttingDown {
		http.Error(w, "Shutting Down", 503)
	}

	w.WriteHeader(http.StatusOK)
}

func getHealthCheckClient(host string, f clientFunc) (*influx.Client, error) {
	healthCheckClientsLock.Lock()
	defer healthCheckClientsLock.Unlock()

	client, exists := healthCheckClients[host]
	if !exists {
		var err error
		clientConfig := createInfluxDBClient(host, f)
		client, err = influx.NewClient(&clientConfig)
		if err != nil {
			log.Printf("err=%q at=getHealthCheckClient host=%q", err, host)
			return nil, err
		}

		healthCheckClients[host] = client
	}

	return client, nil
}

func checkRecentToken(client *influx.Client, token, host string, errors chan error) {
	for _, qfmt := range influxDbSeriesCheckQueries {
		query := fmt.Sprintf(qfmt, token)
		results, err := client.Query(query, influx.Second)
		if err != nil || len(results) == 0 {
			errors <- fmt.Errorf("at=influxdb-health err=%q result_length=%d host=%q query=%q", err, len(results), host, query)
			continue
		}

		t, ok := results[0].Points[0][0].(float64)
		if !ok {
			errors <- fmt.Errorf("at=influxdb-health err=\"time column was not a number\" host=%q query=%q", host, query)
			continue
		}

		ts := time.Unix(int64(t), int64(0)).UTC()
		now := time.Now().UTC()
		if now.Sub(ts) > influxDbStaleTimeout {
			errors <- fmt.Errorf("at=influxdb-health err=\"stale data\" host=%q ts=%q now=%q query=%q", host, ts, now, query)
		}
	}
}

func (s *server) checkRecentTokens() []error {
	var errSlice []error

	wg := new(sync.WaitGroup)

	s.recentTokensLock.RLock()
	tokenMap := make(map[string]string)
	for host, token := range s.recentTokens {
		tokenMap[host] = token
	}
	s.recentTokensLock.RUnlock()

	errors := make(chan error, len(tokenMap)*len(influxDbSeriesCheckQueries))

	for host, token := range tokenMap {
		wg.Add(1)
		go func(token, host string) {
			client, err := getHealthCheckClient(host, newClientFunc)
			if err != nil {
				return
			}
			checkRecentToken(client, token, host, errors)
			wg.Done()
		}(token, host)
	}

	wg.Wait()
	close(errors)

	for err := range errors {
		errSlice = append(errSlice, err)
	}

	return errSlice
}

func (s *server) serveInfluxDBHealth(w http.ResponseWriter, r *http.Request) {
	errors := s.checkRecentTokens()

	if len(errors) > 0 {
		w.WriteHeader(http.StatusServiceUnavailable)
		for _, err := range errors {
			w.Write([]byte(err.Error() + "\n"))
			log.Println(err)
		}
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (s *server) awaitShutdown() {
	<-s.shutdownChan
	log.Printf("Shutting down.")
	s.isShuttingDown = true
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"os"
	"os/signal"
	"syscall"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/rpi"

	"github.com/gicmo/PiCo/hue"
)

func main() {
	fmt.Println("PiCo")

	client, err := hue.ReadConfig("/etc/pico")
	if err != nil {
		panic(err)
	}

	sigch := make(chan os.Signal, 2)
	signal.Notify(sigch, os.Interrupt, syscall.SIGTERM)

	if err = embd.InitGPIO(); err != nil {
		panic(err)
	}

	defer embd.CloseGPIO()

	pin18, err := embd.NewDigitalPin(18)
	defer pin18.Close()

	if err != nil {
		panic(err)
	}

	pin18.SetDirection(embd.Out)

	btn, err := embd.NewDigitalPin(24)
	if err != nil {
		panic(err)
	}
	defer btn.Close()

	btn.SetDirection(embd.In)
	btn.ActiveLow(false)

	pressed := make(chan time.Time, 2)
	err = btn.Watch(embd.EdgeBoth, func(btn embd.DigitalPin) {
		pressed <- time.Now()
	})

	bs, _ := btn.Read()
	fmt.Printf("[D] Button state: %v\n", bs)

	onoff := embd.High
	pin18.Write(onoff)

	last_button := time.Unix(0, 0)
	ctchan := make(chan error, 2)
	bc := make(chan int, 1)
	busy := false

loop:
	for {
		select {
		case pressed_time := <-pressed:
			bs, _ = btn.Read()
			fmt.Printf("[D] B: %v\n", bs)
			duration := pressed_time.Sub(last_button)
			if !busy && duration > 500*time.Millisecond {
				fmt.Printf("[D] Key pressed [starting timer]\n")
				last_button = pressed_time
				busy = true
				pin18.Write(embd.Low)

				time.AfterFunc(200*time.Millisecond, func() {
					if state, berr := btn.Read(); berr != nil {
						fmt.Printf("[E] Button read error\n")
						bc <- 0
					} else {
						bc <- state
					}
				})
			}

		case bstate := <-bc:
			fmt.Printf("[D] Timer report: %v\n", bstate)
			if bstate != 0 {
				go func(clt *hue.Client, ch chan error) {
					herr := client.Toggle()
					ch <- herr
				}(&client, ctchan)
			}

		case sig := <-sigch:
			fmt.Println("[I] Got signal", sig)
			break loop

		case err = <-ctchan:
			if err != nil {
				fmt.Printf("[W] toggle: %v", err)
			}
			busy = false
			pin18.Write(embd.High)
		}
	}
}
<commit_msg>Signal even if button was a false alarm<commit_after>package main

import (
	"fmt"
	"time"

	"os"
	"os/signal"
	"syscall"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/rpi"

	"github.com/gicmo/PiCo/hue"
)

func main() {
	fmt.Println("PiCo")

	client, err := hue.ReadConfig("/etc/pico")
	if err != nil {
		panic(err)
	}

	sigch := make(chan os.Signal, 2)
	signal.Notify(sigch, os.Interrupt, syscall.SIGTERM)

	if err = embd.InitGPIO(); err != nil {
		panic(err)
	}

	defer embd.CloseGPIO()

	pin18, err := embd.NewDigitalPin(18)
	defer pin18.Close()

	if err != nil {
		panic(err)
	}

	pin18.SetDirection(embd.Out)

	btn, err := embd.NewDigitalPin(24)
	if err != nil {
		panic(err)
	}
	defer btn.Close()

	btn.SetDirection(embd.In)
	btn.ActiveLow(false)

	pressed := make(chan time.Time, 2)
	err = btn.Watch(embd.EdgeBoth, func(btn embd.DigitalPin) {
		pressed <- time.Now()
	})

	bs, _ := btn.Read()
	fmt.Printf("[D] Button state: %v\n", bs)

	onoff := embd.High
	pin18.Write(onoff)

	last_button := time.Unix(0, 0)
	ctchan := make(chan error, 2)
	bc := make(chan int, 1)
	busy := false

loop:
	for {
		select {
		case pressed_time := <-pressed:
			bs, _ = btn.Read()
			fmt.Printf("[D] B: %v\n", bs)
			duration := pressed_time.Sub(last_button)
			if !busy && duration > 500*time.Millisecond {
				fmt.Printf("[D] Key pressed [starting timer]\n")
				last_button = pressed_time
				busy = true
				pin18.Write(embd.Low)

				time.AfterFunc(200*time.Millisecond, func() {
					if state, berr := btn.Read(); berr != nil {
						fmt.Printf("[E] Button read error\n")
						bc <- 0
					} else {
						bc <- state
					}
				})
			}

		case bstate := <-bc:
			fmt.Printf("[D] Timer report: %v\n", bstate)
			if bstate != 0 {
				go func(clt *hue.Client, ch chan error) {
					herr := client.Toggle()
					ch <- herr
				}(&client, ctchan)
			} else {
				ctchan <- nil
			}

		case sig := <-sigch:
			fmt.Println("[I] Got signal", sig)
			break loop

		case err = <-ctchan:
			if err != nil {
				fmt.Printf("[W] toggle: %v", err)
			}

			fmt.Println("[D] back to standby (busy = false)")
			busy = false
			pin18.Write(embd.High)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Unknown
//
// 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 i18n is for app Internationalization and Localization.
package i18n

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/Unknwon/goconfig"
)

var (
	locales = &localeStore{store: make(map[string]*locale)}
)

type locale struct {
	id       int
	lang     string
	langDesc string
	message  *goconfig.ConfigFile
}

type localeStore struct {
	langs     []string
	langDescs []string
	store     map[string]*locale
}

// Get target language string
func (d *localeStore) Get(lang, section, format string) (string, bool) {
	if locale, ok := d.store[lang]; ok {
		if section == "" {
			section = goconfig.DEFAULT_SECTION
		}

		if value, err := locale.message.GetValue(section, format); err == nil {
			return value, true
		}
	}

	return "", false
}

func (d *localeStore) Add(lc *locale) bool {
	if _, ok := d.store[lc.lang]; ok {
		return false
	}

	lc.id = len(d.langs)
	d.langs = append(d.langs, lc.lang)
	d.langDescs = append(d.langDescs, lc.langDesc)
	d.store[lc.lang] = lc

	return true
}

func (d *localeStore) Reload(langs ...string) error {
	if len(langs) == 0 {
		for _, lc := range d.store {
			err := lc.message.Reload()
			if err != nil {
				return err
			}
		}
	} else {
		for _, lang := range langs {
			if lc, ok := d.store[lang]; ok {
				err := lc.message.Reload()
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// Reload locales
func ReloadLangs(langs ...string) error {
	return locales.Reload(langs...)
}

// Count returns number of languages that are registered.
func Count() int {
	return len(locales.langs)
}

// List all locale languages
func ListLangs() []string {
	langs := make([]string, len(locales.langs))
	copy(langs, locales.langs)
	return langs
}

func ListLangDescs() []string {
	langDescs := make([]string, len(locales.langDescs))
	copy(langDescs, locales.langDescs)
	return langDescs
}

// Check language name if exist
func IsExist(lang string) bool {
	_, ok := locales.store[lang]
	return ok
}

// Check language name if exist
func IndexLang(lang string) int {
	if lc, ok := locales.store[lang]; ok {
		return lc.id
	}
	return -1
}

// Get language by index id
func GetLangByIndex(index int) string {
	if index < 0 || index >= len(locales.langs) {
		return ""
	}
	return locales.langs[index]
}

func GetDescriptionByIndex(index int) string {
	if index < 0 || index >= len(locales.langDescs) {
		return ""
	}

	return locales.langDescs[index]
}

func GetDescriptionByLang(lang string) string {
	return GetDescriptionByIndex(IndexLang(lang))
}

func SetMessageWithDesc(lang, langDesc, filePath string, appendFiles ...string) error {
	message, err := goconfig.LoadConfigFile(filePath, appendFiles...)
	if err == nil {
		message.BlockMode = false
		lc := new(locale)
		lc.lang = lang
		lc.langDesc = langDesc
		lc.message = message

		if locales.Add(lc) == false {
			return fmt.Errorf("Lang %s alread exist", lang)
		}
	}
	return err
}

// SetMessage sets the message file for localization.
func SetMessage(lang, filePath string, appendFiles ...string) error {
	return SetMessageWithDesc(lang, lang, filePath, appendFiles...)
}

// A Locale describles the information of localization.
type Locale struct {
	Lang string
}

// Tr translate content to target language.
func (l Locale) Tr(format string, args ...interface{}) string {
	return Tr(l.Lang, format, args...)
}

// Index get lang index of LangStore
func (l Locale) Index() int {
	return IndexLang(l.Lang)
}

// Tr translate content to target language.
func Tr(lang, format string, args ...interface{}) string {
	var section string
	parts := strings.SplitN(format, ".", 2)
	if len(parts) == 2 {
		section = parts[0]
		format = parts[1]
	}

	value, ok := locales.Get(lang, section, format)
	if ok {
		format = value
	}

	if len(args) > 0 {
		params := make([]interface{}, 0, len(args))
		for _, arg := range args {
			if arg != nil {
				val := reflect.ValueOf(arg)
				if val.Kind() == reflect.Slice {
					for i := 0; i < val.Len(); i++ {
						params = append(params, val.Index(i).Interface())
					}
				} else {
					params = append(params, arg)
				}
			}
		}
		return fmt.Sprintf(format, params...)
	}
	return fmt.Sprintf(format)
}
<commit_msg>update comments<commit_after>// Copyright 2013 Unknown
//
// 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 i18n is for app Internationalization and Localization.
package i18n

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/Unknwon/goconfig"
)

var (
	locales = &localeStore{store: make(map[string]*locale)}
)

type locale struct {
	id       int
	lang     string
	langDesc string
	message  *goconfig.ConfigFile
}

type localeStore struct {
	langs     []string
	langDescs []string
	store     map[string]*locale
}

// Get target language string
func (d *localeStore) Get(lang, section, format string) (string, bool) {
	if locale, ok := d.store[lang]; ok {
		if section == "" {
			section = goconfig.DEFAULT_SECTION
		}

		if value, err := locale.message.GetValue(section, format); err == nil {
			return value, true
		}
	}

	return "", false
}

func (d *localeStore) Add(lc *locale) bool {
	if _, ok := d.store[lc.lang]; ok {
		return false
	}

	lc.id = len(d.langs)
	d.langs = append(d.langs, lc.lang)
	d.langDescs = append(d.langDescs, lc.langDesc)
	d.store[lc.lang] = lc

	return true
}

func (d *localeStore) Reload(langs ...string) error {
	if len(langs) == 0 {
		for _, lc := range d.store {
			err := lc.message.Reload()
			if err != nil {
				return err
			}
		}
	} else {
		for _, lang := range langs {
			if lc, ok := d.store[lang]; ok {
				err := lc.message.Reload()
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// Reload locales
func ReloadLangs(langs ...string) error {
	return locales.Reload(langs...)
}

// Count returns number of languages that are registered.
func Count() int {
	return len(locales.langs)
}

// List all locale languages
func ListLangs() []string {
	langs := make([]string, len(locales.langs))
	copy(langs, locales.langs)
	return langs
}

func ListLangDescs() []string {
	langDescs := make([]string, len(locales.langDescs))
	copy(langDescs, locales.langDescs)
	return langDescs
}

// Check language name if exist
func IsExist(lang string) bool {
	_, ok := locales.store[lang]
	return ok
}

// Check language name if exist
func IndexLang(lang string) int {
	if lc, ok := locales.store[lang]; ok {
		return lc.id
	}
	return -1
}

// Get language by index id
func GetLangByIndex(index int) string {
	if index < 0 || index >= len(locales.langs) {
		return ""
	}
	return locales.langs[index]
}

func GetDescriptionByIndex(index int) string {
	if index < 0 || index >= len(locales.langDescs) {
		return ""
	}

	return locales.langDescs[index]
}

func GetDescriptionByLang(lang string) string {
	return GetDescriptionByIndex(IndexLang(lang))
}

func SetMessageWithDesc(lang, langDesc, filePath string, appendFiles ...string) error {
	message, err := goconfig.LoadConfigFile(filePath, appendFiles...)
	if err == nil {
		message.BlockMode = false
		lc := new(locale)
		lc.lang = lang
		lc.langDesc = langDesc
		lc.message = message

		if locales.Add(lc) == false {
			return fmt.Errorf("Lang %s alread exist", lang)
		}
	}
	return err
}

// SetMessage sets the message file for localization.
func SetMessage(lang, filePath string, appendFiles ...string) error {
	return SetMessageWithDesc(lang, lang, filePath, appendFiles...)
}

// Locale represents the information of localization.
type Locale struct {
	Lang string
}

// Tr translate content to target language.
func (l Locale) Tr(format string, args ...interface{}) string {
	return Tr(l.Lang, format, args...)
}

// Index get lang index of LangStore
func (l Locale) Index() int {
	return IndexLang(l.Lang)
}

// Tr translate content to target language.
func Tr(lang, format string, args ...interface{}) string {
	var section string
	parts := strings.SplitN(format, ".", 2)
	if len(parts) == 2 {
		section = parts[0]
		format = parts[1]
	}

	value, ok := locales.Get(lang, section, format)
	if ok {
		format = value
	}

	if len(args) > 0 {
		params := make([]interface{}, 0, len(args))
		for _, arg := range args {
			if arg != nil {
				val := reflect.ValueOf(arg)
				if val.Kind() == reflect.Slice {
					for i := 0; i < val.Len(); i++ {
						params = append(params, val.Index(i).Interface())
					}
				} else {
					params = append(params, arg)
				}
			}
		}
		return fmt.Sprintf(format, params...)
	}
	return fmt.Sprintf(format)
}
<|endoftext|>


<commit_before>package graphs

import (
	"fmt"
	"sort"
)

// A Vertex can be just anything.
type Vertex interface{}

// An Edge connects two vertices with a cost.
type Edge struct {
	Start Vertex
	End   Vertex
	Cost  float64
}

// A Halfedge is an edge where just the end vertex is
// stored. The start vertex is inferred from the context.
type Halfedge struct {
	End  Vertex
	Cost float64
}

// A Graph is defined by its vertices and edges stored as
// an adjacency set.
type Graph struct {
	Adjacency map[Vertex]*Set
	Vertices  *Set
	Directed  bool
}

// NewGraph creates a new empty graph.
func NewGraph() *Graph {
	return &Graph{
		Adjacency: map[Vertex]*Set{},
		Vertices:  NewSet(),
		Directed:  false,
	}
}

// NewDigraph creates a new empty directed graph.
func NewDigraph() *Graph {
	graph := NewGraph()
	graph.Directed = true
	return graph
}

// AddVertex adds the given vertex to the graph.
func (g *Graph) AddVertex(v Vertex) {
	g.Vertices.Add(v)
}

// AddEdge adds an edge to the graph. The edge connects
// vertex v1 and vertex v2 with cost c.
func (g *Graph) AddEdge(v1, v2 Vertex, c float64) {
	g.Vertices.Add(v1)
	g.Vertices.Add(v2)

	if _, exists := g.Adjacency[v1]; !exists {
		g.Adjacency[v1] = NewSet()
	}

	g.Adjacency[v1].Add(Halfedge{
		End:  v2,
		Cost: c,
	})

	if !g.Directed {
		if _, exists := g.Adjacency[v2]; !exists {
			g.Adjacency[v2] = NewSet()
		}

		g.Adjacency[v2].Add(Halfedge{
			End:  v1,
			Cost: c,
		})
	}
}

// Dump prints all edges with their cost to stdout.
func (g *Graph) Dump() {
	for e := range g.EdgesIter() {
		fmt.Printf("(%v,%v,%f)\n", e.Start, e.End, e.Cost)
	}
}

// NVertices returns the number of vertices.
func (g *Graph) NVertices() int {
	return g.Vertices.Len()
}

// NEdges returns the number of edges.
func (g *Graph) NEdges() int {
	n := 0

	for _, v := range g.Adjacency {
		n += v.Len()
	}

	// Don’t count a-b and b-a edges for undirected graphs
	// as two separate edges.
	if !g.Directed {
		n /= 2
	}

	return n
}

// Equals returns whether the graph is equal to the given graph.
// Two graphs are equal of their adjacency is equal.
func (g *Graph) Equals(g2 *Graph) bool {
	// Two graphs with differnet vertices aren’t equal.
	if !g.Vertices.Equals(g2.Vertices) {
		return false
	}

	// Some for number of edges.
	if g.NEdges() != g2.NEdges() {
		return false
	}

	// Check if the adjacency for each vertex is equal
	// for both graphs.
	a1 := g.Adjacency
	a2 := g2.Adjacency

	for k, v := range a1 {
		if !v.Equals(a2[k]) {
			return false
		}
	}

	return true
}

// VerticesIter returns a channel where all vertices
// are sent to.
func (g *Graph) VerticesIter() chan Vertex {
	ch := make(chan Vertex)
	go func() {
		for e := range g.Vertices.Iter() {
			ch <- e.(Vertex)
		}
		close(ch)
	}()
	return ch
}

// SortedEdges is an array of edges that can be sorted
// by their cost.
type SortedEdges []Edge

func (se SortedEdges) Len() int {
	return len(se)
}

func (se SortedEdges) Less(i, j int) bool {
	return se[i].Cost < se[j].Cost
}

func (se SortedEdges) Swap(i, j int) {
	se[i], se[j] = se[j], se[i]
}

// SortedEdges returns an array of edges sorted by their cost.
func (g *Graph) SortedEdges() SortedEdges {
	set := NewSet()

	for v := range g.Adjacency {
		for he := range g.HalfedgesIter(v) {
			set.Add(Edge{
				Start: v,
				End:   he.End,
				Cost:  he.Cost,
			})
		}
	}

	edges := make(SortedEdges, set.Len())
	for e := range set.Iter() {
		edges = append(edges, e.(Edge))
	}

	sort.Sort(&edges)
	return edges
}

// EdgesIter returns a channel with all edges of the graph.
func (g *Graph) EdgesIter() chan Edge {
	ch := make(chan Edge)
	go func() {
		for v, s := range g.Adjacency {
			for x := range s.Iter() {
				he := x.(Halfedge)
				ch <- Edge{v, he.End, he.Cost}
			}
		}
		close(ch)
	}()
	return ch
}

// HalfedgesIter returns a channel with all halfedges for
// the given start vertex.
func (g *Graph) HalfedgesIter(v Vertex) chan Halfedge {
	ch := make(chan Halfedge)
	go func() {
		if s, exists := g.Adjacency[v]; exists {
			for x := range s.Iter() {
				he := x.(Halfedge)
				ch <- he
			}
		}
		close(ch)
	}()
	return ch
}
<commit_msg>No need to store vertices in a set<commit_after>package graphs

import (
	"fmt"
	"sort"
)

// A Vertex can be just anything.
type Vertex interface{}

// An Edge connects two vertices with a cost.
type Edge struct {
	Start Vertex
	End   Vertex
	Cost  float64
}

// A Halfedge is an edge where just the end vertex is
// stored. The start vertex is inferred from the context.
type Halfedge struct {
	End  Vertex
	Cost float64
}

// A Graph is defined by its vertices and edges stored as
// an adjacency set.
type Graph struct {
	Adjacency map[Vertex]*Set
	Directed  bool
}

// NewGraph creates a new empty graph.
func NewGraph() *Graph {
	return &Graph{
		Adjacency: map[Vertex]*Set{},
		Directed:  false,
	}
}

// NewDigraph creates a new empty directed graph.
func NewDigraph() *Graph {
	graph := NewGraph()
	graph.Directed = true
	return graph
}

// AddVertex adds the given vertex to the graph.
func (g *Graph) AddVertex(v Vertex) {
	if _, exists := g.Adjacency[v]; !exists {
		g.Adjacency[v] = NewSet()
	}
}

// AddEdge adds an edge to the graph. The edge connects
// vertex v1 and vertex v2 with cost c.
func (g *Graph) AddEdge(v1, v2 Vertex, c float64) {
	g.AddVertex(v1)
	g.AddVertex(v2)

	if _, exists := g.Adjacency[v1]; !exists {
		g.Adjacency[v1] = NewSet()
	}

	g.Adjacency[v1].Add(Halfedge{
		End:  v2,
		Cost: c,
	})

	if !g.Directed {
		g.Adjacency[v2].Add(Halfedge{
			End:  v1,
			Cost: c,
		})
	}
}

// Dump prints all edges with their cost to stdout.
func (g *Graph) Dump() {
	for e := range g.EdgesIter() {
		fmt.Printf("(%v,%v,%f)\n", e.Start, e.End, e.Cost)
	}
}

// NVertices returns the number of vertices.
func (g *Graph) NVertices() int {
	return len(g.Adjacency)
}

// NEdges returns the number of edges.
func (g *Graph) NEdges() int {
	n := 0

	for _, v := range g.Adjacency {
		n += v.Len()
	}

	// Don’t count a-b and b-a edges for undirected graphs
	// as two separate edges.
	if !g.Directed {
		n /= 2
	}

	return n
}

// Equals returns whether the graph is equal to the given graph.
// Two graphs are equal of their adjacency is equal.
func (g *Graph) Equals(g2 *Graph) bool {
	// Two graphs with different number of vertices aren’t equal.
	if g.NVertices() != g2.NVertices() {
		return false
	}

	// Some for number of edges.
	if g.NEdges() != g2.NEdges() {
		return false
	}

	// Check if the adjacency for each vertex is equal
	// for both graphs.
	a1 := g.Adjacency
	a2 := g2.Adjacency

	for k, v := range a1 {
		if !v.Equals(a2[k]) {
			return false
		}
	}

	return true
}

// VerticesIter returns a channel where all vertices
// are sent to.
func (g *Graph) VerticesIter() chan Vertex {
	ch := make(chan Vertex)
	go func() {
		for k, _ := range g.Adjacency {
			ch <- k.(Vertex)
		}
		close(ch)
	}()
	return ch
}

// SortedEdges is an array of edges that can be sorted
// by their cost.
type SortedEdges []Edge

func (se SortedEdges) Len() int {
	return len(se)
}

func (se SortedEdges) Less(i, j int) bool {
	return se[i].Cost < se[j].Cost
}

func (se SortedEdges) Swap(i, j int) {
	se[i], se[j] = se[j], se[i]
}

// SortedEdges returns an array of edges sorted by their cost.
func (g *Graph) SortedEdges() SortedEdges {
	set := NewSet()

	for v := range g.Adjacency {
		for he := range g.HalfedgesIter(v) {
			set.Add(Edge{
				Start: v,
				End:   he.End,
				Cost:  he.Cost,
			})
		}
	}

	edges := make(SortedEdges, set.Len())
	for e := range set.Iter() {
		edges = append(edges, e.(Edge))
	}

	sort.Sort(&edges)
	return edges
}

// EdgesIter returns a channel with all edges of the graph.
func (g *Graph) EdgesIter() chan Edge {
	ch := make(chan Edge)
	go func() {
		for v, s := range g.Adjacency {
			for x := range s.Iter() {
				he := x.(Halfedge)
				ch <- Edge{v, he.End, he.Cost}
			}
		}
		close(ch)
	}()
	return ch
}

// HalfedgesIter returns a channel with all halfedges for
// the given start vertex.
func (g *Graph) HalfedgesIter(v Vertex) chan Halfedge {
	ch := make(chan Halfedge)
	go func() {
		if s, exists := g.Adjacency[v]; exists {
			for x := range s.Iter() {
				he := x.(Halfedge)
				ch <- he
			}
		}
		close(ch)
	}()
	return ch
}
<|endoftext|>


<commit_before>package inj

import "reflect"

// A Graph object represents an flat tree of application
// dependencies, a count of currently unmet dependencies,
// and a list of encountered errors.
type Graph struct {
	Nodes             nodeMap
	UnmetDependencies int
	Errors            []string
}

// Create a new instance of a graph with allocated memory
func NewGraph() (g *Graph) {

	g = &Graph{}

	g.Nodes = make(nodeMap)
	g.Errors = make([]string, 0)

	return
}

// Add a node by reflection type
func (g *Graph) add(typ reflect.Type) (n *GraphNode) {

	n = NewGraphNode()
	g.Nodes[typ] = n

	return
}
<commit_msg>Optionally provide with NewGraph<commit_after>package inj

import "reflect"

// A Graph object represents an flat tree of application
// dependencies, a count of currently unmet dependencies,
// and a list of encountered errors.
type Graph struct {
	Nodes             nodeMap
	UnmetDependencies int
	Errors            []string
}

// Create a new instance of a graph with allocated memory
func NewGraph(providers ...interface{}) (g *Graph) {

	g = &Graph{}

	g.Nodes = make(nodeMap)
	g.Errors = make([]string, 0)

	g.Provide(providers...)

	return
}

// Add a node by reflection type
func (g *Graph) add(typ reflect.Type) (n *GraphNode) {

	n = NewGraphNode()
	g.Nodes[typ] = n

	return
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type ConfigLevel int

const (
	// System-wide configuration file; /etc/gitconfig on Linux systems
	ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM

	// XDG compatible configuration file; typically ~/.config/git/config
	ConfigLevelXDG ConfigLevel = C.GIT_CONFIG_LEVEL_XDG

	// User-specific configuration file (also called Global configuration
	// file); typically ~/.gitconfig
	ConfigLevelGlobal ConfigLevel = C.GIT_CONFIG_LEVEL_GLOBAL

	// Repository specific configuration file; $WORK_DIR/.git/config on
	// non-bare repos
	ConfigLevelLocal ConfigLevel = C.GIT_CONFIG_LEVEL_LOCAL

	// Application specific configuration file; freely defined by applications
	ConfigLevelApp ConfigLevel = C.GIT_CONFIG_LEVEL_APP

	// Represents the highest level available config file (i.e. the most
	// specific config file available that actually is loaded)
	ConfigLevelHighest ConfigLevel = C.GIT_CONFIG_HIGHEST_LEVEL
)

type Config struct {
	ptr *C.git_config
}

// NewConfig creates a new empty configuration object
func NewConfig() (*Config, error) {
	config := new(Config)

	ret := C.git_config_new(&config.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// AddFile adds a file-backed backend to the config object at the specified level.
func (c *Config) AddFile(path string, level ConfigLevel, force bool) error {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) LookupInt32(name string) (int32, error) {
	var out C.int32_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int32(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int32(out), nil
}

func (c *Config) LookupInt64(name string) (int64, error) {
	var out C.int64_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int64(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int64(out), nil
}

func (c *Config) LookupString(name string) (string, error) {
	var ptr *C.char
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_string(&ptr, c.ptr, cname)
	if ret < 0 {
		return "", LastError()
	}

	return C.GoString(ptr), nil
}

func (c *Config) LookupBool(name string) (bool, error) {
	var out C.int
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_get_bool(&out, c.ptr, cname)
	if ret < 0 {
		return false, LastError()
	}

	return out != 0, nil
}

func (c *Config) SetString(name, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_set_string(c.ptr, cname, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Free() {
	runtime.SetFinalizer(c, nil)
	C.git_config_free(c.ptr)
}

func (c *Config) SetInt32(name string, value int32) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetInt64(name string, value int64) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_int64(c.ptr, cname, C.int64_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetBool(name string, value bool) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_bool(c.ptr, cname, cbool(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetMultivar(name, regexp, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cregexp := C.CString(regexp)
	defer C.free(unsafe.Pointer(cregexp))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	ret := C.git_config_set_multivar(c.ptr, cname, cregexp, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Delete(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_delete_entry(c.ptr, cname)

	if ret < 0 {
		return LastError()
	}

	return nil
}

// OpenLevel creates a single-level focused config object from a multi-level one
func (c *Config) OpenLevel(parent *Config, level ConfigLevel) (*Config, error) {
	config := new(Config)
	ret := C.git_config_open_level(&config.ptr, parent.ptr, C.git_config_level_t(level))
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// OpenOndisk creates a new config instance containing a single on-disk file
func OpenOndisk(parent *Config, path string) (*Config, error) {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	config := new(Config)
	ret := C.git_config_open_ondisk(&config.ptr, cpath)
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// Refresh refreshes the configuration to reflect any changes made externally e.g. on disk
func (c *Config) Refresh() error {
	ret := C.git_config_refresh(c.ptr)
	if ret < 0 {
		return LastError()
	}

	return nil
}
<commit_msg>Add iterators and ConfigEntry<commit_after>package git

/*
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type ConfigLevel int

const (
	// System-wide configuration file; /etc/gitconfig on Linux systems
	ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM

	// XDG compatible configuration file; typically ~/.config/git/config
	ConfigLevelXDG ConfigLevel = C.GIT_CONFIG_LEVEL_XDG

	// User-specific configuration file (also called Global configuration
	// file); typically ~/.gitconfig
	ConfigLevelGlobal ConfigLevel = C.GIT_CONFIG_LEVEL_GLOBAL

	// Repository specific configuration file; $WORK_DIR/.git/config on
	// non-bare repos
	ConfigLevelLocal ConfigLevel = C.GIT_CONFIG_LEVEL_LOCAL

	// Application specific configuration file; freely defined by applications
	ConfigLevelApp ConfigLevel = C.GIT_CONFIG_LEVEL_APP

	// Represents the highest level available config file (i.e. the most
	// specific config file available that actually is loaded)
	ConfigLevelHighest ConfigLevel = C.GIT_CONFIG_HIGHEST_LEVEL
)

type ConfigEntry struct {
	Name string
	Value string
	Level ConfigLevel
}

func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
	return &ConfigEntry{
		Name: C.GoString(centry.name),
		Value: C.GoString(centry.value),
		Level: ConfigLevel(centry.level),
	}
}

type Config struct {
	ptr *C.git_config
}

// NewConfig creates a new empty configuration object
func NewConfig() (*Config, error) {
	config := new(Config)

	ret := C.git_config_new(&config.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// AddFile adds a file-backed backend to the config object at the specified level.
func (c *Config) AddFile(path string, level ConfigLevel, force bool) error {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) LookupInt32(name string) (int32, error) {
	var out C.int32_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int32(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int32(out), nil
}

func (c *Config) LookupInt64(name string) (int64, error) {
	var out C.int64_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int64(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int64(out), nil
}

func (c *Config) LookupString(name string) (string, error) {
	var ptr *C.char
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_string(&ptr, c.ptr, cname)
	if ret < 0 {
		return "", LastError()
	}

	return C.GoString(ptr), nil
}

func (c *Config) LookupBool(name string) (bool, error) {
	var out C.int
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_get_bool(&out, c.ptr, cname)
	if ret < 0 {
		return false, LastError()
	}

	return out != 0, nil
}

func (c *Config) NewMultivarIterator(name, regexp string) (*ConfigIterator, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	var cregexp *C.char
	if regexp == "" {
		cregexp = nil
	} else {
		cregexp = C.CString(regexp)
		defer C.free(unsafe.Pointer(cregexp))
	}

	iter := new(ConfigIterator)
	ret := C.git_config_multivar_iterator_new(&iter.ptr, c.ptr, cname, cregexp)
	if ret < 0 {
		return nil, LastError()
	}

	runtime.SetFinalizer(iter, (*ConfigIterator).Free)
	return iter, nil
}

// NewIterator creates an iterator over each entry in the
// configuration
func (c *Config) NewIterator() (*ConfigIterator, error) {
	iter := new(ConfigIterator)
	ret := C.git_config_iterator_new(&iter.ptr, c.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return iter, nil
}

// NewIteratorGlob creates an iterator over each entry in the
// configuration whose name matches the given regular expression
func (c *Config) NewIteratorGlob(regexp string) (*ConfigIterator, error) {
	iter := new(ConfigIterator)
	cregexp := C.CString(regexp)
	defer C.free(unsafe.Pointer(cregexp))

	ret := C.git_config_iterator_glob_new(&iter.ptr, c.ptr, cregexp)
	if ret < 0 {
		return nil, LastError()
	}

	return iter, nil
}

func (c *Config) SetString(name, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_set_string(c.ptr, cname, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Free() {
	runtime.SetFinalizer(c, nil)
	C.git_config_free(c.ptr)
}

func (c *Config) SetInt32(name string, value int32) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetInt64(name string, value int64) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_int64(c.ptr, cname, C.int64_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetBool(name string, value bool) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_bool(c.ptr, cname, cbool(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetMultivar(name, regexp, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cregexp := C.CString(regexp)
	defer C.free(unsafe.Pointer(cregexp))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	ret := C.git_config_set_multivar(c.ptr, cname, cregexp, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Delete(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_delete_entry(c.ptr, cname)

	if ret < 0 {
		return LastError()
	}

	return nil
}

// OpenLevel creates a single-level focused config object from a multi-level one
func (c *Config) OpenLevel(parent *Config, level ConfigLevel) (*Config, error) {
	config := new(Config)
	ret := C.git_config_open_level(&config.ptr, parent.ptr, C.git_config_level_t(level))
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// OpenOndisk creates a new config instance containing a single on-disk file
func OpenOndisk(parent *Config, path string) (*Config, error) {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	config := new(Config)
	ret := C.git_config_open_ondisk(&config.ptr, cpath)
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// Refresh refreshes the configuration to reflect any changes made externally e.g. on disk
func (c *Config) Refresh() error {
	ret := C.git_config_refresh(c.ptr)
	if ret < 0 {
		return LastError()
	}

	return nil
}

type ConfigIterator struct {
	ptr *C.git_config_iterator
}

// Next returns the next entry for this iterator
func (iter *ConfigIterator) Next() (*ConfigEntry, error) {
	var centry *C.git_config_entry

	ret := C.git_config_next(&centry, iter.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return newConfigEntryFromC(centry), nil
}

func (iter *ConfigIterator) Free() {
	runtime.SetFinalizer(iter, nil)
	C.free(unsafe.Pointer(iter.ptr))
}

<|endoftext|>