<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/elazarl/goproxy"

	"bufio"
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"regexp"
)

const DefaultPort = ":8500"

// modes
const VirtualizeMode = "virtualize"
const SynthesizeMode = "sinthesize"
const ModifyMode = "modify"
const CaptureMode = "capture"

// orPanic - wrapper for logging errors
func orPanic(err error) {
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Panic("Got error.")
	}
}

func main() {
	// Output to stderr instead of stdout, could also be a file.
	log.SetOutput(os.Stderr)
	log.SetFormatter(&log.TextFormatter{})

	// getting proxy configuration
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	// modes
	capture := flag.Bool("capture", false, "should proxy capture requests")
	synthesize := flag.Bool("synthesize", false, "should proxy capture requests")
	modify := flag.Bool("modify", false, "should proxy only modify requests")

	destination := flag.String("destination", ".", "destination URI to catch")
	middleware := flag.String("middleware", "", "should proxy use middleware")
	flag.Parse()

	if *verbose {
		// Only log the warning severity or above.
		log.SetLevel(log.DebugLevel)
	}

	// getting settings
	initSettings()

	// overriding default middleware setting
	AppConfig.middleware = *middleware

	// setting default mode
	mode := VirtualizeMode

	if *capture {
		mode = CaptureMode
		// checking whether user supplied other modes
		if *synthesize == true || *modify == true {
			log.Fatal("Two or more modes supplied, check your flags")
		}
	} else if *synthesize {
		mode = SynthesizeMode

		if AppConfig.middleware == "" {
			log.Fatal("Synthesize mode chosen although middleware not supplied")
		}

		if *capture == true || *modify == true {
			log.Fatal("Two or more modes supplied, check your flags")
		}
	} else if *modify {
		mode = ModifyMode

		if AppConfig.middleware == "" {
			log.Fatal("Modify mode chosen although middleware not supplied")
		}

		if *capture == true || *synthesize == true {
			log.Fatal("Two or more modes supplied, check your flags")
		}
	}

	// overriding default settings
	AppConfig.mode = mode

	// overriding destination
	AppConfig.destination = *destination

	// getting default database
	port := os.Getenv("ProxyPort")
	if port == "" {
		port = DefaultPort
	} else {
		port = fmt.Sprintf(":%s", port)
	}

	redisPool := getRedisPool()
	defer redisPool.Close()

	cache := Cache{pool: redisPool,
		prefix: AppConfig.cachePrefix}

	// getting connections
	d := DBClient{
		cache: cache,
		http:  &http.Client{},
	}

	// creating proxy
	proxy := goproxy.NewProxyHttpServer()

	proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
		HandleConnect(goproxy.AlwaysMitm)

	// enable curl -p for all hosts on port 80
	proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile(*destination))).
		HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) {
		defer func() {
			if e := recover(); e != nil {
				ctx.Logf("error connecting to remote: %v", e)
				client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n"))
			}
			client.Close()
		}()
		clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client))
		remote, err := net.Dial("tcp", req.URL.Host)
		orPanic(err)
		remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote))
		for {
			req, err := http.ReadRequest(clientBuf.Reader)
			orPanic(err)
			orPanic(req.Write(remoteBuf))
			orPanic(remoteBuf.Flush())
			resp, err := http.ReadResponse(remoteBuf.Reader, req)

			orPanic(err)
			orPanic(resp.Write(clientBuf.Writer))
			orPanic(clientBuf.Flush())
		}
	})

	// processing connections
	proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile(*destination))).DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			return d.processRequest(r)
		})

	go d.startAdminInterface()

	proxy.Verbose = *verbose
	// proxy starting message
	log.WithFields(log.Fields{
		"RedisAddress": AppConfig.redisAddress,
		"Destination":  *destination,
		"ProxyPort":    port,
		"Mode":         AppConfig.mode,
	}).Info("Proxy is starting...")

	log.Warn(http.ListenAndServe(port, proxy))
}

// processRequest - processes incoming requests and based on proxy state (record/playback)
// returns HTTP response.
func (d *DBClient) processRequest(req *http.Request) (*http.Request, *http.Response) {

	if AppConfig.mode == CaptureMode {
		log.Info("*** Capture ***")
		newResponse, err := d.captureRequest(req)
		if err != nil {
			// something bad happened, passing through
			return req, nil
		} else {
			// discarding original requests and returns supplied response
			return req, newResponse
		}

	} else if AppConfig.mode == SynthesizeMode {
		log.Info("*** Sinthesize ***")
		response := synthesizeResponse(req, AppConfig.middleware)
		return req, response

	} else if AppConfig.mode == ModifyMode {
		log.Info("*** Modify ***")
		response, err := d.modifyRequestResponse(req, AppConfig.middleware)

		if err != nil {
			log.WithFields(log.Fields{
				"error":      err.Error(),
				"middleware": AppConfig.middleware,
			}).Error("Got error when performing request modification")
			return req, nil
		}

		// returning modified response
		return req, response

	} else {
		log.Info("*** Virtualize ***")
		newResponse := d.getResponse(req)
		return req, newResponse

	}
}
<commit_msg>fixed spelling mistake<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/elazarl/goproxy"

	"bufio"
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"regexp"
)

const DefaultPort = ":8500"

// modes
const VirtualizeMode = "virtualize"
const SynthesizeMode = "synthesize"
const ModifyMode = "modify"
const CaptureMode = "capture"

// orPanic - wrapper for logging errors
func orPanic(err error) {
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Panic("Got error.")
	}
}

func main() {
	// Output to stderr instead of stdout, could also be a file.
	log.SetOutput(os.Stderr)
	log.SetFormatter(&log.TextFormatter{})

	// getting proxy configuration
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	// modes
	capture := flag.Bool("capture", false, "should proxy capture requests")
	synthesize := flag.Bool("synthesize", false, "should proxy capture requests")
	modify := flag.Bool("modify", false, "should proxy only modify requests")

	destination := flag.String("destination", ".", "destination URI to catch")
	middleware := flag.String("middleware", "", "should proxy use middleware")
	flag.Parse()

	if *verbose {
		// Only log the warning severity or above.
		log.SetLevel(log.DebugLevel)
	}

	// getting settings
	initSettings()

	// overriding default middleware setting
	AppConfig.middleware = *middleware

	// setting default mode
	mode := VirtualizeMode

	if *capture {
		mode = CaptureMode
		// checking whether user supplied other modes
		if *synthesize == true || *modify == true {
			log.Fatal("Two or more modes supplied, check your flags")
		}
	} else if *synthesize {
		mode = SynthesizeMode

		if AppConfig.middleware == "" {
			log.Fatal("Synthesize mode chosen although middleware not supplied")
		}

		if *capture == true || *modify == true {
			log.Fatal("Two or more modes supplied, check your flags")
		}
	} else if *modify {
		mode = ModifyMode

		if AppConfig.middleware == "" {
			log.Fatal("Modify mode chosen although middleware not supplied")
		}

		if *capture == true || *synthesize == true {
			log.Fatal("Two or more modes supplied, check your flags")
		}
	}

	// overriding default settings
	AppConfig.mode = mode

	// overriding destination
	AppConfig.destination = *destination

	// getting default database
	port := os.Getenv("ProxyPort")
	if port == "" {
		port = DefaultPort
	} else {
		port = fmt.Sprintf(":%s", port)
	}

	redisPool := getRedisPool()
	defer redisPool.Close()

	cache := Cache{pool: redisPool,
		prefix: AppConfig.cachePrefix}

	// getting connections
	d := DBClient{
		cache: cache,
		http:  &http.Client{},
	}

	// creating proxy
	proxy := goproxy.NewProxyHttpServer()

	proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
		HandleConnect(goproxy.AlwaysMitm)

	// enable curl -p for all hosts on port 80
	proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile(*destination))).
		HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) {
		defer func() {
			if e := recover(); e != nil {
				ctx.Logf("error connecting to remote: %v", e)
				client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n"))
			}
			client.Close()
		}()
		clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client))
		remote, err := net.Dial("tcp", req.URL.Host)
		orPanic(err)
		remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote))
		for {
			req, err := http.ReadRequest(clientBuf.Reader)
			orPanic(err)
			orPanic(req.Write(remoteBuf))
			orPanic(remoteBuf.Flush())
			resp, err := http.ReadResponse(remoteBuf.Reader, req)

			orPanic(err)
			orPanic(resp.Write(clientBuf.Writer))
			orPanic(clientBuf.Flush())
		}
	})

	// processing connections
	proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile(*destination))).DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			return d.processRequest(r)
		})

	go d.startAdminInterface()

	proxy.Verbose = *verbose
	// proxy starting message
	log.WithFields(log.Fields{
		"RedisAddress": AppConfig.redisAddress,
		"Destination":  *destination,
		"ProxyPort":    port,
		"Mode":         AppConfig.mode,
	}).Info("Proxy is starting...")

	log.Warn(http.ListenAndServe(port, proxy))
}

// processRequest - processes incoming requests and based on proxy state (record/playback)
// returns HTTP response.
func (d *DBClient) processRequest(req *http.Request) (*http.Request, *http.Response) {

	if AppConfig.mode == CaptureMode {
		log.Info("*** Capture ***")
		newResponse, err := d.captureRequest(req)
		if err != nil {
			// something bad happened, passing through
			return req, nil
		} else {
			// discarding original requests and returns supplied response
			return req, newResponse
		}

	} else if AppConfig.mode == SynthesizeMode {
		log.Info("*** Sinthesize ***")
		response := synthesizeResponse(req, AppConfig.middleware)
		return req, response

	} else if AppConfig.mode == ModifyMode {
		log.Info("*** Modify ***")
		response, err := d.modifyRequestResponse(req, AppConfig.middleware)

		if err != nil {
			log.WithFields(log.Fields{
				"error":      err.Error(),
				"middleware": AppConfig.middleware,
			}).Error("Got error when performing request modification")
			return req, nil
		}

		// returning modified response
		return req, response

	} else {
		log.Info("*** Virtualize ***")
		newResponse := d.getResponse(req)
		return req, newResponse

	}
}
<|endoftext|>


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

package main

import "strings"

// listObjects - wrapper function implemented over file tree walk.
func (xl xlObjects) listObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) {
	// Default is recursive, if delimiter is set then list non recursive.
	recursive := true
	if delimiter == slashSeparator {
		recursive = false
	}

	walkResultCh, endWalkCh := xl.listPool.Release(listParams{bucket, recursive, marker, prefix})
	if walkResultCh == nil {
		endWalkCh = make(chan struct{})
		walkResultCh = xl.startTreeWalk(bucket, prefix, marker, recursive, xl.isObject, endWalkCh)
	}

	var objInfos []ObjectInfo
	var eof bool
	var nextMarker string
	for i := 0; i < maxKeys; {
		walkResult, ok := <-walkResultCh
		if !ok {
			// Closed channel.
			eof = true
			break
		}
		// For any walk error return right away.
		if walkResult.err != nil {
			// File not found is a valid case.
			if walkResult.err == errFileNotFound {
				return ListObjectsInfo{}, nil
			}
			return ListObjectsInfo{}, toObjectErr(walkResult.err, bucket, prefix)
		}
		entry := walkResult.entry
		var objInfo ObjectInfo
		if strings.HasSuffix(entry, slashSeparator) {
			// Object name needs to be full path.
			objInfo.Bucket = bucket
			objInfo.Name = entry
			objInfo.IsDir = true
		} else {
			// Set the Mode to a "regular" file.
			var err error
			objInfo, err = xl.getObjectInfo(bucket, entry)
			if err != nil {
				return ListObjectsInfo{}, toObjectErr(err, bucket, prefix)
			}
		}
		nextMarker = objInfo.Name
		objInfos = append(objInfos, objInfo)
		i++
		if walkResult.end == true {
			eof = true
			break
		}
	}

	params := listParams{bucket, recursive, nextMarker, prefix}
	if !eof {
		xl.listPool.Set(params, walkResultCh, endWalkCh)
	}

	result := ListObjectsInfo{IsTruncated: !eof}
	for _, objInfo := range objInfos {
		result.NextMarker = objInfo.Name
		if objInfo.IsDir {
			result.Prefixes = append(result.Prefixes, objInfo.Name)
			continue
		}
		result.Objects = append(result.Objects, ObjectInfo{
			Name:    objInfo.Name,
			ModTime: objInfo.ModTime,
			Size:    objInfo.Size,
			IsDir:   false,
		})
	}
	return result, nil
}

