<commit_before>package bitclient

import (
	"fmt"
	"net/http"
)

const BASE_URI = "/rest/api/1.0"

type PagedRequest struct {
	Limit uint `url:"limit,omitempty"`
	Start uint `url:"start,omitempty"`
}

type RequestError struct {
	Code    int
	Message string
}

type ErrorResponse struct {
	Errors []Error
}

type PagedResponse struct {
	Size       uint `json:"size"`
	Limit      uint `json:"limit"`
	IsLastPage bool `json:"isLastPage"`
	Start      uint `json:"start"`
}

func (r RequestError) Error() string {
	return r.Message
}

func (bc *BitClient) checkReponse(resp *http.Response, errorResponse *ErrorResponse) (*http.Response, error) {

	if resp != nil && resp.StatusCode > 299 {

		message := fmt.Sprintf("%s - %s\n", resp.Status, resp.Request.URL.String())
		for _, e := range errorResponse.Errors {
			message += e.Message
		}

		return nil, RequestError{
			Code:    resp.StatusCode,
			Message: message,
		}
	}
	return resp, nil
}

func (bc *BitClient) DoGet(uri string, params interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Get(BASE_URI+uri).QueryStruct(params).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPostUrl(uri string, params interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Post(BASE_URI+uri).QueryStruct(params).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPost(uri string, data interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Post(BASE_URI+uri).BodyJSON(data).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPut(uri string, data interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Put(BASE_URI+uri).BodyJSON(data).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPutUrl(uri string, data interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Put(BASE_URI+uri).QueryStruct(data).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoDeleteUrl(uri string, params interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Delete(BASE_URI+uri).QueryStruct(params).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}
<commit_msg>Added better error handling<commit_after>package bitclient

import (
	"fmt"
	"net/http"
)

const BASE_URI = "/rest/api/1.0"

type PagedRequest struct {
	Limit uint `url:"limit,omitempty"`
	Start uint `url:"start,omitempty"`
}

type RequestError struct {
	Code    int
	Message string
}

type ErrorResponse struct {
	Errors []Error
}

type PagedResponse struct {
	Size       uint `json:"size"`
	Limit      uint `json:"limit"`
	IsLastPage bool `json:"isLastPage"`
	Start      uint `json:"start"`
}

func (r RequestError) Error() string {
	return r.Message
}

func (bc *BitClient) checkReponse(resp *http.Response, errorResponse *ErrorResponse) (*http.Response, error) {

	if resp != nil && resp.StatusCode > 299 {

		message := fmt.Sprintf("%s - %s\n", resp.Status, resp.Request.URL.String())
		for _, e := range errorResponse.Errors {
			message += e.Context + ": " + e.Message + "\n"
		}

		return nil, RequestError{
			Code:    resp.StatusCode,
			Message: message,
		}
	}
	return resp, nil
}

func (bc *BitClient) DoGet(uri string, params interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Get(BASE_URI+uri).QueryStruct(params).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPostUrl(uri string, params interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Post(BASE_URI+uri).QueryStruct(params).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPost(uri string, data interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Post(BASE_URI+uri).BodyJSON(data).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPut(uri string, data interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Put(BASE_URI+uri).BodyJSON(data).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoPutUrl(uri string, data interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Put(BASE_URI+uri).QueryStruct(data).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}

func (bc *BitClient) DoDeleteUrl(uri string, params interface{}, rData interface{}) (*http.Response, error) {

	rError := new(ErrorResponse)

	resp, _ := bc.sling.New().Delete(BASE_URI+uri).QueryStruct(params).Receive(rData, rError)

	return bc.checkReponse(resp, rError)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type CloverInt struct {
	value int
}

type CloverFloat struct {
	value float32
}

type CloverBool struct {
	value bool
}

type CloverObj interface {
	ShowValue() string
}

func (s CloverInt) ShowValue() string {
	return fmt.Sprint(s.value)
}

func (s CloverFloat) ShowValue() string {
	return fmt.Sprint(s.value)
}

func (s CloverBool) ShowValue() string {
	return fmt.Sprint(s.value)
}

func main() {
	ci := CloverInt{9}
	c := CloverInt{99}
	op := CloverInt{3}
	bl := CloverBool{true}
	fl := CloverFloat{5.55}
	fmt.Println(ci.ShowValue())
  fmt.Println(Plus(ci, op).ShowValue())
	fmt.Println(Or(bl, CloverBool{false}).ShowValue())
	fmt.Println(Mulf(fl, fl).ShowValue())
	fmt.Println(tes(ci, op).ShowValue())
	println(generated(c, op))
}

func Tes(x, y int) int {
	return x + y
}

// built-in functions
// int
func Plus(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverInt).value
	v2 := o2.(CloverInt).value
	o := CloverInt{v1 + v2}
	return o
}

func Minus(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverInt).value
	v2 := o2.(CloverInt).value
	o := CloverInt{v1 - v2}
	return o
}

func Mul(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverInt).value
	v2 := o2.(CloverInt).value
	o := CloverInt{v1 * v2}
	return o
}

// float
func Plusf(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverFloat).value
	v2 := o2.(CloverFloat).value
	o := CloverFloat{v1 + v2}
	return o
}

func Minusf(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverFloat).value
	v2 := o2.(CloverFloat).value
	o := CloverFloat{v1 - v2}
	return o
}

func Mulf(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverFloat).value
	v2 := o2.(CloverFloat).value
	o := CloverFloat{v1 * v2}
	return o
}

// bool
func And(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverBool).value
	v2 := o2.(CloverBool).value
	o := CloverBool{v1 && v2}
	return o
}

func Or(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverBool).value
	v2 := o2.(CloverBool).value
	o := CloverBool{v1 || v2}
	return o
}

// util
func println(o CloverObj) {
	fmt.Println(o.ShowValue())
}

// user-define

func pluser(x, y CloverObj) CloverObj {
	return Plus(x, y)
}

func tes(x, y interface{}) CloverObj {
	v1 := x.(CloverInt)
	v2 := y.(CloverInt)
	return Plus(v1, v2)
}

func generated(x, y CloverObj) CloverObj {
	return Plus(x, y)
}

// (defn f-name [x y] (+ x y))
// to
// List[Symbol "defn", Symbol "fname", Vector[Symbol "x", Symbol "y"],
//      List[Symbol "+", Symbol "x", Symbol "y"]
// ]
//
<commit_msg>fix base go file<commit_after>package main

import (
	"fmt"
)

type CloverInt struct {
	value int
}

type CloverFloat struct {
	value float32
}

type CloverBool struct {
	value bool
}

type CloverObj interface {
	ShowValue() string
}

func (s CloverInt) ShowValue() string {
	return fmt.Sprint(s.value)
}

func (s CloverFloat) ShowValue() string {
	return fmt.Sprint(s.value)
}

func (s CloverBool) ShowValue() string {
	return fmt.Sprint(s.value)
}

func main() {
	ci := CloverInt{9}
	c := CloverInt{99}
	op := CloverInt{3}
	bl := CloverBool{true}
	fl := CloverFloat{5.55}
	fmt.Println(ci.ShowValue())
  fmt.Println(Plus(ci, op).ShowValue())
	fmt.Println(Or(bl, CloverBool{false}).ShowValue())
	fmt.Println(Mulf(fl, fl).ShowValue())
	fmt.Println(tes(ci, op).ShowValue())
	println(generated(c, op))
}

func Tes(x, y int) int {
	return x + y
}

// built-in functions
// int
func Plus(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverInt).value
	v2 := o2.(CloverInt).value
	o := CloverInt{v1 + v2}
	return o
}

func Minus(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverInt).value
	v2 := o2.(CloverInt).value
	o := CloverInt{v1 - v2}
	return o
}

func Mul(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverInt).value
	v2 := o2.(CloverInt).value
	o := CloverInt{v1 * v2}
	return o
}

// float
func Plusf(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverFloat).value
	v2 := o2.(CloverFloat).value
	o := CloverFloat{v1 + v2}
	return o
}

func Minusf(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverFloat).value
	v2 := o2.(CloverFloat).value
	o := CloverFloat{v1 - v2}
	return o
}

func Mulf(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverFloat).value
	v2 := o2.(CloverFloat).value
	o := CloverFloat{v1 * v2}
	return o
}

// bool
func And(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverBool).value
	v2 := o2.(CloverBool).value
	o := CloverBool{v1 && v2}
	return o
}

func Or(o1, o2 CloverObj) CloverObj {
	v1 := o1.(CloverBool).value
	v2 := o2.(CloverBool).value
	o := CloverBool{v1 || v2}
	return o
}

// util
func println(o CloverObj) {
	fmt.Println(o.ShowValue())
}

// user-define

func pluser(x, y CloverObj) CloverObj {
	return Plus(x, y)
}

func tes(x, y interface{}) CloverObj {
	v1 := x.(CloverInt)
	v2 := y.(CloverInt)
	return Plus(v1, v2)
}

// (defn f-name [x y] (+ x y))
// to
// List[Symbol "defn", Symbol "fname", Vector[Symbol "x", Symbol "y"],
//      List[Symbol "+", Symbol "x", Symbol "y"]
// ]
//
<|endoftext|>


<commit_before>package controllers

import (
	"net/http"
	"strings"

	dbpkg "github.com/wantedly/api-server/db"
	"github.com/wantedly/api-server/helper"
	"github.com/wantedly/api-server/models"
	"github.com/wantedly/api-server/version"

	"github.com/gin-gonic/gin"
)

func GetUsers(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	preloads := c.DefaultQuery("preloads", "")
	fields := helper.ParseFields(c.DefaultQuery("fields", "*"))
	ids := c.DefaultQuery("ids", "")

	pagination := dbpkg.Pagination{}
	db, err := pagination.Paginate(c)

	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db = dbpkg.SetPreloads(preloads, db)

	if ids != "" {
		db = db.Where("id IN (?)", strings.Split(ids, ","))
	}

	var users []models.User
	if err := db.Select("*").Find(&users).Error; err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	// paging
	var index int
	if len(users) < 1 {
		index = 0
	} else {
		index = int(users[len(users)-1].ID)
	}
	pagination.SetHeaderLink(c, index)

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	fieldMap := []map[string]interface{}{}
	for _, user := range users {
		fieldMap = append(fieldMap, helper.FieldToMap(user, fields))
	}

	_, ok := c.GetQuery("pretty")
	if ok {
		c.IndentedJSON(200, fieldMap)
	} else {
		c.JSON(200, fieldMap)
	}
}

func GetUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	id := c.Params.ByName("id")
	preloads := c.DefaultQuery("preloads", "")
	fields := helper.ParseFields(c.DefaultQuery("fields", "*"))

	db := dbpkg.DBInstance(c)
	db = dbpkg.SetPreloads(preloads, db)

	var user models.User
	if err := db.Select("*").First(&user, id).Error; err != nil {
		content := gin.H{"error": "user with id#" + id + " not found"}
		c.JSON(404, content)
		return
	}

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	fieldMap := helper.FieldToMap(user, fields)

	_, ok := c.GetQuery("pretty")
	if ok {
		c.IndentedJSON(200, fieldMap)
	} else {
		c.JSON(200, fieldMap)
	}
}

func CreateUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db := dbpkg.DBInstance(c)
	var user models.User
	c.Bind(&user)
	if db.Create(&user).Error != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	c.JSON(201, user)
}

func UpdateUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db := dbpkg.DBInstance(c)
	id := c.Params.ByName("id")
	var user models.User
	if db.First(&user, id).Error != nil {
		content := gin.H{"error": "user with id#" + id + " not found"}
		c.JSON(404, content)
		return
	}
	c.Bind(&user)
	db.Save(&user)

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	c.JSON(200, user)
}

func DeleteUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db := dbpkg.DBInstance(c)
	id := c.Params.ByName("id")
	var user models.User
	if db.First(&user, id).Error != nil {
		content := gin.H{"error": "user with id#" + id + " not found"}
		c.JSON(404, content)
		return
	}
	db.Delete(&user)

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	c.Writer.WriteHeader(http.StatusNoContent)
}
<commit_msg>Update testdata to narrow fields<commit_after>package controllers

import (
	"net/http"
	"strings"

	dbpkg "github.com/wantedly/api-server/db"
	"github.com/wantedly/api-server/helper"
	"github.com/wantedly/api-server/models"
	"github.com/wantedly/api-server/version"

	"github.com/gin-gonic/gin"
)

func GetUsers(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	ids := c.DefaultQuery("ids", "")
	preloads := c.DefaultQuery("preloads", "")
	fields := helper.ParseFields(c.DefaultQuery("fields", "*"))
	queryFields := helper.QueryFields(models.User{}, fields)

	pagination := dbpkg.Pagination{}
	db, err := pagination.Paginate(c)

	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db = dbpkg.SetPreloads(preloads, db)

	if ids != "" {
		db = db.Where("id IN (?)", strings.Split(ids, ","))
	}

	var users []models.User
	if err := db.Select(queryFields).Find(&users).Error; err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	// paging
	var index int
	if len(users) < 1 {
		index = 0
	} else {
		index = int(users[len(users)-1].ID)
	}
	pagination.SetHeaderLink(c, index)

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	fieldMap := []map[string]interface{}{}
	for _, user := range users {
		fieldMap = append(fieldMap, helper.FieldToMap(user, fields))
	}

	_, ok := c.GetQuery("pretty")
	if ok {
		c.IndentedJSON(200, fieldMap)
	} else {
		c.JSON(200, fieldMap)
	}
}

func GetUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	id := c.Params.ByName("id")
	preloads := c.DefaultQuery("preloads", "")
	fields := helper.ParseFields(c.DefaultQuery("fields", "*"))
	queryFields := helper.QueryFields(models.User{}, fields)

	db := dbpkg.DBInstance(c)
	db = dbpkg.SetPreloads(preloads, db)

	var user models.User
	if err := db.Select(queryFields).First(&user, id).Error; err != nil {
		content := gin.H{"error": "user with id#" + id + " not found"}
		c.JSON(404, content)
		return
	}

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	fieldMap := helper.FieldToMap(user, fields)

	_, ok := c.GetQuery("pretty")
	if ok {
		c.IndentedJSON(200, fieldMap)
	} else {
		c.JSON(200, fieldMap)
	}
}

func CreateUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db := dbpkg.DBInstance(c)
	var user models.User
	c.Bind(&user)
	if db.Create(&user).Error != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	c.JSON(201, user)
}

func UpdateUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db := dbpkg.DBInstance(c)
	id := c.Params.ByName("id")
	var user models.User
	if db.First(&user, id).Error != nil {
		content := gin.H{"error": "user with id#" + id + " not found"}
		c.JSON(404, content)
		return
	}
	c.Bind(&user)
	db.Save(&user)

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	c.JSON(200, user)
}

func DeleteUser(c *gin.Context) {
	ver, err := version.New(c)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	db := dbpkg.DBInstance(c)
	id := c.Params.ByName("id")
	var user models.User
	if db.First(&user, id).Error != nil {
		content := gin.H{"error": "user with id#" + id + " not found"}
		c.JSON(404, content)
		return
	}
	db.Delete(&user)

	if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") {
		// conditional branch by version.
		// 1.0.0 <= this version < 2.0.0 !!
	}

	c.Writer.WriteHeader(http.StatusNoContent)
}
<|endoftext|>


<commit_before>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you 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 gremlingo

import (
	"crypto/tls"
	"encoding/base64"
	"net/http"
	"sync"
	"time"
)

// protocol handles invoking serialization and deserialization, as well as handling the lifecycle of raw data passed to
// and received from the transport layer.
type protocol interface {
	readLoop(resultSets *synchronizedMap, errorCallback func(), log *logHandler)
	write(request *request) error
	close() (err error)
}

const authenticationFailed = uint16(151)

type protocolBase struct {
	protocol

	transporter transporter
}

type gremlinServerWSProtocol struct {
	*protocolBase

	serializer       serializer
	logHandler       *logHandler
	maxContentLength int
	closed           bool
	mutex            sync.Mutex
	wg               *sync.WaitGroup
}

func (protocol *gremlinServerWSProtocol) readLoop(resultSets *synchronizedMap, errorCallback func(), log *logHandler) {
	for {
		// Read from transport layer. If the channel is closed, this will error out and exit.
		msg, err := protocol.transporter.Read()
		protocol.mutex.Lock()
		if protocol.closed {
			protocol.mutex.Unlock()
			protocol.wg.Done()
			return
		}
		protocol.mutex.Unlock()
		if err != nil {
			// Ignore error here, we already got an error on read, cannot do anything with this.
			_ = protocol.transporter.Close()
			log.logf(Error, readLoopError, err.Error())
			readErrorHandler(resultSets, errorCallback, err, log)
			protocol.wg.Done()
			return
		}

		// Deserialize message and unpack.
		resp, err := protocol.serializer.deserializeMessage(msg)
		if err != nil {
			log.logger.Log(Error, err)
			readErrorHandler(resultSets, errorCallback, err, log)
			protocol.wg.Done()
			return
		}

		err = protocol.responseHandler(resultSets, resp, log)
		if err != nil {
			readErrorHandler(resultSets, errorCallback, err, log)
			protocol.wg.Done()
			return
		}
	}
}

// If there is an error, we need to close the ResultSets and then pass the error back.
func readErrorHandler(resultSets *synchronizedMap, errorCallback func(), err error, log *logHandler) {
	log.logf(Error, readLoopError, err.Error())
	resultSets.synchronizedRange(func(_ string, resultSet ResultSet) {
		resultSet.Close()
	})
	errorCallback()
}

func (protocol *gremlinServerWSProtocol) responseHandler(resultSets *synchronizedMap, response response, log *logHandler) error {
	responseID, statusCode, metadata, data := response.responseID, response.responseStatus.code,
		response.responseResult.meta, response.responseResult.data
	responseIDString := responseID.String()
	if resultSets.load(responseIDString) == nil {
		return newError(err0501ResponseHandlerResultSetNotCreatedError)
	}
	if aggregateTo, ok := metadata["aggregateTo"]; ok {
		resultSets.load(responseIDString).setAggregateTo(aggregateTo.(string))
	}

	// Handle status codes appropriately. If status code is http.StatusPartialContent, we need to re-read data.
	if statusCode == http.StatusNoContent {
		resultSets.load(responseIDString).addResult(&Result{make([]interface{}, 0)})
		resultSets.load(responseIDString).Close()
		log.logger.Log(Info, "No content.")
		log.logf(Info, readComplete, responseIDString)
	} else if statusCode == http.StatusOK {
		// Add data and status attributes to the ResultSet.
		resultSets.load(responseIDString).addResult(&Result{data})
		resultSets.load(responseIDString).setStatusAttributes(response.responseStatus.attributes)
		resultSets.load(responseIDString).Close()
		log.logger.Logf(Info, "OK %v===>%v", response.responseStatus, data)
		log.logf(Info, readComplete, responseIDString)
	} else if statusCode == http.StatusPartialContent {
		// Add data to the ResultSet.
		resultSets.load(responseIDString).addResult(&Result{data})
		log.logger.Logf(Info, "Partial %v===>%v", response.responseStatus, data)
	} else if statusCode == http.StatusProxyAuthRequired || statusCode == authenticationFailed {
		// http status code 151 is not defined here, but corresponds with 403, i.e. authentication has failed.
		// Server has requested basic auth.
		authInfo := protocol.transporter.getAuthInfo()
		if authInfo.getUseBasicAuth() {
			username := []byte(authInfo.Username)
			password := []byte(authInfo.Password)

			authBytes := make([]byte, 0)
			authBytes = append(authBytes, 0)
			authBytes = append(authBytes, username...)
			authBytes = append(authBytes, 0)
			authBytes = append(authBytes, password...)
			encoded := base64.StdEncoding.EncodeToString(authBytes)
			request := makeBasicAuthRequest(encoded)
			err := protocol.write(&request)
			if err != nil {
				return err
			}
		} else {
			resultSets.load(responseIDString).Close()
			return newError(err0503ResponseHandlerAuthError, response.responseStatus, response.responseResult)
		}
	} else {
		newError := newError(err0502ResponseHandlerReadLoopError, response.responseStatus, statusCode)
		resultSets.load(responseIDString).setError(newError)
		resultSets.load(responseIDString).Close()
		log.logger.Log(Info, newError.Error())
	}
	return nil
}

