<commit_before>package main

import (
	"github.com/rcrowley/go-metrics"
	"io"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"path"
	"stathat.com/c/consistent"
	"strings"
)

// Sattolo shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Sattolo.27s_algorithm
func shuffle(list []string) {
	for i := range list {
		j := rand.Intn(i + 1)
		list[i], list[j] = list[j], list[i]
	}
}

type MeteredTransport struct {
	http.Transport
	cacheproxy *CacheProxy
}

func (t *MeteredTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	t.cacheproxy.requests.Mark(1)
	resp, err = t.Transport.RoundTrip(req)
	switch {
	case resp.Header.Get("X-Cache") == "HIT":
		t.cacheproxy.hits.Mark(1)
	default:
		t.cacheproxy.misses.Mark(1)
	}
	return
}

type CacheProxy struct {
	backends *consistent.Consistent
	manager  *http.ServeMux
	proxy    *httputil.ReverseProxy
	registry metrics.Registry
	hits     metrics.Meter
	misses   metrics.Meter
	requests metrics.Meter
}

func New() *CacheProxy {
	c := new(CacheProxy)
	c.backends = consistent.New()

	c.registry = metrics.NewRegistry()
	c.hits = metrics.NewMeter()
	c.misses = metrics.NewMeter()
	c.requests = metrics.NewMeter()
	c.registry.Register("hits", c.hits)
	c.registry.Register("misses", c.misses)
	c.registry.Register("requests", c.requests)

	// RESTful interface for cache members
	c.manager = http.NewServeMux()
	c.manager.HandleFunc("/members/", func(w http.ResponseWriter, req *http.Request) {
		switch {
		case req.Method == "PUT":
			// Adds "member" as a cache member
			c.backends.Add(path.Base(req.URL.Path))
		case req.Method == "DELETE":
			// Deletes "member" from cache ring
			c.backends.Remove(path.Base(req.URL.Path))
		}
		// Always returns a list of current cache members
		io.WriteString(w, strings.Join(c.backends.Members(), ","))
	})

	// RESTful interface for metrics
	c.manager.HandleFunc("/metrics/", func(w http.ResponseWriter, req *http.Request) {
		metrics.WriteOnce(c.registry, w)
	})

	// Reverse proxy that selects the backend by nearest match to the request URL
	// on the consistent hash ring.
	c.proxy = &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			req.URL.Scheme = "http"
			backends := c.Select(req.URL.Path)
			req.URL.Host = backends[0]
			req.Header.Set("X-Backends", strings.Join(backends, ","))
		},
		Transport: &MeteredTransport{cacheproxy: c},
	}

	return c
}

func (c *CacheProxy) Select(path string) []string {
	selection, _ := c.backends.GetN(path, 3)
	shuffle(selection)
	return selection
}

func main() {
	proxy := New()

	// Replace with runtime arguments for initialization
	proxy.backends.Add("localhost:9091")
	proxy.backends.Add("localhost:9092")
	proxy.backends.Add("localhost:9093")
	proxy.backends.Add("localhost:9094")
	proxy.backends.Add("localhost:9095")

	// Initialize and run manager server in background via goroutine
	go http.ListenAndServe(":9190", proxy.manager)

	http.ListenAndServe(":9090", proxy.proxy)
}
<commit_msg>Adding backend response timer<commit_after>package main

import (
	"github.com/rcrowley/go-metrics"
	"io"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"path"
	"stathat.com/c/consistent"
	"strings"
)

// Sattolo shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Sattolo.27s_algorithm
func shuffle(list []string) {
	for i := range list {
		j := rand.Intn(i + 1)
		list[i], list[j] = list[j], list[i]
	}
}

// Embeds http.Transport for use along with metrics registry
type MeteredTransport struct {
	http.Transport
	cacheproxy *CacheProxy
}

// Shadows http.Transport.RoundTrip method with meter and timer updates
func (t *MeteredTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	// Mark the request meter
	t.cacheproxy.requests.Mark(1)

	// Time the real transport method
	t.cacheproxy.timer.Time(func() {
		resp, err = t.Transport.RoundTrip(req)
	})

	// Mark the appropriate cache response meter
	switch {
	case resp.Header.Get("X-Cache") == "HIT":
		t.cacheproxy.hits.Mark(1)
	default:
		t.cacheproxy.misses.Mark(1)
	}

	return
}

type CacheProxy struct {
	backends *consistent.Consistent
	manager  *http.ServeMux
	proxy    *httputil.ReverseProxy
	registry metrics.Registry
	hits     metrics.Meter
	misses   metrics.Meter
	requests metrics.Meter
	timer    metrics.Timer
}

func New() *CacheProxy {
	c := new(CacheProxy)
	c.backends = consistent.New()

	// Metric registry
	c.registry = metrics.NewRegistry()

	// Meters
	c.hits = metrics.NewMeter()
	c.misses = metrics.NewMeter()
	c.requests = metrics.NewMeter()
	c.registry.Register("hits", c.hits)
	c.registry.Register("misses", c.misses)
	c.registry.Register("requests", c.requests)

	// Backend response timer
	c.timer = metrics.NewTimer()
	c.registry.Register("backend", c.timer)

	// RESTful interface for cache members
	c.manager = http.NewServeMux()
	c.manager.HandleFunc("/members/", func(w http.ResponseWriter, req *http.Request) {
		switch {
		case req.Method == "PUT":
			// Adds "member" as a cache member
			c.backends.Add(path.Base(req.URL.Path))
		case req.Method == "DELETE":
			// Deletes "member" from cache ring
			c.backends.Remove(path.Base(req.URL.Path))
		}
		// Always returns a list of current cache members
		io.WriteString(w, strings.Join(c.backends.Members(), ","))
	})

	// RESTful interface for metrics
	c.manager.HandleFunc("/metrics/", func(w http.ResponseWriter, req *http.Request) {
		metrics.WriteOnce(c.registry, w)
	})

	// Reverse proxy that selects the backend by nearest match to the request URL
	// on the consistent hash ring.
	c.proxy = &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			req.URL.Scheme = "http"
			backends := c.Select(req.URL.Path)
			req.URL.Host = backends[0]
			req.Header.Set("X-Backends", strings.Join(backends, ","))
		},
		Transport: &MeteredTransport{cacheproxy: c},
	}

	return c
}

func (c *CacheProxy) Select(path string) []string {
	selection, _ := c.backends.GetN(path, 3)
	shuffle(selection)
	return selection
}

func main() {
	proxy := New()

	// Replace with runtime arguments for initialization
	proxy.backends.Add("localhost:9091")
	proxy.backends.Add("localhost:9092")
	proxy.backends.Add("localhost:9093")
	proxy.backends.Add("localhost:9094")
	proxy.backends.Add("localhost:9095")

	// Initialize and run manager server in background via goroutine
	go http.ListenAndServe(":9190", proxy.manager)

	http.ListenAndServe(":9090", proxy.proxy)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"encoding/csv"
	"math/rand"
	"strconv"
	"reflect"
)

const path = "/Users/plorenzo/dev/uni/rna/p1"

func readCSV(filepath string) [][]float64 {

	csvfile, err := os.Open(filepath)
	if err != nil {
		return nil
	}

	reader := csv.NewReader(csvfile)
	stringMatrix, err := reader.ReadAll()

	csvfile.Close()

	matrix := make([][]float64, len(stringMatrix))

	for i := range stringMatrix {
		matrix[i] = make([]float64, len(stringMatrix[0]))
		for y := range stringMatrix[i] {
			matrix[i][y], err = strconv.ParseFloat(stringMatrix[i][y], 64)
		}
	}

	return matrix
}

func initWeights(length int) []float64 {

	weights := make([]float64, length)
	for index := range weights {
		weights[index] = rand.Float64()
	}

	return weights
}

func main() {

	data := readCSV(path)

}
<commit_msg>1st version learning loop finished<commit_after>package main

import (
	"fmt"
	"os"
	"encoding/csv"
	"math/rand"
	"strconv"
	"reflect"
	"time"
)

const trainPath = "/Users/plorenzo/dev/uni/rna/final/train.csv"
const validatePath = "/Users/plorenzo/dev/uni/rna/final/train.csv"
const testPath = "/Users/plorenzo/dev/uni/rna/final/train.csv"

// TODO: refacto to improve performance (S.O. QUESTION)
func readCSV(filepath string) ([][]float64, []float64) {

	csvfile, err := os.Open(filepath)
	if err != nil {
		return nil, nil
	}

	reader := csv.NewReader(csvfile)
	reader.Comma = ';'
	stringMatrix, err := reader.ReadAll()

	csvfile.Close()

	matrix := make([][]float64, len(stringMatrix))
	expectedY := make([]float64, len(stringMatrix))

	//Parse string matrix into float64
	for i := range stringMatrix {
		matrix[i] = make([]float64, len(stringMatrix[0]))
		for j := range stringMatrix[i] {
			if j < 8 {
				matrix[i][j], err = strconv.ParseFloat(stringMatrix[i][j], 64)
			} else {
				//Extract expected output date from file (last column)
				expectedY[i], err = strconv.ParseFloat(stringMatrix[i][j], 64)
				matrix[i][j] = 1
			}

		}
	}
	return matrix, expectedY
}

//This also inits the threshold
func initWeights(length int) []float64 {

	weights := make([]float64, length)

	//Inits the slice with random numbers betwen [-1, 1]
	for index := range weights {
		w := rand.Float64()
		s := rand.Float64()

		if s < 0.5 {
			weights[index] = w
		} else {
			weights[index] = w * -1
		}

	}
	return weights
}

func main() {

	start := time.Now()

	//Read data from csv file
	data, expectedY := readCSV(trainPath)

	//PARAMETERS
	var cylces int = 10000
	var learningRate float64 = 0.1

	// Sanity checking
	fmt.Println("Size data: ")
	fmt.Println(len(data[0]))
	fmt.Println(len(data))
	fmt.Println("First data value: ")
	fmt.Println(data[0][0])
	fmt.Println("Data type: ")
	fmt.Println(reflect.TypeOf(data))
	fmt.Println("Expected Y: ")
	fmt.Println(expectedY[0])

	inputLength := len(data[0])
	weights := initWeights(inputLength)

	var errors []float64

	// Learning
	for i := 0; i < cylces; i++ {
		for j := range data {
			//Calculate estimate
			var estimate float64 = 0
			for x := range data[j]{
				estimate += data[j][x] * weights[x]
			}

			// Update weights (range passes calues as a copy)
			for x := 0 ; x < len(weights); x++ {
				weights[x] = learningRate * (expectedY[j] - estimate) * data[j][x]
			}
		}

		// Measure cylce error
		var totalErr float64 = 0

		for j := range data {
				var estimate float64 = 0
				for x := range data[j] {
					estimate += data[j][x] * weights[x]
				}
				// Cuadratic error E = (Yd - Ye)^2
				totalErr += (expectedY[j] - estimate) * (expectedY[j] - estimate)
		}
		errors = append(errors, totalErr / float64(len(errors)))
	}

	//TODO save global error to file

	elapsed := time.Since(start)
	fmt	.Println(elapsed)
	//fmt.Println(errors)

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"github.com/Sirupsen/logrus"
	"io"
	"net"
	"strconv"
	"sync"
)