// ListObjects - list all objects at prefix, delimited by '/'.
func (xl xlObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) {
	// Verify if bucket is valid.
	if !IsValidBucketName(bucket) {
		return ListObjectsInfo{}, BucketNameInvalid{Bucket: bucket}
	}
	// Verify if bucket exists.
	if !xl.isBucketExist(bucket) {
		return ListObjectsInfo{}, BucketNotFound{Bucket: bucket}
	}
	if !IsValidObjectPrefix(prefix) {
		return ListObjectsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix}
	}
	// Verify if delimiter is anything other than '/', which we do not support.
	if delimiter != "" && delimiter != slashSeparator {
		return ListObjectsInfo{}, UnsupportedDelimiter{
			Delimiter: delimiter,
		}
	}
	// Verify if marker has prefix.
	if marker != "" {
		if !strings.HasPrefix(marker, prefix) {
			return ListObjectsInfo{}, InvalidMarkerPrefixCombination{
				Marker: marker,
				Prefix: prefix,
			}
		}
	}

	// With max keys of zero we have reached eof, return right here.
	if maxKeys == 0 {
		return ListObjectsInfo{}, nil
	}

	// For delimiter and prefix as '/' we do not list anything at all
	// since according to s3 spec we stop at the 'delimiter' along
	// with the prefix. On a flat namespace with 'prefix' as '/'
	// we don't have any entries, since all the keys are of form 'keyName/...'
	if delimiter == slashSeparator && prefix == slashSeparator {
		return ListObjectsInfo{}, nil
	}

	// Over flowing count - reset to maxObjectList.
	if maxKeys < 0 || maxKeys > maxObjectList {
		maxKeys = maxObjectList
	}

	// Initiate a list operation, if successful filter and return quickly.
	listObjInfo, err := xl.listObjects(bucket, prefix, marker, delimiter, maxKeys)
	if err == nil {
		// We got the entries successfully return.
		return listObjInfo, nil
	}

	// Return error at the end.
	return ListObjectsInfo{}, toObjectErr(err, bucket, prefix)
}
<commit_msg>XL/listObjects: Ignore entry if getObjectInfo() returns errFileNotFound (#2004)<commit_after>/*
 * Minio Cloud Storage, (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import "strings"

// listObjects - wrapper function implemented over file tree walk.
func (xl xlObjects) listObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) {
	// Default is recursive, if delimiter is set then list non recursive.
	recursive := true
	if delimiter == slashSeparator {
		recursive = false
	}

	walkResultCh, endWalkCh := xl.listPool.Release(listParams{bucket, recursive, marker, prefix})
	if walkResultCh == nil {
		endWalkCh = make(chan struct{})
		walkResultCh = xl.startTreeWalk(bucket, prefix, marker, recursive, xl.isObject, endWalkCh)
	}

	var objInfos []ObjectInfo
	var eof bool
	var nextMarker string
	for i := 0; i < maxKeys; {
		walkResult, ok := <-walkResultCh
		if !ok {
			// Closed channel.
			eof = true
			break
		}
		// For any walk error return right away.
		if walkResult.err != nil {
			// File not found is a valid case.
			if walkResult.err == errFileNotFound {
				return ListObjectsInfo{}, nil
			}
			return ListObjectsInfo{}, toObjectErr(walkResult.err, bucket, prefix)
		}
		entry := walkResult.entry
		var objInfo ObjectInfo
		if strings.HasSuffix(entry, slashSeparator) {
			// Object name needs to be full path.
			objInfo.Bucket = bucket
			objInfo.Name = entry
			objInfo.IsDir = true
		} else {
			// Set the Mode to a "regular" file.
			var err error
			objInfo, err = xl.getObjectInfo(bucket, entry)
			if err != nil {
				// Ignore errFileNotFound
				if err == errFileNotFound {
					errorIf(err, "Unable to get object info", bucket, entry)
					continue
				}
				return ListObjectsInfo{}, toObjectErr(err, bucket, prefix)
			}
		}
		nextMarker = objInfo.Name
		objInfos = append(objInfos, objInfo)
		i++
		if walkResult.end == true {
			eof = true
			break
		}
	}

	params := listParams{bucket, recursive, nextMarker, prefix}
	if !eof {
		xl.listPool.Set(params, walkResultCh, endWalkCh)
	}

	result := ListObjectsInfo{IsTruncated: !eof}
	for _, objInfo := range objInfos {
		result.NextMarker = objInfo.Name
		if objInfo.IsDir {
			result.Prefixes = append(result.Prefixes, objInfo.Name)
			continue
		}
		result.Objects = append(result.Objects, ObjectInfo{
			Name:    objInfo.Name,
			ModTime: objInfo.ModTime,
			Size:    objInfo.Size,
			IsDir:   false,
		})
	}
	return result, nil
}

// ListObjects - list all objects at prefix, delimited by '/'.
func (xl xlObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) {
	// Verify if bucket is valid.
	if !IsValidBucketName(bucket) {
		return ListObjectsInfo{}, BucketNameInvalid{Bucket: bucket}
	}
	// Verify if bucket exists.
	if !xl.isBucketExist(bucket) {
		return ListObjectsInfo{}, BucketNotFound{Bucket: bucket}
	}
	if !IsValidObjectPrefix(prefix) {
		return ListObjectsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix}
	}
	// Verify if delimiter is anything other than '/', which we do not support.
	if delimiter != "" && delimiter != slashSeparator {
		return ListObjectsInfo{}, UnsupportedDelimiter{
			Delimiter: delimiter,
		}
	}
	// Verify if marker has prefix.
	if marker != "" {
		if !strings.HasPrefix(marker, prefix) {
			return ListObjectsInfo{}, InvalidMarkerPrefixCombination{
				Marker: marker,
				Prefix: prefix,
			}
		}
	}

	// With max keys of zero we have reached eof, return right here.
	if maxKeys == 0 {
		return ListObjectsInfo{}, nil
	}

	// For delimiter and prefix as '/' we do not list anything at all
	// since according to s3 spec we stop at the 'delimiter' along
	// with the prefix. On a flat namespace with 'prefix' as '/'
	// we don't have any entries, since all the keys are of form 'keyName/...'
	if delimiter == slashSeparator && prefix == slashSeparator {
		return ListObjectsInfo{}, nil
	}

	// Over flowing count - reset to maxObjectList.
	if maxKeys < 0 || maxKeys > maxObjectList {
		maxKeys = maxObjectList
	}

	// Initiate a list operation, if successful filter and return quickly.
	listObjInfo, err := xl.listObjects(bucket, prefix, marker, delimiter, maxKeys)
	if err == nil {
		// We got the entries successfully return.
		return listObjInfo, nil
	}

	// Return error at the end.
	return ListObjectsInfo{}, toObjectErr(err, bucket, prefix)
}
<|endoftext|>


<commit_before>package app

import (
	"github.com/robfig/revel"
)

var (
	Db string
)

func AppInit() {
	var found bool
	if Db, found = revel.Config.String("bloggo.db"); !found {
		Db = "bloggo"
	}
}
<commit_msg>Switching from Db to DB to be consistent with mgo<commit_after>package app

import (
	"github.com/robfig/revel"
)

var (
	DB string
)

func AppInit() {
	RegisterDB()
}

func RegisterDB() {
	var found bool
	if DB, found = revel.Config.String("bloggo.db"); !found {
		DB = "bloggo"
	}
}
<|endoftext|>


<commit_before>/*
 Copyright 2016 Crunchy Data Solutions, Inc.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

package proxy

import (
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"fmt"
	"github.com/crunchydata/crunchy-proxy/config"
	"github.com/golang/glog"
	"net"
)

func ProtocolMsgType(buf []byte) (string, int) {
	var msgLen int32

	// Read the message length.
	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] %d msgLen\n", msgLen)

	return string(buf[0]), int(msgLen)
}

func LogProtocol(direction string, hint string, buf []byte, bufLen int) {
	var msgType byte

	if hint == "startup" {
		glog.V(2).Infof("[protocol] %s %s [%s]\n", direction, hint, "startup")
		StartupRequest(buf, bufLen)
		return
	} else {
		msgType = buf[0]
		glog.V(2).Infof("[protocol] %s %s [%c]\n", direction, hint, msgType)
		switch msgType {
		case 'R':
			AuthenticationRequest(buf)
			return
		case 'E':
			ErrorResponse(buf)
			return
		case 'Q':
			QueryRequest(buf)
			return
		case 'N':
			NoticeResponse(buf)
			return
		case 'T':
			RowDescription(buf, bufLen)
			return
		case 'D':
			DataRow(buf)
			return
		case 'C':
			CommandComplete(buf)
			return
		case 'X':
			TerminateMessage(buf)
			return
		case 'p':
			PasswordMessage(buf)
			return
		default:
			glog.Errorf("[protocol] %s %s [%c] NOT handled!!\n", direction, hint, msgType)
			return
		}
	}
}

func NullTermToStrings(b []byte) (s []string) {
	var zb = []byte{0}
	for _, x := range bytes.Split(b, zb) {
		s = append(s, string(x))
	}
	if len(s) > 0 && s[len(s)-1] == "" {
		s = s[:len(s)-1]
	}
	return
}

func AuthenticationRequest(buf []byte) []byte {
	var msgLength int32
	var authType int32

	// Read message length.
	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLength)

	// Read authentication type.
	reader.Reset(buf[5:9])
	binary.Read(reader, binary.BigEndian, &authType)

	var salt = []byte{buf[9], buf[10], buf[11], buf[12]}
	var saltstr = string(salt)
	glog.V(2).Infof("[protocol] AuthenticationRequest: msglen=%d type=%d salt=%x saltstr=%s\n", msgLength, authType, salt, saltstr)
	return salt
}

func ErrorResponse(buf []byte) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] ErrorResponse: msglen=%d\n", msgLen)
	var errorMessage = string(buf[5:msgLen])
	glog.V(2).Infof("[protocol] ErrorResponse: message=%s\n", errorMessage)
}

func StartupRequest(buf []byte, bufLen int) {
	var msgLen int32
	var startupProtocol int32

	reader := bytes.NewReader(buf[0:4])
	binary.Read(reader, binary.BigEndian, &msgLen)

	reader.Reset(buf[4:8])
	binary.Read(reader, binary.BigEndian, &startupProtocol)

	glog.V(2).Infof("[protocol] StartupRequest: msglen=%d protocol=%d\n", msgLen, startupProtocol)
	//parameters = string(buf[8 : bufLen-8])
	/**
	parameters = NullTermToStrings(buf[8 : bufLen-1])
	for i := 0; i < len(parameters); i++ {
		log.Printf("[protocol] startup parameter key:value: %s:%s \n", parameters[i], parameters[i+1])
		i++
	}
	*/
}