func (protocol *gremlinServerWSProtocol) write(request *request) error {
	bytes, err := protocol.serializer.serializeMessage(request)
	if err != nil {
		return err
	}
	return protocol.transporter.Write(bytes)
}

func (protocol *gremlinServerWSProtocol) close() (err error) {
	protocol.mutex.Lock()
	if !protocol.closed {
		err = protocol.transporter.Close()
		protocol.closed = true
	}
	protocol.mutex.Unlock()
	protocol.wg.Wait()
	return
}

func newGremlinServerWSProtocol(handler *logHandler, transporterType TransporterType, url string, authInfo *AuthInfo,
	tlsConfig *tls.Config, keepAliveInterval time.Duration, writeDeadline time.Duration, results *synchronizedMap,
	errorCallback func()) (protocol, error) {
	wg := &sync.WaitGroup{}
	transport, err := getTransportLayer(transporterType, url, authInfo, tlsConfig, keepAliveInterval, writeDeadline)
	if err != nil {
		return nil, err
	}

	gremlinProtocol := &gremlinServerWSProtocol{
		protocolBase:     &protocolBase{transporter: transport},
		serializer:       newGraphBinarySerializer(handler),
		logHandler:       handler,
		maxContentLength: 1,
		closed:           false,
		mutex:            sync.Mutex{},
		wg:               wg,
	}
	err = gremlinProtocol.transporter.Connect()
	if err != nil {
		return nil, err
	}
	wg.Add(1)
	go gremlinProtocol.readLoop(results, errorCallback, handler)
	return gremlinProtocol, nil
}
<commit_msg>Fix issue in log levels ignored in some cases (#70)<commit_after>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you 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 gremlingo

import (
	"crypto/tls"
	"encoding/base64"
	"net/http"
	"sync"
	"time"
)

// protocol handles invoking serialization and deserialization, as well as handling the lifecycle of raw data passed to
// and received from the transport layer.
type protocol interface {
	readLoop(resultSets *synchronizedMap, errorCallback func())
	write(request *request) error
	close() (err error)
}

const authenticationFailed = uint16(151)

type protocolBase struct {
	protocol

	transporter transporter
}

type gremlinServerWSProtocol struct {
	*protocolBase

	serializer       serializer
	logHandler       *logHandler
	maxContentLength int
	closed           bool
	mutex            sync.Mutex
	wg               *sync.WaitGroup
}

func (protocol *gremlinServerWSProtocol) readLoop(resultSets *synchronizedMap, errorCallback func()) {
	for {
		// Read from transport layer. If the channel is closed, this will error out and exit.
		msg, err := protocol.transporter.Read()
		protocol.mutex.Lock()
		if protocol.closed {
			protocol.mutex.Unlock()
			protocol.wg.Done()
			return
		}
		protocol.mutex.Unlock()
		if err != nil {
			// Ignore error here, we already got an error on read, cannot do anything with this.
			_ = protocol.transporter.Close()
			protocol.logHandler.logf(Error, readLoopError, err.Error())
			readErrorHandler(resultSets, errorCallback, err, protocol.logHandler)
			protocol.wg.Done()
			return
		}

		// Deserialize message and unpack.
		resp, err := protocol.serializer.deserializeMessage(msg)
		if err != nil {
			protocol.logHandler.logf(Error, logErrorGeneric, "gremlinServerWSProtocol.readLoop()", err.Error())
			readErrorHandler(resultSets, errorCallback, err, protocol.logHandler)
			protocol.wg.Done()
			return
		}

		err = protocol.responseHandler(resultSets, resp)
		if err != nil {
			readErrorHandler(resultSets, errorCallback, err, protocol.logHandler)
			protocol.wg.Done()
			return
		}
	}
}

// If there is an error, we need to close the ResultSets and then pass the error back.
func readErrorHandler(resultSets *synchronizedMap, errorCallback func(), err error, log *logHandler) {
	log.logf(Error, readLoopError, err.Error())
	resultSets.synchronizedRange(func(_ string, resultSet ResultSet) {
		resultSet.Close()
	})
	errorCallback()
}

func (protocol *gremlinServerWSProtocol) responseHandler(resultSets *synchronizedMap, response response) error {
	responseID, statusCode, metadata, data := response.responseID, response.responseStatus.code,
		response.responseResult.meta, response.responseResult.data
	responseIDString := responseID.String()
	if resultSets.load(responseIDString) == nil {
		return newError(err0501ResponseHandlerResultSetNotCreatedError)
	}
	if aggregateTo, ok := metadata["aggregateTo"]; ok {
		resultSets.load(responseIDString).setAggregateTo(aggregateTo.(string))
	}

	// Handle status codes appropriately. If status code is http.StatusPartialContent, we need to re-read data.
	if statusCode == http.StatusNoContent {
		resultSets.load(responseIDString).addResult(&Result{make([]interface{}, 0)})
		resultSets.load(responseIDString).Close()
		protocol.logHandler.logf(Info, readComplete, responseIDString)
	} else if statusCode == http.StatusOK {
		// Add data and status attributes to the ResultSet.
		resultSets.load(responseIDString).addResult(&Result{data})
		resultSets.load(responseIDString).setStatusAttributes(response.responseStatus.attributes)
		resultSets.load(responseIDString).Close()
		protocol.logHandler.logf(Info, readComplete, responseIDString)
	} else if statusCode == http.StatusPartialContent {
		// Add data to the ResultSet.
		resultSets.load(responseIDString).addResult(&Result{data})
	} else if statusCode == http.StatusProxyAuthRequired || statusCode == authenticationFailed {
		// http status code 151 is not defined here, but corresponds with 403, i.e. authentication has failed.
		// Server has requested basic auth.
		authInfo := protocol.transporter.getAuthInfo()
		if authInfo.getUseBasicAuth() {
			username := []byte(authInfo.Username)
			password := []byte(authInfo.Password)

			authBytes := make([]byte, 0)
			authBytes = append(authBytes, 0)
			authBytes = append(authBytes, username...)
			authBytes = append(authBytes, 0)
			authBytes = append(authBytes, password...)
			encoded := base64.StdEncoding.EncodeToString(authBytes)
			request := makeBasicAuthRequest(encoded)
			err := protocol.write(&request)
			if err != nil {
				return err
			}
		} else {
			resultSets.load(responseIDString).Close()
			return newError(err0503ResponseHandlerAuthError, response.responseStatus, response.responseResult)
		}
	} else {
		newError := newError(err0502ResponseHandlerReadLoopError, response.responseStatus, statusCode)
		resultSets.load(responseIDString).setError(newError)
		resultSets.load(responseIDString).Close()
		protocol.logHandler.logf(Error, logErrorGeneric, "gremlinServerWSProtocol.responseHandler()", newError.Error())
	}
	return nil
}

func (protocol *gremlinServerWSProtocol) write(request *request) error {
	bytes, err := protocol.serializer.serializeMessage(request)
	if err != nil {
		return err
	}
	return protocol.transporter.Write(bytes)
}

func (protocol *gremlinServerWSProtocol) close() (err error) {
	protocol.mutex.Lock()
	if !protocol.closed {
		err = protocol.transporter.Close()
		protocol.closed = true
	}
	protocol.mutex.Unlock()
	protocol.wg.Wait()
	return
}

func newGremlinServerWSProtocol(handler *logHandler, transporterType TransporterType, url string, authInfo *AuthInfo,
	tlsConfig *tls.Config, keepAliveInterval time.Duration, writeDeadline time.Duration, results *synchronizedMap,
	errorCallback func()) (protocol, error) {
	wg := &sync.WaitGroup{}
	transport, err := getTransportLayer(transporterType, url, authInfo, tlsConfig, keepAliveInterval, writeDeadline)
	if err != nil {
		return nil, err
	}

	gremlinProtocol := &gremlinServerWSProtocol{
		protocolBase:     &protocolBase{transporter: transport},
		serializer:       newGraphBinarySerializer(handler),
		logHandler:       handler,
		maxContentLength: 1,
		closed:           false,
		mutex:            sync.Mutex{},
		wg:               wg,
	}
	err = gremlinProtocol.transporter.Connect()
	if err != nil {
		return nil, err
	}
	wg.Add(1)
	go gremlinProtocol.readLoop(results, errorCallback)
	return gremlinProtocol, nil
}
<|endoftext|>


<commit_before>package main

import (
    "bytes"
    "fmt"
    "github.com/coreos/go-etcd/etcd"
    "github.com/miekg/dns"
    "github.com/rcrowley/go-metrics"
    "net"
    "strconv"
    "strings"
    "sync"
    "time"
)

type Resolver struct {
    etcd        *etcd.Client
}

// GetFromStorage looks up a key in etcd and returns a slice of nodes. It supports two storage structures;
//  - File:         /foo/bar/.A -> "value"
//  - Directory:    /foo/bar/.A/0 -> "value-0"
//                  /foo/bar/.A/1 -> "value-1"
func (r *Resolver) GetFromStorage(key string) (nodes []*etcd.Node, err error) {

    counter := metrics.GetOrRegisterCounter("resolver.etcd.query_count", metrics.DefaultRegistry)
    error_counter := metrics.GetOrRegisterCounter("resolver.etcd.query_error_count", metrics.DefaultRegistry)

    counter.Inc(1)
    debugMsg("Querying etcd for " + key)

    response, err := r.etcd.Get(key, false, false)
    if err != nil {
        error_counter.Inc(1)
        return
    }

    if response.Node.Dir == true {
        // TODO(orls): Does this need to convert to a slice?
        nodes = make([]*etcd.Node, len(response.Node.Nodes))
        for i := 0; i < len(response.Node.Nodes); i++ {
            nodes[i] = response.Node.Nodes[i]
        }
    } else {
        nodes = make([]*etcd.Node, 1)
        nodes[0] = response.Node
    }

    return
}

// Authority returns a dns.RR describing the know authority for the given
// domain. It will recurse up the domain structure to find an SOA record that
// matches.
func (r *Resolver) Authority(domain string) ([]dns.RR, *dns.SOA) {
    tree := strings.Split(domain, ".")
    for i, _ := range tree {
        subdomain := strings.Join(tree[i:], ".")

        // Check for an SOA entry
        answers, err := r.LookupAnswersForType(subdomain, dns.TypeSOA)
        if err != nil {
            return make([]dns.RR, 0), &dns.SOA{}
        }

        if len(answers) == 1 {
            soa := answers[0].(*dns.SOA)
            soa.Serial = uint32(time.Now().Truncate(time.Hour).Unix())

            nameservers, err := r.LookupAnswersForType(subdomain, dns.TypeNS)
            if err != nil {
                return make([]dns.RR, 0), &dns.SOA{}
            }

            // Maintain a counter for when we don't have at least two NS records.
            if len(nameservers) <= 1 {
                missing_ns_counter := metrics.GetOrRegisterCounter("resolver.authority.missing_ns", metrics.DefaultRegistry)
                missing_ns_counter.Inc(1)
            }

            return nameservers, soa
        }
    }

    // Maintain a counter for when we don't have an authority for a domain.
    missing_counter := metrics.GetOrRegisterCounter("resolver.authority.missing_soa", metrics.DefaultRegistry)
    missing_counter.Inc(1)

    return make([]dns.RR, 0), &dns.SOA{}
}

// Lookup responds to DNS messages of type Query, with a dns message containing Answers.
// In the event that the query's value+type yields no known records, this falls back to
// querying the given nameservers instead.
func (r *Resolver) Lookup(req *dns.Msg) (msg *dns.Msg) {
    q := req.Question[0]

    msg = new(dns.Msg)
    msg.SetReply(req)
    msg.Authoritative = true
    msg.RecursionAvailable = false // We're a nameserver, no recursion for you!

    wait := sync.WaitGroup{}
    answers := make(chan dns.RR)
    errors := make(chan error)

    if q.Qclass == dns.ClassINET {
        r.AnswerQuestion(answers, errors, q, &wait)
    }

    // Spawn a goroutine to close the channel as soon as all of the things
    // are done. This allows us to ensure we'll wait for all workers to finish
    // but allows us to collect up answers concurrently.
    go func() {
        wait.Wait()

        debugMsg("Finished processing all goroutines, closing channels")
        close(answers)
        close(errors)
    }()

    miss_counter := metrics.GetOrRegisterCounter("resolver.answers.miss", metrics.DefaultRegistry)
    hit_counter := metrics.GetOrRegisterCounter("resolver.answers.hit", metrics.DefaultRegistry)
    error_counter := metrics.GetOrRegisterCounter("resolver.answers.error", metrics.DefaultRegistry)

    // Collect up all of the answers and any errors
    done := 0
    for done < 2 {
        select {
        case rr, ok := <-answers:
            if ok {
                msg.Answer = append(msg.Answer, rr)
            } else {
                done++
            }
        case err, ok := <-errors:
            if ok {
                error_counter.Inc(1)
                // TODO(tarnfeld): Send special TXT records with a server error response code
                debugMsg("Error")
                debugMsg(err)
            } else {
                done++
            }
        }
    }

    // Send the correct authority records
    nameservers, soa := r.Authority(q.Name)
    if len(msg.Answer) == 0 {
        miss_counter.Inc(1)
        msg.SetRcode(req, dns.RcodeNameError)
        if len(soa.Ns) > 0 {
            msg.Ns = []dns.RR{soa}
        }
    } else {
        hit_counter.Inc(1)
        msg.Ns = nameservers
    }

    return
}

// AnswerQuestion takes two channels, one for answers and one for errors. It will answer the
// given question writing the answers as dns.RR structures, and any errors it encounters along
// the way. The function will return immediately, and spawn off a bunch of goroutines
// to do the work, when using this function one should use a WaitGroup to know when all work
// has been completed.
func (r *Resolver) AnswerQuestion(answers chan dns.RR, errors chan error, q dns.Question, wg *sync.WaitGroup) {

    typeStr := strings.ToLower(dns.TypeToString[q.Qtype])
    type_counter := metrics.GetOrRegisterCounter("resolver.answers.type." + typeStr, metrics.DefaultRegistry)
    type_counter.Inc(1)

    debugMsg("Answering question ", q)
    if q.Qtype == dns.TypeANY {
        wg.Add(len(converters))

        for rrType, _ := range converters {
            go func(rrType uint16) {
                defer func() { recover() }()
                defer wg.Done()

                results, err := r.LookupAnswersForType(q.Name, rrType)
                if err != nil {
                    errors <- err
                } else {
                    for _, answer := range results {
                        answers <- answer
                    }
                }
            }(rrType)
        }
    } else if _, ok := converters[q.Qtype]; ok {
        wg.Add(1)

        go func() {
            records, err := r.LookupAnswersForType(q.Name, q.Qtype)
            if err != nil {
                errors <- err
            } else {
                if len(records) > 0 {
                    for _, rr := range records {
                        answers <- rr
                    }
                } else {
                    cnames, err := r.LookupAnswersForType(q.Name, dns.TypeCNAME)
                    if err != nil {
                        errors <- err
                    } else {
                        if len(cnames) > 1 {
                            errors <- &RecordValueError{
                                Message: "Multiple CNAME records is invalid",
                                AttemptedType: dns.TypeCNAME}
                        } else if len(cnames) > 0 {
                            answers <- cnames[0]
                        }
                    }
                }
            }

            wg.Done()
        }()
    }
}

func (r *Resolver) LookupAnswersForType(name string, rrType uint16) (answers []dns.RR, err error) {
    name = strings.ToLower(name)

    typeStr := dns.TypeToString[rrType]
    nodes, err := r.GetFromStorage(nameToKey(name, "/." + typeStr))

    if err != nil {
        if e, ok := err.(*etcd.EtcdError); ok {
            if e.ErrorCode == 100 {
                return answers, nil
            }
        }

        return
    }

    answers = make([]dns.RR, len(nodes))
    for i, node := range nodes {

        // TODO(tarnfeld): TTL 0 - make this configurable
        header := dns.RR_Header{Name: name, Class: dns.ClassINET, Rrtype: rrType, Ttl: 0}
        answer, err := converters[rrType](node, header)

        if err != nil {
            return nil, err
        }

        answers[i] = answer
    }

    return
}

func nameToKey(name string, suffix string) string {
    segments := strings.Split(name, ".")

    var keyBuffer bytes.Buffer
    for i := len(segments) - 1; i >= 0; i-- {
        if len(segments[i]) > 0 {
            keyBuffer.WriteString("/")
            keyBuffer.WriteString(segments[i])
        }
    }

    keyBuffer.WriteString(suffix)
    return keyBuffer.String()
}

// Map of conversion functions that turn individual etcd nodes into dns.RR answers
var converters = map[uint16]func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {

    dns.TypeA: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {

        ip := net.ParseIP(node.Value)
        if ip == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Failed to parse %s as IP Address", node.Value),
                AttemptedType: dns.TypeA,
            }
        } else if ip.To4() == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Value %s isn't an IPv4 address", node.Value),
                AttemptedType: dns.TypeA,
            }
        } else {
            rr = &dns.A{header, ip}
        }

        return
    },

    dns.TypeAAAA: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {

        ip := net.ParseIP(node.Value)
        if ip == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Failed to parse IP Address %s", node.Value),
                AttemptedType: dns.TypeAAAA,
            }
        } else if ip.To16() == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Value %s isn't an IPv6 address", node.Value),
                AttemptedType: dns.TypeA,
            }
        } else {
            rr = &dns.AAAA{header, ip}
        }
        return
    },

    dns.TypeTXT: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        rr = &dns.TXT{header, []string{node.Value}}
        return
    },

    dns.TypeCNAME: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        rr = &dns.CNAME{header, dns.Fqdn(node.Value)}
        return
    },

    dns.TypeNS: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        rr = &dns.NS{header, dns.Fqdn(node.Value)}
        return
    },

    dns.TypeSOA: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        parts := strings.SplitN(node.Value, "\\t", 6)

        refresh, err := strconv.ParseUint(parts[2], 10, 32)
        if err != nil {
            return nil, err
        }

        retry, err := strconv.ParseUint(parts[3], 10, 32)
        if err != nil {
            return nil, err
        }

        expire, err := strconv.ParseUint(parts[4], 10, 32)
        if err != nil {
            return nil, err
        }

        minttl, err := strconv.ParseUint(parts[5], 10, 32)
        if err != nil {
            return nil, err
        }

        rr = &dns.SOA{
            Hdr:     header,
            Ns:      dns.Fqdn(parts[0]),
            Mbox:    dns.Fqdn(parts[1]),
            Refresh: uint32(refresh),
            Retry:   uint32(retry),
            Expire:  uint32(expire),
            Minttl:  uint32(minttl)}

        return
    },
}
<commit_msg>We're not an authority without any SOA<commit_after>package main