const (
	NETBUF_SIZE = 2048 // bytes
)

var (
	poolNetBuffers sync.Pool
)

// var-constants
var (
	HEAD_CONNECTION            = []byte("CONNECTION")
	HEAD_CONNECTION_KEEP_ALIVE = []byte("KEEP-ALIVE")
	HEAD_CONTENT_LENGTH        = []byte("CONTENT-LENGTH")
)

// Get or create network buffer for proxy
func netbufGet() (buf []byte) {
	bufInterface := poolNetBuffers.Get()
	if bufInterface == nil {
		buf = make([]byte, NETBUF_SIZE)
	} else {
		buf = bufInterface.([]byte)
		// prevent data leak
		for i := range buf {
			buf[i] = 0
		}

	}
	return buf
}

func netbufPut(buf []byte) {
	poolNetBuffers.Put(buf)
}

func proxyHTTPHeaders(targetConn net.Conn, sourceConn net.Conn) (keepalive bool, contentLength int64) {
	buf := netbufGet()
	defer netbufPut(buf)
	var totalReadBytes int

	// Read lines
readHeaderLines:
	for {
		var i int
		var headerStart []byte
		for i = 0; i < len(buf); i++ {
			readBytes, err := sourceConn.Read(buf[i : i+1])
			totalReadBytes += readBytes
			if err != nil {
				if err == io.EOF && totalReadBytes == 0 {
					// pass - normal end connection.
				} else {
					logrus.Infof("Error while read header from '%v': %v", sourceConn.RemoteAddr(), err)
				}
				targetConn.Close()
				sourceConn.Close()
				return
			}
			if readBytes != 1 {
				logrus.Infof("Can't read a byte from header from '%v'", sourceConn.RemoteAddr())
				targetConn.Close()
				sourceConn.Close()
				return
			}
			if buf[i] == ':' || buf[i] == '\n' {
				headerStart = buf[:i+1]
				logrus.Debugf("Header Name '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), buf[:i])
				targetConn.Close()
				sourceConn.Close()
				break
			}
		}
		if len(headerStart) == 0 {
			logrus.Infof("Header line longer then buffer (%v bytes). Force close connection. '%v' -> '%v'.", len(buf), sourceConn.RemoteAddr(), targetConn.RemoteAddr())
			targetConn.Close()
			sourceConn.Close()
			return
		}

		// Empty line - end http headers
		if bytes.Equal(headerStart, []byte("\n")) || bytes.Equal(headerStart, []byte("\r\n")) {
			break readHeaderLines
		}

		headerName := bytes.ToUpper(headerStart[:len(headerStart)-1]) // Cut trailing colon from start

		skipHeader := false
		for _, ownHeader := range cutHeaders {
			if bytes.Equal(ownHeader, headerName) {
				skipHeader = true
				break
			}
			ownHeaderS := string(ownHeader)
			headerNameS := string(headerName)
			skipHeader = ownHeaderS == headerNameS
		}

		if skipHeader {
			logrus.Debugf("Skip header: '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerName)
			buf[0] = headerStart[len(headerStart)-1]

			for buf[0] != '\n' {
				_, err := sourceConn.Read(buf[:1])
				if err != nil {
					logrus.Infof("Error read header. Close connections. '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
					sourceConn.Close()
					targetConn.Close()
					return
				}
			}
			continue readHeaderLines
		}

		logrus.Debugf("Copy header: '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerName)

		// copy header without changes
		_, err := targetConn.Write(headerStart)
		if err != nil {
			logrus.Infof("Write header start, from '%v' to '%v', headerStart='%s': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerStart, err)
			sourceConn.Close()
			targetConn.Close()
			return
		}

		needHeaderContent := bytes.Equal(headerName, HEAD_CONTENT_LENGTH) || bytes.Equal(headerName, HEAD_CONNECTION)
		headerContent := bytes.NewBuffer(buf[1:])
		headerContent.Reset()

		for buf[0] != '\n' {
			readBytes, err := sourceConn.Read(buf[:1])
			if err != nil {
				logrus.Infof("Error read header to copy. Close connections. '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
				sourceConn.Close()
				targetConn.Close()
				return
			}
			if readBytes != 1 {
				logrus.Infof("Header copy read bytes != 1. Error. Close connections. '%v' -> '%v'", sourceConn.RemoteAddr(), targetConn.RemoteAddr())
				sourceConn.Close()
				targetConn.Close()
				return
			}
			_, err = targetConn.Write(buf[:1])
			if err != nil {
				logrus.Infof("Error write header. Close connections. '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
				sourceConn.Close()
				targetConn.Close()
				return
			}
			if needHeaderContent {
				headerContent.WriteByte(buf[0])
			}
		}
		if needHeaderContent {
			switch {
			case bytes.Equal(headerName, HEAD_CONNECTION):
				keepalive = bytes.EqualFold(HEAD_CONNECTION_KEEP_ALIVE, bytes.TrimSpace(headerContent.Bytes()))

			case bytes.Equal(headerName, HEAD_CONTENT_LENGTH):
				contentLength, err = strconv.ParseInt(string(bytes.TrimSpace(headerContent.Bytes())), 10, 64)
				if err == nil {
					logrus.Debugf("Header content-length parsed from '%v' to '%v': %v", sourceConn.RemoteAddr(),
						targetConn.RemoteAddr(), contentLength)
				} else {
					logrus.Infof("Can't header content-length parsed from '%v' to '%v' content '%s': %v", sourceConn.RemoteAddr(),
						targetConn.RemoteAddr(), headerContent.Bytes(), err)
					contentLength = 0
				}

			default:
				logrus.Debug("ERROR. Unknow why i need header content. Code error.")
			}
		}
	}

	remoteTcpAddr := sourceConn.RemoteAddr().(*net.TCPAddr)
	remoteAddrString := remoteTcpAddr.IP.String()

	headerBuf := bytes.NewBuffer(buf)
	headerBuf.Reset()

	// Write real IP
	for _, header := range realIPHeaderNames {
		headerBuf.Write(header)
		headerBuf.WriteByte(':')
		headerBuf.WriteString(remoteAddrString)
		headerBuf.Write([]byte("\r\n"))
	}
	headerBuf.Write(additionalHeaders)
	headerBuf.Write([]byte("\r\n")) // end http headers
	logrus.Debugf("Add headers. '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerBuf.Bytes())

	_, err := targetConn.Write(headerBuf.Bytes())
	if err != nil {
		logrus.Infof("Error while write real ip headers to target '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
	}

	return
}

func startProxy(targetAddr net.TCPAddr, in net.Conn) {
	targetConnCommon, err := net.DialTimeout("tcp", targetAddr.String(), *targetConnTimeout)
	if err != nil {
		logrus.Warnf("Can't connect to target '%v': %v", targetAddr.String(), err)
		return
	}

	targetConn, ok := targetConnCommon.(*net.TCPConn)
	if !ok {
		logrus.Errorf("Can't cast connection to tcp connection, target '%v'", targetAddr.String())
		return
	}

	switch *proxyMode {
	case "http":
		startProxyHTTP(targetConn, in)
	case "tcp":
		startProxyTCP(targetConn, in)
	default:
		logrus.Panicf("Unknow proxy mode: %v", *proxyMode)
	}
}

func startProxyHTTP(targetConn net.Conn, sourceConn net.Conn) {
	logrus.Infof("Start http-proxy connection from '%v' to'%v'", sourceConn.RemoteAddr(), targetConn.RemoteAddr())

	// answer from server proxy without changes
	go func() {
		buf := netbufGet()
		defer netbufPut(buf)

		_, err := io.CopyBuffer(sourceConn, targetConn, buf)
		logrus.Debugf("Connection closed with error '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
		sourceConn.Close()
		targetConn.Close()
	}()

	// proxy incoming traffic, parse every headers
	go func() {
		buf := netbufGet()
		defer netbufPut(buf)
		var summBytesCopied int64
		for {
			keepalive, contentLength := proxyHTTPHeaders(targetConn, sourceConn)
			if keepalive {
				logrus.Debugf("Start keep-alieved proxy. '%v' -> '%v', content-length '%v'", sourceConn.RemoteAddr(),
					targetConn.RemoteAddr(), contentLength)

				bytesCopied, err := io.CopyBuffer(targetConn, io.LimitReader(sourceConn, contentLength), buf)
				summBytesCopied += bytesCopied
				logrus.Debugf("Connection chunk copied '%v' -> '%v', bytes transferred '%v' (%v), error: %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), bytesCopied, summBytesCopied, err)
				if err != nil {
					logrus.Debugf("Connection closed '%v' -> '%v', bytes transferred '%v' (%v), error: %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), bytesCopied, summBytesCopied, err)
				}
			} else {
				bytesCopied, err := io.CopyBuffer(targetConn, sourceConn, buf)
				summBytesCopied += bytesCopied
				logrus.Debugf("Connection closed '%v' -> '%v', bytes transferred '%v' (%v), error: %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), bytesCopied, summBytesCopied, err)
				sourceConn.Close()
				targetConn.Close()
				return
			}
		}
	}()
}

func startProxyTCP(targetConn net.Conn, sourceConn net.Conn) {
	logrus.Infof("Start tcp-proxy connection from '%v' to'%v'", sourceConn.RemoteAddr(), targetConn.RemoteAddr())

	go func() {
		buf := netbufGet()
		defer netbufPut(buf)

		_, err := io.CopyBuffer(targetConn, sourceConn, buf)
		logrus.Debugf("Connection closed with error '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
		sourceConn.Close()
		targetConn.Close()
	}()
	go func() {
		buf := netbufGet()
		defer netbufPut(buf)

		_, err := io.CopyBuffer(sourceConn, targetConn, buf)
		logrus.Debugf("Connection closed with error '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
		sourceConn.Close()
		targetConn.Close()
	}()
}
<commit_msg>fix error close connection<commit_after>package main

import (
	"bytes"
	"github.com/Sirupsen/logrus"
	"io"
	"net"
	"strconv"
	"sync"
)

const (
	NETBUF_SIZE = 2048 // bytes
)

var (
	poolNetBuffers sync.Pool
)

// var-constants
var (
	HEAD_CONNECTION            = []byte("CONNECTION")
	HEAD_CONNECTION_KEEP_ALIVE = []byte("KEEP-ALIVE")
	HEAD_CONTENT_LENGTH        = []byte("CONTENT-LENGTH")
)

// Get or create network buffer for proxy
func netbufGet() (buf []byte) {
	bufInterface := poolNetBuffers.Get()
	if bufInterface == nil {
		buf = make([]byte, NETBUF_SIZE)
	} else {
		buf = bufInterface.([]byte)
		// prevent data leak
		for i := range buf {
			buf[i] = 0
		}

	}
	return buf
}

func netbufPut(buf []byte) {
	poolNetBuffers.Put(buf)
}

func proxyHTTPHeaders(targetConn net.Conn, sourceConn net.Conn) (keepalive bool, contentLength int64) {
	buf := netbufGet()
	defer netbufPut(buf)
	var totalReadBytes int

	// Read lines
readHeaderLines:
	for {
		var i int
		var headerStart []byte
		for i = 0; i < len(buf); i++ {
			readBytes, err := sourceConn.Read(buf[i : i+1])
			totalReadBytes += readBytes
			if err != nil {
				if err == io.EOF && totalReadBytes == 0 {
					// pass - normal end connection.
				} else {
					logrus.Infof("Error while read header from '%v': %v", sourceConn.RemoteAddr(), err)
				}
				targetConn.Close()
				sourceConn.Close()
				return
			}
			if readBytes != 1 {
				logrus.Infof("Can't read a byte from header from '%v'", sourceConn.RemoteAddr())
				targetConn.Close()
				sourceConn.Close()
				return
			}
			if buf[i] == ':' || buf[i] == '\n' {
				headerStart = buf[:i+1]
				logrus.Debugf("Header Name '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), buf[:i])
				break
			}
		}
		if len(headerStart) == 0 {
			logrus.Infof("Header line longer then buffer (%v bytes). Force close connection. '%v' -> '%v'.", len(buf), sourceConn.RemoteAddr(), targetConn.RemoteAddr())
			targetConn.Close()
			sourceConn.Close()
			return
		}

		// Empty line - end http headers
		if bytes.Equal(headerStart, []byte("\n")) || bytes.Equal(headerStart, []byte("\r\n")) {
			break readHeaderLines
		}

		headerName := bytes.ToUpper(headerStart[:len(headerStart)-1]) // Cut trailing colon from start

		skipHeader := false
		for _, ownHeader := range cutHeaders {
			if bytes.Equal(ownHeader, headerName) {
				skipHeader = true
				break
			}
			ownHeaderS := string(ownHeader)
			headerNameS := string(headerName)
			skipHeader = ownHeaderS == headerNameS
		}

		if skipHeader {
			logrus.Debugf("Skip header: '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerName)
			buf[0] = headerStart[len(headerStart)-1]

			for buf[0] != '\n' {
				_, err := sourceConn.Read(buf[:1])
				if err != nil {
					logrus.Infof("Error read header. Close connections. '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
					sourceConn.Close()
					targetConn.Close()
					return
				}
			}
			continue readHeaderLines
		}

		logrus.Debugf("Copy header: '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerName)

		// copy header without changes
		_, err := targetConn.Write(headerStart)
		if err != nil {
			logrus.Infof("Write header start, from '%v' to '%v', headerStart='%s': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerStart, err)
			sourceConn.Close()
			targetConn.Close()
			return
		}

		needHeaderContent := bytes.Equal(headerName, HEAD_CONTENT_LENGTH) || bytes.Equal(headerName, HEAD_CONNECTION)
		headerContent := bytes.NewBuffer(buf[1:])
		headerContent.Reset()

		for buf[0] != '\n' {
			readBytes, err := sourceConn.Read(buf[:1])
			if err != nil {
				logrus.Infof("Error read header to copy. Close connections. '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
				sourceConn.Close()
				targetConn.Close()
				return
			}
			if readBytes != 1 {
				logrus.Infof("Header copy read bytes != 1. Error. Close connections. '%v' -> '%v'", sourceConn.RemoteAddr(), targetConn.RemoteAddr())
				sourceConn.Close()
				targetConn.Close()
				return
			}
			_, err = targetConn.Write(buf[:1])
			if err != nil {
				logrus.Infof("Error write header. Close connections. '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
				sourceConn.Close()
				targetConn.Close()
				return
			}
			if needHeaderContent {
				headerContent.WriteByte(buf[0])
			}
		}
		if needHeaderContent {
			switch {
			case bytes.Equal(headerName, HEAD_CONNECTION):
				keepalive = bytes.EqualFold(HEAD_CONNECTION_KEEP_ALIVE, bytes.TrimSpace(headerContent.Bytes()))

			case bytes.Equal(headerName, HEAD_CONTENT_LENGTH):
				contentLength, err = strconv.ParseInt(string(bytes.TrimSpace(headerContent.Bytes())), 10, 64)
				if err == nil {
					logrus.Debugf("Header content-length parsed from '%v' to '%v': %v", sourceConn.RemoteAddr(),
						targetConn.RemoteAddr(), contentLength)
				} else {
					logrus.Infof("Can't header content-length parsed from '%v' to '%v' content '%s': %v", sourceConn.RemoteAddr(),
						targetConn.RemoteAddr(), headerContent.Bytes(), err)
					contentLength = 0
				}

			default:
				logrus.Debug("ERROR. Unknow why i need header content. Code error.")
			}
		}
	}

	remoteTcpAddr := sourceConn.RemoteAddr().(*net.TCPAddr)
	remoteAddrString := remoteTcpAddr.IP.String()

	headerBuf := bytes.NewBuffer(buf)
	headerBuf.Reset()

	// Write real IP
	for _, header := range realIPHeaderNames {
		headerBuf.Write(header)
		headerBuf.WriteByte(':')
		headerBuf.WriteString(remoteAddrString)
		headerBuf.Write([]byte("\r\n"))
	}
	headerBuf.Write(additionalHeaders)
	headerBuf.Write([]byte("\r\n")) // end http headers
	logrus.Debugf("Add headers. '%v' -> '%v': '%s'", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), headerBuf.Bytes())

	_, err := targetConn.Write(headerBuf.Bytes())
	if err != nil {
		logrus.Infof("Error while write real ip headers to target '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
	}

	return
}

func startProxy(targetAddr net.TCPAddr, in net.Conn) {
	targetConnCommon, err := net.DialTimeout("tcp", targetAddr.String(), *targetConnTimeout)
	if err != nil {
		logrus.Warnf("Can't connect to target '%v': %v", targetAddr.String(), err)
		return
	}

	targetConn, ok := targetConnCommon.(*net.TCPConn)
	if !ok {
		logrus.Errorf("Can't cast connection to tcp connection, target '%v'", targetAddr.String())
		return
	}

	switch *proxyMode {
	case "http":
		startProxyHTTP(targetConn, in)
	case "tcp":
		startProxyTCP(targetConn, in)
	default:
		logrus.Panicf("Unknow proxy mode: %v", *proxyMode)
	}
}

func startProxyHTTP(targetConn net.Conn, sourceConn net.Conn) {
	logrus.Infof("Start http-proxy connection from '%v' to'%v'", sourceConn.RemoteAddr(), targetConn.RemoteAddr())

	// answer from server proxy without changes
	go func() {
		buf := netbufGet()
		defer netbufPut(buf)

		_, err := io.CopyBuffer(sourceConn, targetConn, buf)
		logrus.Debugf("Connection closed with error '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
		sourceConn.Close()
		targetConn.Close()
	}()

	// proxy incoming traffic, parse every headers
	go func() {
		buf := netbufGet()
		defer netbufPut(buf)
		var summBytesCopied int64
		for {
			keepalive, contentLength := proxyHTTPHeaders(targetConn, sourceConn)
			if keepalive {
				logrus.Debugf("Start keep-alieved proxy. '%v' -> '%v', content-length '%v'", sourceConn.RemoteAddr(),
					targetConn.RemoteAddr(), contentLength)

				bytesCopied, err := io.CopyBuffer(targetConn, io.LimitReader(sourceConn, contentLength), buf)
				summBytesCopied += bytesCopied
				logrus.Debugf("Connection chunk copied '%v' -> '%v', bytes transferred '%v' (%v), error: %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), bytesCopied, summBytesCopied, err)
				if err != nil {
					logrus.Debugf("Connection closed '%v' -> '%v', bytes transferred '%v' (%v), error: %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), bytesCopied, summBytesCopied, err)
				}
			} else {
				bytesCopied, err := io.CopyBuffer(targetConn, sourceConn, buf)
				summBytesCopied += bytesCopied
				logrus.Debugf("Connection closed '%v' -> '%v', bytes transferred '%v' (%v), error: %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), bytesCopied, summBytesCopied, err)
				sourceConn.Close()
				targetConn.Close()
				return
			}
		}
	}()
}

func startProxyTCP(targetConn net.Conn, sourceConn net.Conn) {
	logrus.Infof("Start tcp-proxy connection from '%v' to'%v'", sourceConn.RemoteAddr(), targetConn.RemoteAddr())

	go func() {
		buf := netbufGet()
		defer netbufPut(buf)

		_, err := io.CopyBuffer(targetConn, sourceConn, buf)
		logrus.Debugf("Connection closed with error '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
		sourceConn.Close()
		targetConn.Close()
	}()
	go func() {
		buf := netbufGet()
		defer netbufPut(buf)

		_, err := io.CopyBuffer(sourceConn, targetConn, buf)
		logrus.Debugf("Connection closed with error '%v' -> '%v': %v", sourceConn.RemoteAddr(), targetConn.RemoteAddr(), err)
		sourceConn.Close()
		targetConn.Close()
	}()
}
<|endoftext|>


<commit_before>package goarchive

import (
	"archive/tar"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	. "fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"strings"
)

// Struct used to decompress
type Tar struct {
	Verbose bool
	Debug   bool
}

const (
	Bzip = "application/x-bzip2"
	Gz   = "application/x-gzip"
)

func GetReader(path string) (r io.Reader, err error) {
	magic, err := fileMagic(path)
	if err != nil {
		return nil, err
	}
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	switch magic {
	case Bzip:
		r = bzip2.NewReader(fd)
	case Gz:
		r, err = gzip.NewReader(fd)
		if err != nil {
			return nil, err
		}
	}
	return r, err
}

// Returns a new Zip struct
func NewTar() *Tar {
	return new(Tar)
}

func (z *Tar) Peek(cr io.Reader) (dir string, err error) {
	tr := tar.NewReader(cr)
	hdr, err := tr.Next()
	if err != nil && err != io.EOF {
		return "", err
	}
	return path.Clean(hdr.Name), nil
}

// Decompress bzip2 or gzip Reader to destination directory
func (z *Tar) Untar(cr io.Reader, dest string) (err error) {
	tr := tar.NewReader(cr)
	for {
		hdr, err := tr.Next()
		if err != nil && err != io.EOF {
			return err
		}
		if hdr == nil {
			break
		}
		if z.Debug {
			Printf("%v\n", hdr)
		}
		// Switch through header Typeflag and handle tar entry accordingly
		switch hdr.Typeflag {
		// Handles Directories
		case tar.TypeDir:
			path := path.Join(dest, hdr.Name)
			if z.Verbose {
				Printf("%v\n", hdr.Name)
			}
			if err := mkDir(path, hdr.Mode); err != nil {
				return err
			}
			continue
		// TODO: handle symlinks
		case tar.TypeSymlink, tar.TypeLink:
		case tar.TypeReg, tar.TypeRegA:
			path := path.Join(dest, hdr.Name)
			if z.Verbose {
				Printf("%v\n", hdr.Name)
			}
			if err := writeFile(path, hdr, tr); err != nil {
				return err
			}
			continue
		default:
			// Handles gnu LongLink long file names
			if string(hdr.Typeflag) == "L" {
				lfile := new(bytes.Buffer)
				// Get longlink path from tar file data
				lfile.ReadFrom(tr)
				if z.Verbose {
					Printf("%v\n", lfile.String())
				}
				fpath := path.Join(dest, lfile.String())
				// Read next iteration for file data
				hdr, err := tr.Next()
				if hdr.Typeflag == tar.TypeDir {
					err := mkDir(fpath, hdr.Mode)
					if err != nil {
						return err
					}
					continue
				}
				if err != nil && err != io.EOF {
					return err
				}
				// Write long file data to disk
				if err := writeFile(fpath, hdr, tr); err != nil {
					return err
				}
			}
		}
	}
	return
}

// Make directory with permission
func mkDir(path string, mode int64) (err error) {
	if fileExists(path) {
		return
	}
	err = os.Mkdir(path, os.FileMode(mode))
	if err != nil {
		return err
	}
	return
}

// Write file from tar reader
func writeFile(path string, hdr *tar.Header, tr *tar.Reader) (err error) {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	_, err = io.Copy(f, tr)
	f.Close()
	if err != nil {
		return err
	}
	return
}

func (z *Tar) pVerbose(path string) {
	if z.Verbose {
		Printf("%v\n", path)
	}
}

// helper function to test if file/directory exists
func fileExists(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	if !fi.IsDir() || fi.IsDir() {
		return true
	}
	return false
}

func fileMagic(path string) (string, error) {
	output, err := exec.Command("file", "-b", "-i", path).Output()
	if err != nil {
		return "", err
	}
	mime := strings.Split(string(output), " ")
	return mime[0][:len(mime[0])-1], nil
}
<commit_msg>use hdr.Mode when untarring<commit_after>package goarchive

import (
	"archive/tar"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	. "fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"strings"
)

// Struct used to decompress
type Tar struct {
	Verbose bool
	Debug   bool
}

const (
	Bzip = "application/x-bzip2"
	Gz   = "application/x-gzip"
)

func GetReader(path string) (r io.Reader, err error) {
	magic, err := fileMagic(path)
	if err != nil {
		return nil, err
	}
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	switch magic {
	case Bzip:
		r = bzip2.NewReader(fd)
	case Gz:
		r, err = gzip.NewReader(fd)
		if err != nil {
			return nil, err
		}
	}
	return r, err
}

// Returns a new Zip struct
func NewTar() *Tar {
	return new(Tar)
}

func (z *Tar) Peek(cr io.Reader) (dir string, err error) {
	tr := tar.NewReader(cr)
	hdr, err := tr.Next()
	if err != nil && err != io.EOF {
		return "", err
	}
	return path.Clean(hdr.Name), nil
}

// Decompress bzip2 or gzip Reader to destination directory
func (z *Tar) Untar(cr io.Reader, dest string) (err error) {
	tr := tar.NewReader(cr)
	for {
		hdr, err := tr.Next()
		if err != nil && err != io.EOF {
			return err
		}
		if hdr == nil {
			break
		}
		if z.Debug {
			Printf("%v\n", hdr)
		}
		// Switch through header Typeflag and handle tar entry accordingly
		switch hdr.Typeflag {
		// Handles Directories
		case tar.TypeDir:
			path := path.Join(dest, hdr.Name)
			if z.Verbose {
				Printf("%v\n", hdr.Name)
			}
			if err := mkDir(path, hdr.Mode); err != nil {
				return err
			}
			continue
		// TODO: handle symlinks
		case tar.TypeSymlink, tar.TypeLink:
		case tar.TypeReg, tar.TypeRegA:
			path := path.Join(dest, hdr.Name)
			if z.Verbose {
				Printf("%v\n", hdr.Name)
			}
			if err := writeFile(path, hdr, tr); err != nil {
				return err
			}
			continue
		default:
			// Handles gnu LongLink long file names
			if string(hdr.Typeflag) == "L" {
				lfile := new(bytes.Buffer)
				// Get longlink path from tar file data
				lfile.ReadFrom(tr)
				if z.Verbose {
					Printf("%v\n", lfile.String())
				}
				fpath := path.Join(dest, lfile.String())
				// Read next iteration for file data
				hdr, err := tr.Next()
				if hdr.Typeflag == tar.TypeDir {
					err := mkDir(fpath, hdr.Mode)
					if err != nil {
						return err
					}
					continue
				}
				if err != nil && err != io.EOF {
					return err
				}
				// Write long file data to disk
				if err := writeFile(fpath, hdr, tr); err != nil {
					return err
				}
			}
		}
	}
	return
}

// Make directory with permission
func mkDir(path string, mode int64) (err error) {
	if fileExists(path) {
		return
	}
	err = os.Mkdir(path, os.FileMode(mode))
	if err != nil {
		return err
	}
	return
}

// Write file from tar reader
func writeFile(path string, hdr *tar.Header, tr *tar.Reader) (err error) {
	f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, os.FileMode(hdr.Mode))
	if err != nil {
		return err
	}
	_, err = io.Copy(f, tr)
	f.Close()
	if err != nil {
		return err
	}
	return
}

func (z *Tar) pVerbose(path string) {
	if z.Verbose {
		Printf("%v\n", path)
	}
}

// helper function to test if file/directory exists
func fileExists(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	if !fi.IsDir() || fi.IsDir() {
		return true
	}
	return false
}

func fileMagic(path string) (string, error) {
	output, err := exec.Command("file", "-b", "-i", path).Output()
	if err != nil {
		return "", err
	}
	mime := strings.Split(string(output), " ")
	return mime[0][:len(mime[0])-1], nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Peter Goetz
//
// 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 main

import (
	"fmt"
	"go/ast"
	"go/token"
	"os"
	"os/exec"
	"sort"
	"strings"

	"github.com/petergtz/goextract/util"
)

func ExtractFileToFile(inputFileName string, selection Selection, extractedFuncName string, outputFilename string) {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	util.WriteFileAsStringOrPanic(outputFilename, stringFrom(fileSet, astFile))
	err := exec.Command("gofmt", "-w", outputFilename).Run()
	if err != nil {
		fmt.Fprintln(os.Stderr, util.ReadFileAsStringOrPanic(outputFilename))
		panic(err)
	}
}

func ExtractFileToString(inputFileName string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func ExtractStringToString(input string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromInput(input)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func doExtraction(fileSet *token.FileSet, astFile *ast.File, selection Selection, extractedFuncName string) {
	visitor := &astNodeVisitorForExpressions{parentNode: nil, context: &expressionVisitorContext{fset: fileSet, selection: selection}}
	ast.Walk(visitor, astFile)
	if visitor.context.exprToExtract != nil {
		extractExpression(astFile, fileSet, visitor.context.exprToExtract, visitor.context.parent, extractedFuncName)
	} else {
		v := &astNodeVisitorForMultipleStatements{parentNode: nil, context: &multipleStatementVisitorContext{fset: fileSet, selection: selection}}
		ast.Walk(v, astFile)
		if v.context.posParent != v.context.endParent {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		if v.context.posParent == nil {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		extractMultipleStatements(astFile, fileSet, v.context.nodesToExtract, v.context.posParent, extractedFuncName)
	}
}

func globalVarIdents(astFile *ast.File) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	ast.Inspect(astFile, func(node ast.Node) bool {
		switch typedNode := node.(type) {
		case *ast.FuncDecl:
			return false
		case *ast.GenDecl:
			if typedNode.Tok.String() == "var" {
				for _, spec := range typedNode.Specs {
					for _, name := range spec.(*ast.ValueSpec).Names {
						result[name.Name] = name
					}
				}
			}
			return true
		default:
			return true
		}
	})
	return result
}

func varIdentsDeclaredWithin(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			switch typedNode := node.(type) {

			case *ast.AssignStmt:
				if typedNode.Tok.String() == ":=" {
					for i := range typedNode.Lhs {
						result[typedNode.Lhs[i].(*ast.Ident).Name] = typedNode.Lhs[i].(*ast.Ident)
					}
				}

			case *ast.RangeStmt:
				if typedNode.Tok.String() == ":=" {
					result[typedNode.Key.(*ast.Ident).Name] = typedNode.Key.(*ast.Ident)
					if typedNode.Value != nil {
						result[typedNode.Value.(*ast.Ident).Name] = typedNode.Value.(*ast.Ident)
					}
				}
			}

			// TODO: Add IfStmt, ForStmt, SwitchStmt etc.
			return true
		})
	}
	return result
}

func varIdentsUsedIn(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok &&
				ident.Obj != nil && ident.Obj.Kind == ast.Var {
				result[ident.Name] = ident
			}
			return true
		})
	}
	return result
}

// TODO rename to varIdentsUsedIn
func overlappingVarsIdentsUsedIn(stmts []ast.Stmt, outOf map[string]*ast.Ident) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, stmt := range stmts {
		ast.Inspect(stmt, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok {
				if outOf[ident.Name] != nil {
					result[ident.Name] = ident
				}
			}
			return true
		})
	}
	return result
}

func namesOf(idents map[string]*ast.Ident) []string {
	result := make([]string, 0, len(idents))
	for _, k := range sortedKeysFrom(idents) {
		result = append(result, k)
	}
	return result
}

func exprsFrom(idents map[string]*ast.Ident) []ast.Expr {
	result := make([]ast.Expr, len(idents))
	for i, key := range sortedKeysFrom(idents) {
		result[i] = idents[key]
	}
	return result
}

func callExprWith(funcName string, params map[string]*ast.Ident) *ast.CallExpr {
	return &ast.CallExpr{
		Fun:  ast.NewIdent(funcName),
		Args: exprsFrom(params),
	}
}

func fieldsFrom(params map[string]*ast.Ident) (result []*ast.Field) {
	for _, key := range sortedKeysFrom(params) {
		result = append(result, &ast.Field{
			Names: []*ast.Ident{ast.NewIdent(key)},
			Type:  deduceTypeIdentForVarIdent(params[key]),
		})
	}
	return
}

func sortedKeysFrom(m map[string]*ast.Ident) []string {
	var keys []string
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

func deduceTypeIdentsForExpr(expr ast.Expr) []ast.Expr {
	switch typedExpr := expr.(type) {
	case *ast.Ident:
		// TODO why do we even get type-like idents in here? Shouldn't this be already be handled one level higher?
		if typedExpr.Obj == nil {
			return []ast.Expr{typedExpr}
		}
		switch typedExpr.Obj.Kind {
		case ast.Var:
			return []ast.Expr{deduceTypeIdentForVarIdent(typedExpr)}
		case ast.Typ:
			return []ast.Expr{typedExpr}
		default:
			panic("Unexpected ident obj kind")
		}
	case *ast.Ellipsis:
		return []ast.Expr{typedExpr.Elt.(*ast.Ident)}
	case *ast.BasicLit:
		return []ast.Expr{ast.NewIdent(strings.ToLower(typedExpr.Kind.String()))}
	case *ast.FuncLit:
		if typedExpr.Type.Results == nil {
			return nil
		}
		var result []ast.Expr
		for _, res := range typedExpr.Type.Results.List {
			result = append(result, res.Type.(*ast.Ident))
		}
		return result
	case *ast.CompositeLit:
		return deduceTypeIdentsForExpr(typedExpr.Type)
	case *ast.ParenExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.SelectorExpr:
		return deduceTypeIdentsForExpr(typedExpr.Sel)
	case *ast.IndexExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.SliceExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.TypeAssertExpr:
		if typedExpr.Type == nil {
			return []ast.Expr{ast.NewIdent("TypeSwitch")}
		} else {
			return []ast.Expr{typedExpr.Type.(*ast.Ident)}
		}
	case *ast.CallExpr:
		if typedExpr.Fun.(*ast.Ident).Obj == nil {
			return []ast.Expr{ast.NewIdent("UnresolvedIdent_" + typedExpr.Fun.(*ast.Ident).Name)}
		}
		if typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results == nil {
			return nil
		}
		var result []ast.Expr
		for _, res := range typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results.List {
			result = append(result, res.Type.(*ast.Ident))
		}
		return result
	case *ast.StarExpr:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.UnaryExpr:
		switch typedExpr.Op {
		case token.RANGE:
			return []ast.Expr{ast.NewIdent("int"), deduceTypeIdentsForExpr(typedExpr.X)[0]}
		case token.AND:
			return []ast.Expr{&ast.StarExpr{X: deduceTypeIdentsForExpr(typedExpr.X)[0]}}
		default:
			panic(fmt.Sprintf("UnaryExpr not implemented with Op \"%v\" yet", typedExpr.Op))
		}
	case *ast.BinaryExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.KeyValueExpr:
		return []ast.Expr{typedExpr.Value.(*ast.Ident)}
	case *ast.ArrayType:
		return []ast.Expr{typedExpr.Elt.(*ast.Ident)}
	case *ast.StructType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.FuncType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.InterfaceType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.MapType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.ChanType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))

	default:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	}
}

// Note: a type can also be a *SelectorExpr e.g., therefore return type here
// cannot be *ast.Ident
func deduceTypeIdentForVarIdent(ident *ast.Ident) ast.Expr {
	// ast.Fprint(os.Stderr, nil, ident, ast.NotNilFilter)
	// spew.Dump(ident)
	if ident.Obj.Kind != ast.Var {
		panic(fmt.Sprintf("Expected ObjKind \"var\" for ident, but got \"%v\"", ident.Obj.Kind))
	}
	switch typedDecl := ident.Obj.Decl.(type) {
	case *ast.AssignStmt:
		for i, lhs := range typedDecl.Lhs {
			if lhs.(*ast.Ident).Obj == ident.Obj {
				if len(typedDecl.Rhs) == 0 {
					panic("Unexpected empty Rhs")
				}
				if len(typedDecl.Rhs) == 1 {
					return deduceTypeIdentsForExpr(typedDecl.Rhs[0])[i]
				} else {
					return deduceTypeIdentsForExpr(typedDecl.Rhs[i])[0]
				}
			}
		}
		panic("Unexpected: no result in AssignStmt")
	case *ast.ValueSpec:
		for i, name := range typedDecl.Names {
			if name.Obj == ident.Obj {
				if typedDecl.Type != nil {
					return typedDecl.Type
				} else {
					if len(typedDecl.Values) == 0 {
						panic("Unexpected empty value")
					}
					if len(typedDecl.Values) == 1 {
						return deduceTypeIdentsForExpr(typedDecl.Values[0])[i]
					} else {
						return deduceTypeIdentsForExpr(typedDecl.Values[i])[0]
					}

				}
			}
		}
		panic("Unexpected: no result in ValueSpec")
	case *ast.Field:
		for _, name := range typedDecl.Names {
			if name.Obj == ident.Obj {
				return typedDecl.Type //deduceTypeIdentsForExpr(typedDecl.Type)[0]
			}
		}
		panic("Unexpected: no result in Field")

	default:
		panic(fmt.Sprintf("Unexpected decl type %T", typedDecl))
	}
}

func deduceTypeIdentsForVarIdents(varIdents []*ast.Ident) (typeIdents []ast.Expr) {
	for _, ident := range varIdents {
		typeIdents = append(typeIdents, deduceTypeIdentForVarIdent(ident))
	}
	return
}
<commit_msg>Remove obsolete TODO<commit_after>// Copyright 2015 Peter Goetz
//
// 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 main

import (
	"fmt"
	"go/ast"
	"go/token"
	"os"
	"os/exec"
	"sort"
	"strings"

	"github.com/petergtz/goextract/util"
)

func ExtractFileToFile(inputFileName string, selection Selection, extractedFuncName string, outputFilename string) {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	util.WriteFileAsStringOrPanic(outputFilename, stringFrom(fileSet, astFile))
	err := exec.Command("gofmt", "-w", outputFilename).Run()
	if err != nil {
		fmt.Fprintln(os.Stderr, util.ReadFileAsStringOrPanic(outputFilename))
		panic(err)
	}
}

func ExtractFileToString(inputFileName string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func ExtractStringToString(input string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromInput(input)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func doExtraction(fileSet *token.FileSet, astFile *ast.File, selection Selection, extractedFuncName string) {
	visitor := &astNodeVisitorForExpressions{parentNode: nil, context: &expressionVisitorContext{fset: fileSet, selection: selection}}
	ast.Walk(visitor, astFile)
	if visitor.context.exprToExtract != nil {
		extractExpression(astFile, fileSet, visitor.context.exprToExtract, visitor.context.parent, extractedFuncName)
	} else {
		v := &astNodeVisitorForMultipleStatements{parentNode: nil, context: &multipleStatementVisitorContext{fset: fileSet, selection: selection}}
		ast.Walk(v, astFile)
		if v.context.posParent != v.context.endParent {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		if v.context.posParent == nil {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		extractMultipleStatements(astFile, fileSet, v.context.nodesToExtract, v.context.posParent, extractedFuncName)
	}
}

func globalVarIdents(astFile *ast.File) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	ast.Inspect(astFile, func(node ast.Node) bool {
		switch typedNode := node.(type) {
		case *ast.FuncDecl:
			return false
		case *ast.GenDecl:
			if typedNode.Tok.String() == "var" {
				for _, spec := range typedNode.Specs {
					for _, name := range spec.(*ast.ValueSpec).Names {
						result[name.Name] = name
					}
				}
			}
			return true
		default:
			return true
		}
	})
	return result
}

func varIdentsDeclaredWithin(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			switch typedNode := node.(type) {

			case *ast.AssignStmt:
				if typedNode.Tok.String() == ":=" {
					for i := range typedNode.Lhs {
						result[typedNode.Lhs[i].(*ast.Ident).Name] = typedNode.Lhs[i].(*ast.Ident)
					}
				}

			case *ast.RangeStmt:
				if typedNode.Tok.String() == ":=" {
					result[typedNode.Key.(*ast.Ident).Name] = typedNode.Key.(*ast.Ident)
					if typedNode.Value != nil {
						result[typedNode.Value.(*ast.Ident).Name] = typedNode.Value.(*ast.Ident)
					}
				}
			}
			return true
		})
	}
	return result
}

func varIdentsUsedIn(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok &&
				ident.Obj != nil && ident.Obj.Kind == ast.Var {
				result[ident.Name] = ident
			}
			return true
		})
	}
	return result
}

// TODO rename to varIdentsUsedIn
func overlappingVarsIdentsUsedIn(stmts []ast.Stmt, outOf map[string]*ast.Ident) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, stmt := range stmts {
		ast.Inspect(stmt, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok {
				if outOf[ident.Name] != nil {
					result[ident.Name] = ident
				}
			}
			return true
		})
	}
	return result
}

func namesOf(idents map[string]*ast.Ident) []string {
	result := make([]string, 0, len(idents))
	for _, k := range sortedKeysFrom(idents) {
		result = append(result, k)
	}
	return result
}

func exprsFrom(idents map[string]*ast.Ident) []ast.Expr {
	result := make([]ast.Expr, len(idents))
	for i, key := range sortedKeysFrom(idents) {
		result[i] = idents[key]
	}
	return result
}

func callExprWith(funcName string, params map[string]*ast.Ident) *ast.CallExpr {
	return &ast.CallExpr{
		Fun:  ast.NewIdent(funcName),
		Args: exprsFrom(params),
	}
}

func fieldsFrom(params map[string]*ast.Ident) (result []*ast.Field) {
	for _, key := range sortedKeysFrom(params) {
		result = append(result, &ast.Field{
			Names: []*ast.Ident{ast.NewIdent(key)},
			Type:  deduceTypeIdentForVarIdent(params[key]),
		})
	}
	return
}

func sortedKeysFrom(m map[string]*ast.Ident) []string {
	var keys []string
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

func deduceTypeIdentsForExpr(expr ast.Expr) []ast.Expr {
	switch typedExpr := expr.(type) {
	case *ast.Ident:
		// TODO why do we even get type-like idents in here? Shouldn't this be already be handled one level higher?
		if typedExpr.Obj == nil {
			return []ast.Expr{typedExpr}
		}
		switch typedExpr.Obj.Kind {
		case ast.Var:
			return []ast.Expr{deduceTypeIdentForVarIdent(typedExpr)}
		case ast.Typ:
			return []ast.Expr{typedExpr}
		default:
			panic("Unexpected ident obj kind")
		}
	case *ast.Ellipsis:
		return []ast.Expr{typedExpr.Elt.(*ast.Ident)}
	case *ast.BasicLit:
		return []ast.Expr{ast.NewIdent(strings.ToLower(typedExpr.Kind.String()))}
	case *ast.FuncLit:
		if typedExpr.Type.Results == nil {
			return nil
		}
		var result []ast.Expr
		for _, res := range typedExpr.Type.Results.List {
			result = append(result, res.Type.(*ast.Ident))
		}
		return result
	case *ast.CompositeLit:
		return deduceTypeIdentsForExpr(typedExpr.Type)
	case *ast.ParenExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.SelectorExpr:
		return deduceTypeIdentsForExpr(typedExpr.Sel)
	case *ast.IndexExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.SliceExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.TypeAssertExpr:
		if typedExpr.Type == nil {
			return []ast.Expr{ast.NewIdent("TypeSwitch")}
		} else {
			return []ast.Expr{typedExpr.Type.(*ast.Ident)}
		}
	case *ast.CallExpr:
		if typedExpr.Fun.(*ast.Ident).Obj == nil {
			return []ast.Expr{ast.NewIdent("UnresolvedIdent_" + typedExpr.Fun.(*ast.Ident).Name)}
		}
		if typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results == nil {
			return nil
		}
		var result []ast.Expr
		for _, res := range typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results.List {
			result = append(result, res.Type.(*ast.Ident))
		}
		return result
	case *ast.StarExpr:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.UnaryExpr:
		switch typedExpr.Op {
		case token.RANGE:
			return []ast.Expr{ast.NewIdent("int"), deduceTypeIdentsForExpr(typedExpr.X)[0]}
		case token.AND:
			return []ast.Expr{&ast.StarExpr{X: deduceTypeIdentsForExpr(typedExpr.X)[0]}}
		default:
			panic(fmt.Sprintf("UnaryExpr not implemented with Op \"%v\" yet", typedExpr.Op))
		}
	case *ast.BinaryExpr:
		return deduceTypeIdentsForExpr(typedExpr.X)
	case *ast.KeyValueExpr:
		return []ast.Expr{typedExpr.Value.(*ast.Ident)}
	case *ast.ArrayType:
		return []ast.Expr{typedExpr.Elt.(*ast.Ident)}
	case *ast.StructType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.FuncType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.InterfaceType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.MapType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	case *ast.ChanType:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))

	default:
		panic(fmt.Sprintf("Type deduction for %T not implemented yet", expr))
	}
}

// Note: a type can also be a *SelectorExpr e.g., therefore return type here
// cannot be *ast.Ident
func deduceTypeIdentForVarIdent(ident *ast.Ident) ast.Expr {
	// ast.Fprint(os.Stderr, nil, ident, ast.NotNilFilter)
	// spew.Dump(ident)
	if ident.Obj.Kind != ast.Var {
		panic(fmt.Sprintf("Expected ObjKind \"var\" for ident, but got \"%v\"", ident.Obj.Kind))
	}
	switch typedDecl := ident.Obj.Decl.(type) {
	case *ast.AssignStmt:
		for i, lhs := range typedDecl.Lhs {
			if lhs.(*ast.Ident).Obj == ident.Obj {
				if len(typedDecl.Rhs) == 0 {
					panic("Unexpected empty Rhs")
				}
				if len(typedDecl.Rhs) == 1 {
					return deduceTypeIdentsForExpr(typedDecl.Rhs[0])[i]
				} else {
					return deduceTypeIdentsForExpr(typedDecl.Rhs[i])[0]
				}
			}
		}
		panic("Unexpected: no result in AssignStmt")
	case *ast.ValueSpec:
		for i, name := range typedDecl.Names {
			if name.Obj == ident.Obj {
				if typedDecl.Type != nil {
					return typedDecl.Type
				} else {
					if len(typedDecl.Values) == 0 {
						panic("Unexpected empty value")
					}
					if len(typedDecl.Values) == 1 {
						return deduceTypeIdentsForExpr(typedDecl.Values[0])[i]
					} else {
						return deduceTypeIdentsForExpr(typedDecl.Values[i])[0]
					}

				}
			}
		}
		panic("Unexpected: no result in ValueSpec")
	case *ast.Field:
		for _, name := range typedDecl.Names {
			if name.Obj == ident.Obj {
				return typedDecl.Type //deduceTypeIdentsForExpr(typedDecl.Type)[0]
			}
		}
		panic("Unexpected: no result in Field")

	default:
		panic(fmt.Sprintf("Unexpected decl type %T", typedDecl))
	}
}

func deduceTypeIdentsForVarIdents(varIdents []*ast.Ident) (typeIdents []ast.Expr) {
	for _, ident := range varIdents {
		typeIdents = append(typeIdents, deduceTypeIdentForVarIdent(ident))
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/rcrowley/go-metrics"
	"io"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"path"
	"stathat.com/c/consistent"
	"strings"
)

// Sattolo shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Sattolo.27s_algorithm
func shuffle(list []string) {
	for i := range list {
		j := rand.Intn(i + 1)
		list[i], list[j] = list[j], list[i]
	}
}

// Embeds http.Transport for use along with metrics registry
type MeteredTransport struct {
	http.Transport
	cacheproxy *CacheProxy
}

// Shadows http.Transport.RoundTrip method with meter and timer updates
func (t *MeteredTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	// Mark the request meter
	t.cacheproxy.requests.Mark(1)

	// Time the real transport method
	t.cacheproxy.timer.Time(func() {
		resp, err = t.Transport.RoundTrip(req)
	})

	// Mark the appropriate cache response meter
	switch {
	case resp.Header.Get("X-Cache") == "HIT":
		t.cacheproxy.hits.Mark(1)
	default:
		t.cacheproxy.misses.Mark(1)
	}

	return
}

type CacheProxy struct {
	backends *consistent.Consistent
	manager  *http.ServeMux
	proxy    *httputil.ReverseProxy
	registry metrics.Registry
	hits     metrics.Meter
	misses   metrics.Meter
	requests metrics.Meter
	timer    metrics.Timer
}

func New() *CacheProxy {
	c := new(CacheProxy)
	c.backends = consistent.New()

	// Metric registry
	c.registry = metrics.NewRegistry()

	// Meters
	c.hits = metrics.NewMeter()
	c.misses = metrics.NewMeter()
	c.requests = metrics.NewMeter()
	c.registry.Register("hits", c.hits)
	c.registry.Register("misses", c.misses)
	c.registry.Register("requests", c.requests)

	// Backend response timer
	c.timer = metrics.NewTimer()
	c.registry.Register("backend", c.timer)

	// RESTful interface for cache members
	c.manager = http.NewServeMux()
	c.manager.HandleFunc("/members/", func(w http.ResponseWriter, req *http.Request) {
		switch {
		case req.Method == "PUT":
			// Adds "member" as a cache member
			c.backends.Add(path.Base(req.URL.Path))
		case req.Method == "DELETE":
			// Deletes "member" from cache ring
			c.backends.Remove(path.Base(req.URL.Path))
		}
		// Always returns a list of current cache members
		io.WriteString(w, strings.Join(c.backends.Members(), ","))
	})

	// RESTful interface for metrics
	c.manager.HandleFunc("/metrics/", func(w http.ResponseWriter, req *http.Request) {
		metrics.WriteOnce(c.registry, w)
	})

	// Reverse proxy that selects the backend by nearest match to the request URL
	// on the consistent hash ring.
	c.proxy = &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			req.URL.Scheme = "http"
			backends := c.Select(req.URL.Path)
			req.URL.Host = backends[0]
			req.Header.Set("X-Backends", strings.Join(backends, ","))
		},
		Transport: &MeteredTransport{cacheproxy: c},
	}

	return c
}

func (c *CacheProxy) Select(path string) []string {
	selection, _ := c.backends.GetN(path, 3)
	shuffle(selection)
	return selection
}

func main() {
	proxy := New()

	// Replace with runtime arguments for initialization
	proxy.backends.Add("localhost:9091")
	proxy.backends.Add("localhost:9092")
	proxy.backends.Add("localhost:9093")
	proxy.backends.Add("localhost:9094")
	proxy.backends.Add("localhost:9095")

	// Initialize and run manager server in background via goroutine
	go http.ListenAndServe(":9190", proxy.manager)

	http.ListenAndServe(":9090", proxy.proxy)
}
<commit_msg>Refactored Select method into Route method<commit_after>package main

import (
	"github.com/rcrowley/go-metrics"
	"io"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"path"
	"stathat.com/c/consistent"
	"strings"
)

// Sattolo shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Sattolo.27s_algorithm
func shuffle(list []string) {
	for i := range list {
		j := rand.Intn(i + 1)
		list[i], list[j] = list[j], list[i]
	}
}

// Embeds http.Transport for use along with metrics registry
type MeteredTransport struct {
	http.Transport
	cacheproxy *CacheProxy
}

// Shadows http.Transport.RoundTrip method with meter and timer updates
func (t *MeteredTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	t.cacheproxy.requests.Mark(1)

	// Time the real transport method
	t.cacheproxy.timer.Time(func() {
		resp, err = t.Transport.RoundTrip(req)
	})

	// Mark the appropriate cache response meter
	switch {
	case resp.Header.Get("X-Cache") == "HIT":
		t.cacheproxy.hits.Mark(1)
	default:
		t.cacheproxy.misses.Mark(1)
	}

	return
}

type CacheProxy struct {
	backends *consistent.Consistent
	manager  *http.ServeMux
	proxy    *httputil.ReverseProxy
	registry metrics.Registry
	hits     metrics.Meter
	misses   metrics.Meter
	requests metrics.Meter
	timer    metrics.Timer
}

func New() *CacheProxy {
	c := new(CacheProxy)
	c.backends = consistent.New()

	// Metric registry
	c.registry = metrics.NewRegistry()

	// Meters
	c.hits = metrics.NewMeter()
	c.misses = metrics.NewMeter()
	c.requests = metrics.NewMeter()
	c.registry.Register("hits", c.hits)
	c.registry.Register("misses", c.misses)
	c.registry.Register("requests", c.requests)

	// Backend response timer
	c.timer = metrics.NewTimer()
	c.registry.Register("backend", c.timer)

	// RESTful interface for cache members
	c.manager = http.NewServeMux()
	c.manager.HandleFunc("/members/", func(w http.ResponseWriter, req *http.Request) {
		switch {
		case req.Method == "PUT":
			// Adds "member" as a cache member
			c.backends.Add(path.Base(req.URL.Path))
		case req.Method == "DELETE":
			// Deletes "member" from cache ring
			c.backends.Remove(path.Base(req.URL.Path))
		}
		// Always returns a list of current cache members
		io.WriteString(w, strings.Join(c.backends.Members(), ","))
	})

	// RESTful interface for metrics
	c.manager.HandleFunc("/metrics/", func(w http.ResponseWriter, req *http.Request) {
		metrics.WriteOnce(c.registry, w)
	})

	// Reverse proxy that selects the backend by nearest match to the request URL
	// on the consistent hash ring.
	c.proxy = &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			req.URL.Scheme = "http"
			backend, alternates := c.Route(req)
			req.URL.Host = backend
			req.Header.Set("X-Backends", strings.Join(alternates, ","))
		},
		Transport: &MeteredTransport{cacheproxy: c},
	}

	return c
}

func (c *CacheProxy) Route(req *http.Request) (string, []string) {
	var backends []string

	switch {
	case req.Header.Get("X-Backends") != "":
		backends = strings.Split(req.Header.Get("X-Backends"), ",")
	default:
		backends, _ = c.backends.GetN(req.URL.Path, 3)
		shuffle(backends)
	}

	return backends[0], backends[1:]
}

func main() {
	proxy := New()

	// Replace with runtime arguments for initialization
	proxy.backends.Add("localhost:9091")
	proxy.backends.Add("localhost:9092")
	proxy.backends.Add("localhost:9093")
	proxy.backends.Add("localhost:9094")
	proxy.backends.Add("localhost:9095")

	// Initialize and run manager server in background via goroutine
	go http.ListenAndServe(":9190", proxy.manager)

	http.ListenAndServe(":9090", proxy.proxy)
}
<|endoftext|>


<commit_before>package resource

import (
	"fmt"
	"strings"
)

type NotFoundError struct {
	LastError    error
	LastRequest  interface{}
	LastResponse interface{}
	Message      string
	Retries      int
}

func (e *NotFoundError) Error() string {
	if e.Message != "" {
		return e.Message
	}

	return "couldn't find resource"
}

// UnexpectedStateError is returned when Refresh returns a state that's neither in Target nor Pending
type UnexpectedStateError struct {
	LastError     error
	State         string
	ExpectedState []string
}

func (e *UnexpectedStateError) Error() string {
	return fmt.Sprintf(
		"unexpected state '%s', wanted target '%s'. last error: %s",
		e.State,
		strings.Join(e.ExpectedState, ", "),
		e.LastError,
	)
}

// TimeoutError is returned when WaitForState times out
type TimeoutError struct {
	LastError     error
	ExpectedState []string
}

func (e *TimeoutError) Error() string {
	msg := fmt.Sprintf("timeout while waiting for state to become '%s'",
		strings.Join(e.ExpectedState, ", "))
	if e.LastError != nil {
		msg += fmt.Sprintf(". last error: %s", e.LastError)
	}
	return msg
}
<commit_msg>Change error message return structure<commit_after>package resource

import (
	"fmt"
	"strings"
)

type NotFoundError struct {
	LastError    error
	LastRequest  interface{}
	LastResponse interface{}
	Message      string
	Retries      int
}

func (e *NotFoundError) Error() string {
	if e.Message != "" {
		return e.Message
	}

	return "couldn't find resource"
}

// UnexpectedStateError is returned when Refresh returns a state that's neither in Target nor Pending
type UnexpectedStateError struct {
	LastError     error
	State         string
	ExpectedState []string
}

func (e *UnexpectedStateError) Error() string {
	return fmt.Sprintf(
		"unexpected state '%s', wanted target '%s'. last error: %s",
		e.State,
		strings.Join(e.ExpectedState, ", "),
		e.LastError,
	)
}

// TimeoutError is returned when WaitForState times out
type TimeoutError struct {
	LastError     error
	ExpectedState []string
}

func (e *TimeoutError) Error() string {
	if e.LastError != nil {
		return fmt.Sprintf("timeout while waiting for state to become '%s': %s",
			strings.Join(e.ExpectedState, ", "), e.LastError)
	}

	return fmt.Sprintf("timeout while waiting for state to become '%s'",
		strings.Join(e.ExpectedState, ", "))
}
<|endoftext|>


<commit_before>package drivers

import (
	"testing"

	"github.com/docker/go-plugins-helpers/volume"
	"github.com/mellanox-senior-design/docker-volume-rdma/db"
)

/*
 * based on InMemDatabase & OnDiskStorageController
 * the driver will always connect correctly
 */
func TestConnect(t *testing.T) {
	t.Parallel()
	dataBase := db.NewInMemoryVolumeDatabase()
	storageController := NewOnDiskStorageController("")
	rdmaVolumeDriver := NewRDMAVolumeDriver(storageController, dataBase)

	err := rdmaVolumeDriver.Connect()
	if err != nil {
		t.Fatal(err)
	}
}

func TestValidation(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	rdmaVolDriver.validateOrCrash()
}

func TestDisconnect(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	err := rdmaVolDriver.Disconnect()
	if err != nil {
		t.Fatal(err)
	}
}

func TestCreate(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{Name: "test"}
	response := rdmaVolDriver.Create(request)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	_, err := db.Get("test")

	if err != nil {
		t.Fatal(err)
	}

	response = rdmaVolDriver.Create(request)

	if len(response.Err) == 0 {
		t.Fatal("We should receive an error because a volume cannot be created twice")
	}

}

func TestList(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	req := volume.Request{Name: "testDos"}

	list := rdmaVolDriver.List(req)

	if len(list.Volumes) != 0 {
		t.Fatal("We should not have any volumes Listed as there were none created")
	}

	rdmaVolDriver.Create(req)

	list = rdmaVolDriver.List(req)

	if len(list.Err) != 0 {
		t.Fatal(list.Err)
	}

	if len(list.Volumes) != 1 {
		t.Fatal("Expected to see only one volume, but didn't. Saw ", len(list.Volumes), " number of volumes")
	}

	secondReq := volume.Request{Name: "testDosUno"}
	rdmaVolDriver.Create(secondReq)

	list = rdmaVolDriver.List(volume.Request{})

	if len(list.Err) != 0 {
		t.Fatal(list.Err)
	}

	if len(list.Volumes) != 2 {
		t.Fatal("Expected to see two volumes, but saw ", len(list.Volumes), " volumes")
	}

	for i := 0; i < len(list.Volumes); i++ {
		name := list.Volumes[i].Name
		if name != "testDos" {
			if name != "testDosUno" {
				t.Fatal("List grabbed the wrong volume. Expected testDos or testDosUno, but got "+list.Volumes[i].Name, i)
			}
		}
	}
}

func TestGet(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	response := rdmaVolDriver.Get(volume.Request{})

	if len(response.Err) == 0 {
		t.Fatal("Volume does not exist so there should be an error, but there is not")
	}

	if len(response.Volumes) != 0 {
		t.Fatal("Get should not return any volumes as there haven't been any created")
	}

	req := volume.Request{Name: "testGet"}

	rdmaVolDriver.Create(req)
	response = rdmaVolDriver.Get(req)

	if response.Volume.Name != "testGet" {
		t.Fatal("Wrong volume returned when calling Get")
	}

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	secondReq := volume.Request{Name: "notCreated"}
	secondRes := rdmaVolDriver.Get(secondReq)
	if len(secondRes.Err) == 0 {
		t.Fatal("There should have been an error when Getting a volume that has not been created")
	}
}

func TestRemove(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{}

	response := rdmaVolDriver.Remove(request)

	if len(response.Err) == 0 {
		t.Fatal("Can't remove something that does not exist, we expect to receive an error")
	}

	request = volume.Request{Name: "removeMe"}

	rdmaVolDriver.Create(request)

	rdmaVolDriver.Create(volume.Request{Name: "keepMe"})

	response = rdmaVolDriver.Remove(request)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	_, err := db.Get("removeMe")

	if err == nil {
		t.Fatal("Volume should not exist, so we should not get one back")
	}

	_, err = db.Get("keepMe")

	if err != nil {
		t.Fatal(err)
	}
}

func TestPath(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{Name: "vol1"}

	response := rdmaVolDriver.Path(request)

	if len(response.Err) == 0 {
		t.Fatal("We should not hava a path sine volume was not created")
	}

	rdmaVolDriver.Create(request)
	response = rdmaVolDriver.Path(request)

	if len(response.Mountpoint) != 0 {
		t.Fatal("We should not have a path since we have not mounted the volume")
	}

	response = rdmaVolDriver.Mount(volume.MountRequest{Name: "vol1"})

	if len(response.Err) != 0 {
		t.Fatal("Encountered issue while mounting volume ", response.Err)
	}

	response = rdmaVolDriver.Path(request)

	if len(response.Err) != 0 {
		t.Fatal("Encountered issue while requesting path of volume ", response.Err)
	}

	if response.Mountpoint != "/etc/docker/mounts/vol1" {
		t.Fatal("Did not receive the expected path, instead got ", response.Mountpoint)
	}

}

func TestMount(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{Name: "movies"}
	// sRequest := volume.Request{Name: "songs"}
	mountRequest := volume.MountRequest{Name: "", ID: "42"}

	response := rdmaVolDriver.Mount(mountRequest)
	if len(response.Err) == 0 {
		t.Fatal("We should not be able to mount a volume whose name we don't know")
	}

	response = rdmaVolDriver.Create(request)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	mountRequest.Name = "movies"
	response = rdmaVolDriver.Mount(mountRequest)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	if response.Mountpoint != "/etc/docker/mounts/movies" {
		t.Fatal("The mountpoint generated: ", response.Mountpoint, " does not correspond to the proper path")
	}

	mountRequest.Name = "songs"
	mountRequest.ID = "505050"

	response = rdmaVolDriver.Mount(mountRequest)

	if len(response.Err) == 0 {
		t.Fatal("We should not be able to mount and uncreated volume")
	}
}

func TestUnmount(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	response := rdmaVolDriver.Unmount(volume.UnmountRequest{Name: "uncreated", ID: "500901234"})

	if len(response.Err) == 0 {
		t.Fatal("We should not be able to Unmount a volume that has not been created yet")
	}

	response = rdmaVolDriver.Unmount(volume.UnmountRequest{})

	if len(response.Err) == 0 {
		t.Fatal("We should not be able to Unmount a volume without a name or ID")
	}

	response = rdmaVolDriver.Create(volume.Request{Name: "Manual Pages"})

	if len(response.Err) != 0 {
		t.Fatal("Encountered error while creating a volume ", response.Err)
	}

	response = rdmaVolDriver.Mount(volume.MountRequest{Name: "Manual Pages", ID: " 909090"})

	if len(response.Err) != 0 {
		t.Fatal("Encountered error while mounting a volume ", response.Err)
	}

	response = rdmaVolDriver.Unmount(volume.UnmountRequest{Name: "Manual Pages", ID: "909090"})

	if len(response.Err) == 0 {
		t.Fatal("There is a difference in IDs when mounting and unmounting, so there should be an error")
	}

	response = rdmaVolDriver.Unmount(volume.UnmountRequest{Name: "Manual Pages", ID: " 909090"})

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}
}

func TestCapabilities(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	response := rdmaVolDriver.Capabilities(volume.Request{})

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}
}
<commit_msg>mounts created in current dir not in etc, then deleted<commit_after>package drivers

import (
	"os"
	"testing"

	"github.com/docker/go-plugins-helpers/volume"
	"github.com/mellanox-senior-design/docker-volume-rdma/db"
)

/*
 * based on InMemDatabase & OnDiskStorageController
 * the driver will always connect correctly
 */
func TestConnect(t *testing.T) {
	t.Parallel()
	dataBase := db.NewInMemoryVolumeDatabase()
	storageController := NewOnDiskStorageController("tests/docker/mounts/")
	rdmaVolumeDriver := NewRDMAVolumeDriver(storageController, dataBase)

	err := rdmaVolumeDriver.Connect()
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestValidation(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	rdmaVolDriver.validateOrCrash()

	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestDisconnect(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	err := rdmaVolDriver.Disconnect()
	if err != nil {
		t.Fatal(err)
	}

	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestCreate(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{Name: "test"}
	response := rdmaVolDriver.Create(request)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	_, err := db.Get("test")

	if err != nil {
		t.Fatal(err)
	}

	response = rdmaVolDriver.Create(request)

	if len(response.Err) == 0 {
		t.Fatal("We should receive an error because a volume cannot be created twice")
	}

	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestList(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	req := volume.Request{Name: "testDos"}

	list := rdmaVolDriver.List(req)

	if len(list.Volumes) != 0 {
		t.Fatal("We should not have any volumes Listed as there were none created")
	}

	rdmaVolDriver.Create(req)

	list = rdmaVolDriver.List(req)

	if len(list.Err) != 0 {
		t.Fatal(list.Err)
	}

	if len(list.Volumes) != 1 {
		t.Fatal("Expected to see only one volume, but didn't. Saw ", len(list.Volumes), " number of volumes")
	}

	secondReq := volume.Request{Name: "testDosUno"}
	rdmaVolDriver.Create(secondReq)

	list = rdmaVolDriver.List(volume.Request{})

	if len(list.Err) != 0 {
		t.Fatal(list.Err)
	}

	if len(list.Volumes) != 2 {
		t.Fatal("Expected to see two volumes, but saw ", len(list.Volumes), " volumes")
	}

	for i := 0; i < len(list.Volumes); i++ {
		name := list.Volumes[i].Name
		if name != "testDos" {
			if name != "testDosUno" {
				t.Fatal("List grabbed the wrong volume. Expected testDos or testDosUno, but got "+list.Volumes[i].Name, i)
			}
		}
	}
	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestGet(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	response := rdmaVolDriver.Get(volume.Request{})

	if len(response.Err) == 0 {
		t.Fatal("Volume does not exist so there should be an error, but there is not")
	}

	if len(response.Volumes) != 0 {
		t.Fatal("Get should not return any volumes as there haven't been any created")
	}

	req := volume.Request{Name: "testGet"}

	rdmaVolDriver.Create(req)
	response = rdmaVolDriver.Get(req)

	if response.Volume.Name != "testGet" {
		t.Fatal("Wrong volume returned when calling Get")
	}

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	secondReq := volume.Request{Name: "notCreated"}
	secondRes := rdmaVolDriver.Get(secondReq)
	if len(secondRes.Err) == 0 {
		t.Fatal("There should have been an error when Getting a volume that has not been created")
	}

	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestRemove(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{}

	response := rdmaVolDriver.Remove(request)

	if len(response.Err) == 0 {
		t.Fatal("Can't remove something that does not exist, we expect to receive an error")
	}

	request = volume.Request{Name: "removeMe"}

	rdmaVolDriver.Create(request)

	rdmaVolDriver.Create(volume.Request{Name: "keepMe"})

	response = rdmaVolDriver.Remove(request)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	_, err := db.Get("removeMe")

	if err == nil {
		t.Fatal("Volume should not exist, so we should not get one back")
	}

	_, err = db.Get("keepMe")

	if err != nil {
		t.Fatal(err)
	}

	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestPath(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{Name: "vol1"}

	response := rdmaVolDriver.Path(request)

	if len(response.Err) == 0 {
		t.Fatal("We should not hava a path sine volume was not created")
	}

	rdmaVolDriver.Create(request)
	response = rdmaVolDriver.Path(request)

	if len(response.Mountpoint) != 0 {
		t.Fatal("We should not have a path since we have not mounted the volume")
	}

	response = rdmaVolDriver.Mount(volume.MountRequest{Name: "vol1"})

	if len(response.Err) != 0 {
		t.Fatal("Encountered issue while mounting volume ", response.Err)
	}

	response = rdmaVolDriver.Path(request)

	if len(response.Err) != 0 {
		t.Fatal("Encountered issue while requesting path of volume ", response.Err)
	}

	if response.Mountpoint != "tests/docker/mounts/vol1" {
		t.Fatal("Did not receive the expected path, instead got ", response.Mountpoint)
	}
	defer os.RemoveAll("tests/")
	defer os.Remove("tests")

}

func TestMount(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)

	request := volume.Request{Name: "movies"}
	mountRequest := volume.MountRequest{Name: "", ID: "42"}

	response := rdmaVolDriver.Mount(mountRequest)
	if len(response.Err) == 0 {
		t.Fatal("We should not be able to mount a volume whose name we don't know")
	}

	response = rdmaVolDriver.Create(request)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	mountRequest.Name = "movies"
	response = rdmaVolDriver.Mount(mountRequest)

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	if response.Mountpoint != "tests/docker/mounts/movies" {
		t.Fatal("The mountpoint generated: ", response.Mountpoint, " does not correspond to the proper path")
	}

	mountRequest.Name = "songs"
	mountRequest.ID = "505050"

	response = rdmaVolDriver.Mount(mountRequest)

	if len(response.Err) == 0 {
		t.Fatal("We should not be able to mount and uncreated volume")
	}
	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestUnmount(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	response := rdmaVolDriver.Unmount(volume.UnmountRequest{Name: "uncreated", ID: "500901234"})

	if len(response.Err) == 0 {
		t.Fatal("We should not be able to Unmount a volume that has not been created yet")
	}

	response = rdmaVolDriver.Unmount(volume.UnmountRequest{})

	if len(response.Err) == 0 {
		t.Fatal("We should not be able to Unmount a volume without a name or ID")
	}

	response = rdmaVolDriver.Create(volume.Request{Name: "Manual Pages"})

	if len(response.Err) != 0 {
		t.Fatal("Encountered error while creating a volume ", response.Err)
	}

	response = rdmaVolDriver.Mount(volume.MountRequest{Name: "Manual Pages", ID: " 909090"})

	if len(response.Err) != 0 {
		t.Fatal("Encountered error while mounting a volume ", response.Err)
	}

	response = rdmaVolDriver.Unmount(volume.UnmountRequest{Name: "Manual Pages", ID: "909090"})

	if len(response.Err) == 0 {
		t.Fatal("There is a difference in IDs when mounting and unmounting, so there should be an error")
	}

	response = rdmaVolDriver.Unmount(volume.UnmountRequest{Name: "Manual Pages", ID: " 909090"})

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}

func TestCapabilities(t *testing.T) {
	t.Parallel()
	db := db.NewInMemoryVolumeDatabase()
	sc := NewOnDiskStorageController("tests/docker/mounts/")

	rdmaVolDriver := NewRDMAVolumeDriver(sc, db)
	response := rdmaVolDriver.Capabilities(volume.Request{})

	if len(response.Err) != 0 {
		t.Fatal(response.Err)
	}

	defer os.RemoveAll("tests/")
	defer os.Remove("tests")
}
<|endoftext|>


<commit_before>package gocouchlib

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

type CouchResponse struct {
	// json payload
	Json JsonObj

	// error responses
	StatusCode int
	Headers	http.Header
	//	Error      string
	//	Reason     string
}

type JsonObj interface{}

var hc = &http.Client{}

type HttpClient struct {
}

var httpClient = &HttpClient{}

func (this *HttpClient) Get(url string, headers http.Header) (*CouchResponse, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		throwError(err)
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}
	defer resp.Body.Close()

	var jsonObj JsonObj

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		throwError(err)
	}

	//fmt.Println("=> [Log]: HTTP response dump:", string(body))

	err = json.Unmarshal(body, &jsonObj)
	if err != nil {
		throwError(err)
	}

	return &CouchResponse{Json: jsonObj, StatusCode: resp.StatusCode}, nil
}

func (this *HttpClient) Head(url string, headers http.Header) (*CouchResponse, error) {
	fmt.Println("=> URL", url)

	req, err := http.NewRequest("HEAD", url, nil)
	if err != nil {
		throwError(err)
	}

	req.Header = headers

	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}

	var jsonObj JsonObj

	return &CouchResponse{Json: jsonObj, StatusCode: resp.StatusCode, Headers: resp.Header}, nil
}

func (this *HttpClient) Delete(url string) (*CouchResponse, error) {
	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		throwError(err)
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}

	return &CouchResponse{Json: nil, StatusCode: resp.StatusCode}, nil
}

func (this *HttpClient) Put(url string) (*CouchResponse, error) {
	req, err := http.NewRequest("PUT", url, nil)
	if err != nil {
		throwError(err)
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}

	return &CouchResponse{Json: nil, StatusCode: resp.StatusCode}, nil
}

func throwError(err error) (JsonObj, error) {
	return nil, &CouchError{
		time.Now(), err.Error(),
	}
}
<commit_msg>Added Put() and Post() functions with some refactoring<commit_after>package gocouchlib

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

// CouchResponse contains the elements of the HTTP response returned by the CouchDB Server
type CouchResponse struct {
	// Json payload returned by the CouchDB server as a response to the request
	Json JsonObj

	// HTTP Status code returned by the CouchDB server
	StatusCode int

	// HTTP Response Headers returned by the CouchDB server
	Headers http.Header
}

type JsonObj interface{}

var hc = &http.Client{}

type HttpClient struct {
}

var httpClient = &HttpClient{}

func (this *HttpClient) Get(url string, headers http.Header) (*CouchResponse, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		throwError(err)
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}
	defer resp.Body.Close()

	return &CouchResponse{Json: getResponseJson(resp), StatusCode: resp.StatusCode}, nil
}

func (this *HttpClient) Head(url string, headers http.Header) (*CouchResponse, error) {
	fmt.Println("=> URL", url)

	req, err := http.NewRequest("HEAD", url, nil)
	if err != nil {
		throwError(err)
	}

	if headers != nil {
		req.Header = headers
	}

	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}

	return &CouchResponse{Json: getResponseJson(resp), StatusCode: resp.StatusCode, Headers: resp.Header}, nil
}

func (this *HttpClient) Delete(url string) (*CouchResponse, error) {
	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		throwError(err)
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}

	return &CouchResponse{Json: getResponseJson(resp), StatusCode: resp.StatusCode}, nil
}

func (this *HttpClient) Put(url string, jsonObj JsonObj, headers http.Header) (*CouchResponse, error) {
	var req *http.Request
	var err error

	if jsonObj != nil {

		jsonBytes, err := json.Marshal(&jsonObj)
		if err != nil {
			throwError(err)
		}

		req, err = http.NewRequest("PUT", url, bytes.NewBuffer(jsonBytes))
		if err != nil {
			throwError(err)
		}
	} else {
		req, err = http.NewRequest("PUT", url, nil)
		if err != nil {
			throwError(err)
		}
	}

	if headers != nil {
		req.Header = headers
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}

	return &CouchResponse{Json: getResponseJson(resp), StatusCode: resp.StatusCode}, nil
}

func (this *HttpClient) Post(url string, jsonObj JsonObj) (*CouchResponse, error) {
	fmt.Println("=> utils.Save() entry: ")

	jsonBytes, err := json.Marshal(&jsonObj)
	if err != nil {
		throwError(err)
	}

	fmt.Println("=> utils.Save(): ", string(jsonBytes))

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes))
	if err != nil {
		throwError(err)
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := hc.Do(req)
	if err != nil {
		throwError(err)
	}

	return &CouchResponse{Json: getResponseJson(resp), StatusCode: resp.StatusCode}, nil
}

func throwError(err error) (JsonObj, error) {
	return nil, &CouchError{
		time.Now(), err.Error(),
	}
}

func getResponseJson(resp *http.Response) JsonObj {
	var jsonRespObj JsonObj

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		throwError(err)
	}

	//fmt.Println("=> [Log]: HTTP response dump:", string(body))

	err = json.Unmarshal(body, &jsonRespObj)
	if err != nil {
		throwError(err)
	}
	return jsonRespObj
}
<|endoftext|>