func QueryRequest(buf []byte) {
	var msgLen int32
	var query string

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	query = string(buf[5:msgLen])

	glog.V(2).Infof("[protocol] QueryRequest: msglen=%d query=%s\n", msgLen, query)
}

func NoticeResponse(buf []byte) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	var fieldType = buf[5]
	var fieldMsg = string(buf[6:msgLen])

	glog.V(2).Infof("[protocol] NoticeResponse: msglen=%d fieldType=%x fieldMsg=%s\n", msgLen, fieldType, fieldMsg)
}

func RowDescription(buf []byte, bufLen int) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] RowDescription: msglen=%d\n", msgLen)
	var data []byte

	data = buf[4+msgLen : bufLen]

	var dataRowType = string(data[0])
	glog.V(2).Infof("[protocol] datarow type%s found \n", dataRowType)

}

func DataRow(buf []byte) {
	var numFields int
	var msgLen int32
	var fieldLen int32
	var fieldValue string

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	reader.Reset(buf[5:7])
	binary.Read(reader, binary.BigEndian, &numFields)

	reader.Reset(buf[7:11])
	binary.Read(reader, binary.BigEndian, &fieldLen)

	fieldValue = string(buf[11 : fieldLen+11])

	glog.V(2).Infof("[protocol] DataRow: numfields=%d msglen=%d fieldLen=%d fieldValue=%s\n", numFields, msgLen, fieldLen, fieldValue)
}

func CommandComplete(buf []byte) {
	var msgLen int32

	buffer := bytes.NewReader(buf[1:5])
	binary.Read(buffer, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] Command Complete: msglen=%d\n", msgLen)
}

func TerminateMessage(buf []byte) {
	var msgLen int32
	buffer := bytes.NewReader(buf[1:5])
	binary.Read(buffer, binary.BigEndian, &msgLen)
	glog.V(2).Infof("[protocol] Terminate: msglen=%d\n", msgLen)
}

func GetTerminateMessage() []byte {
	var buffer []byte
	buffer = append(buffer, 'X')

	//make msg len 1 for now
	x := make([]byte, 4)
	binary.BigEndian.PutUint32(x, uint32(4))
	buffer = append(buffer, x...)
	return buffer
}

func PasswordMessage(buf []byte) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	var hash = string(buf[5:msgLen])

	glog.V(2).Infof("[protocol] PasswordMessage: msglen=%d password hash=%s\n", msgLen, hash)
}

func PasswordMessageFake(buf []byte, salt []byte, username string, password string) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	var hash = string(buf[5:msgLen])

	glog.V(2).Infof("[protocol] PasswordMessageFake: username=%s password=%s\n", username, password)
	glog.V(2).Infof("[protocol] PasswordMessageFake: msglen=%d password hash=%s salt=%x saltlen=%d\n", msgLen, hash, salt, len(salt))

	s := string(salt)
	hashstr := "md5" + md5s(md5s(password+username)+s)

	glog.V(2).Infof("[protocol] PasswordMessageFake: hashstr=%s\n", hashstr)
	hashbytes := []byte(hashstr)
	copy(buf[5:], hashbytes)
	glog.V(2).Infoln("generated hash " + hashstr)
}

func md5s(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func Authenticate(cfg *config.Config, node *config.Node, conn *net.TCPConn) {
	var readLen, writeLen int
	var err error
	var buf []byte

	startupMsg := getStartupMessage(cfg, node)

	//write to backend
	writeLen, err = conn.Write(startupMsg)
	if err != nil {
		glog.Errorln(err.Error() + " at this pt")
	}
	glog.V(2).Infof("wrote %d to backend\n", writeLen)

	//read from backend
	buf = make([]byte, 2048)
	readLen, err = conn.Read(buf)
	if err != nil {
		glog.Errorln(err.Error() + " at this pt2")
	}

	//should get back an AuthenticationRequest 'R'
	LogProtocol("<--", "pool node", buf, len(buf))
	msgType, msgLen := ProtocolMsgType(buf)
	if msgType != "R" {
		glog.Errorln("pool error: should have got R message here")
	}
	salt := AuthenticationRequest(buf)
	glog.V(2).Infof("salt from AuthenticationRequest was %s %x\n", string(salt), salt)

	//create password message and send back to backend
	pswMsg := getPasswordMessage(salt, cfg.Credentials.Username, cfg.Credentials.Password)

	//write to backend
	writeLen, err = conn.Write(pswMsg)
	glog.V(2).Infof("wrote %d to backend\n", writeLen)
	if err != nil {
		glog.Errorln(err.Error() + " at this pta")
	}

	//read from backend
	readLen, err = conn.Read(buf)
	if err != nil {
		glog.Errorln(err.Error() + " at this pt3")
	}

	msgType, msgLen = ProtocolMsgType(buf)
	glog.V(2).Infof("after passwordmsg got msgType %s msgLen=%d\n", msgType, msgLen)
	if msgType == "R" {
		LogProtocol("<--", "AuthenticationOK", buf, readLen)
	}

}

func getPasswordMessage(salt []byte, username string, password string) []byte {
	var buffer []byte

	buffer = append(buffer, 'p')

	//make msg len 1 for now
	x := make([]byte, 4)
	binary.BigEndian.PutUint32(x, uint32(1))
	buffer = append(buffer, x...)

	s := string(salt)
	hashstr := "md5" + md5s(md5s(password+username)+s)

	glog.V(2).Infof("[protocol] getPasswordMessage: hashstr=%s\n", hashstr)
	buffer = append(buffer, hashstr...)

	//null terminate the string
	buffer = append(buffer, 0)

	//update the msg len subtracting for msgType byte
	binary.BigEndian.PutUint32(x, uint32(len(buffer)-1))
	copy(buffer[1:], x)

	glog.V(2).Infof(" psw msg len=%d\n", len(buffer))
	glog.V(2).Infof(" psw msg =%s\n", string(buffer))
	return buffer

}

func getStartupMessage(cfg *config.Config, node *config.Node) []byte {

	//send startup packet
	var buffer []byte

	x := make([]byte, 4)

	//make msg len 1 for now
	binary.BigEndian.PutUint32(x, uint32(1))
	buffer = append(buffer, x...)

	//w.int32(196608)
	binary.BigEndian.PutUint32(x, uint32(196608))
	buffer = append(buffer, x...)

	var key, value string
	key = "database"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = cfg.Credentials.Database
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "user"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = cfg.Credentials.Username
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "client_encoding"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "UTF8"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "datestyle"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "ISO, MDY"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "application_name"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "proxypool"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "extra_float_digits"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "2"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	buffer = append(buffer, 0)

	//update the msg len
	binary.BigEndian.PutUint32(buffer, uint32(len(buffer)))

	return buffer
}
<commit_msg>Refactored protocol version as constant.<commit_after>/*
 Copyright 2016 Crunchy Data Solutions, Inc.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

package proxy

import (
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"fmt"
	"github.com/crunchydata/crunchy-proxy/config"
	"github.com/golang/glog"
	"net"
)

const PROTOCOL_VERSION int32 = 196608

func ProtocolMsgType(buf []byte) (string, int) {
	var msgLen int32

	// Read the message length.
	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] %d msgLen\n", msgLen)

	return string(buf[0]), int(msgLen)
}

func LogProtocol(direction string, hint string, buf []byte, bufLen int) {
	var msgType byte

	if hint == "startup" {
		glog.V(2).Infof("[protocol] %s %s [%s]\n", direction, hint, "startup")
		StartupRequest(buf, bufLen)
		return
	} else {
		msgType = buf[0]
		glog.V(2).Infof("[protocol] %s %s [%c]\n", direction, hint, msgType)
		switch msgType {
		case 'R':
			AuthenticationRequest(buf)
			return
		case 'E':
			ErrorResponse(buf)
			return
		case 'Q':
			QueryRequest(buf)
			return
		case 'N':
			NoticeResponse(buf)
			return
		case 'T':
			RowDescription(buf, bufLen)
			return
		case 'D':
			DataRow(buf)
			return
		case 'C':
			CommandComplete(buf)
			return
		case 'X':
			TerminateMessage(buf)
			return
		case 'p':
			PasswordMessage(buf)
			return
		default:
			glog.Errorf("[protocol] %s %s [%c] NOT handled!!\n", direction, hint, msgType)
			return
		}
	}
}

func NullTermToStrings(b []byte) (s []string) {
	var zb = []byte{0}
	for _, x := range bytes.Split(b, zb) {
		s = append(s, string(x))
	}
	if len(s) > 0 && s[len(s)-1] == "" {
		s = s[:len(s)-1]
	}
	return
}

func AuthenticationRequest(buf []byte) []byte {
	var msgLength int32
	var authType int32

	// Read message length.
	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLength)

	// Read authentication type.
	reader.Reset(buf[5:9])
	binary.Read(reader, binary.BigEndian, &authType)

	var salt = []byte{buf[9], buf[10], buf[11], buf[12]}
	var saltstr = string(salt)
	glog.V(2).Infof("[protocol] AuthenticationRequest: msglen=%d type=%d salt=%x saltstr=%s\n", msgLength, authType, salt, saltstr)
	return salt
}

func ErrorResponse(buf []byte) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] ErrorResponse: msglen=%d\n", msgLen)
	var errorMessage = string(buf[5:msgLen])
	glog.V(2).Infof("[protocol] ErrorResponse: message=%s\n", errorMessage)
}

func StartupRequest(buf []byte, bufLen int) {
	var msgLen int32
	var startupProtocol int32

	reader := bytes.NewReader(buf[0:4])
	binary.Read(reader, binary.BigEndian, &msgLen)

	reader.Reset(buf[4:8])
	binary.Read(reader, binary.BigEndian, &startupProtocol)

	glog.V(2).Infof("[protocol] StartupRequest: msglen=%d protocol=%d\n", msgLen, startupProtocol)
	//parameters = string(buf[8 : bufLen-8])
	/**
	parameters = NullTermToStrings(buf[8 : bufLen-1])
	for i := 0; i < len(parameters); i++ {
		log.Printf("[protocol] startup parameter key:value: %s:%s \n", parameters[i], parameters[i+1])
		i++
	}
	*/
}

func QueryRequest(buf []byte) {
	var msgLen int32
	var query string

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	query = string(buf[5:msgLen])

	glog.V(2).Infof("[protocol] QueryRequest: msglen=%d query=%s\n", msgLen, query)
}

func NoticeResponse(buf []byte) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	var fieldType = buf[5]
	var fieldMsg = string(buf[6:msgLen])

	glog.V(2).Infof("[protocol] NoticeResponse: msglen=%d fieldType=%x fieldMsg=%s\n", msgLen, fieldType, fieldMsg)
}