import (
    "bytes"
    "fmt"
    "github.com/coreos/go-etcd/etcd"
    "github.com/miekg/dns"
    "github.com/rcrowley/go-metrics"
    "net"
    "strconv"
    "strings"
    "sync"
    "time"
)

type Resolver struct {
    etcd        *etcd.Client
}

// GetFromStorage looks up a key in etcd and returns a slice of nodes. It supports two storage structures;
//  - File:         /foo/bar/.A -> "value"
//  - Directory:    /foo/bar/.A/0 -> "value-0"
//                  /foo/bar/.A/1 -> "value-1"
func (r *Resolver) GetFromStorage(key string) (nodes []*etcd.Node, err error) {

    counter := metrics.GetOrRegisterCounter("resolver.etcd.query_count", metrics.DefaultRegistry)
    error_counter := metrics.GetOrRegisterCounter("resolver.etcd.query_error_count", metrics.DefaultRegistry)

    counter.Inc(1)
    debugMsg("Querying etcd for " + key)

    response, err := r.etcd.Get(key, false, false)
    if err != nil {
        error_counter.Inc(1)
        return
    }

    if response.Node.Dir == true {
        // TODO(orls): Does this need to convert to a slice?
        nodes = make([]*etcd.Node, len(response.Node.Nodes))
        for i := 0; i < len(response.Node.Nodes); i++ {
            nodes[i] = response.Node.Nodes[i]
        }
    } else {
        nodes = make([]*etcd.Node, 1)
        nodes[0] = response.Node
    }

    return
}