func RowDescription(buf []byte, bufLen int) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] RowDescription: msglen=%d\n", msgLen)
	var data []byte

	data = buf[4+msgLen : bufLen]

	var dataRowType = string(data[0])
	glog.V(2).Infof("[protocol] datarow type%s found \n", dataRowType)

}

func DataRow(buf []byte) {
	var numFields int
	var msgLen int32
	var fieldLen int32
	var fieldValue string

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	reader.Reset(buf[5:7])
	binary.Read(reader, binary.BigEndian, &numFields)

	reader.Reset(buf[7:11])
	binary.Read(reader, binary.BigEndian, &fieldLen)

	fieldValue = string(buf[11 : fieldLen+11])

	glog.V(2).Infof("[protocol] DataRow: numfields=%d msglen=%d fieldLen=%d fieldValue=%s\n", numFields, msgLen, fieldLen, fieldValue)
}

func CommandComplete(buf []byte) {
	var msgLen int32

	buffer := bytes.NewReader(buf[1:5])
	binary.Read(buffer, binary.BigEndian, &msgLen)

	glog.V(2).Infof("[protocol] Command Complete: msglen=%d\n", msgLen)
}

func TerminateMessage(buf []byte) {
	var msgLen int32
	buffer := bytes.NewReader(buf[1:5])
	binary.Read(buffer, binary.BigEndian, &msgLen)
	glog.V(2).Infof("[protocol] Terminate: msglen=%d\n", msgLen)
}

func GetTerminateMessage() []byte {
	var buffer []byte
	buffer = append(buffer, 'X')

	//make msg len 1 for now
	x := make([]byte, 4)
	binary.BigEndian.PutUint32(x, uint32(4))
	buffer = append(buffer, x...)
	return buffer
}

func PasswordMessage(buf []byte) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	var hash = string(buf[5:msgLen])

	glog.V(2).Infof("[protocol] PasswordMessage: msglen=%d password hash=%s\n", msgLen, hash)
}

func PasswordMessageFake(buf []byte, salt []byte, username string, password string) {
	var msgLen int32

	reader := bytes.NewReader(buf[1:5])
	binary.Read(reader, binary.BigEndian, &msgLen)

	var hash = string(buf[5:msgLen])

	glog.V(2).Infof("[protocol] PasswordMessageFake: username=%s password=%s\n", username, password)
	glog.V(2).Infof("[protocol] PasswordMessageFake: msglen=%d password hash=%s salt=%x saltlen=%d\n", msgLen, hash, salt, len(salt))

	s := string(salt)
	hashstr := "md5" + md5s(md5s(password+username)+s)

	glog.V(2).Infof("[protocol] PasswordMessageFake: hashstr=%s\n", hashstr)
	hashbytes := []byte(hashstr)
	copy(buf[5:], hashbytes)
	glog.V(2).Infoln("generated hash " + hashstr)
}

func md5s(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func Authenticate(cfg *config.Config, node *config.Node, conn *net.TCPConn) {
	var readLen, writeLen int
	var err error
	var buf []byte

	startupMsg := getStartupMessage(cfg, node)

	//write to backend
	writeLen, err = conn.Write(startupMsg)
	if err != nil {
		glog.Errorln(err.Error() + " at this pt")
	}
	glog.V(2).Infof("wrote %d to backend\n", writeLen)

	//read from backend
	buf = make([]byte, 2048)
	readLen, err = conn.Read(buf)
	if err != nil {
		glog.Errorln(err.Error() + " at this pt2")
	}

	//should get back an AuthenticationRequest 'R'
	LogProtocol("<--", "pool node", buf, len(buf))
	msgType, msgLen := ProtocolMsgType(buf)
	if msgType != "R" {
		glog.Errorln("pool error: should have got R message here")
	}
	salt := AuthenticationRequest(buf)
	glog.V(2).Infof("salt from AuthenticationRequest was %s %x\n", string(salt), salt)

	//create password message and send back to backend
	pswMsg := getPasswordMessage(salt, cfg.Credentials.Username, cfg.Credentials.Password)

	//write to backend
	writeLen, err = conn.Write(pswMsg)
	glog.V(2).Infof("wrote %d to backend\n", writeLen)
	if err != nil {
		glog.Errorln(err.Error() + " at this pta")
	}

	//read from backend
	readLen, err = conn.Read(buf)
	if err != nil {
		glog.Errorln(err.Error() + " at this pt3")
	}

	msgType, msgLen = ProtocolMsgType(buf)
	glog.V(2).Infof("after passwordmsg got msgType %s msgLen=%d\n", msgType, msgLen)
	if msgType == "R" {
		LogProtocol("<--", "AuthenticationOK", buf, readLen)
	}

}

func getPasswordMessage(salt []byte, username string, password string) []byte {
	var buffer []byte

	buffer = append(buffer, 'p')

	//make msg len 1 for now
	x := make([]byte, 4)
	binary.BigEndian.PutUint32(x, uint32(1))
	buffer = append(buffer, x...)

	s := string(salt)
	hashstr := "md5" + md5s(md5s(password+username)+s)

	glog.V(2).Infof("[protocol] getPasswordMessage: hashstr=%s\n", hashstr)
	buffer = append(buffer, hashstr...)

	//null terminate the string
	buffer = append(buffer, 0)

	//update the msg len subtracting for msgType byte
	binary.BigEndian.PutUint32(x, uint32(len(buffer)-1))
	copy(buffer[1:], x)

	glog.V(2).Infof(" psw msg len=%d\n", len(buffer))
	glog.V(2).Infof(" psw msg =%s\n", string(buffer))
	return buffer

}

func getStartupMessage(cfg *config.Config, node *config.Node) []byte {

	//send startup packet
	var buffer []byte

	x := make([]byte, 4)

	//make msg len 1 for now
	binary.BigEndian.PutUint32(x, uint32(1))
	buffer = append(buffer, x...)

	// Set the protocol version.
	binary.BigEndian.PutUint32(x, uint32(PROTOCOL_VERSION))
	buffer = append(buffer, x...)

	var key, value string
	key = "database"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = cfg.Credentials.Database
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "user"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = cfg.Credentials.Username
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "client_encoding"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "UTF8"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "datestyle"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "ISO, MDY"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "application_name"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "proxypool"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	key = "extra_float_digits"
	buffer = append(buffer, key...)
	//null terminate the string
	buffer = append(buffer, 0)

	value = "2"
	buffer = append(buffer, value...)
	//null terminate the string
	buffer = append(buffer, 0)

	buffer = append(buffer, 0)

	//update the msg len
	binary.BigEndian.PutUint32(buffer, uint32(len(buffer)))

	return buffer
}
<|endoftext|>


<commit_before>package cloudtoolkit

import (
	"fmt"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	zipkin "github.com/openzipkin/zipkin-go-opentracing"
	"github.com/spf13/viper"
	"net/http"
)

var ZIPKIN_SERVICE_URL string = "zipkin.service.url"

var Tracer opentracing.Tracer

var zipkinUrl string

// Typical URL: http://192.168.99.100:9411
func InitTracingFromConfigProperty(serviceName string) {
	if viper.IsSet(ZIPKIN_SERVICE_URL) {
		zipkinUrl = viper.GetString(ZIPKIN_SERVICE_URL)
		initTracing(serviceName)
	} else {
		panic("Config property " + ZIPKIN_SERVICE_URL + " not set, panicing...")
	}
}

// Typical URL: http://192.168.99.100:9411
func InitTracingUsingUrl(serviceName string, zipkinHost string) {
	zipkinUrl = zipkinHost
	initTracing(serviceName)
}

func initTracing(serviceName string) {

	collector, err := zipkin.NewHTTPCollector(
		fmt.Sprintf("%s/api/v1/spans", zipkinUrl))
	if err != nil {
		Log.Errorln("Error connecting to zipkin server at " +
			fmt.Sprintf("%s/api/v1/spans", zipkinUrl) + ". Error: " + err.Error())
	}
	Tracer, err = zipkin.NewTracer(
		zipkin.NewRecorder(collector, false, "127.0.0.1:0", serviceName))
	if err != nil {
		Log.Errorln("Error starting new zipkin tracer. Error: " + err.Error())
	}

}

// Loads tracing information from an INCOMING HTTP request.
func StartHTTPTrace(r *http.Request, opName string) opentracing.Span {
	carrier := opentracing.HTTPHeadersCarrier(r.Header)
	clientContext, err := Tracer.Extract(opentracing.HTTPHeaders, carrier)
	var span opentracing.Span
	if err == nil {
		span = Tracer.StartSpan(
			opName, ext.RPCServerOption(clientContext))
	} else {
		span = Tracer.StartSpan(opName)
	}
	return span
}

// Adds tracing information to an OUTGOING HTTP request
func ApplyTracing(req *http.Request, span opentracing.Span) {
	carrier := opentracing.HTTPHeadersCarrier(req.Header)
	err := Tracer.Inject(
		span.Context(),
		opentracing.HTTPHeaders,
		carrier)
	if err != nil {
		panic("Unable to inject tracing context: " + err.Error())
	}
}
<commit_msg>Renamed add tracing to req method<commit_after>package cloudtoolkit

import (
	"fmt"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	zipkin "github.com/openzipkin/zipkin-go-opentracing"
	"github.com/spf13/viper"
	"net/http"
)

var ZIPKIN_SERVICE_URL string = "zipkin.service.url"

var Tracer opentracing.Tracer

var zipkinUrl string

// Typical URL: http://192.168.99.100:9411
func InitTracingFromConfigProperty(serviceName string) {
	if viper.IsSet(ZIPKIN_SERVICE_URL) {
		zipkinUrl = viper.GetString(ZIPKIN_SERVICE_URL)
		initTracing(serviceName)
	} else {
		panic("Config property " + ZIPKIN_SERVICE_URL + " not set, panicing...")
	}
}

// Typical URL: http://192.168.99.100:9411
func InitTracingUsingUrl(serviceName string, zipkinHost string) {
	zipkinUrl = zipkinHost
	initTracing(serviceName)
}

func initTracing(serviceName string) {

	collector, err := zipkin.NewHTTPCollector(
		fmt.Sprintf("%s/api/v1/spans", zipkinUrl))
	if err != nil {
		Log.Errorln("Error connecting to zipkin server at " +
			fmt.Sprintf("%s/api/v1/spans", zipkinUrl) + ". Error: " + err.Error())
	}
	Tracer, err = zipkin.NewTracer(
		zipkin.NewRecorder(collector, false, "127.0.0.1:0", serviceName))
	if err != nil {
		Log.Errorln("Error starting new zipkin tracer. Error: " + err.Error())
	}

}

// Loads tracing information from an INCOMING HTTP request.
func StartHTTPTrace(r *http.Request, opName string) opentracing.Span {
	carrier := opentracing.HTTPHeadersCarrier(r.Header)
	clientContext, err := Tracer.Extract(opentracing.HTTPHeaders, carrier)
	var span opentracing.Span
	if err == nil {
		span = Tracer.StartSpan(
			opName, ext.RPCServerOption(clientContext))
	} else {
		span = Tracer.StartSpan(opName)
	}
	return span
}

// Adds tracing information to an OUTGOING HTTP request
func AddTracingToReq(req *http.Request, span opentracing.Span) {
	carrier := opentracing.HTTPHeadersCarrier(req.Header)
	err := Tracer.Inject(
		span.Context(),
		opentracing.HTTPHeaders,
		carrier)
	if err != nil {
		panic("Unable to inject tracing context: " + err.Error())
	}
}
<|endoftext|>


<commit_before>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"database/sql/driver"
	"net"
	"strconv"
	"strings"
	"time"
)