// Authority returns a dns.RR describing the know authority for the given
// domain. It will recurse up the domain structure to find an SOA record that
// matches.
func (r *Resolver) Authority(domain string) ([]dns.RR, *dns.SOA) {
    tree := strings.Split(domain, ".")
    for i, _ := range tree {
        subdomain := strings.Join(tree[i:], ".")

        // Check for an SOA entry
        answers, err := r.LookupAnswersForType(subdomain, dns.TypeSOA)
        if err != nil {
            return make([]dns.RR, 0), &dns.SOA{}
        }

        if len(answers) == 1 {
            soa := answers[0].(*dns.SOA)
            soa.Serial = uint32(time.Now().Truncate(time.Hour).Unix())

            nameservers, err := r.LookupAnswersForType(subdomain, dns.TypeNS)
            if err != nil {
                return make([]dns.RR, 0), &dns.SOA{}
            }

            // Maintain a counter for when we don't have at least two NS records.
            if len(nameservers) <= 1 {
                missing_ns_counter := metrics.GetOrRegisterCounter("resolver.authority.missing_ns", metrics.DefaultRegistry)
                missing_ns_counter.Inc(1)
            }

            return nameservers, soa
        }
    }

    // Maintain a counter for when we don't have an authority for a domain.
    missing_counter := metrics.GetOrRegisterCounter("resolver.authority.missing_soa", metrics.DefaultRegistry)
    missing_counter.Inc(1)

    return make([]dns.RR, 0), &dns.SOA{}
}

// Lookup responds to DNS messages of type Query, with a dns message containing Answers.
// In the event that the query's value+type yields no known records, this falls back to
// querying the given nameservers instead.
func (r *Resolver) Lookup(req *dns.Msg) (msg *dns.Msg) {
    q := req.Question[0]

    msg = new(dns.Msg)
    msg.SetReply(req)
    msg.Authoritative = true
    msg.RecursionAvailable = false // We're a nameserver, no recursion for you!

    wait := sync.WaitGroup{}
    answers := make(chan dns.RR)
    errors := make(chan error)

    if q.Qclass == dns.ClassINET {
        r.AnswerQuestion(answers, errors, q, &wait)
    }

    // Spawn a goroutine to close the channel as soon as all of the things
    // are done. This allows us to ensure we'll wait for all workers to finish
    // but allows us to collect up answers concurrently.
    go func() {
        wait.Wait()

        debugMsg("Finished processing all goroutines, closing channels")
        close(answers)
        close(errors)
    }()

    miss_counter := metrics.GetOrRegisterCounter("resolver.answers.miss", metrics.DefaultRegistry)
    hit_counter := metrics.GetOrRegisterCounter("resolver.answers.hit", metrics.DefaultRegistry)
    error_counter := metrics.GetOrRegisterCounter("resolver.answers.error", metrics.DefaultRegistry)

    // Collect up all of the answers and any errors
    done := 0
    for done < 2 {
        select {
        case rr, ok := <-answers:
            if ok {
                msg.Answer = append(msg.Answer, rr)
            } else {
                done++
            }
        case err, ok := <-errors:
            if ok {
                error_counter.Inc(1)
                // TODO(tarnfeld): Send special TXT records with a server error response code
                debugMsg("Error")
                debugMsg(err)
            } else {
                done++
            }
        }
    }

    // Send the correct authority records
    nameservers, soa := r.Authority(q.Name)
    if len(msg.Answer) == 0 {
        miss_counter.Inc(1)
        msg.SetRcode(req, dns.RcodeNameError)
        if len(soa.Ns) > 0 {
            msg.Ns = []dns.RR{soa}
        } else {
            msg.Authoritative = false // No SOA? We're not authoritative
        }
    } else {
        hit_counter.Inc(1)
        msg.Ns = nameservers
    }

    return
}

// AnswerQuestion takes two channels, one for answers and one for errors. It will answer the
// given question writing the answers as dns.RR structures, and any errors it encounters along
// the way. The function will return immediately, and spawn off a bunch of goroutines
// to do the work, when using this function one should use a WaitGroup to know when all work
// has been completed.
func (r *Resolver) AnswerQuestion(answers chan dns.RR, errors chan error, q dns.Question, wg *sync.WaitGroup) {

    typeStr := strings.ToLower(dns.TypeToString[q.Qtype])
    type_counter := metrics.GetOrRegisterCounter("resolver.answers.type." + typeStr, metrics.DefaultRegistry)
    type_counter.Inc(1)

    debugMsg("Answering question ", q)
    if q.Qtype == dns.TypeANY {
        wg.Add(len(converters))

        for rrType, _ := range converters {
            go func(rrType uint16) {
                defer func() { recover() }()
                defer wg.Done()

                results, err := r.LookupAnswersForType(q.Name, rrType)
                if err != nil {
                    errors <- err
                } else {
                    for _, answer := range results {
                        answers <- answer
                    }
                }
            }(rrType)
        }
    } else if _, ok := converters[q.Qtype]; ok {
        wg.Add(1)

        go func() {
            records, err := r.LookupAnswersForType(q.Name, q.Qtype)
            if err != nil {
                errors <- err
            } else {
                if len(records) > 0 {
                    for _, rr := range records {
                        answers <- rr
                    }
                } else {
                    cnames, err := r.LookupAnswersForType(q.Name, dns.TypeCNAME)
                    if err != nil {
                        errors <- err
                    } else {
                        if len(cnames) > 1 {
                            errors <- &RecordValueError{
                                Message: "Multiple CNAME records is invalid",
                                AttemptedType: dns.TypeCNAME}
                        } else if len(cnames) > 0 {
                            answers <- cnames[0]
                        }
                    }
                }
            }

            wg.Done()
        }()
    }
}

func (r *Resolver) LookupAnswersForType(name string, rrType uint16) (answers []dns.RR, err error) {
    name = strings.ToLower(name)

    typeStr := dns.TypeToString[rrType]
    nodes, err := r.GetFromStorage(nameToKey(name, "/." + typeStr))

    if err != nil {
        if e, ok := err.(*etcd.EtcdError); ok {
            if e.ErrorCode == 100 {
                return answers, nil
            }
        }

        return
    }

    answers = make([]dns.RR, len(nodes))
    for i, node := range nodes {

        // TODO(tarnfeld): TTL 0 - make this configurable
        header := dns.RR_Header{Name: name, Class: dns.ClassINET, Rrtype: rrType, Ttl: 0}
        answer, err := converters[rrType](node, header)

        if err != nil {
            return nil, err
        }

        answers[i] = answer
    }

    return
}

func nameToKey(name string, suffix string) string {
    segments := strings.Split(name, ".")

    var keyBuffer bytes.Buffer
    for i := len(segments) - 1; i >= 0; i-- {
        if len(segments[i]) > 0 {
            keyBuffer.WriteString("/")
            keyBuffer.WriteString(segments[i])
        }
    }

    keyBuffer.WriteString(suffix)
    return keyBuffer.String()
}

// Map of conversion functions that turn individual etcd nodes into dns.RR answers
var converters = map[uint16]func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {

    dns.TypeA: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {

        ip := net.ParseIP(node.Value)
        if ip == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Failed to parse %s as IP Address", node.Value),
                AttemptedType: dns.TypeA,
            }
        } else if ip.To4() == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Value %s isn't an IPv4 address", node.Value),
                AttemptedType: dns.TypeA,
            }
        } else {
            rr = &dns.A{header, ip}
        }

        return
    },

    dns.TypeAAAA: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {

        ip := net.ParseIP(node.Value)
        if ip == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Failed to parse IP Address %s", node.Value),
                AttemptedType: dns.TypeAAAA,
            }
        } else if ip.To16() == nil {
            err = &NodeConversionError{
                Node: node,
                Message: fmt.Sprintf("Value %s isn't an IPv6 address", node.Value),
                AttemptedType: dns.TypeA,
            }
        } else {
            rr = &dns.AAAA{header, ip}
        }
        return
    },

    dns.TypeTXT: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        rr = &dns.TXT{header, []string{node.Value}}
        return
    },

    dns.TypeCNAME: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        rr = &dns.CNAME{header, dns.Fqdn(node.Value)}
        return
    },

    dns.TypeNS: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        rr = &dns.NS{header, dns.Fqdn(node.Value)}
        return
    },

    dns.TypeSOA: func (node *etcd.Node, header dns.RR_Header) (rr dns.RR, err error) {
        parts := strings.SplitN(node.Value, "\\t", 6)

        refresh, err := strconv.ParseUint(parts[2], 10, 32)
        if err != nil {
            return nil, err
        }

        retry, err := strconv.ParseUint(parts[3], 10, 32)
        if err != nil {
            return nil, err
        }

        expire, err := strconv.ParseUint(parts[4], 10, 32)
        if err != nil {
            return nil, err
        }

        minttl, err := strconv.ParseUint(parts[5], 10, 32)
        if err != nil {
            return nil, err
        }

        rr = &dns.SOA{
            Hdr:     header,
            Ns:      dns.Fqdn(parts[0]),
            Mbox:    dns.Fqdn(parts[1]),
            Refresh: uint32(refresh),
            Retry:   uint32(retry),
            Expire:  uint32(expire),
            Minttl:  uint32(minttl)}

        return
    },
}
<|endoftext|>


<commit_before>package grab

import (
	"bytes"
	"encoding/hex"
	"io"
	"net/http"
	"os"
	"sync/atomic"
	"time"
)

// Response represents the response from an HTTP transfer request.
//
// For asyncronous operations, the Response also provides context for the file
// transfer while it is process.
type Response struct {
	// The Request that was sent to obtain this Response.
	Request *Request

	// HTTPResponse specifies the HTTP response received from the remote server.
	// The response's Body is nil (having already been consumed).
	HTTPResponse *http.Response

	// Filename specifies the path where the file transfer is stored in local
	// storage.
	Filename string

	// Size specifies the total size of the file transfer.
	Size uint64

	// Error specifies any error that may have occurred during the file transfer
	// that created this response.
	Error error

	// Start specifies the time at which the file transfer started.
	Start time.Time

	// End specifies the time at which the file transfer completed.
	End time.Time

	// DidResume specifies that the file transfer resumed a previously
	// incomplete transfer.
	DidResume bool

	// writer is the file handle used to write the downloaded file to local
	// storage
	writer io.WriteCloser

	// bytesTransferred specifies the number of bytes which have already been
	// transferred and should only be accessed atomically.
	bytesTransferred uint64

	// doneFlag is incremented once the transfer is finalized, either
	// successfully or with errors.
	doneFlag int32
}

// IsComplete indicates whether the Response transfer context has completed with
// either a success or failure.
func (c *Response) IsComplete() bool {
	return atomic.LoadInt32(&c.doneFlag) > 0
}

// BytesTransferred atomically returns the number of bytes which have already been
// downloaded.
func (c *Response) BytesTransferred() uint64 {
	return atomic.LoadUint64(&c.bytesTransferred)
}

// Progress returns the ratio of bytes which have already been downloaded over
// the total content length as a fraction of 1.00.
func (c *Response) Progress() float64 {
	if c.Size == 0 {
		return 0
	}

	return float64(atomic.LoadUint64(&c.bytesTransferred)) / float64(c.Size)
}

// copy transfers content for a HTTP connection established via Client.do()
func (c *Response) copy() error {
	// close writer when finished
	defer c.writer.Close()

	// download and update progress
	var buffer [4096]byte
	complete := false
	for complete == false {
		// read HTTP stream
		n, err := c.HTTPResponse.Body.Read(buffer[:])
		if err != nil && err != io.EOF {
			return c.close(err)
		}

		// write to file
		if _, werr := c.writer.Write(buffer[:n]); werr != nil {
			return c.close(werr)
		}

		// increment progress
		atomic.AddUint64(&c.bytesTransferred, uint64(n))

		// break when finished
		if err == io.EOF {
			// download is ready for checksum validation
			c.writer.Close()
			complete = true
		}
	}

	// validate checksum
	if complete && c.Request.Hash != nil && c.Request.Checksum != nil {
		// open downloaded file
		if f, err := os.Open(c.Filename); err != nil {
			return c.close(err)
		} else {
			defer f.Close()

			// hash file
			if _, err := io.Copy(c.Request.Hash, f); err != nil {
				return c.close(err)
			}

			// compare checksum
			sum := c.Request.Hash.Sum(nil)
			if !bytes.Equal(sum, c.Request.Checksum) {
				// delete file
				if c.Request.RemoveOnError {
					f.Close()
					os.Remove(c.Filename)
				}

				return c.close(newGrabError(errChecksumMismatch, "Checksum mismatch: %v", hex.EncodeToString(sum)))
			}
		}
	}

	return c.close(nil)
}

// close finalizes the response context
func (c *Response) close(err error) error {
	// close any file handle
	if c.writer != nil {
		c.writer.Close()
		c.writer = nil
	}

	// set result error (if any)
	c.Error = err

	// stop time
	c.End = time.Now()

	// set done flag
	atomic.AddInt32(&c.doneFlag, 1)

	// notify
	if c.Request.notifyOnCloseInternal != nil {
		c.Request.notifyOnCloseInternal <- c
	}

	if c.Request.NotifyOnClose != nil {
		c.Request.NotifyOnClose <- c
	}

	// pass error back to caller
	return err
}
<commit_msg>Added Response.Duration() and Response.AverageBytesPerSecond()<commit_after>package grab

import (
	"bytes"
	"encoding/hex"
	"io"
	"net/http"
	"os"
	"sync/atomic"
	"time"
)

// Response represents the response from an HTTP transfer request.
//
// For asyncronous operations, the Response also provides context for the file
// transfer while it is process.
type Response struct {
	// The Request that was sent to obtain this Response.
	Request *Request

	// HTTPResponse specifies the HTTP response received from the remote server.
	// The response's Body is nil (having already been consumed).
	HTTPResponse *http.Response

	// Filename specifies the path where the file transfer is stored in local
	// storage.
	Filename string

	// Size specifies the total size of the file transfer.
	Size uint64

	// Error specifies any error that may have occurred during the file transfer
	// that created this response.
	Error error

	// Start specifies the time at which the file transfer started.
	Start time.Time

	// End specifies the time at which the file transfer completed.
	End time.Time

	// DidResume specifies that the file transfer resumed a previously
	// incomplete transfer.
	DidResume bool

	// writer is the file handle used to write the downloaded file to local
	// storage
	writer io.WriteCloser

	// bytesTransferred specifies the number of bytes which have already been
	// transferred and should only be accessed atomically.
	bytesTransferred uint64

	// doneFlag is incremented once the transfer is finalized, either
	// successfully or with errors.
	doneFlag int32
}

// IsComplete indicates whether the Response transfer context has completed with
// either a success or failure.
func (c *Response) IsComplete() bool {
	return atomic.LoadInt32(&c.doneFlag) > 0
}

// BytesTransferred atomically returns the number of bytes which have already been
// downloaded.
func (c *Response) BytesTransferred() uint64 {
	return atomic.LoadUint64(&c.bytesTransferred)
}

// Progress returns the ratio of bytes which have already been downloaded over
// the total content length as a fraction of 1.00.
func (c *Response) Progress() float64 {
	if c.Size == 0 {
		return 0
	}

	return float64(atomic.LoadUint64(&c.bytesTransferred)) / float64(c.Size)
}

// Duration returns the duration of a file transfer. If the transfer is in
// process, the duration will be between now and the start of the transfer. If
// the transfer is complete, the duration will be between the start and end of
// the completed transfer process.
func (c *Response) Duration() time.Duration {
	if c.IsComplete() {
		return c.End.Sub(c.Start)
	} else {
		return time.Now().Sub(c.Start)
	}
}

// AverageBytesPerSecond returns the average bytes transferred per second over
// the duration of the file transfer.
func (c *Response) AverageBytesPerSecond() float64 {
	return float64(c.BytesTransferred()) / c.Duration().Seconds()
}

// copy transfers content for a HTTP connection established via Client.do()
func (c *Response) copy() error {
	// close writer when finished
	defer c.writer.Close()

	// download and update progress
	var buffer [4096]byte
	complete := false
	for complete == false {
		// read HTTP stream
		n, err := c.HTTPResponse.Body.Read(buffer[:])
		if err != nil && err != io.EOF {
			return c.close(err)
		}

		// write to file
		if _, werr := c.writer.Write(buffer[:n]); werr != nil {
			return c.close(werr)
		}

		// increment progress
		atomic.AddUint64(&c.bytesTransferred, uint64(n))

		// break when finished
		if err == io.EOF {
			// download is ready for checksum validation
			c.writer.Close()
			complete = true
		}
	}

	// validate checksum
	if complete && c.Request.Hash != nil && c.Request.Checksum != nil {
		// open downloaded file
		if f, err := os.Open(c.Filename); err != nil {
			return c.close(err)
		} else {
			defer f.Close()

			// hash file
			if _, err := io.Copy(c.Request.Hash, f); err != nil {
				return c.close(err)
			}

			// compare checksum
			sum := c.Request.Hash.Sum(nil)
			if !bytes.Equal(sum, c.Request.Checksum) {
				// delete file
				if c.Request.RemoveOnError {
					f.Close()
					os.Remove(c.Filename)
				}

				return c.close(newGrabError(errChecksumMismatch, "Checksum mismatch: %v", hex.EncodeToString(sum)))
			}
		}
	}

	return c.close(nil)
}

// close finalizes the response context
func (c *Response) close(err error) error {
	// close any file handle
	if c.writer != nil {
		c.writer.Close()
		c.writer = nil
	}

	// set result error (if any)
	c.Error = err

	// stop time
	c.End = time.Now()

	// set done flag
	atomic.AddInt32(&c.doneFlag, 1)

	// notify
	if c.Request.notifyOnCloseInternal != nil {
		c.Request.notifyOnCloseInternal <- c
	}

	if c.Request.NotifyOnClose != nil {
		c.Request.NotifyOnClose <- c
	}

	// pass error back to caller
	return err
}
<|endoftext|>


<commit_before>package messenger

import (
	"bytes"
	"encoding/json"
	"fmt"
	"image"
	"image/jpeg"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"
)

// AttachmentType is attachment type.
type AttachmentType string
type MessagingType string
type TopElementStyle string
type ImageAspectRatio string

const (
	// SendMessageURL is API endpoint for sending messages.
	SendMessageURL = "https://graph.facebook.com/v2.11/me/messages"

	// ImageAttachment is image attachment type.
	ImageAttachment AttachmentType = "image"
	// AudioAttachment is audio attachment type.
	AudioAttachment AttachmentType = "audio"
	// VideoAttachment is video attachment type.
	VideoAttachment AttachmentType = "video"
	// FileAttachment is file attachment type.
	FileAttachment AttachmentType = "file"

	// ResponseType is response messaging type
	ResponseType MessagingType = "RESPONSE"
	// UpdateType is update messaging type
	UpdateType MessagingType = "UPDATE"
	// MessageTagType is message_tag messaging type
	MessageTagType MessagingType = "MESSAGE_TAG"
	// NonPromotionalSubscriptionType is NON_PROMOTIONAL_SUBSCRIPTION messaging type
	NonPromotionalSubscriptionType MessagingType = "NON_PROMOTIONAL_SUBSCRIPTION"

	// TopElementStyle is compact.
	CompactTopElementStyle TopElementStyle = "compact"
	// TopElementStyle is large.
	LargeTopElementStyle TopElementStyle = "large"

	// ImageAspectRatio is horizontal (1.91:1). Default.
	HorizontalImageAspectRatio ImageAspectRatio = "horizontal"
	// ImageAspectRatio is square.
	SquareImageAspectRatio ImageAspectRatio = "square"
)

// QueryResponse is the response sent back by Facebook when setting up things
// like greetings or call-to-actions
type QueryResponse struct {
	Error  *QueryError `json:"error,omitempty"`
	Result string      `json:"result,omitempty"`
}

// QueryError is representing an error sent back by Facebook
type QueryError struct {
	Message   string `json:"message"`
	Type      string `json:"type"`
	Code      int    `json:"code"`
	FBTraceID string `json:"fbtrace_id"`
}

func checkFacebookError(r io.Reader) error {
	var err error

	qr := QueryResponse{}
	err = json.NewDecoder(r).Decode(&qr)
	if qr.Error != nil {
		err = fmt.Errorf("Facebook error : %s", qr.Error.Message)
		return err
	}

	return nil
}

// Response is used for responding to events with messages.
type Response struct {
	token string
	to    Recipient
}

// SetToken is for using DispatchMessage from outside.
func (r *Response) SetToken(token string) {
	r.token = token
}

// Text sends a textual message.
func (r *Response) Text(message string, messagingType MessagingType, tags ...string) error {
	return r.TextWithReplies(message, nil, messagingType, tags...)
}