type mysqlConn struct {
	buf              buffer
	netConn          net.Conn
	affectedRows     uint64
	insertId         uint64
	cfg              *Config
	maxPacketAllowed int
	maxWriteSize     int
	writeTimeout     time.Duration
	flags            clientFlag
	status           statusFlag
	sequence         uint8
	parseTime        bool
	strict           bool
}

// Handles parameters set in DSN after the connection is established
func (mc *mysqlConn) handleParams() (err error) {
	for param, val := range mc.cfg.Params {
		switch param {
		// Charset
		case "charset":
			charsets := strings.Split(val, ",")
			for i := range charsets {
				// ignore errors here - a charset may not exist
				err = mc.exec("SET NAMES " + charsets[i])
				if err == nil {
					break
				}
			}
			if err != nil {
				return
			}

		// System Vars
		default:
			err = mc.exec("SET " + param + "=" + val + "")
			if err != nil {
				return
			}
		}
	}

	return
}

func (mc *mysqlConn) Begin() (driver.Tx, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	err := mc.exec("START TRANSACTION")
	if err == nil {
		return &mysqlTx{mc}, err
	}

	return nil, err
}

func (mc *mysqlConn) Close() (err error) {
	// Makes Close idempotent
	if mc.netConn != nil {
		err = mc.writeCommandPacket(comQuit)
	}

	mc.cleanup()

	return
}

// Closes the network connection and unsets internal variables. Do not call this
// function after successfully authentication, call Close instead. This function
// is called before auth or on auth failure because MySQL will have already
// closed the network connection.
func (mc *mysqlConn) cleanup() {
	// Makes cleanup idempotent
	if mc.netConn != nil {
		if err := mc.netConn.Close(); err != nil {
			errLog.Print(err)
		}
		mc.netConn = nil
	}
	mc.cfg = nil
	mc.buf.nc = nil
}

func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	// Send command
	err := mc.writeCommandPacketStr(comStmtPrepare, query)
	if err != nil {
		return nil, err
	}

	stmt := &mysqlStmt{
		mc: mc,
	}

	// Read Result
	columnCount, err := stmt.readPrepareResultPacket()
	if err == nil {
		if stmt.paramCount > 0 {
			if err = mc.readUntilEOF(); err != nil {
				return nil, err
			}
		}

		if columnCount > 0 {
			err = mc.readUntilEOF()
		}
	}

	return stmt, err
}

func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
	buf := mc.buf.takeCompleteBuffer()
	if buf == nil {
		// can not take the buffer. Something must be wrong with the connection
		errLog.Print(ErrBusyBuffer)
		return "", driver.ErrBadConn
	}
	buf = buf[:0]
	argPos := 0

	for i := 0; i < len(query); i++ {
		q := strings.IndexByte(query[i:], '?')
		if q == -1 {
			buf = append(buf, query[i:]...)
			break
		}
		buf = append(buf, query[i:i+q]...)
		i += q

		arg := args[argPos]
		argPos++

		if arg == nil {
			buf = append(buf, "NULL"...)
			continue
		}

		switch v := arg.(type) {
		case int64:
			buf = strconv.AppendInt(buf, v, 10)
		case float64:
			buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
		case bool:
			if v {
				buf = append(buf, '1')
			} else {
				buf = append(buf, '0')
			}
		case time.Time:
			if v.IsZero() {
				buf = append(buf, "'0000-00-00'"...)
			} else {
				v := v.In(mc.cfg.Loc)
				v = v.Add(time.Nanosecond * 500) // To round under microsecond
				year := v.Year()
				year100 := year / 100
				year1 := year % 100
				month := v.Month()
				day := v.Day()
				hour := v.Hour()
				minute := v.Minute()
				second := v.Second()
				micro := v.Nanosecond() / 1000

				buf = append(buf, []byte{
					'\'',
					digits10[year100], digits01[year100],
					digits10[year1], digits01[year1],
					'-',
					digits10[month], digits01[month],
					'-',
					digits10[day], digits01[day],
					' ',
					digits10[hour], digits01[hour],
					':',
					digits10[minute], digits01[minute],
					':',
					digits10[second], digits01[second],
				}...)

				if micro != 0 {
					micro10000 := micro / 10000
					micro100 := micro / 100 % 100
					micro1 := micro % 100
					buf = append(buf, []byte{
						'.',
						digits10[micro10000], digits01[micro10000],
						digits10[micro100], digits01[micro100],
						digits10[micro1], digits01[micro1],
					}...)
				}
				buf = append(buf, '\'')
			}
		case []byte:
			if v == nil {
				buf = append(buf, "NULL"...)
			} else {
				buf = append(buf, "_binary'"...)
				if mc.status&statusNoBackslashEscapes == 0 {
					buf = escapeBytesBackslash(buf, v)
				} else {
					buf = escapeBytesQuotes(buf, v)
				}
				buf = append(buf, '\'')
			}
		case string:
			buf = append(buf, '\'')
			if mc.status&statusNoBackslashEscapes == 0 {
				buf = escapeStringBackslash(buf, v)
			} else {
				buf = escapeStringQuotes(buf, v)
			}
			buf = append(buf, '\'')
		default:
			return "", driver.ErrSkip
		}

		if len(buf)+4 > mc.maxPacketAllowed {
			return "", driver.ErrSkip
		}
	}
	if argPos != len(args) {
		return "", driver.ErrSkip
	}
	return string(buf), nil
}

func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	if len(args) != 0 {
		if !mc.cfg.InterpolateParams {
			return nil, driver.ErrSkip
		}
		// try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
		prepared, err := mc.interpolateParams(query, args)
		if err != nil {
			return nil, err
		}
		query = prepared
		args = nil
	}
	mc.affectedRows = 0
	mc.insertId = 0

	err := mc.exec(query)
	if err == nil {
		return &mysqlResult{
			affectedRows: int64(mc.affectedRows),
			insertId:     int64(mc.insertId),
		}, err
	}
	return nil, err
}

// Internal function to execute commands
func (mc *mysqlConn) exec(query string) error {
	// Send command
	err := mc.writeCommandPacketStr(comQuery, query)
	if err != nil {
		return err
	}

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err == nil && resLen > 0 {
		if err = mc.readUntilEOF(); err != nil {
			return err
		}

		err = mc.readUntilEOF()
	}

	return err
}

func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	if len(args) != 0 {
		if !mc.cfg.InterpolateParams {
			return nil, driver.ErrSkip
		}
		// try client-side prepare to reduce roundtrip
		prepared, err := mc.interpolateParams(query, args)
		if err != nil {
			return nil, err
		}
		query = prepared
		args = nil
	}
	// Send command
	err := mc.writeCommandPacketStr(comQuery, query)
	if err == nil {
		// Read Result
		var resLen int
		resLen, err = mc.readResultSetHeaderPacket()
		if err == nil {
			rows := new(textRows)
			rows.mc = mc

			if resLen == 0 {
				// no columns, no more data
				return emptyRows{}, nil
			}
			// Columns
			rows.columns, err = mc.readColumns(resLen)
			return rows, err
		}
	}
	return nil, err
}

// Gets the value of the given MySQL System Variable
// The returned byte slice is only valid until the next read
func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
	// Send command
	if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
		return nil, err
	}

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err == nil {
		rows := new(textRows)
		rows.mc = mc
		rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}

		if resLen > 0 {
			// Columns
			if err := mc.readUntilEOF(); err != nil {
				return nil, err
			}
		}

		dest := make([]driver.Value, resLen)
		if err = rows.readRow(dest); err == nil {
			return dest[0].([]byte), mc.readUntilEOF()
		}
	}
	return nil, err
}
<commit_msg>Fix index out of range in interpolateParams.<commit_after>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"database/sql/driver"
	"net"
	"strconv"
	"strings"
	"time"
)

type mysqlConn struct {
	buf              buffer
	netConn          net.Conn
	affectedRows     uint64
	insertId         uint64
	cfg              *Config
	maxPacketAllowed int
	maxWriteSize     int
	writeTimeout     time.Duration
	flags            clientFlag
	status           statusFlag
	sequence         uint8
	parseTime        bool
	strict           bool
}

// Handles parameters set in DSN after the connection is established
func (mc *mysqlConn) handleParams() (err error) {
	for param, val := range mc.cfg.Params {
		switch param {
		// Charset
		case "charset":
			charsets := strings.Split(val, ",")
			for i := range charsets {
				// ignore errors here - a charset may not exist
				err = mc.exec("SET NAMES " + charsets[i])
				if err == nil {
					break
				}
			}
			if err != nil {
				return
			}

		// System Vars
		default:
			err = mc.exec("SET " + param + "=" + val + "")
			if err != nil {
				return
			}
		}
	}

	return
}

func (mc *mysqlConn) Begin() (driver.Tx, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	err := mc.exec("START TRANSACTION")
	if err == nil {
		return &mysqlTx{mc}, err
	}

	return nil, err
}

func (mc *mysqlConn) Close() (err error) {
	// Makes Close idempotent
	if mc.netConn != nil {
		err = mc.writeCommandPacket(comQuit)
	}

	mc.cleanup()

	return
}

// Closes the network connection and unsets internal variables. Do not call this
// function after successfully authentication, call Close instead. This function
// is called before auth or on auth failure because MySQL will have already
// closed the network connection.
func (mc *mysqlConn) cleanup() {
	// Makes cleanup idempotent
	if mc.netConn != nil {
		if err := mc.netConn.Close(); err != nil {
			errLog.Print(err)
		}
		mc.netConn = nil
	}
	mc.cfg = nil
	mc.buf.nc = nil
}

func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	// Send command
	err := mc.writeCommandPacketStr(comStmtPrepare, query)
	if err != nil {
		return nil, err
	}

	stmt := &mysqlStmt{
		mc: mc,
	}

	// Read Result
	columnCount, err := stmt.readPrepareResultPacket()
	if err == nil {
		if stmt.paramCount > 0 {
			if err = mc.readUntilEOF(); err != nil {
				return nil, err
			}
		}

		if columnCount > 0 {
			err = mc.readUntilEOF()
		}
	}

	return stmt, err
}

func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
	buf := mc.buf.takeCompleteBuffer()
	if buf == nil {
		// can not take the buffer. Something must be wrong with the connection
		errLog.Print(ErrBusyBuffer)
		return "", driver.ErrBadConn
	}
	buf = buf[:0]
	argPos := 0

	for i := 0; i < len(query); i++ {
		q := strings.IndexByte(query[i:], '?')
		if q == -1 {
			buf = append(buf, query[i:]...)
			break
		}
		buf = append(buf, query[i:i+q]...)
		i += q

		if argPos >= len(args) {
			return "", driver.ErrSkip
		}
		arg := args[argPos]
		argPos++

		if arg == nil {
			buf = append(buf, "NULL"...)
			continue
		}

		switch v := arg.(type) {
		case int64:
			buf = strconv.AppendInt(buf, v, 10)
		case float64:
			buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
		case bool:
			if v {
				buf = append(buf, '1')
			} else {
				buf = append(buf, '0')
			}
		case time.Time:
			if v.IsZero() {
				buf = append(buf, "'0000-00-00'"...)
			} else {
				v := v.In(mc.cfg.Loc)
				v = v.Add(time.Nanosecond * 500) // To round under microsecond
				year := v.Year()
				year100 := year / 100
				year1 := year % 100
				month := v.Month()
				day := v.Day()
				hour := v.Hour()
				minute := v.Minute()
				second := v.Second()
				micro := v.Nanosecond() / 1000

				buf = append(buf, []byte{
					'\'',
					digits10[year100], digits01[year100],
					digits10[year1], digits01[year1],
					'-',
					digits10[month], digits01[month],
					'-',
					digits10[day], digits01[day],
					' ',
					digits10[hour], digits01[hour],
					':',
					digits10[minute], digits01[minute],
					':',
					digits10[second], digits01[second],
				}...)

				if micro != 0 {
					micro10000 := micro / 10000
					micro100 := micro / 100 % 100
					micro1 := micro % 100
					buf = append(buf, []byte{
						'.',
						digits10[micro10000], digits01[micro10000],
						digits10[micro100], digits01[micro100],
						digits10[micro1], digits01[micro1],
					}...)
				}
				buf = append(buf, '\'')
			}
		case []byte:
			if v == nil {
				buf = append(buf, "NULL"...)
			} else {
				buf = append(buf, "_binary'"...)
				if mc.status&statusNoBackslashEscapes == 0 {
					buf = escapeBytesBackslash(buf, v)
				} else {
					buf = escapeBytesQuotes(buf, v)
				}
				buf = append(buf, '\'')
			}
		case string:
			buf = append(buf, '\'')
			if mc.status&statusNoBackslashEscapes == 0 {
				buf = escapeStringBackslash(buf, v)
			} else {
				buf = escapeStringQuotes(buf, v)
			}
			buf = append(buf, '\'')
		default:
			return "", driver.ErrSkip
		}

		if len(buf)+4 > mc.maxPacketAllowed {
			return "", driver.ErrSkip
		}
	}
	if argPos != len(args) {
		return "", driver.ErrSkip
	}
	return string(buf), nil
}

func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	if len(args) != 0 {
		if !mc.cfg.InterpolateParams {
			return nil, driver.ErrSkip
		}
		// try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
		prepared, err := mc.interpolateParams(query, args)
		if err != nil {
			return nil, err
		}
		query = prepared
		args = nil
	}
	mc.affectedRows = 0
	mc.insertId = 0

	err := mc.exec(query)
	if err == nil {
		return &mysqlResult{
			affectedRows: int64(mc.affectedRows),
			insertId:     int64(mc.insertId),
		}, err
	}
	return nil, err
}

// Internal function to execute commands
func (mc *mysqlConn) exec(query string) error {
	// Send command
	err := mc.writeCommandPacketStr(comQuery, query)
	if err != nil {
		return err
	}

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err == nil && resLen > 0 {
		if err = mc.readUntilEOF(); err != nil {
			return err
		}

		err = mc.readUntilEOF()
	}

	return err
}

func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
	if mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	if len(args) != 0 {
		if !mc.cfg.InterpolateParams {
			return nil, driver.ErrSkip
		}
		// try client-side prepare to reduce roundtrip
		prepared, err := mc.interpolateParams(query, args)
		if err != nil {
			return nil, err
		}
		query = prepared
		args = nil
	}
	// Send command
	err := mc.writeCommandPacketStr(comQuery, query)
	if err == nil {
		// Read Result
		var resLen int
		resLen, err = mc.readResultSetHeaderPacket()
		if err == nil {
			rows := new(textRows)
			rows.mc = mc

			if resLen == 0 {
				// no columns, no more data
				return emptyRows{}, nil
			}
			// Columns
			rows.columns, err = mc.readColumns(resLen)
			return rows, err
		}
	}
	return nil, err
}

// Gets the value of the given MySQL System Variable
// The returned byte slice is only valid until the next read
func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
	// Send command
	if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
		return nil, err
	}

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err == nil {
		rows := new(textRows)
		rows.mc = mc
		rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}

		if resLen > 0 {
			// Columns
			if err := mc.readUntilEOF(); err != nil {
				return nil, err
			}
		}

		dest := make([]driver.Value, resLen)
		if err = rows.readRow(dest); err == nil {
			return dest[0].([]byte), mc.readUntilEOF()
		}
	}
	return nil, err
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:    logger,
		wrapped:   wrapped,
		parentCtx: parentCtx,
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// Set up state for an op that is about to be returned to the user.
func (c *Connection) beginOp()

// Clean up all state associated with an op to which the user has responded.
func (c *Connection) finishOp()

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	var bfReq bazilfuse.Request

	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		bfReq, err = c.wrapped.ReadRequest()
		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "<- %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Convert it, if possible.
		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp() }

		op = fuseops.Convert(c.parentCtx, bfReq, logForOp, finished)
		if op == nil {
			c.log(opID, 1, "-> ENOSYS: %v", bfReq)
			bfReq.RespondError(ENOSYS)
			continue
		}

		c.beginOp()
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<commit_msg>Connection.finishOp<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:    logger,
		wrapped:   wrapped,
		parentCtx: parentCtx,
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// Set up state for an op that is about to be returned to the user.
func (c *Connection) beginOp() {
	c.opsInFlight.Add(1)
}

// Clean up all state associated with an op to which the user has responded.
func (c *Connection) finishOp() {
	c.opsInFlight.Done()
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	var bfReq bazilfuse.Request

	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		bfReq, err = c.wrapped.ReadRequest()
		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "<- %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Convert it, if possible.
		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp() }

		op = fuseops.Convert(c.parentCtx, bfReq, logForOp, finished)
		if op == nil {
			c.log(opID, 1, "-> ENOSYS: %v", bfReq)
			bfReq.RespondError(ENOSYS)
			continue
		}

		c.beginOp()
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<|endoftext|>


<commit_before>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/rs/xid"
)

var (
	guid = xid.New()
)

// NewConnection returns a new ws connection instance
func NewConnection(w http.ResponseWriter, r *http.Request) (*Connection, error) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err == nil {
		return &Connection{guid.String(), []*Channel{}, make(chan []byte), r, conn}, nil
	}
	return nil, err
}

// Connection allows you to interact with backend and other client sockets in realtime
type Connection struct {
	// the id of the connection
	id string
	// list of channels that this connection has been subscribed
	channels []*Channel
	// buffered channel of outbound messages
	send chan []byte
	// http request
	request *http.Request
	// websocket connection
	*websocket.Conn
}

// write writes a message with the given message type and payload.
func (conn *Connection) emit(mt int, payload []byte) error {
	conn.SetWriteDeadline(time.Now().Add(writeWait))
	return conn.WriteMessage(mt, payload)
}

// writePump pumps messages from the sphere to the websocket connection.
func (conn *Connection) writePump() error {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
	}()
	for {
		select {
		case msg, ok := <-conn.send:
			if !ok {
				return conn.emit(websocket.CloseMessage, []byte{})
			}
			if err := conn.emit(websocket.TextMessage, msg); err != nil {
				return err
			}
		case <-ticker.C:
			if err := conn.emit(websocket.PingMessage, []byte{}); err != nil {
				return err
			}
		}
	}
}

func (conn *Connection) readPump() {
	conn.SetReadLimit(maxMessageSize)
	conn.SetReadDeadline(time.Now().Add(pongWait))
	conn.SetPingHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, msg, err := conn.ReadMessage()
		if err != nil {
		}
		if msg != nil {
		}
	}
}
<commit_msg>remove unused code<commit_after>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

// NewConnection returns a new ws connection instance
func NewConnection(w http.ResponseWriter, r *http.Request) (*Connection, error) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err == nil {
		return &Connection{guid.String(), []*Channel{}, make(chan []byte), r, conn}, nil
	}
	return nil, err
}

// Connection allows you to interact with backend and other client sockets in realtime
type Connection struct {
	// the id of the connection
	id string
	// list of channels that this connection has been subscribed
	channels []*Channel
	// buffered channel of outbound messages
	send chan []byte
	// http request
	request *http.Request
	// websocket connection
	*websocket.Conn
}

// write writes a message with the given message type and payload.
func (conn *Connection) emit(mt int, payload []byte) error {
	conn.SetWriteDeadline(time.Now().Add(writeWait))
	return conn.WriteMessage(mt, payload)
}

// writePump pumps messages from the sphere to the websocket connection.
func (conn *Connection) writePump() error {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
	}()
	for {
		select {
		case msg, ok := <-conn.send:
			if !ok {
				return conn.emit(websocket.CloseMessage, []byte{})
			}
			if err := conn.emit(websocket.TextMessage, msg); err != nil {
				return err
			}
		case <-ticker.C:
			if err := conn.emit(websocket.PingMessage, []byte{}); err != nil {
				return err
			}
		}
	}
}

func (conn *Connection) readPump() {
	conn.SetReadLimit(maxMessageSize)
	conn.SetReadDeadline(time.Now().Add(pongWait))
	conn.SetPingHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, msg, err := conn.ReadMessage()
		if err != nil {
		}
		if msg != nil {
		}
	}
}
<|endoftext|>


<commit_before>package spdy

import (
  "bufio"
  "crypto/tls"
  "fmt"
  "log"
  "net/http"
  "net/url"
  "runtime"
  "sync"
  "time"
)

type connection struct {
  sync.RWMutex
  remoteAddr          string // network address of remote side
  server              *Server
  conn                *tls.Conn
  buf                 *bufio.Reader
  tlsState            *tls.ConnectionState
  tlsConfig           *tls.Config
  streams             map[uint32]*stream
  streamInputs        map[uint32]chan<- []byte
  streamOutputs       [8]chan Frame
  compressor          *Compressor
  decompressor        *Decompressor
  receivedSettings    []*Setting
  nextServerStreamID  uint32 // even
  nextClientStreamID  uint32 // odd
  goaway              bool
  version             int
  numInvalidStreamIDs int
}

func (conn *connection) send() {
  for {
    frame := conn.selectFrameToSend()
    err := frame.WriteHeaders(conn.compressor)
    if err != nil {
      panic(err)
    }
    err = frame.WriteTo(conn.conn)
    if err != nil {
      panic(err)
    }
  }
}

func (conn *connection) selectFrameToSend() (frame Frame) {
  // Try in priority order first.
  for i := 0; i < 8; i++ {
    select {
    case frame = <-conn.streamOutputs[i]:
      return frame
    default:
    }
  }

  // Wait for any frame.
  select {
  case frame = <-conn.streamOutputs[0]:
    return frame
  case frame = <-conn.streamOutputs[1]:
    return frame
  case frame = <-conn.streamOutputs[2]:
    return frame
  case frame = <-conn.streamOutputs[3]:
    return frame
  case frame = <-conn.streamOutputs[4]:
    return frame
  case frame = <-conn.streamOutputs[5]:
    return frame
  case frame = <-conn.streamOutputs[6]:
    return frame
  case frame = <-conn.streamOutputs[7]:
    return frame
  }

  panic("Unreachable")
}