// TextWithReplies sends a textual message with some replies
// messagingType should be one of the following: "RESPONSE","UPDATE","MESSAGE_TAG","NON_PROMOTIONAL_SUBSCRIPTION"
// only supply tags when messagingType == "MESSAGE_TAG" (see https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types for more)
func (r *Response) TextWithReplies(message string, replies []QuickReply, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: MessageData{
			Text:         message,
			Attachment:   nil,
			QuickReplies: replies,
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// AttachmentWithReplies sends a attachment message with some replies
func (r *Response) AttachmentWithReplies(attachment *StructuredMessageAttachment, replies []QuickReply, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: MessageData{
			Attachment:   attachment,
			QuickReplies: replies,
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// Image sends an image.
func (r *Response) Image(im image.Image) error {
	imageBytes := new(bytes.Buffer)
	err := jpeg.Encode(imageBytes, im, nil)
	if err != nil {
		return err
	}

	return r.AttachmentData(ImageAttachment, "meme.jpg", imageBytes)
}

// Attachment sends an image, sound, video or a regular file to a chat.
func (r *Response) Attachment(dataType AttachmentType, url string, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendStructuredMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: StructuredMessageData{
			Attachment: StructuredMessageAttachment{
				Type: dataType,
				Payload: StructuredMessagePayload{
					Url: url,
				},
			},
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// copied from multipart package
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

// copied from multipart package
func escapeQuotes(s string) string {
	return quoteEscaper.Replace(s)
}

// copied from multipart package with slight changes due to fixed content-type there
func createFormFile(filename string, w *multipart.Writer, contentType string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="filedata"; filename="%s"`,
			escapeQuotes(filename)))
	h.Set("Content-Type", contentType)
	return w.CreatePart(h)
}

// AttachmentData sends an image, sound, video or a regular file to a chat via an io.Reader.
func (r *Response) AttachmentData(dataType AttachmentType, filename string, filedata io.Reader) error {

	filedataBytes, err := ioutil.ReadAll(filedata)
	if err != nil {
		return err
	}
	contentType := http.DetectContentType(filedataBytes[:512])
	fmt.Println("Content-type detected:", contentType)

	var body bytes.Buffer
	multipartWriter := multipart.NewWriter(&body)
	data, err := createFormFile(filename, multipartWriter, contentType)
	if err != nil {
		return err
	}

	_, err = bytes.NewBuffer(filedataBytes).WriteTo(data)
	if err != nil {
		return err
	}

	multipartWriter.WriteField("recipient", fmt.Sprintf(`{"id":"%v"}`, r.to.ID))
	multipartWriter.WriteField("message", fmt.Sprintf(`{"attachment":{"type":"%v", "payload":{}}}`, dataType))

	req, err := http.NewRequest("POST", SendMessageURL, &body)
	if err != nil {
		return err
	}

	req.URL.RawQuery = "access_token=" + r.token

	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	return checkFacebookError(resp.Body)
}

// ButtonTemplate sends a message with the main contents being button elements
func (r *Response) ButtonTemplate(text string, buttons *[]StructuredMessageButton, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendStructuredMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: StructuredMessageData{
			Attachment: StructuredMessageAttachment{
				Type: "template",
				Payload: StructuredMessagePayload{
					TemplateType: "button",
					Text:         text,
					Buttons:      buttons,
					Elements:     nil,
				},
			},
		},
		Tag: tag,
	}

	return r.DispatchMessage(&m)
}

// GenericTemplate is a message which allows for structural elements to be sent
func (r *Response) GenericTemplate(elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendStructuredMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: StructuredMessageData{
			Attachment: StructuredMessageAttachment{
				Type: "template",
				Payload: StructuredMessagePayload{
					TemplateType: "generic",
					Buttons:      nil,
					Elements:     elements,
				},
			},
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// SenderAction sends a info about sender action
func (r *Response) SenderAction(action string) error {
	m := SendSenderAction{
		Recipient:    r.to,
		SenderAction: action,
	}
	return r.DispatchMessage(&m)
}

// DispatchMessage posts the message to messenger, return the error if there's any
func (r *Response) DispatchMessage(m interface{}) error {
	data, err := json.Marshal(m)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	req.URL.RawQuery = "access_token=" + r.token

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == 200 {
		return nil
	}
	return checkFacebookError(resp.Body)
}

// SendMessage is the information sent in an API request to Facebook.
type SendMessage struct {
	MessagingType MessagingType `json:"messaging_type"`
	Recipient     Recipient     `json:"recipient"`
	Message       MessageData   `json:"message"`
	Tag           string        `json:"tag,omitempty"`
}

// MessageData is a message consisting of text or an attachment, with an additional selection of optional quick replies.
type MessageData struct {
	Text         string                       `json:"text,omitempty"`
	Attachment   *StructuredMessageAttachment `json:"attachment,omitempty"`
	QuickReplies []QuickReply                 `json:"quick_replies,omitempty"`
}

// SendStructuredMessage is a structured message template.
type SendStructuredMessage struct {
	MessagingType MessagingType         `json:"messaging_type"`
	Recipient     Recipient             `json:"recipient"`
	Message       StructuredMessageData `json:"message"`
	Tag           string                `json:"tag,omitempty"`
}

// StructuredMessageData is an attachment sent with a structured message.
type StructuredMessageData struct {
	Attachment StructuredMessageAttachment `json:"attachment"`
}

// StructuredMessageAttachment is the attachment of a structured message.
type StructuredMessageAttachment struct {
	// Type must be template
	Title string         `json:"title,omitempty"`
	URL   string         `json:"url,omitempty"`
	Type  AttachmentType `json:"type"`
	// Payload is the information for the file which was sent in the attachment.
	Payload StructuredMessagePayload `json:"payload"`
}

// StructuredMessagePayload is the actual payload of an attachment
type StructuredMessagePayload struct {
	// TemplateType must be button, generic or receipt
	TemplateType     string                      `json:"template_type,omitempty"`
	TopElementStyle  TopElementStyle             `json:"top_element_style,omitempty"`
	Text             string                      `json:"text,omitempty"`
	ImageAspectRatio ImageAspectRatio            `json:"image_aspect_ratio,omitempty"`
	Sharable         bool                        `json:"sharable,omitempty"`
	Elements         *[]StructuredMessageElement `json:"elements,omitempty"`
	Buttons          *[]StructuredMessageButton  `json:"buttons,omitempty"`
	Url              string                      `json:"url,omitempty"`
}

// StructuredMessageElement is a response containing structural elements
type StructuredMessageElement struct {
	Title         string                    `json:"title"`
	ImageURL      string                    `json:"image_url"`
	ItemURL       string                    `json:"item_url,omitempty"`
	Subtitle      string                    `json:"subtitle"`
	DefaultAction *DefaultAction            `json:"default_action,omitempty"`
	Buttons       []StructuredMessageButton `json:"buttons"`
}

// DefaultAction is a response containing default action properties
type DefaultAction struct {
	Type                string `json:"type"`
	URL                 string `json:"url,omitempty"`
	WebviewHeightRatio  string `json:"webview_height_ratio,omitempty"`
	MessengerExtensions bool   `json:"messenger_extensions,omitempty"`
	FallbackURL         string `json:"fallback_url,omitempty"`
	WebviewShareButton  string `json:"webview_share_button,omitempty"`
}

// StructuredMessageButton is a response containing buttons
type StructuredMessageButton struct {
	Type                string `json:"type"`
	URL                 string `json:"url,omitempty"`
	Title               string `json:"title,omitempty"`
	Payload             string `json:"payload,omitempty"`
	WebviewHeightRatio  string `json:"webview_height_ratio,omitempty"`
	MessengerExtensions bool   `json:"messenger_extensions,omitempty"`
	FallbackURL         string `json:"fallback_url,omitempty"`
	WebviewShareButton  string `json:"webview_share_button,omitempty"`
}

// SendSenderAction is the information about sender action
type SendSenderAction struct {
	Recipient    Recipient `json:"recipient"`
	SenderAction string    `json:"sender_action"`
}
<commit_msg>add list template<commit_after>package messenger

import (
	"bytes"
	"encoding/json"
	"fmt"
	"image"
	"image/jpeg"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"
)

// AttachmentType is attachment type.
type AttachmentType string
type MessagingType string
type TopElementStyle string
type ImageAspectRatio string

const (
	// SendMessageURL is API endpoint for sending messages.
	SendMessageURL = "https://graph.facebook.com/v2.11/me/messages"

	// ImageAttachment is image attachment type.
	ImageAttachment AttachmentType = "image"
	// AudioAttachment is audio attachment type.
	AudioAttachment AttachmentType = "audio"
	// VideoAttachment is video attachment type.
	VideoAttachment AttachmentType = "video"
	// FileAttachment is file attachment type.
	FileAttachment AttachmentType = "file"

	// ResponseType is response messaging type
	ResponseType MessagingType = "RESPONSE"
	// UpdateType is update messaging type
	UpdateType MessagingType = "UPDATE"
	// MessageTagType is message_tag messaging type
	MessageTagType MessagingType = "MESSAGE_TAG"
	// NonPromotionalSubscriptionType is NON_PROMOTIONAL_SUBSCRIPTION messaging type
	NonPromotionalSubscriptionType MessagingType = "NON_PROMOTIONAL_SUBSCRIPTION"

	// TopElementStyle is compact.
	CompactTopElementStyle TopElementStyle = "compact"
	// TopElementStyle is large.
	LargeTopElementStyle TopElementStyle = "large"

	// ImageAspectRatio is horizontal (1.91:1). Default.
	HorizontalImageAspectRatio ImageAspectRatio = "horizontal"
	// ImageAspectRatio is square.
	SquareImageAspectRatio ImageAspectRatio = "square"
)

// QueryResponse is the response sent back by Facebook when setting up things
// like greetings or call-to-actions
type QueryResponse struct {
	Error  *QueryError `json:"error,omitempty"`
	Result string      `json:"result,omitempty"`
}

// QueryError is representing an error sent back by Facebook
type QueryError struct {
	Message   string `json:"message"`
	Type      string `json:"type"`
	Code      int    `json:"code"`
	FBTraceID string `json:"fbtrace_id"`
}

func checkFacebookError(r io.Reader) error {
	var err error

	qr := QueryResponse{}
	err = json.NewDecoder(r).Decode(&qr)
	if qr.Error != nil {
		err = fmt.Errorf("Facebook error : %s", qr.Error.Message)
		return err
	}

	return nil
}

// Response is used for responding to events with messages.
type Response struct {
	token string
	to    Recipient
}

// SetToken is for using DispatchMessage from outside.
func (r *Response) SetToken(token string) {
	r.token = token
}

// Text sends a textual message.
func (r *Response) Text(message string, messagingType MessagingType, tags ...string) error {
	return r.TextWithReplies(message, nil, messagingType, tags...)
}

// TextWithReplies sends a textual message with some replies
// messagingType should be one of the following: "RESPONSE","UPDATE","MESSAGE_TAG","NON_PROMOTIONAL_SUBSCRIPTION"
// only supply tags when messagingType == "MESSAGE_TAG" (see https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types for more)
func (r *Response) TextWithReplies(message string, replies []QuickReply, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: MessageData{
			Text:         message,
			Attachment:   nil,
			QuickReplies: replies,
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// AttachmentWithReplies sends a attachment message with some replies
func (r *Response) AttachmentWithReplies(attachment *StructuredMessageAttachment, replies []QuickReply, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: MessageData{
			Attachment:   attachment,
			QuickReplies: replies,
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// Image sends an image.
func (r *Response) Image(im image.Image) error {
	imageBytes := new(bytes.Buffer)
	err := jpeg.Encode(imageBytes, im, nil)
	if err != nil {
		return err
	}

	return r.AttachmentData(ImageAttachment, "meme.jpg", imageBytes)
}

// Attachment sends an image, sound, video or a regular file to a chat.
func (r *Response) Attachment(dataType AttachmentType, url string, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendStructuredMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: StructuredMessageData{
			Attachment: StructuredMessageAttachment{
				Type: dataType,
				Payload: StructuredMessagePayload{
					Url: url,
				},
			},
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// copied from multipart package
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

// copied from multipart package
func escapeQuotes(s string) string {
	return quoteEscaper.Replace(s)
}

// copied from multipart package with slight changes due to fixed content-type there
func createFormFile(filename string, w *multipart.Writer, contentType string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="filedata"; filename="%s"`,
			escapeQuotes(filename)))
	h.Set("Content-Type", contentType)
	return w.CreatePart(h)
}

// AttachmentData sends an image, sound, video or a regular file to a chat via an io.Reader.
func (r *Response) AttachmentData(dataType AttachmentType, filename string, filedata io.Reader) error {

	filedataBytes, err := ioutil.ReadAll(filedata)
	if err != nil {
		return err
	}
	contentType := http.DetectContentType(filedataBytes[:512])
	fmt.Println("Content-type detected:", contentType)

	var body bytes.Buffer
	multipartWriter := multipart.NewWriter(&body)
	data, err := createFormFile(filename, multipartWriter, contentType)
	if err != nil {
		return err
	}

	_, err = bytes.NewBuffer(filedataBytes).WriteTo(data)
	if err != nil {
		return err
	}

	multipartWriter.WriteField("recipient", fmt.Sprintf(`{"id":"%v"}`, r.to.ID))
	multipartWriter.WriteField("message", fmt.Sprintf(`{"attachment":{"type":"%v", "payload":{}}}`, dataType))

	req, err := http.NewRequest("POST", SendMessageURL, &body)
	if err != nil {
		return err
	}

	req.URL.RawQuery = "access_token=" + r.token

	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	return checkFacebookError(resp.Body)
}

// ButtonTemplate sends a message with the main contents being button elements
func (r *Response) ButtonTemplate(text string, buttons *[]StructuredMessageButton, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendStructuredMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: StructuredMessageData{
			Attachment: StructuredMessageAttachment{
				Type: "template",
				Payload: StructuredMessagePayload{
					TemplateType: "button",
					Text:         text,
					Buttons:      buttons,
					Elements:     nil,
				},
			},
		},
		Tag: tag,
	}

	return r.DispatchMessage(&m)
}

// GenericTemplate is a message which allows for structural elements to be sent
func (r *Response) GenericTemplate(elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendStructuredMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: StructuredMessageData{
			Attachment: StructuredMessageAttachment{
				Type: "template",
				Payload: StructuredMessagePayload{
					TemplateType: "generic",
					Buttons:      nil,
					Elements:     elements,
				},
			},
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// ListTemplate sends a list of elements
func (r *Response) ListTemplate(elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) error {
	var tag string
	if len(tags) > 0 {
		tag = tags[0]
	}

	m := SendStructuredMessage{
		MessagingType: messagingType,
		Recipient:     r.to,
		Message: StructuredMessageData{
			Attachment: StructuredMessageAttachment{
				Type: "template",
				Payload: StructuredMessagePayload{
					TopElementStyle: "compact",
					TemplateType:    "list",
					Buttons:         nil,
					Elements:        elements,
				},
			},
		},
		Tag: tag,
	}
	return r.DispatchMessage(&m)
}

// SenderAction sends a info about sender action
func (r *Response) SenderAction(action string) error {
	m := SendSenderAction{
		Recipient:    r.to,
		SenderAction: action,
	}
	return r.DispatchMessage(&m)
}

// DispatchMessage posts the message to messenger, return the error if there's any
func (r *Response) DispatchMessage(m interface{}) error {
	data, err := json.Marshal(m)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	req.URL.RawQuery = "access_token=" + r.token

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == 200 {
		return nil
	}
	return checkFacebookError(resp.Body)
}

// SendMessage is the information sent in an API request to Facebook.
type SendMessage struct {
	MessagingType MessagingType `json:"messaging_type"`
	Recipient     Recipient     `json:"recipient"`
	Message       MessageData   `json:"message"`
	Tag           string        `json:"tag,omitempty"`
}

// MessageData is a message consisting of text or an attachment, with an additional selection of optional quick replies.
type MessageData struct {
	Text         string                       `json:"text,omitempty"`
	Attachment   *StructuredMessageAttachment `json:"attachment,omitempty"`
	QuickReplies []QuickReply                 `json:"quick_replies,omitempty"`
}

// SendStructuredMessage is a structured message template.
type SendStructuredMessage struct {
	MessagingType MessagingType         `json:"messaging_type"`
	Recipient     Recipient             `json:"recipient"`
	Message       StructuredMessageData `json:"message"`
	Tag           string                `json:"tag,omitempty"`
}

// StructuredMessageData is an attachment sent with a structured message.
type StructuredMessageData struct {
	Attachment StructuredMessageAttachment `json:"attachment"`
}

// StructuredMessageAttachment is the attachment of a structured message.
type StructuredMessageAttachment struct {
	// Type must be template
	Title string         `json:"title,omitempty"`
	URL   string         `json:"url,omitempty"`
	Type  AttachmentType `json:"type"`
	// Payload is the information for the file which was sent in the attachment.
	Payload StructuredMessagePayload `json:"payload"`
}

// StructuredMessagePayload is the actual payload of an attachment
type StructuredMessagePayload struct {
	// TemplateType must be button, generic or receipt
	TemplateType     string                      `json:"template_type,omitempty"`
	TopElementStyle  TopElementStyle             `json:"top_element_style,omitempty"`
	Text             string                      `json:"text,omitempty"`
	ImageAspectRatio ImageAspectRatio            `json:"image_aspect_ratio,omitempty"`
	Sharable         bool                        `json:"sharable,omitempty"`
	Elements         *[]StructuredMessageElement `json:"elements,omitempty"`
	Buttons          *[]StructuredMessageButton  `json:"buttons,omitempty"`
	Url              string                      `json:"url,omitempty"`
}

// StructuredMessageElement is a response containing structural elements
type StructuredMessageElement struct {
	Title         string                    `json:"title"`
	ImageURL      string                    `json:"image_url"`
	ItemURL       string                    `json:"item_url,omitempty"`
	Subtitle      string                    `json:"subtitle"`
	DefaultAction *DefaultAction            `json:"default_action,omitempty"`
	Buttons       []StructuredMessageButton `json:"buttons"`
}

// DefaultAction is a response containing default action properties
type DefaultAction struct {
	Type                string `json:"type"`
	URL                 string `json:"url,omitempty"`
	WebviewHeightRatio  string `json:"webview_height_ratio,omitempty"`
	MessengerExtensions bool   `json:"messenger_extensions,omitempty"`
	FallbackURL         string `json:"fallback_url,omitempty"`
	WebviewShareButton  string `json:"webview_share_button,omitempty"`
}

// StructuredMessageButton is a response containing buttons
type StructuredMessageButton struct {
	Type                string `json:"type"`
	URL                 string `json:"url,omitempty"`
	Title               string `json:"title,omitempty"`
	Payload             string `json:"payload,omitempty"`
	WebviewHeightRatio  string `json:"webview_height_ratio,omitempty"`
	MessengerExtensions bool   `json:"messenger_extensions,omitempty"`
	FallbackURL         string `json:"fallback_url,omitempty"`
	WebviewShareButton  string `json:"webview_share_button,omitempty"`
}

// SendSenderAction is the information about sender action
type SendSenderAction struct {
	Recipient    Recipient `json:"recipient"`
	SenderAction string    `json:"sender_action"`
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
)

type Constructor struct {
	request *http.Request
	payload Payload
}

func NewConstructor(req *http.Request, payload Payload) Constructor {
	c := Constructor{request: req, payload: payload}
	return c
}

<commit_msg>middleware apply function<commit_after>package main

import (
	"bytes"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
)

type Constructor struct {
	request *http.Request
	payload Payload
}

func NewConstructor(req *http.Request, payload Payload) Constructor {
	c := Constructor{request: req, payload: payload}
	return c
}

func (c *Constructor) ApplyMiddleware(middleware string) error {

	newPayload, err := ExecuteMiddleware(middleware, c.payload)

	if err != nil {
		log.WithFields(log.Fields{
			"error":      err.Error(),
			"middleware": AppConfig.middleware,
		}).Error("Error during middleware transformation, not modifying payload!")

		return err
	} else {

		log.WithFields(log.Fields{
			"middleware": AppConfig.middleware,
			"newPayload": newPayload,
		}).Info("Middleware transformation complete!")
		// override payload with transformed new payload
		c.payload = newPayload

		return nil
	}
}
<|endoftext|>