func (conn *connection) newStream(frame *SynStreamFrame, input <-chan []byte,
  output chan<- Frame) *stream {

  stream := new(stream)
  stream.conn = conn
  stream.streamID = frame.StreamID
  stream.state = STATE_OPEN
  if frame.Flags&FLAG_FIN != 0 {
    stream.state = STATE_HALF_CLOSED_THERE
  }
  stream.priority = frame.Priority
  stream.input = input
  stream.output = output
  stream.handler = DefaultServeMux
  stream.certificates = make([]Certificate, 1)
  stream.headers = make(Header)
  stream.settings = make([]*Setting, 1)
  stream.unidirectional = frame.Flags&FLAG_UNIDIRECTIONAL != 0
  stream.version = conn.version
  stream.contentLength = -1

  headers := frame.Headers
  rawUrl := headers.Get(":scheme") + "://" + headers.Get(":host") + headers.Get(":path")
  url, err := url.Parse(rawUrl)
  if err != nil {
    panic(err)
  }
  major, minor, ok := http.ParseHTTPVersion(headers.Get(":version"))
  if !ok {
    panic("Invalid HTTP version: " + headers.Get(":version"))
  }
  stream.request = &Request{
    Method:     headers.Get(":method"),
    URL:        url,
    Proto:      headers.Get(":version"),
    ProtoMajor: major,
    ProtoMinor: minor,
    Header:     headers,
    Host:       url.Host,
    RequestURI: url.Path,
    TLS:        conn.tlsState,
  }

  return stream
}

func (conn *connection) WriteFrame(frame Frame) error {
  return nil
}

func (conn *connection) handleSynStream(frame *SynStreamFrame) {
  conn.RLock()
  defer func() { conn.RUnlock() }()

  // Check stream creation is allowed.
  if conn.goaway {
    return
  }

  // Check version.
  if frame.Version != uint16(conn.version) {

    // This is currently strict; only one version allowed per connection.
    log.Printf("Error: Received frame with SPDY version %d on connection with version %d.\n",
      frame.Version, conn.version)
    if frame.Version > SPDY_VERSION {
      log.Printf("Error: Received frame with SPDY version %d, which is not supported.\n",
        frame.Version)
    }
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_UNSUPPORTED_VERSION
    conn.WriteFrame(reply)
    return
  }

  protocolError := func() {
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
    conn.WriteFrame(reply)
  }

  // Check Stream ID is odd.
  if frame.StreamID&1 == 0 {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be odd.\n",
      frame.StreamID)
    protocolError()
    return
  }

  // Check Stream ID is the right number.
  if frame.StreamID != conn.nextClientStreamID+2 && frame.StreamID != 1 &&
    conn.nextClientStreamID != 0 {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be %d.\n",
      frame.StreamID, conn.nextClientStreamID+2)
    protocolError()
    return
  }

  // Check Stream ID is not too large.
  if frame.StreamID > MAX_STREAM_ID {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which is too large.\n",
      frame.StreamID)
    protocolError()
    return
  }

  // Stream ID is fine.

  // Create and start new stream.
  conn.RUnlock()
  conn.Lock()
  input := make(chan []byte)
  conn.streamInputs[frame.StreamID] = input
  if frame.Flags&FLAG_FIN != 0 {
    close(input)
  }
  conn.streams[frame.StreamID] = conn.newStream(frame, input, conn.streamOutputs[frame.Priority])
  conn.Unlock()
  conn.RLock()

  go func() { conn.streams[frame.StreamID].run() }()

  return
}

func (conn *connection) handleDataFrame(frame *DataFrame) {
  conn.RLock()
  defer func() { conn.RUnlock() }()

  // Check Stream ID is odd.
  if frame.StreamID&1 == 0 {
    log.Printf("Error: Received DATA with Stream ID %d, which should be odd.\n",
      frame.StreamID)
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
    conn.WriteFrame(reply)
    return
  }

  // Check stream is open.
  if frame.StreamID != conn.nextClientStreamID+2 && frame.StreamID != 1 &&
    conn.nextClientStreamID != 0 {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be %d.\n",
      frame.StreamID, conn.nextClientStreamID+2)
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
    conn.WriteFrame(reply)
    return
  }

  // Stream ID is fine.

  // Send data to stream.
  conn.streamInputs[frame.StreamID] <- frame.Data

  // Handle flags.
  if frame.Flags&FLAG_FIN != 0 {
    stream := conn.streams[frame.StreamID]
    stream.Lock()
    stream.state = STATE_HALF_CLOSED_THERE
    close(conn.streamInputs[frame.StreamID])
    stream.Unlock()
  }

  return
}

func (conn *connection) readFrames() {
  if d := conn.server.ReadTimeout; d != 0 {
    conn.conn.SetReadDeadline(time.Now().Add(d))
  }
  if d := conn.server.WriteTimeout; d != 0 {
    defer func() {
      conn.conn.SetWriteDeadline(time.Now().Add(d))
    }()
  }

  for {
    frame, err := ReadFrame(conn.buf)
    if err != nil {
      // TODO: handle error
      panic(err)
    }
    err = frame.ReadHeaders(conn.decompressor)
    if err != nil {
      panic(err)
    }

    if DebugMode {
      fmt.Println("Received Frame:")
      fmt.Println(frame)
    }

  FrameHandling:
    switch frame := frame.(type) {
    default:
      panic(fmt.Sprintf("unexpected frame type %T", frame))

    /*** COMPLETE! ***/
    case *SynStreamFrame:
      conn.handleSynStream(frame)

    case *SynReplyFrame:
      log.Println("Got SYN_REPLY")

    case *RstStreamFrame:
      log.Printf("Received RST_STREAM on stream %d with status %q.\n", frame.StreamID,
        StatusCodeText(int(frame.StatusCode)))

      /*** COMPLETE! ***/
    case *SettingsFrame:
      if conn.receivedSettings == nil {
        conn.receivedSettings = frame.Settings
      } else {
        conn.receivedSettings = append(conn.receivedSettings, frame.Settings...)
      }
      // TODO: Sort out duplication and updating.
      // TODO: Perhaps add some handling by the server here?

    /*** COMPLETE! ***/
    case *PingFrame:
      // Check Ping ID is odd.
      if frame.PingID&1 == 0 {
        log.Printf("Error: Received PING with Stream ID %d, which should be odd.\n", frame.PingID)
        reply := new(RstStreamFrame)
        reply.Version = SPDY_VERSION
        reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
        conn.WriteFrame(reply)
        break FrameHandling
      }
      log.Println("Received PING. Replying...")
      conn.WriteFrame(frame)

    case *GoawayFrame:
      // Check version.
      if frame.Version != uint16(conn.version) {
        log.Printf("Warning: Received frame with SPDY version %d on connection with version %d.\n",
          frame.Version, conn.version)
        if frame.Version > SPDY_VERSION {
          log.Printf("Error: Received frame with SPDY version %d, which is not supported.\n",
            frame.Version)
          reply := new(RstStreamFrame)
          reply.Version = SPDY_VERSION
          reply.StatusCode = RST_STREAM_UNSUPPORTED_VERSION
          conn.WriteFrame(reply)
          break FrameHandling
        }
      }

      // TODO: inform push streams that they haven't been processed if
      // the last good stream ID is less than their ID.

      conn.Lock()
      conn.goaway = true
      conn.Unlock()

    case *HeadersFrame:
      log.Println("Got HEADERS")

    case *WindowUpdateFrame:
      log.Println("Got WINDOW_UPDATE")

    case *CredentialFrame:
      log.Println("Got CREDENTIAL")

      /*** COMPLETE! ***/
    case *DataFrame:
      conn.handleDataFrame(frame)
    }
  }
}

func (conn *connection) serve() {
  defer func() {
    if err := recover(); err != nil {
      const size = 4096
      buf := make([]byte, size)
      buf = buf[:runtime.Stack(buf, false)]
      log.Printf("spdy: panic serving %v: %v\n%s", conn.remoteAddr, err, buf)
    }
  }()

  go func() { conn.send() }()
  if conn.server.GlobalSettings != nil {
    settings := new(SettingsFrame)
    settings.Version = uint16(conn.version)
    settings.Settings = conn.server.GlobalSettings
    conn.streamOutputs[3] <- settings
  }
  conn.readFrames()
}

func acceptDefaultSPDYv2(srv *http.Server, tlsConn *tls.Conn, _ http.Handler) {
  server := new(Server)
  server.TLSConfig = srv.TLSConfig
  acceptSPDYv2(server, tlsConn, nil)
}

func acceptSPDYv2(server *Server, tlsConn *tls.Conn, _ http.Handler) {
  conn := newConn(tlsConn)
  conn.server = server
  conn.tlsConfig = server.TLSConfig
  conn.version = 2

  conn.serve()
}

func acceptDefaultSPDYv3(srv *http.Server, tlsConn *tls.Conn, _ http.Handler) {
  server := new(Server)
  server.TLSConfig = srv.TLSConfig
  acceptSPDYv3(server, tlsConn, nil)
}

func acceptSPDYv3(server *Server, tlsConn *tls.Conn, _ http.Handler) {
  conn := newConn(tlsConn)
  conn.server = server
  conn.tlsConfig = server.TLSConfig
  conn.version = 3

  conn.serve()
}

func newConn(tlsConn *tls.Conn) *connection {
  conn := new(connection)
  conn.remoteAddr = tlsConn.RemoteAddr().String()
  conn.conn = tlsConn
  conn.buf = bufio.NewReader(tlsConn)
  conn.tlsState = new(tls.ConnectionState)
  *conn.tlsState = tlsConn.ConnectionState()
  conn.compressor = new(Compressor)
  conn.decompressor = new(Decompressor)
  conn.streams = make(map[uint32]*stream)
  conn.streamInputs = make(map[uint32]chan<- []byte)
  conn.streamOutputs = [8]chan Frame{}
  conn.streamOutputs[0] = make(chan Frame)
  conn.streamOutputs[1] = make(chan Frame)
  conn.streamOutputs[2] = make(chan Frame)
  conn.streamOutputs[3] = make(chan Frame)
  conn.streamOutputs[4] = make(chan Frame)
  conn.streamOutputs[5] = make(chan Frame)
  conn.streamOutputs[6] = make(chan Frame)
  conn.streamOutputs[7] = make(chan Frame)

  return conn
}
<commit_msg>Improved safety in received settings handling<commit_after>package spdy

import (
  "bufio"
  "crypto/tls"
  "fmt"
  "log"
  "net/http"
  "net/url"
  "runtime"
  "sync"
  "time"
)

type connection struct {
  sync.RWMutex
  remoteAddr          string // network address of remote side
  server              *Server
  conn                *tls.Conn
  buf                 *bufio.Reader
  tlsState            *tls.ConnectionState
  tlsConfig           *tls.Config
  streams             map[uint32]*stream
  streamInputs        map[uint32]chan<- []byte
  streamOutputs       [8]chan Frame
  compressor          *Compressor
  decompressor        *Decompressor
  receivedSettings    []*Setting
  nextServerStreamID  uint32 // even
  nextClientStreamID  uint32 // odd
  goaway              bool
  version             int
  numInvalidStreamIDs int
}

func (conn *connection) send() {
  for {
    frame := conn.selectFrameToSend()
    err := frame.WriteHeaders(conn.compressor)
    if err != nil {
      panic(err)
    }
    err = frame.WriteTo(conn.conn)
    if err != nil {
      panic(err)
    }
  }
}

func (conn *connection) selectFrameToSend() (frame Frame) {
  // Try in priority order first.
  for i := 0; i < 8; i++ {
    select {
    case frame = <-conn.streamOutputs[i]:
      return frame
    default:
    }
  }

  // Wait for any frame.
  select {
  case frame = <-conn.streamOutputs[0]:
    return frame
  case frame = <-conn.streamOutputs[1]:
    return frame
  case frame = <-conn.streamOutputs[2]:
    return frame
  case frame = <-conn.streamOutputs[3]:
    return frame
  case frame = <-conn.streamOutputs[4]:
    return frame
  case frame = <-conn.streamOutputs[5]:
    return frame
  case frame = <-conn.streamOutputs[6]:
    return frame
  case frame = <-conn.streamOutputs[7]:
    return frame
  }

  panic("Unreachable")
}

func (conn *connection) newStream(frame *SynStreamFrame, input <-chan []byte,
  output chan<- Frame) *stream {

  stream := new(stream)
  stream.conn = conn
  stream.streamID = frame.StreamID
  stream.state = STATE_OPEN
  if frame.Flags&FLAG_FIN != 0 {
    stream.state = STATE_HALF_CLOSED_THERE
  }
  stream.priority = frame.Priority
  stream.input = input
  stream.output = output
  stream.handler = DefaultServeMux
  stream.certificates = make([]Certificate, 1)
  stream.headers = make(Header)
  stream.settings = make([]*Setting, 1)
  stream.unidirectional = frame.Flags&FLAG_UNIDIRECTIONAL != 0
  stream.version = conn.version
  stream.contentLength = -1

  headers := frame.Headers
  rawUrl := headers.Get(":scheme") + "://" + headers.Get(":host") + headers.Get(":path")
  url, err := url.Parse(rawUrl)
  if err != nil {
    panic(err)
  }
  major, minor, ok := http.ParseHTTPVersion(headers.Get(":version"))
  if !ok {
    panic("Invalid HTTP version: " + headers.Get(":version"))
  }
  stream.request = &Request{
    Method:     headers.Get(":method"),
    URL:        url,
    Proto:      headers.Get(":version"),
    ProtoMajor: major,
    ProtoMinor: minor,
    Header:     headers,
    Host:       url.Host,
    RequestURI: url.Path,
    TLS:        conn.tlsState,
  }

  return stream
}

func (conn *connection) WriteFrame(frame Frame) error {
  return nil
}

func (conn *connection) handleSynStream(frame *SynStreamFrame) {
  conn.RLock()
  defer func() { conn.RUnlock() }()

  // Check stream creation is allowed.
  if conn.goaway {
    return
  }

  // Check version.
  if frame.Version != uint16(conn.version) {

    // This is currently strict; only one version allowed per connection.
    log.Printf("Error: Received frame with SPDY version %d on connection with version %d.\n",
      frame.Version, conn.version)
    if frame.Version > SPDY_VERSION {
      log.Printf("Error: Received frame with SPDY version %d, which is not supported.\n",
        frame.Version)
    }
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_UNSUPPORTED_VERSION
    conn.WriteFrame(reply)
    return
  }

  protocolError := func() {
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
    conn.WriteFrame(reply)
  }

  // Check Stream ID is odd.
  if frame.StreamID&1 == 0 {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be odd.\n",
      frame.StreamID)
    protocolError()
    return
  }

  // Check Stream ID is the right number.
  if frame.StreamID != conn.nextClientStreamID+2 && frame.StreamID != 1 &&
    conn.nextClientStreamID != 0 {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be %d.\n",
      frame.StreamID, conn.nextClientStreamID+2)
    protocolError()
    return
  }

  // Check Stream ID is not too large.
  if frame.StreamID > MAX_STREAM_ID {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which is too large.\n",
      frame.StreamID)
    protocolError()
    return
  }

  // Stream ID is fine.

  // Create and start new stream.
  conn.RUnlock()
  conn.Lock()
  input := make(chan []byte)
  conn.streamInputs[frame.StreamID] = input
  if frame.Flags&FLAG_FIN != 0 {
    close(input)
  }
  conn.streams[frame.StreamID] = conn.newStream(frame, input, conn.streamOutputs[frame.Priority])
  conn.Unlock()
  conn.RLock()

  go func() { conn.streams[frame.StreamID].run() }()

  return
}

func (conn *connection) handleDataFrame(frame *DataFrame) {
  conn.RLock()
  defer func() { conn.RUnlock() }()

  // Check Stream ID is odd.
  if frame.StreamID&1 == 0 {
    log.Printf("Error: Received DATA with Stream ID %d, which should be odd.\n",
      frame.StreamID)
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
    conn.WriteFrame(reply)
    return
  }

  // Check stream is open.
  if frame.StreamID != conn.nextClientStreamID+2 && frame.StreamID != 1 &&
    conn.nextClientStreamID != 0 {
    log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be %d.\n",
      frame.StreamID, conn.nextClientStreamID+2)
    reply := new(RstStreamFrame)
    reply.Version = SPDY_VERSION
    reply.StreamID = frame.StreamID
    reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
    conn.WriteFrame(reply)
    return
  }

  // Stream ID is fine.

  // Send data to stream.
  conn.streamInputs[frame.StreamID] <- frame.Data

  // Handle flags.
  if frame.Flags&FLAG_FIN != 0 {
    stream := conn.streams[frame.StreamID]
    stream.Lock()
    stream.state = STATE_HALF_CLOSED_THERE
    close(conn.streamInputs[frame.StreamID])
    stream.Unlock()
  }

  return
}

func (conn *connection) readFrames() {
  if d := conn.server.ReadTimeout; d != 0 {
    conn.conn.SetReadDeadline(time.Now().Add(d))
  }
  if d := conn.server.WriteTimeout; d != 0 {
    defer func() {
      conn.conn.SetWriteDeadline(time.Now().Add(d))
    }()
  }

  for {
    frame, err := ReadFrame(conn.buf)
    if err != nil {
      // TODO: handle error
      panic(err)
    }
    err = frame.ReadHeaders(conn.decompressor)
    if err != nil {
      panic(err)
    }

    if DebugMode {
      fmt.Println("Received Frame:")
      fmt.Println(frame)
    }

  FrameHandling:
    switch frame := frame.(type) {
    default:
      panic(fmt.Sprintf("unexpected frame type %T", frame))

    /*** COMPLETE! ***/
    case *SynStreamFrame:
      conn.handleSynStream(frame)

    case *SynReplyFrame:
      log.Println("Got SYN_REPLY")

    case *RstStreamFrame:
      log.Printf("Received RST_STREAM on stream %d with status %q.\n", frame.StreamID,
        StatusCodeText(int(frame.StatusCode)))

      /*** COMPLETE! ***/
    case *SettingsFrame:
      if conn.receivedSettings == nil {
        conn.receivedSettings = frame.Settings
      } else {
        for _, new := range frame.Settings {
          for i, old := range conn.receivedSettings {
            if new.ID == old.ID {
              conn.receivedSettings[i] = new
            }
          }
          conn.receivedSettings = append(conn.receivedSettings, new)
        }
      }
      // TODO: Perhaps add some handling by the server here?

    /*** COMPLETE! ***/
    case *PingFrame:
      // Check Ping ID is odd.
      if frame.PingID&1 == 0 {
        log.Printf("Error: Received PING with Stream ID %d, which should be odd.\n", frame.PingID)
        reply := new(RstStreamFrame)
        reply.Version = SPDY_VERSION
        reply.StatusCode = RST_STREAM_PROTOCOL_ERROR
        conn.WriteFrame(reply)
        break FrameHandling
      }
      log.Println("Received PING. Replying...")
      conn.WriteFrame(frame)

    case *GoawayFrame:
      // Check version.
      if frame.Version != uint16(conn.version) {
        log.Printf("Warning: Received frame with SPDY version %d on connection with version %d.\n",
          frame.Version, conn.version)
        if frame.Version > SPDY_VERSION {
          log.Printf("Error: Received frame with SPDY version %d, which is not supported.\n",
            frame.Version)
          reply := new(RstStreamFrame)
          reply.Version = SPDY_VERSION
          reply.StatusCode = RST_STREAM_UNSUPPORTED_VERSION
          conn.WriteFrame(reply)
          break FrameHandling
        }
      }

      // TODO: inform push streams that they haven't been processed if
      // the last good stream ID is less than their ID.

      conn.Lock()
      conn.goaway = true
      conn.Unlock()

    case *HeadersFrame:
      log.Println("Got HEADERS")

    case *WindowUpdateFrame:
      log.Println("Got WINDOW_UPDATE")

    case *CredentialFrame:
      log.Println("Got CREDENTIAL")

      /*** COMPLETE! ***/
    case *DataFrame:
      conn.handleDataFrame(frame)
    }
  }
}

func (conn *connection) serve() {
  defer func() {
    if err := recover(); err != nil {
      const size = 4096
      buf := make([]byte, size)
      buf = buf[:runtime.Stack(buf, false)]
      log.Printf("spdy: panic serving %v: %v\n%s", conn.remoteAddr, err, buf)
    }
  }()

  go func() { conn.send() }()
  if conn.server.GlobalSettings != nil {
    settings := new(SettingsFrame)
    settings.Version = uint16(conn.version)
    settings.Settings = conn.server.GlobalSettings
    conn.streamOutputs[3] <- settings
  }
  conn.readFrames()
}

func acceptDefaultSPDYv2(srv *http.Server, tlsConn *tls.Conn, _ http.Handler) {
  server := new(Server)
  server.TLSConfig = srv.TLSConfig
  acceptSPDYv2(server, tlsConn, nil)
}

func acceptSPDYv2(server *Server, tlsConn *tls.Conn, _ http.Handler) {
  conn := newConn(tlsConn)
  conn.server = server
  conn.tlsConfig = server.TLSConfig
  conn.version = 2

  conn.serve()
}

func acceptDefaultSPDYv3(srv *http.Server, tlsConn *tls.Conn, _ http.Handler) {
  server := new(Server)
  server.TLSConfig = srv.TLSConfig
  acceptSPDYv3(server, tlsConn, nil)
}

func acceptSPDYv3(server *Server, tlsConn *tls.Conn, _ http.Handler) {
  conn := newConn(tlsConn)
  conn.server = server
  conn.tlsConfig = server.TLSConfig
  conn.version = 3

  conn.serve()
}

func newConn(tlsConn *tls.Conn) *connection {
  conn := new(connection)
  conn.remoteAddr = tlsConn.RemoteAddr().String()
  conn.conn = tlsConn
  conn.buf = bufio.NewReader(tlsConn)
  conn.tlsState = new(tls.ConnectionState)
  *conn.tlsState = tlsConn.ConnectionState()
  conn.compressor = new(Compressor)
  conn.decompressor = new(Decompressor)
  conn.streams = make(map[uint32]*stream)
  conn.streamInputs = make(map[uint32]chan<- []byte)
  conn.streamOutputs = [8]chan Frame{}
  conn.streamOutputs[0] = make(chan Frame)
  conn.streamOutputs[1] = make(chan Frame)
  conn.streamOutputs[2] = make(chan Frame)
  conn.streamOutputs[3] = make(chan Frame)
  conn.streamOutputs[4] = make(chan Frame)
  conn.streamOutputs[5] = make(chan Frame)
  conn.streamOutputs[6] = make(chan Frame)
  conn.streamOutputs[7] = make(chan Frame)

  return conn
}
<|endoftext|>