<commit_before>package gitmediaclient

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/github/git-media/gitmedia"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

const (
	gitMediaType     = "application/vnd.git-media"
	gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
	gitMediaHeader   = "--git-media."
)

func Options(filehash string) error {
	oid := filepath.Base(filehash)
	_, err := os.Stat(filehash)
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("OPTIONS", oid)
	if err != nil {
		return err
	}

	_, err = doRequest(req, creds)
	if err != nil {
		return err
	}

	return nil
}

func Put(filehash, filename string) error {
	if filename == "" {
		filename = filehash
	}

	oid := filepath.Base(filehash)
	stat, err := os.Stat(filehash)
	if err != nil {
		return err
	}

	file, err := os.Open(filehash)
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	bar := pb.StartNew(int(stat.Size()))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Header.Set("Content-Type", gitMediaType)
	req.Header.Set("Accept", gitMediaMetaType)
	req.Body = ioutil.NopCloser(bar.NewProxyReader(file))
	req.ContentLength = stat.Size()

	fmt.Printf("Sending %s\n", filename)

	_, err = doRequest(req, creds)
	if err != nil {
		return err
	}

	return nil
}

func Get(filename string) (io.ReadCloser, error) {
	oid := filepath.Base(filename)
	if stat, err := os.Stat(filename); err != nil || stat == nil {
		req, creds, err := clientRequest("GET", oid)
		if err != nil {
			return nil, err
		}

		req.Header.Set("Accept", gitMediaType)
		res, err := doRequest(req, creds)

		if err != nil {
			return nil, err
		}

		contentType := res.Header.Get("Content-Type")
		if contentType == "" {
			return nil, errors.New("Invalid Content-Type")
		}

		if ok, err := validateMediaHeader(contentType, res.Body); !ok {
			return nil, err
		}

		return res.Body, nil
	}

	return os.Open(filename)
}

func validateMediaHeader(contentType string, reader io.Reader) (bool, error) {
	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		return false, errors.New("Invalid Media Type")
	}

	if mediaType != gitMediaType {
		return false, errors.New("Invalid Media Type")
	}

	givenHeader, ok := params["header"]
	if !ok {
		return false, errors.New("Invalid header")
	}

	fullGivenHeader := "--" + givenHeader + "\n"

	header := make([]byte, len(fullGivenHeader))
	_, err = io.ReadAtLeast(reader, header, len(fullGivenHeader))
	if err != nil {
		return false, err
	}

	if string(header) != fullGivenHeader {
		return false, errors.New("Invalid header")
	}

	return true, nil
}

func doRequest(req *http.Request, creds Creds) (*http.Response, error) {
	res, err := http.DefaultClient.Do(req)

	if err == nil {
		if res.StatusCode > 299 {
			execCreds(creds, "reject")

			apierr := &Error{}
			dec := json.NewDecoder(res.Body)
			if err := dec.Decode(apierr); err != nil {
				return res, err
			}

			return res, apierr
		}

		execCreds(creds, "approve")
	}

	return res, err
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := gitmedia.Config
	u, _ := url.Parse(c.Endpoint())
	u.Path = filepath.Join(u.Path, "/objects/"+oid)
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<commit_msg>gitmediaclient doesn't need to check .git/media dir<commit_after>package gitmediaclient

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/github/git-media/gitmedia"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

const (
	gitMediaType     = "application/vnd.git-media"
	gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
	gitMediaHeader   = "--git-media."
)

func Options(filehash string) error {
	oid := filepath.Base(filehash)
	_, err := os.Stat(filehash)
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("OPTIONS", oid)
	if err != nil {
		return err
	}

	_, err = doRequest(req, creds)
	if err != nil {
		return err
	}

	return nil
}

func Put(filehash, filename string) error {
	if filename == "" {
		filename = filehash
	}

	oid := filepath.Base(filehash)
	stat, err := os.Stat(filehash)
	if err != nil {
		return err
	}

	file, err := os.Open(filehash)
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	bar := pb.StartNew(int(stat.Size()))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Header.Set("Content-Type", gitMediaType)
	req.Header.Set("Accept", gitMediaMetaType)
	req.Body = ioutil.NopCloser(bar.NewProxyReader(file))
	req.ContentLength = stat.Size()

	fmt.Printf("Sending %s\n", filename)

	_, err = doRequest(req, creds)
	if err != nil {
		return err
	}

	return nil
}

func Get(filename string) (io.ReadCloser, error) {
	oid := filepath.Base(filename)
	req, creds, err := clientRequest("GET", oid)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Accept", gitMediaType)
	res, err := doRequest(req, creds)

	if err != nil {
		return nil, err
	}

	contentType := res.Header.Get("Content-Type")
	if contentType == "" {
		return nil, errors.New("Invalid Content-Type")
	}

	if ok, err := validateMediaHeader(contentType, res.Body); !ok {
		return nil, err
	}

	return res.Body, nil
}

func validateMediaHeader(contentType string, reader io.Reader) (bool, error) {
	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		return false, errors.New("Invalid Media Type")
	}

	if mediaType != gitMediaType {
		return false, errors.New("Invalid Media Type")
	}

	givenHeader, ok := params["header"]
	if !ok {
		return false, errors.New("Invalid header")
	}

	fullGivenHeader := "--" + givenHeader + "\n"

	header := make([]byte, len(fullGivenHeader))
	_, err = io.ReadAtLeast(reader, header, len(fullGivenHeader))
	if err != nil {
		return false, err
	}

	if string(header) != fullGivenHeader {
		return false, errors.New("Invalid header")
	}

	return true, nil
}

func doRequest(req *http.Request, creds Creds) (*http.Response, error) {
	res, err := http.DefaultClient.Do(req)

	if err == nil {
		if res.StatusCode > 299 {
			execCreds(creds, "reject")

			apierr := &Error{}
			dec := json.NewDecoder(res.Body)
			if err := dec.Decode(apierr); err != nil {
				return res, err
			}

			return res, apierr
		}

		execCreds(creds, "approve")
	}

	return res, err
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := gitmedia.Config
	u, _ := url.Parse(c.Endpoint())
	u.Path = filepath.Join(u.Path, "/objects/"+oid)
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// 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 gnmiclient

import (
	"context"
	"io"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"

	gpb "github.com/openconfig/gnmi/proto/gnmi"
	"github.com/openconfig/lemming/gnmi/gnmistore"
)

// New creates a state-based gNMI client for the gNMI cache.
// The client calls the server gRPC implementation with a custom streaming gRPC implementation
// in order to bypass the regular gRPC wire marshalling/unmarshalling handling.
func New(srv gpb.GNMIServer) (gpb.GNMIClient, error) {
	return &cacheClient{
		gnmiMode: gnmistore.StateMode,
		srv:      srv,
	}, nil
}

// NewReadOnly creates a config-based gNMI client for the gNMI cache.
// The client calls the server gRPC implementation with a custom streaming gRPC implementation
// in order to bypass the regular gRPC wire marshalling/unmarshalling handling.
func NewReadOnly(srv gpb.GNMIServer) (gpb.GNMIClient, error) {
	return &cacheClient{
		gnmiMode: gnmistore.ConfigMode,
		srv:      srv,
	}, nil
}

// cacheClient is a gNMI client talks directly to a server, without sending messages over the wire.
type cacheClient struct {
	gpb.GNMIClient
	gnmiMode gnmistore.GNMIMode
	srv      gpb.GNMIServer
}

// Set uses the datastore client for Set, instead of the public cache endpoint.
func (m *cacheClient) Set(ctx context.Context, in *gpb.SetRequest, _ ...grpc.CallOption) (*gpb.SetResponse, error) {
	return m.srv.Set(metadata.NewIncomingContext(ctx, metadata.Pairs(gnmistore.GNMIModeMetadataKey, string(m.gnmiMode))), in)
}

// Subscribe implements gNMI Subscribe, by calling a gNMI server directly.
func (cc *cacheClient) Subscribe(ctx context.Context, _ ...grpc.CallOption) (gpb.GNMI_SubscribeClient, error) {
	errCh := make(chan error)
	respCh := make(chan *gpb.SubscribeResponse, 10)
	reqCh := make(chan *gpb.SubscribeRequest)

	sub := &subServer{
		respCh: respCh,
		reqCh:  reqCh,
		ctx:    peer.NewContext(ctx, &peer.Peer{}), // Add empty Peer, since the cache expects to be set.
	}
	client := &subClient{
		errCh:  errCh,
		respCh: respCh,
		reqCh:  reqCh,
	}

	go func() {
		err := cc.srv.Subscribe(sub)
		errCh <- err
	}()
	return client, nil
}

// subClient is an implementation of GNMI_SubscribeClient that use channels to pass messages.
type subClient struct {
	gpb.GNMI_SubscribeClient
	errCh  chan error
	respCh chan *gpb.SubscribeResponse
	reqCh  chan *gpb.SubscribeRequest
}

func (sc *subClient) CloseSend() error {
	close(sc.reqCh)
	return nil
}

func (sc *subClient) Send(req *gpb.SubscribeRequest) error {
	sc.reqCh <- req
	return nil
}

func (sc *subClient) Recv() (*gpb.SubscribeResponse, error) {
	for {
		select {
		case resp := <-sc.respCh:
			return resp, nil
		case err := <-sc.errCh:
			return nil, err
		}
	}
}

// subServer is an implementation of GNMI_SubscribeServer that use channels to pass messages.
type subServer struct {
	gpb.GNMI_SubscribeServer
	respCh chan *gpb.SubscribeResponse
	reqCh  chan *gpb.SubscribeRequest
	ctx    context.Context
}

func (ss *subServer) Context() context.Context {
	return ss.ctx
}

func (ss *subServer) Send(resp *gpb.SubscribeResponse) error {
	ss.respCh <- resp
	return nil
}

func (ss *subServer) Recv() (*gpb.SubscribeRequest, error) {
	req, ok := <-ss.reqCh
	if !ok {
		return nil, io.EOF
	}
	return req, nil
}
<commit_msg>fix lint<commit_after>// Copyright 2022 Google LLC
//
// 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 gnmiclient

import (
	"context"
	"io"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"

	gpb "github.com/openconfig/gnmi/proto/gnmi"
	"github.com/openconfig/lemming/gnmi/gnmistore"
)

// New creates a state-based gNMI client for the gNMI cache.
// The client calls the server gRPC implementation with a custom streaming gRPC implementation
// in order to bypass the regular gRPC wire marshalling/unmarshalling handling.
func New(srv gpb.GNMIServer) (gpb.GNMIClient, error) {
	return &cacheClient{
		gnmiMode: gnmistore.StateMode,
		srv:      srv,
	}, nil
}

// NewReadOnly creates a config-based gNMI client for the gNMI cache.
// The client calls the server gRPC implementation with a custom streaming gRPC implementation
// in order to bypass the regular gRPC wire marshalling/unmarshalling handling.
func NewReadOnly(srv gpb.GNMIServer) (gpb.GNMIClient, error) {
	return &cacheClient{
		gnmiMode: gnmistore.ConfigMode,
		srv:      srv,
	}, nil
}

// cacheClient is a gNMI client talks directly to a server, without sending messages over the wire.
type cacheClient struct {
	gpb.GNMIClient
	gnmiMode gnmistore.GNMIMode
	srv      gpb.GNMIServer
}

// Set uses the datastore client for Set, instead of the public cache endpoint.
func (c *cacheClient) Set(ctx context.Context, in *gpb.SetRequest, _ ...grpc.CallOption) (*gpb.SetResponse, error) {
	return c.srv.Set(metadata.NewIncomingContext(ctx, metadata.Pairs(gnmistore.GNMIModeMetadataKey, string(c.gnmiMode))), in)
}

// Subscribe implements gNMI Subscribe, by calling a gNMI server directly.
func (c *cacheClient) Subscribe(ctx context.Context, _ ...grpc.CallOption) (gpb.GNMI_SubscribeClient, error) {
	errCh := make(chan error)
	respCh := make(chan *gpb.SubscribeResponse, 10)
	reqCh := make(chan *gpb.SubscribeRequest)

	sub := &subServer{
		respCh: respCh,
		reqCh:  reqCh,
		ctx:    peer.NewContext(ctx, &peer.Peer{}), // Add empty Peer, since the cache expects to be set.
	}
	client := &subClient{
		errCh:  errCh,
		respCh: respCh,
		reqCh:  reqCh,
	}

	go func() {
		err := c.srv.Subscribe(sub)
		errCh <- err
	}()
	return client, nil
}

// subClient is an implementation of GNMI_SubscribeClient that use channels to pass messages.
type subClient struct {
	gpb.GNMI_SubscribeClient
	errCh  chan error
	respCh chan *gpb.SubscribeResponse
	reqCh  chan *gpb.SubscribeRequest
}

func (sc *subClient) CloseSend() error {
	close(sc.reqCh)
	return nil
}

func (sc *subClient) Send(req *gpb.SubscribeRequest) error {
	sc.reqCh <- req
	return nil
}

func (sc *subClient) Recv() (*gpb.SubscribeResponse, error) {
	for {
		select {
		case resp := <-sc.respCh:
			return resp, nil
		case err := <-sc.errCh:
			return nil, err
		}
	}
}

// subServer is an implementation of GNMI_SubscribeServer that use channels to pass messages.
type subServer struct {
	gpb.GNMI_SubscribeServer
	respCh chan *gpb.SubscribeResponse
	reqCh  chan *gpb.SubscribeRequest
	ctx    context.Context
}

func (ss *subServer) Context() context.Context {
	return ss.ctx
}

func (ss *subServer) Send(resp *gpb.SubscribeResponse) error {
	ss.respCh <- resp
	return nil
}

func (ss *subServer) Recv() (*gpb.SubscribeRequest, error) {
	req, ok := <-ss.reqCh
	if !ok {
		return nil, io.EOF
	}
	return req, nil
}
<|endoftext|>


<commit_before>package keybase

import (
	"errors"
	"fmt"
	"runtime"
	"time"

	"github.com/keybase/client/go/chat"
	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/chat/storage"
	"github.com/keybase/client/go/chat/types"
	"github.com/keybase/client/go/chat/utils"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/gregor1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/kyokomi/emoji"
	context "golang.org/x/net/context"
)

type Person struct {
	KeybaseUsername string
	KeybaseAvatar   string
	IsBot           bool
}

type Message struct {
	ID            int
	Kind          string // "Text" | "Reaction"
	Plaintext     string
	ServerMessage string // This is the server's suggested display message for the notification
	From          *Person
	At            int64
}

type ChatNotification struct {
	Message   *Message
	ConvID    string
	TeamName  string
	TopicName string
	TlfName   string
	// e.g. "keybase#general, CoolTeam, Susannah,Jake"
	ConversationName    string
	IsGroupConversation bool
	IsPlaintext         bool
	SoundName           string
	BadgeCount          int
}

func HandlePostTextReply(strConvID, tlfName string, body string) (err error) {
	outboxID, err := storage.NewOutboxID()
	if err != nil {
		return err
	}
	convID, err := chat1.MakeConvID(strConvID)
	if err != nil {
		return err
	}
	_, err = kbCtx.ChatHelper.SendTextByIDNonblock(context.Background(), convID, tlfName, body, &outboxID, nil)
	return err
}

func HandleBackgroundNotification(strConvID, body, serverMessageBody, sender string, intMembersType int,
	displayPlaintext bool, intMessageID int, pushID string, badgeCount, unixTime int, soundName string,
	pusher PushNotifier) (err error) {
	if err := waitForInit(5 * time.Second); err != nil {
		return nil
	}
	gc := globals.NewContext(kbCtx, kbChatCtx)
	ctx := globals.ChatCtx(context.Background(), gc,
		keybase1.TLFIdentifyBehavior_CHAT_GUI, nil, chat.NewCachingIdentifyNotifier(gc))

	defer kbCtx.CTraceTimed(ctx, fmt.Sprintf("HandleBackgroundNotification(%s,%s,%v,%d,%d,%s,%d,%d)",
		strConvID, sender, displayPlaintext, intMembersType, intMessageID, pushID, badgeCount, unixTime),
		func() error { return flattenError(err) })()

	// Unbox
	if !kbCtx.ActiveDevice.HaveKeys() {
		return libkb.LoginRequiredError{}
	}
	mp := chat.NewMobilePush(gc)
	uid := gregor1.UID(kbCtx.Env.GetUID().ToBytes())
	convID, err := chat1.MakeConvID(strConvID)
	if err != nil {
		kbCtx.Log.CDebugf(ctx, "HandleBackgroundNotification: invalid convID: %s msg: %s", strConvID, err)
		return err
	}
	membersType := chat1.ConversationMembersType(intMembersType)
	conv, err := utils.GetVerifiedConv(ctx, gc, uid, convID, types.InboxSourceDataSourceAll)
	if err != nil {
		kbCtx.Log.CDebugf(ctx, "Failed to get conversation info", err)
		return err
	}

	chatNotification := ChatNotification{
		IsPlaintext: displayPlaintext,
		Message: &Message{
			ID:            intMessageID,
			ServerMessage: serverMessageBody,
			From:          &Person{},
			At:            int64(unixTime) * 1000,
		},
		ConvID:              strConvID,
		TopicName:           conv.Info.TopicName,
		TlfName:             conv.Info.TlfName,
		IsGroupConversation: len(conv.Info.Participants) > 2,
		ConversationName:    utils.FormatConversationName(conv.Info, string(kbCtx.Env.GetUsername())),
		SoundName:           soundName,
		BadgeCount:          badgeCount,
	}

	msgUnboxed, err := mp.UnboxPushNotification(ctx, uid, convID, membersType, body)
	if err == nil && msgUnboxed.IsValid() {
		chatNotification.Message.From.IsBot = msgUnboxed.SenderIsBot()
		username := msgUnboxed.Valid().SenderUsername
		chatNotification.Message.From.KeybaseUsername = username

		if displayPlaintext && !msgUnboxed.Valid().IsEphemeral() {
			// We show avatars on Android
			if runtime.GOOS == "android" {
				avatar, err := kbSvc.GetUserAvatar(username)

				if err != nil {
					kbCtx.Log.CDebugf(ctx, "Push Notif: Err in getting user avatar %v", err)
				} else {
					chatNotification.Message.From.KeybaseAvatar = avatar
				}
			}

			switch msgUnboxed.GetMessageType() {
			case chat1.MessageType_TEXT:
				chatNotification.Message.Kind = "Text"
				chatNotification.Message.Plaintext = msgUnboxed.Valid().MessageBody.Text().Body
			case chat1.MessageType_REACTION:
				chatNotification.Message.Kind = "Reaction"
				reaction, err := utils.GetReaction(msgUnboxed)
				if err != nil {
					return err
				}
				chatNotification.Message.Plaintext =
					emoji.Sprintf("Reacted to your message with %v", reaction)
			default:
				kbCtx.Log.CDebugf(ctx, "unboxNotification: Unknown message type: %v",
					msgUnboxed.GetMessageType())
				return errors.New("invalid message type for plaintext")
			}
		}
	} else {
		kbCtx.Log.CDebugf(ctx, "unboxNotification: failed to unbox: %s", err)
		chatNotification.Message.From.KeybaseUsername = sender
		// just bail out of here at this point since we won't be displaying anything useful,
		// and we don't want to accidentally ack the plaintext notification when we didn't really
		// display it.
		if len(serverMessageBody) == 0 {
			return nil
		}
	}

	age := time.Since(time.Unix(int64(unixTime), 0))
	if age >= 2*time.Minute {
		kbCtx.Log.CDebugf(ctx, "HandleBackgroundNotification: stale notification: %v", age)
		return errors.New("stale notification")
	}
	if pusher != nil {
		pusher.DisplayChatNotification(&chatNotification)
		if len(pushID) != 0 {
			mp.AckNotificationSuccess(ctx, []string{pushID})
		}
	}
	return nil
}
<commit_msg>only ack if we have text to display (#19157)<commit_after>package keybase

import (
	"errors"
	"fmt"
	"runtime"
	"time"

	"github.com/keybase/client/go/chat"
	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/chat/storage"
	"github.com/keybase/client/go/chat/types"
	"github.com/keybase/client/go/chat/utils"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/gregor1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/kyokomi/emoji"
	context "golang.org/x/net/context"
)

type Person struct {
	KeybaseUsername string
	KeybaseAvatar   string
	IsBot           bool
}

type Message struct {
	ID            int
	Kind          string // "Text" | "Reaction"
	Plaintext     string
	ServerMessage string // This is the server's suggested display message for the notification
	From          *Person
	At            int64
}

type ChatNotification struct {
	Message   *Message
	ConvID    string
	TeamName  string
	TopicName string
	TlfName   string
	// e.g. "keybase#general, CoolTeam, Susannah,Jake"
	ConversationName    string
	IsGroupConversation bool
	IsPlaintext         bool
	SoundName           string
	BadgeCount          int
}

func HandlePostTextReply(strConvID, tlfName string, body string) (err error) {
	outboxID, err := storage.NewOutboxID()
	if err != nil {
		return err
	}
	convID, err := chat1.MakeConvID(strConvID)
	if err != nil {
		return err
	}
	_, err = kbCtx.ChatHelper.SendTextByIDNonblock(context.Background(), convID, tlfName, body, &outboxID, nil)
	return err
}

func HandleBackgroundNotification(strConvID, body, serverMessageBody, sender string, intMembersType int,
	displayPlaintext bool, intMessageID int, pushID string, badgeCount, unixTime int, soundName string,
	pusher PushNotifier) (err error) {
	if err := waitForInit(5 * time.Second); err != nil {
		return nil
	}
	gc := globals.NewContext(kbCtx, kbChatCtx)
	ctx := globals.ChatCtx(context.Background(), gc,
		keybase1.TLFIdentifyBehavior_CHAT_GUI, nil, chat.NewCachingIdentifyNotifier(gc))

	defer kbCtx.CTraceTimed(ctx, fmt.Sprintf("HandleBackgroundNotification(%s,%s,%v,%d,%d,%s,%d,%d)",
		strConvID, sender, displayPlaintext, intMembersType, intMessageID, pushID, badgeCount, unixTime),
		func() error { return flattenError(err) })()

	// Unbox
	if !kbCtx.ActiveDevice.HaveKeys() {
		return libkb.LoginRequiredError{}
	}
	mp := chat.NewMobilePush(gc)
	uid := gregor1.UID(kbCtx.Env.GetUID().ToBytes())
	convID, err := chat1.MakeConvID(strConvID)
	if err != nil {
		kbCtx.Log.CDebugf(ctx, "HandleBackgroundNotification: invalid convID: %s msg: %s", strConvID, err)
		return err
	}
	membersType := chat1.ConversationMembersType(intMembersType)
	conv, err := utils.GetVerifiedConv(ctx, gc, uid, convID, types.InboxSourceDataSourceAll)
	if err != nil {
		kbCtx.Log.CDebugf(ctx, "Failed to get conversation info", err)
		return err
	}

	chatNotification := ChatNotification{
		IsPlaintext: displayPlaintext,
		Message: &Message{
			ID:            intMessageID,
			ServerMessage: serverMessageBody,
			From:          &Person{},
			At:            int64(unixTime) * 1000,
		},
		ConvID:              strConvID,
		TopicName:           conv.Info.TopicName,
		TlfName:             conv.Info.TlfName,
		IsGroupConversation: len(conv.Info.Participants) > 2,
		ConversationName:    utils.FormatConversationName(conv.Info, string(kbCtx.Env.GetUsername())),
		SoundName:           soundName,
		BadgeCount:          badgeCount,
	}

	msgUnboxed, err := mp.UnboxPushNotification(ctx, uid, convID, membersType, body)
	if err == nil && msgUnboxed.IsValid() {
		chatNotification.Message.From.IsBot = msgUnboxed.SenderIsBot()
		username := msgUnboxed.Valid().SenderUsername
		chatNotification.Message.From.KeybaseUsername = username

		if displayPlaintext && !msgUnboxed.Valid().IsEphemeral() {
			// We show avatars on Android
			if runtime.GOOS == "android" {
				avatar, err := kbSvc.GetUserAvatar(username)

				if err != nil {
					kbCtx.Log.CDebugf(ctx, "Push Notif: Err in getting user avatar %v", err)
				} else {
					chatNotification.Message.From.KeybaseAvatar = avatar
				}
			}

			switch msgUnboxed.GetMessageType() {
			case chat1.MessageType_TEXT:
				chatNotification.Message.Kind = "Text"
				chatNotification.Message.Plaintext = msgUnboxed.Valid().MessageBody.Text().Body
			case chat1.MessageType_REACTION:
				chatNotification.Message.Kind = "Reaction"
				reaction, err := utils.GetReaction(msgUnboxed)
				if err != nil {
					return err
				}
				chatNotification.Message.Plaintext =
					emoji.Sprintf("Reacted to your message with %v", reaction)
			default:
				kbCtx.Log.CDebugf(ctx, "unboxNotification: Unknown message type: %v",
					msgUnboxed.GetMessageType())
				return errors.New("invalid message type for plaintext")
			}
		}
	} else {
		kbCtx.Log.CDebugf(ctx, "unboxNotification: failed to unbox: %s", err)
		chatNotification.Message.From.KeybaseUsername = sender
		// just bail out of here at this point since we won't be displaying anything useful,
		// and we don't want to accidentally ack the plaintext notification when we didn't really
		// display it.
		if len(serverMessageBody) == 0 {
			return nil
		}
	}

	age := time.Since(time.Unix(int64(unixTime), 0))
	if age >= 2*time.Minute {
		kbCtx.Log.CDebugf(ctx, "HandleBackgroundNotification: stale notification: %v", age)
		return errors.New("stale notification")
	}

	// only display and ack this notification if we actually have something to display
	if pusher != nil && (len(chatNotification.Message.Plaintext) > 0 || len(chatNotification.Message.ServerMessage) > 0) {
		pusher.DisplayChatNotification(&chatNotification)
		if len(pushID) > 0 {
			mp.AckNotificationSuccess(ctx, []string{pushID})
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package consul

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/doubledutch/lager"
	"github.com/doubledutch/quantum"
	"github.com/doubledutch/quantum/client"
	"github.com/hashicorp/consul/api"
	"github.com/miekg/dns"
)

type resolveResult struct {
	address string
}

// NewClientResolver creates a consul client resolver
func NewClientResolver(httpAddr, dnsAddr string, config *quantum.ConnConfig) quantum.ClientResolver {
	if config == nil {
		config = quantum.DefaultConnConfig()
	}
	if config.Config == nil {
		config.Config = quantum.DefaultConfig()
	}
	return &ClientResolver{
		config:   config,
		httpAddr: httpAddr,
		dnsAddr:  dnsAddr,
	}
}

// ClientResolver is a client resolver that leverages Consul's service discovery.
type ClientResolver struct {
	config *quantum.ConnConfig
	lgr    lager.Lager

	httpAddr string
	dnsAddr  string

	dnsc  *dns.Client
	httpc *api.Client
}

// Resolve resolves a ClientConn using a ResolveRequest
func (cr *ClientResolver) Resolve(request quantum.ResolveRequest) (quantum.ClientConn, error) {
	// Get ResolveResults
	results, err := cr.resolveResults(request)
	if err != nil {
		return nil, err
	}

	if len(results) == 0 {
		return nil, quantum.NoAgentsFromRequest(request)
	}

	// Ping each one, return the first one to respond
	return cr.resolveClient(results)
}

// ResolveConfigs resolves client configs given the specified arguments
func (cr *ClientResolver) resolveResults(rr quantum.ResolveRequest) (results []resolveResult, err error) {
	if rr.Agent == "" {
		return cr.resolveWithDNS(rr)
	}

	return cr.resolveWithAPI(rr)
}

func (cr *ClientResolver) resolveWithDNS(rr quantum.ResolveRequest) (results []resolveResult, err error) {
	m := new(dns.Msg)
	// For now, assume .service.consul. for domain
	srv := rr.Type + ".service.consul."
	m.SetQuestion(srv, dns.TypeSRV)

	if cr.dnsc == nil {
		cr.dnsc = &dns.Client{Net: "tcp"}
	}
	in, _, err := cr.dnsc.Exchange(m, cr.dnsAddr)
	if err != nil {
		cr.lgr.Errorf("DNS Exchange failed: %s\n", err)
		return nil, err
	}

	return newResolveResults(in, rr), nil
}

func newResolveResults(in *dns.Msg, rr quantum.ResolveRequest) (results []resolveResult) {
	for i, a := range in.Answer {
		srv := a.(*dns.SRV)
		if rr.Agent != "" {
			// We were given an agent name, match it to the hostname of the SRV record
			targetSplit := strings.Split(srv.Target, ".")
			if len(targetSplit) < 5 {
				// We expect name.node.dc1.consul. (5)
				continue
			}
			// Drop .node.dc1.consul.
			hostnameSplit := targetSplit[:len(targetSplit)-4]
			hostname := strings.Join(hostnameSplit, ".")
			if hostname != rr.Agent {
				continue
			}
		}
		a := in.Extra[i].(*dns.A)
		sPort := ":" + strconv.Itoa(int(srv.Port))
		results = append(results, resolveResult{
			address: a.A.String() + sPort,
		})
	}
	return
}

func (cr *ClientResolver) resolveWithAPI(rr quantum.ResolveRequest) (results []resolveResult, err error) {
	if cr.httpc == nil {
		cr.httpc, err = api.NewClient(&api.Config{
			Address: cr.httpAddr,
		})
		if err != nil {
			return nil, err
		}
	}

	catalog := cr.httpc.Catalog()
	node, _, err := catalog.Node(rr.Agent, nil)
	if err != nil || node == nil {
		return nil, quantum.NoAgentsFromRequest(rr)
	}

	// Quantum services are registered with an UUID for an ID for uniqueness.
	// The agent consul dictionary maps services by the ID. Since we don't know
	// UUID we're looking for, loop over all services to search by service name.
	var service *api.AgentService
	for _, srv := range node.Services {
		if srv.Service == rr.Type {
			service = srv
			break
		}
	}

	if service == nil {
		return nil, quantum.NoAgentsFromRequest(rr)
	}

	if service.Address == "" {
		service.Address = node.Node.Address
	}

	return []resolveResult{{address: fmt.Sprintf("%s:%d", service.Address, service.Port)}}, nil
}

func (cr *ClientResolver) resolveClient(results []resolveResult) (conn quantum.ClientConn, err error) {
	// TODO: Do this concurrently, first one to respond wins
	for _, result := range results {
		client := client.New(cr.config)
		conn, err = client.Dial(result.address)
		if err == nil {
			break
		}
	}
	return
}
<commit_msg>lower consul node name for match<commit_after>package consul

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/doubledutch/lager"
	"github.com/doubledutch/quantum"
	"github.com/doubledutch/quantum/client"
	"github.com/hashicorp/consul/api"
	"github.com/miekg/dns"
)

type resolveResult struct {
	address string
}

// NewClientResolver creates a consul client resolver
func NewClientResolver(httpAddr, dnsAddr string, config *quantum.ConnConfig) quantum.ClientResolver {
	if config == nil {
		config = quantum.DefaultConnConfig()
	}
	if config.Config == nil {
		config.Config = quantum.DefaultConfig()
	}
	return &ClientResolver{
		config:   config,
		httpAddr: httpAddr,
		dnsAddr:  dnsAddr,
	}
}

// ClientResolver is a client resolver that leverages Consul's service discovery.
type ClientResolver struct {
	config *quantum.ConnConfig
	lgr    lager.Lager

	httpAddr string
	dnsAddr  string

	dnsc  *dns.Client
	httpc *api.Client
}

// Resolve resolves a ClientConn using a ResolveRequest
func (cr *ClientResolver) Resolve(request quantum.ResolveRequest) (quantum.ClientConn, error) {
	// Get ResolveResults
	results, err := cr.resolveResults(request)
	if err != nil {
		return nil, err
	}

	if len(results) == 0 {
		return nil, quantum.NoAgentsFromRequest(request)
	}

	// Ping each one, return the first one to respond
	return cr.resolveClient(results)
}

// ResolveConfigs resolves client configs given the specified arguments
func (cr *ClientResolver) resolveResults(rr quantum.ResolveRequest) (results []resolveResult, err error) {
	if rr.Agent == "" {
		return cr.resolveWithDNS(rr)
	}

	return cr.resolveWithAPI(rr)
}

func (cr *ClientResolver) resolveWithDNS(rr quantum.ResolveRequest) (results []resolveResult, err error) {
	m := new(dns.Msg)
	// For now, assume .service.consul. for domain
	srv := rr.Type + ".service.consul."
	m.SetQuestion(srv, dns.TypeSRV)

	if cr.dnsc == nil {
		cr.dnsc = &dns.Client{Net: "tcp"}
	}
	in, _, err := cr.dnsc.Exchange(m, cr.dnsAddr)
	if err != nil {
		cr.lgr.Errorf("DNS Exchange failed: %s\n", err)
		return nil, err
	}

	return newResolveResults(in, rr), nil
}

func newResolveResults(in *dns.Msg, rr quantum.ResolveRequest) (results []resolveResult) {
	for i, a := range in.Answer {
		srv := a.(*dns.SRV)
		if rr.Agent != "" {
			// We were given an agent name, match it to the hostname of the SRV record
			targetSplit := strings.Split(srv.Target, ".")
			if len(targetSplit) < 5 {
				// We expect name.node.dc1.consul. (5)
				continue
			}
			// Drop .node.dc1.consul.
			hostnameSplit := targetSplit[:len(targetSplit)-4]
			hostname := strings.Join(hostnameSplit, ".")
			if hostname != rr.Agent {
				continue
			}
		}
		a := in.Extra[i].(*dns.A)
		sPort := ":" + strconv.Itoa(int(srv.Port))
		results = append(results, resolveResult{
			address: a.A.String() + sPort,
		})
	}
	return
}

func (cr *ClientResolver) resolveWithAPI(rr quantum.ResolveRequest) (results []resolveResult, err error) {
	if cr.httpc == nil {
		cr.httpc, err = api.NewClient(&api.Config{
			Address: cr.httpAddr,
		})
		if err != nil {
			return nil, err
		}
	}

	catalog := cr.httpc.Catalog()
	var nodeName string
	nodes, _, err := catalog.Nodes(nil)
	if err != nil {
		return nil, err
	}
	for _, node := range nodes {
		if strings.ToLower(node.Node) == strings.ToLower(rr.Agent) {
			nodeName = node.Node
			break
		}
	}
	if nodeName == "" {
		return nil, quantum.NoAgentsFromRequest(rr)
	}
	node, _, err := catalog.Node(nodeName, nil)
	if node == nil || err != nil {
		return nil, quantum.NoAgentsFromRequest(rr)
	}

	// Quantum services are registered with an UUID for an ID for uniqueness.
	// The agent consul dictionary maps services by the ID. Since we don't know
	// UUID we're looking for, loop over all services to search by service name.
	var service *api.AgentService
	for _, srv := range node.Services {
		if srv.Service == rr.Type {
			service = srv
			break
		}
	}

	if service == nil {
		return nil, quantum.NoAgentsFromRequest(rr)
	}

	if service.Address == "" {
		service.Address = node.Node.Address
	}

	return []resolveResult{{address: fmt.Sprintf("%s:%d", service.Address, service.Port)}}, nil
}

func (cr *ClientResolver) resolveClient(results []resolveResult) (conn quantum.ClientConn, err error) {
	// TODO: Do this concurrently, first one to respond wins
	for _, result := range results {
		client := client.New(cr.config)
		conn, err = client.Dial(result.address)
		if err == nil {
			break
		}
	}
	return
}
<|endoftext|>


<commit_before>package gormsupport

import (
	"github.com/jinzhu/gorm"
)

// A DBScript holds a list of functions to be executed against a database
type DBScript struct {
	script []func(db *gorm.DB) error
}

// Run executes all functions in the script, collecting any errors
func (s *DBScript) Run(db *gorm.DB) []error {
	var errs []error
	for _, f := range s.script {
		err := f(db)
		if err != nil {
			errs = append(errs, err)
		}
	}
	return errs
}

// Append appends a function to the script
func (s *DBScript) Append(f func(db *gorm.DB) error) {
	s.script = append(s.script, f)
}
<commit_msg>Remove unused gormsupport/db_script.go (#942)<commit_after><|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 thrift

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
)

const DEFAULT_MAX_LENGTH = 16384000

type TFramedTransport struct {
	transport TTransport
	buf       bytes.Buffer
	reader    *bufio.Reader
	frameSize int //Current remaining size of the frame. if ==0 read next frame header
	buffer    [4]byte
	maxLength int
}

type tFramedTransportFactory struct {
	factory   TTransportFactory
	maxLength int
}

func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory {
	return &tFramedTransportFactory{factory: factory, maxLength: DEFAULT_MAX_LENGTH}
}

func (p *tFramedTransportFactory) GetTransport(base TTransport) TTransport {
	return NewTFramedTransportMaxLength(p.factory.GetTransport(base), p.maxLength)
}

func NewTFramedTransport(transport TTransport) *TFramedTransport {
	return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: DEFAULT_MAX_LENGTH}
}

func NewTFramedTransportMaxLength(transport TTransport, maxLength int) *TFramedTransport {
	return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: maxLength}
}

func (p *TFramedTransport) Open() error {
	return p.transport.Open()
}

func (p *TFramedTransport) IsOpen() bool {
	return p.transport.IsOpen()
}

func (p *TFramedTransport) Close() error {
	return p.transport.Close()
}

func (p *TFramedTransport) Read(buf []byte) (l int, err error) {
	if p.frameSize == 0 {
		p.frameSize, err = p.readFrameHeader()
		if err != nil {
			return
		}
	}
	if p.frameSize < len(buf) {
		frameSize := p.frameSize
		tmp := make([]byte, p.frameSize)
		l, err = p.Read(tmp)
		copy(buf, tmp)
		if err == nil {
			err = NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", frameSize, len(buf)))
			return
		}
	}
	got, err := p.reader.Read(buf)
	p.frameSize = p.frameSize - got
	//sanity check
	if p.frameSize < 0 {
		return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Negative frame size")
	}
	return got, NewTTransportExceptionFromError(err)
}

func (p *TFramedTransport) ReadByte() (c byte, err error) {
	if p.frameSize == 0 {
		p.frameSize, err = p.readFrameHeader()
		if err != nil {
			return
		}
	}
	if p.frameSize < 1 {
		return 0, NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", p.frameSize, 1))
	}
	c, err = p.reader.ReadByte()
	if err == nil {
		p.frameSize--
	}
	return
}

func (p *TFramedTransport) Write(buf []byte) (int, error) {
	n, err := p.buf.Write(buf)
	return n, NewTTransportExceptionFromError(err)
}

func (p *TFramedTransport) WriteByte(c byte) error {
	return p.buf.WriteByte(c)
}

func (p *TFramedTransport) WriteString(s string) (n int, err error) {
	return p.buf.WriteString(s)
}

func (p *TFramedTransport) Flush() error {
	size := p.buf.Len()
	buf := p.buffer[:4]
	binary.BigEndian.PutUint32(buf, uint32(size))
	_, err := p.transport.Write(buf)
	if err != nil {
		return NewTTransportExceptionFromError(err)
	}
	if size > 0 {
		if n, err := p.buf.WriteTo(p.transport); err != nil {
			print("Error while flushing write buffer of size ", size, " to transport, only wrote ", n, " bytes: ", err.Error(), "\n")
			return NewTTransportExceptionFromError(err)
		}
	}
	err = p.transport.Flush()
	return NewTTransportExceptionFromError(err)
}

func (p *TFramedTransport) readFrameHeader() (int, error) {
	buf := p.buffer[:4]
	if _, err := io.ReadFull(p.reader, buf); err != nil {
		return 0, err
	}
	size := int(binary.BigEndian.Uint32(buf))
	if size < 0 || size > p.maxLength {
		return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size))
	}
	return size, nil
}
<commit_msg>THRIFT-3198 Allow construction of TTransportFactory with a specified maxLength Client: Go Patch: Sean <DrTall@users.noreply.github.com><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 thrift

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
)

const DEFAULT_MAX_LENGTH = 16384000

type TFramedTransport struct {
	transport TTransport
	buf       bytes.Buffer
	reader    *bufio.Reader
	frameSize int //Current remaining size of the frame. if ==0 read next frame header
	buffer    [4]byte
	maxLength int
}

type tFramedTransportFactory struct {
	factory   TTransportFactory
	maxLength int
}

func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory {
	return &tFramedTransportFactory{factory: factory, maxLength: DEFAULT_MAX_LENGTH}
}

func NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength int) TTransportFactory {
        return &tFramedTransportFactory{factory: factory, maxLength: maxLength}
}

func (p *tFramedTransportFactory) GetTransport(base TTransport) TTransport {
	return NewTFramedTransportMaxLength(p.factory.GetTransport(base), p.maxLength)
}

func NewTFramedTransport(transport TTransport) *TFramedTransport {
	return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: DEFAULT_MAX_LENGTH}
}

func NewTFramedTransportMaxLength(transport TTransport, maxLength int) *TFramedTransport {
	return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: maxLength}
}

func (p *TFramedTransport) Open() error {
	return p.transport.Open()
}

func (p *TFramedTransport) IsOpen() bool {
	return p.transport.IsOpen()
}

func (p *TFramedTransport) Close() error {
	return p.transport.Close()
}

func (p *TFramedTransport) Read(buf []byte) (l int, err error) {
	if p.frameSize == 0 {
		p.frameSize, err = p.readFrameHeader()
		if err != nil {
			return
		}
	}
	if p.frameSize < len(buf) {
		frameSize := p.frameSize
		tmp := make([]byte, p.frameSize)
		l, err = p.Read(tmp)
		copy(buf, tmp)
		if err == nil {
			err = NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", frameSize, len(buf)))
			return
		}
	}
	got, err := p.reader.Read(buf)
	p.frameSize = p.frameSize - got
	//sanity check
	if p.frameSize < 0 {
		return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Negative frame size")
	}
	return got, NewTTransportExceptionFromError(err)
}

func (p *TFramedTransport) ReadByte() (c byte, err error) {
	if p.frameSize == 0 {
		p.frameSize, err = p.readFrameHeader()
		if err != nil {
			return
		}
	}
	if p.frameSize < 1 {
		return 0, NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", p.frameSize, 1))
	}
	c, err = p.reader.ReadByte()
	if err == nil {
		p.frameSize--
	}
	return
}

func (p *TFramedTransport) Write(buf []byte) (int, error) {
	n, err := p.buf.Write(buf)
	return n, NewTTransportExceptionFromError(err)
}

func (p *TFramedTransport) WriteByte(c byte) error {
	return p.buf.WriteByte(c)
}

func (p *TFramedTransport) WriteString(s string) (n int, err error) {
	return p.buf.WriteString(s)
}

func (p *TFramedTransport) Flush() error {
	size := p.buf.Len()
	buf := p.buffer[:4]
	binary.BigEndian.PutUint32(buf, uint32(size))
	_, err := p.transport.Write(buf)
	if err != nil {
		return NewTTransportExceptionFromError(err)
	}
	if size > 0 {
		if n, err := p.buf.WriteTo(p.transport); err != nil {
			print("Error while flushing write buffer of size ", size, " to transport, only wrote ", n, " bytes: ", err.Error(), "\n")
			return NewTTransportExceptionFromError(err)
		}
	}
	err = p.transport.Flush()
	return NewTTransportExceptionFromError(err)
}

func (p *TFramedTransport) readFrameHeader() (int, error) {
	buf := p.buffer[:4]
	if _, err := io.ReadFull(p.reader, buf); err != nil {
		return 0, err
	}
	size := int(binary.BigEndian.Uint32(buf))
	if size < 0 || size > p.maxLength {
		return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size))
	}
	return size, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/adnanh/webhook/hook"
)

// AndRule that implements Stringer interface
type AndRule hook.AndRule

// OrRule that implements Stringer interface
type OrRule hook.OrRule

// NotRule that implements Stringer interface
type NotRule hook.NotRule

// MatchRule that implements Stringer interface
type MatchRule hook.MatchRule

// Rules that implements Stringer interface
type Rules hook.Rules

// Hook that implements Stringer interface
type Hook hook.Hook

// CompactHook that implements Stringer interface
type CompactHook hook.Hook

func (r OrRule) String() string {
	stringSlice := make([]string, len(r))

	for idx, rule := range r {
		stringSlice[idx] = fmt.Sprintf("%s", (Rules)(rule))
	}

	return fmt.Sprintf("(%s)", strings.Join(stringSlice, " or "))
}

func (r NotRule) String() string {
	return fmt.Sprintf("!(%s)", (Rules)(r))
}

func (r MatchRule) String() string {
	switch {
	case r.Type == hook.MatchValue:
		return fmt.Sprintf("matchValue(\"%s.%s\", \"%s\")", r.Parameter.Source, r.Parameter.Name, r.Value)
	case r.Type == hook.MatchRegex:
		return fmt.Sprintf("matchRegex(\"%s.%s\", \"%s\")", r.Parameter.Source, r.Parameter.Name, r.Regex)
	case r.Type == hook.MatchHashSHA1:
		return fmt.Sprintf("matchHashSHA1(\"%s.%s\", \"%s\")", r.Parameter.Source, r.Parameter.Name, r.Secret)
	default:
		return "Invalid match rule"
	}
}

func (r Rules) String() string {
	switch {
	case r.And != nil:
		return fmt.Sprintf("%s", (*AndRule)(r.And))
	case r.Or != nil:
		return fmt.Sprintf("%s", (*OrRule)(r.Or))
	case r.Not != nil:
		return fmt.Sprintf("%s", (*NotRule)(r.Not))
	case r.Match != nil:
		return fmt.Sprintf("%s", (*MatchRule)(r.Match))
	default:
		return "No rule"
	}
}

func (r *AndRule) String() string {
	stringSlice := make([]string, len(*r))

	for idx, rule := range *r {
		stringSlice[idx] = fmt.Sprintf("%s", Rules(rule))
	}

	return fmt.Sprintf("(%s)", strings.Join(stringSlice, " and "))
}

func (h Hook) String() string {
	var result []string

	result = append(result, fmt.Sprintf("HOOK ID:\n   %s\n\n", h.ID))

	if h.ExecuteCommand != "" {
		result = append(result, fmt.Sprintf("EXECUTE COMMAND:\n   %s\n\n", h.ExecuteCommand))
	}

	if h.CommandWorkingDirectory != "" {
		result = append(result, fmt.Sprintf("COMMAND WORKING DIRECTORY:\n   %s\n\n", h.CommandWorkingDirectory))
	}

	if h.JSONStringParameters != nil {
		result = append(result, fmt.Sprintf("JSON STRING PARAMETERS TO BE DECODED:\n   %s\n\n", argumentsToString(h.JSONStringParameters)))
	}

	if h.PassArgumentsToCommand != nil {
		result = append(result, fmt.Sprintf("PASS ARGUMENTS TO COMMAND:\n   %s\n\n", argumentsToString(h.PassArgumentsToCommand)))
	}

	if h.PassEnvironmentToCommand != nil {
		result = append(result, fmt.Sprintf("PASS ENVIRONMENT TO COMMAND:\n   %s\n\n", environmentToString(h.PassEnvironmentToCommand)))
	}

	if h.TriggerRule != nil {
		result = append(result, fmt.Sprintf("TRIGGER RULE:\n   %s\n\n", (*Rules)(h.TriggerRule)))
	}

	if h.ResponseMessage != "" {
		result = append(result, fmt.Sprintf("RESPONSE MESSAGE FOR THE HOOK INITIATOR:\n   %s\n\n", h.ResponseMessage))
	}

	result = append(result, fmt.Sprintf("INCLUDE COMMAND OUTPUT IN RESPONSE:\n   %t\n\n", h.CaptureCommandOutput))

	return strings.Join(result, "")
}

func (h CompactHook) String() string {
	var result []string

	result = append(result, fmt.Sprintf("%s", h.ID))

	if h.ExecuteCommand != "" {
		result = append(result, fmt.Sprintf("%s", h.ExecuteCommand))
	} else {
		result = append(result, "No command")
	}

	return strings.Join(result, " => ")
}

func argumentsToString(args []hook.Argument) string {
	argsSlice := make([]string, len(args))

	for idx, arg := range args {
		argsSlice[idx] = fmt.Sprintf("%s.%s", arg.Source, arg.Name)
	}

	return fmt.Sprintf("[%s]", strings.Join(argsSlice, ", "))
}

func environmentToString(args []hook.Argument) string {
	argsSlice := make([]string, len(args))

	for idx, arg := range args {
		argsSlice[idx] = fmt.Sprintf("HOOK_%s=%s.%s", arg.Name, arg.Source, arg.Name)
	}

	return fmt.Sprintf("[%s]", strings.Join(argsSlice, ", "))
}
<commit_msg>conform to new format<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/adnanh/webhook/hook"
)

// AndRule that implements Stringer interface
type AndRule hook.AndRule

// OrRule that implements Stringer interface
type OrRule hook.OrRule

// NotRule that implements Stringer interface
type NotRule hook.NotRule

// MatchRule that implements Stringer interface
type MatchRule hook.MatchRule

// Rules that implements Stringer interface
type Rules hook.Rules

// Hook that implements Stringer interface
type Hook hook.Hook

// CompactHook that implements Stringer interface
type CompactHook hook.Hook

func (r OrRule) String() string {
	stringSlice := make([]string, len(r))

	for idx, rule := range r {
		stringSlice[idx] = fmt.Sprintf("%s", (Rules)(rule))
	}

	return fmt.Sprintf("%s", strings.Join(stringSlice, " || "))
}

func (r NotRule) String() string {
	return fmt.Sprintf("!(%s)", (Rules)(r))
}

func (r MatchRule) String() string {
	switch {
	case r.Type == hook.MatchValue:
		return fmt.Sprintf("\"%s.%s\" == \"%s\"", r.Parameter.Source, r.Parameter.Name, r.Value)
	case r.Type == hook.MatchRegex:
		return fmt.Sprintf("\"%s.%s\" ~= \"%s\"", r.Parameter.Source, r.Parameter.Name, r.Regex)
	case r.Type == hook.MatchHashSHA1:
		return fmt.Sprintf("\"%s.%s\" == sha1(\"payload\", \"%s\")", r.Parameter.Source, r.Parameter.Name, r.Secret)
	default:
		return "<INVALID MATCH RULE>"
	}
}

func (r Rules) String() string {
	switch {
	case r.And != nil:
		return fmt.Sprintf("%s", (*AndRule)(r.And))
	case r.Or != nil:
		return fmt.Sprintf("%s", (*OrRule)(r.Or))
	case r.Not != nil:
		return fmt.Sprintf("%s", (*NotRule)(r.Not))
	case r.Match != nil:
		return fmt.Sprintf("%s", (*MatchRule)(r.Match))
	default:
		return "<NO RULE>"
	}
}

func (r *AndRule) String() string {
	stringSlice := make([]string, len(*r))

	for idx, rule := range *r {
		stringSlice[idx] = fmt.Sprintf("%s", Rules(rule))
	}

	return fmt.Sprintf("%s", strings.Join(stringSlice, " && "))
}

func (h Hook) String() string {
	var result []string

	result = append(result, fmt.Sprintf("HOOK ID:\n   %s\n\n", h.ID))

	if h.ExecuteCommand != "" {
		result = append(result, fmt.Sprintf("EXECUTE COMMAND:\n   %s\n\n", h.ExecuteCommand))
	}

	if h.CommandWorkingDirectory != "" {
		result = append(result, fmt.Sprintf("COMMAND WORKING DIRECTORY:\n   %s\n\n", h.CommandWorkingDirectory))
	}

	if h.JSONStringParameters != nil {
		result = append(result, fmt.Sprintf("JSON STRING PARAMETERS TO BE DECODED:\n   %s\n\n", argumentsToString(h.JSONStringParameters)))
	}

	if h.PassArgumentsToCommand != nil {
		result = append(result, fmt.Sprintf("PASS ARGUMENTS TO COMMAND:\n   %s\n\n", argumentsToString(h.PassArgumentsToCommand)))
	}

	if h.PassEnvironmentToCommand != nil {
		result = append(result, fmt.Sprintf("PASS ENVIRONMENT TO COMMAND:\n   %s\n\n", environmentToString(h.PassEnvironmentToCommand)))
	}

	if h.TriggerRule != nil {
		result = append(result, fmt.Sprintf("TRIGGER RULE:\n   %s\n\n", (*Rules)(h.TriggerRule)))
	}

	if h.ResponseMessage != "" {
		result = append(result, fmt.Sprintf("RESPONSE MESSAGE FOR THE HOOK INITIATOR:\n   %s\n\n", h.ResponseMessage))
	}

	result = append(result, fmt.Sprintf("INCLUDE COMMAND OUTPUT IN RESPONSE:\n   %t\n\n", h.CaptureCommandOutput))

	return strings.Join(result, "")
}

func (h CompactHook) String() string {
	var result []string

	result = append(result, fmt.Sprintf("%s", h.ID))

	if h.ExecuteCommand != "" {
		result = append(result, fmt.Sprintf("%s", h.ExecuteCommand))
	} else {
		result = append(result, "No command")
	}

	return strings.Join(result, " => ")
}

func argumentsToString(args []hook.Argument) string {
	argsSlice := make([]string, len(args))

	for idx, arg := range args {
		argsSlice[idx] = fmt.Sprintf("%s.%s", arg.Source, arg.Name)
	}

	return fmt.Sprintf("[%s]", strings.Join(argsSlice, ", "))
}

func environmentToString(args []hook.Argument) string {
	argsSlice := make([]string, len(args))

	for idx, arg := range args {
		argsSlice[idx] = fmt.Sprintf("HOOK_%s=%s.%s", arg.Name, arg.Source, arg.Name)
	}

	return fmt.Sprintf("[%s]", strings.Join(argsSlice, ", "))
}
<|endoftext|>


<commit_before>package zabbix

import (
	"github.com/AlekSi/reflector"
)

type (
	AvailableType int
	StatusType    int
)

const (
	Available   AvailableType = 1
	Unavailable AvailableType = 2

	Monitored   StatusType = 0
	Unmonitored StatusType = 1
)

// https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/definitions
type Host struct {
	HostId    string        `json:"hostid,omitempty"`
	Host      string        `json:"host"`
	Available AvailableType `json:"available"`
	Error     string        `json:"error"`
	Name      string        `json:"name"`
	Status    StatusType    `json:"status"`
	ProxyId   string        `json:"proxy_hostid,omitempty"`

	// Fields below used only when creating hosts
	GroupIds   HostGroupIds   `json:"groups,omitempty"`
	Interfaces HostInterfaces `json:"interfaces,omitempty"`
}

type Hosts []Host

// Wrapper for host.get: https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/get
func (api *API) HostsGet(params Params) (res Hosts, err error) {
	if _, present := params["output"]; !present {
		params["output"] = "extend"
	}
	response, err := api.CallWithError("host.get", params)
	if err != nil {
		return
	}

	reflector.MapsToStructs2(response.Result.([]interface{}), &res, reflector.Strconv, "json")
	return
}

// Gets hosts by host group Ids.
func (api *API) HostsGetByHostGroupIds(ids []string) (res Hosts, err error) {
	return api.HostsGet(Params{"groupids": ids})
}

// Gets hosts by host groups.
func (api *API) HostsGetByHostGroups(hostGroups HostGroups) (res Hosts, err error) {
	ids := make([]string, len(hostGroups))
	for i, id := range hostGroups {
		ids[i] = id.GroupId
	}
	return api.HostsGetByHostGroupIds(ids)
}

// Gets host by Id only if there is exactly 1 matching host.
func (api *API) HostGetById(id string) (res *Host, err error) {
	hosts, err := api.HostsGet(Params{"hostids": id})
	if err != nil {
		return
	}

	if len(hosts) == 1 {
		res = &hosts[0]
	} else {
		e := ExpectedOneResult(len(hosts))
		err = &e
	}
	return
}

// Gets host by Host only if there is exactly 1 matching host.
func (api *API) HostGetByHost(host string) (res *Host, err error) {
	hosts, err := api.HostsGet(Params{"filter": map[string]string{"host": host}})
	if err != nil {
		return
	}

	if len(hosts) == 1 {
		res = &hosts[0]
	} else {
		e := ExpectedOneResult(len(hosts))
		err = &e
	}
	return
}

// Wrapper for host.create: https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/create
func (api *API) HostsCreate(hosts Hosts) (err error) {
	response, err := api.CallWithError("host.create", hosts)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	hostids := result["hostids"].([]interface{})
	for i, id := range hostids {
		hosts[i].HostId = id.(string)
	}
	return
}

// Wrapper for host.delete: https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/delete
// Cleans HostId in all hosts elements if call succeed.
func (api *API) HostsDelete(hosts Hosts) (err error) {
	ids := make([]string, len(hosts))
	for i, host := range hosts {
		ids[i] = host.HostId
	}

	err = api.HostsDeleteByIds(ids)
	if err == nil {
		for i := range hosts {
			hosts[i].HostId = ""
		}
	}
	return
}

// Wrapper for host.delete: https://www.zabbxix.com/documentation/2.0/manual/appendix/api/host/delete
func (api *API) HostsDeleteByIds(ids []string) (err error) {
	hostIds := make([]map[string]string, len(ids))
	for i, id := range ids {
		hostIds[i] = map[string]string{"hostid": id}
	}

	response, err := api.CallWithError("host.delete", hostIds)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	hostids := result["hostids"].([]interface{})
	if len(ids) != len(hostids) {
		err = &ExpectedMore{len(ids), len(hostids)}
	}
	return
}
<commit_msg>Added 'host.exists' call<commit_after>package zabbix

import (
	"github.com/AlekSi/reflector"
)

type (
	AvailableType int
	StatusType    int
)

const (
	Available   AvailableType = 1
	Unavailable AvailableType = 2

	Monitored   StatusType = 0
	Unmonitored StatusType = 1
)

// https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/definitions
type Host struct {
	HostId    string        `json:"hostid,omitempty"`
	Host      string        `json:"host"`
	Available AvailableType `json:"available"`
	Error     string        `json:"error"`
	Name      string        `json:"name"`
	Status    StatusType    `json:"status"`
	ProxyId   string        `json:"proxy_hostid,omitempty"`

	// Fields below used only when creating hosts
	GroupIds   HostGroupIds   `json:"groups,omitempty"`
	Interfaces HostInterfaces `json:"interfaces,omitempty"`
}

type Hosts []Host

// Wrapper for host.get: https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/get
func (api *API) HostsGet(params Params) (res Hosts, err error) {
	if _, present := params["output"]; !present {
		params["output"] = "extend"
	}
	response, err := api.CallWithError("host.get", params)
	if err != nil {
		return
	}

	reflector.MapsToStructs2(response.Result.([]interface{}), &res, reflector.Strconv, "json")
	return
}

// Gets hosts by host group Ids.
func (api *API) HostsGetByHostGroupIds(ids []string) (res Hosts, err error) {
	return api.HostsGet(Params{"groupids": ids})
}

// Gets hosts by host groups.
func (api *API) HostsGetByHostGroups(hostGroups HostGroups) (res Hosts, err error) {
	ids := make([]string, len(hostGroups))
	for i, id := range hostGroups {
		ids[i] = id.GroupId
	}
	return api.HostsGetByHostGroupIds(ids)
}

// Gets host by Id only if there is exactly 1 matching host.
func (api *API) HostGetById(id string) (res *Host, err error) {
	hosts, err := api.HostsGet(Params{"hostids": id})
	if err != nil {
		return
	}

	if len(hosts) == 1 {
		res = &hosts[0]
	} else {
		e := ExpectedOneResult(len(hosts))
		err = &e
	}
	return
}

// Gets host by Host only if there is exactly 1 matching host.
func (api *API) HostGetByHost(host string) (res *Host, err error) {
	hosts, err := api.HostsGet(Params{"filter": map[string]string{"host": host}})
	if err != nil {
		return
	}

	if len(hosts) == 1 {
		res = &hosts[0]
	} else {
		e := ExpectedOneResult(len(hosts))
		err = &e
	}
	return
}

// Wrapper for host.create: https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/create
func (api *API) HostsCreate(hosts Hosts) (err error) {
	response, err := api.CallWithError("host.create", hosts)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	hostids := result["hostids"].([]interface{})
	for i, id := range hostids {
		hosts[i].HostId = id.(string)
	}
	return
}

// Wrapper for host.delete: https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/delete
// Cleans HostId in all hosts elements if call succeed.
func (api *API) HostsDelete(hosts Hosts) (err error) {
	ids := make([]string, len(hosts))
	for i, host := range hosts {
		ids[i] = host.HostId
	}

	err = api.HostsDeleteByIds(ids)
	if err == nil {
		for i := range hosts {
			hosts[i].HostId = ""
		}
	}
	return
}

// Wrapper for host.delete: https://www.zabbxix.com/documentation/2.0/manual/appendix/api/host/delete
func (api *API) HostsDeleteByIds(ids []string) (err error) {
	hostIds := make([]map[string]string, len(ids))
	for i, id := range ids {
		hostIds[i] = map[string]string{"hostid": id}
	}

	response, err := api.CallWithError("host.delete", hostIds)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	hostids := result["hostids"].([]interface{})
	if len(ids) != len(hostids) {
		err = &ExpectedMore{len(ids), len(hostids)}
	}
	return
}

// Wrapper for host.eists: https://www.zabbix.com/documentation/2.0/manual/appendix/api/host/exists
func (api *API) HostExists(host Host) (bool, error) {
	response, err := api.CallWithError("host.exists", host)
	if err != nil {
		return false, err
	}

	result := response.Result.(bool)

	return result, err
}
<|endoftext|>


<commit_before>/* This file is part of VoltDB.
 * Copyright (C) 2008-2018 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
 */

package voltdbclient

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"time"
)

type procedureInvocation struct {
	handle     int64
	isQuery    bool // as opposed to an exec.
	query      string
	params     []driver.Value
	responseCh chan voltResponse
	timeout    time.Duration
	arc        AsyncResponseConsumer
	async      bool
	slen       int // length of pi once serialized
	conn       *nodeConn
}

func newSyncProcedureInvocation(handle int64, isQuery bool, query string, params []driver.Value, responseCh chan voltResponse, timeout time.Duration) *procedureInvocation {
	return &procedureInvocation{
		handle:     handle,
		isQuery:    isQuery,
		query:      query,
		params:     params,
		responseCh: responseCh,
		timeout:    timeout,
		async:      false,
		slen:       -1,
	}
}

func newAsyncProcedureInvocation(handle int64, isQuery bool, query string, params []driver.Value, timeout time.Duration, arc AsyncResponseConsumer) *procedureInvocation {
	return &procedureInvocation{
		handle:  handle,
		isQuery: isQuery,
		query:   query,
		params:  params,
		timeout: timeout,
		arc:     arc,
		async:   true,
		slen:    -1,
	}
}

// a procedure invocation that will be processed based on its handle.
func newProcedureInvocationByHandle(handle int64, isQuery bool, query string, params []driver.Value) *procedureInvocation {
	return &procedureInvocation{
		handle:  handle,
		isQuery: isQuery,
		query:   query,
		params:  params,
		async:   true,
		slen:    -1,
	}
}

func (pi *procedureInvocation) getLen() int {
	if pi.slen == -1 {
		pi.slen = pi.calcLen()
	}
	return pi.slen
}

func (pi *procedureInvocation) calcLen() int {
	// fixed - 1 for batch timeout type, 4 for str length (proc name),
	// 8 for handle, 2 for paramCount
	var slen = 15
	slen += len(pi.query)
	for _, param := range pi.params {
		slen += pi.calcParamLen(param)
	}
	return slen
}

func (pi *procedureInvocation) calcParamLen(param interface{}) int {
	// add one to each because the type itself takes one byte
	// nil is 1
	if param == nil {
		return 1
	}
	v := reflect.ValueOf(param)
	switch v.Kind() {
	case reflect.Bool:
		return 2
	case reflect.Int8:
		return 2
	case reflect.Int16:
		return 3
	case reflect.Int32:
		return 5
	case reflect.Int64:
		return 9
	case reflect.Float64:
		return 9
	case reflect.String:
		return 5 + v.Len()
	case reflect.Slice:
		return 5 + v.Len()
	case reflect.Struct:
		if _, ok := v.Interface().(time.Time); ok {
			return 9
		}
		panic("Can't determine length of struct")

	case reflect.Ptr:
		panic("Can't marshal a pointer")
	default:
		panic(fmt.Sprintf("Can't marshal %v-type parameters", v.Kind()))
	}
}

func (pi procedureInvocation) getPassedParamCount() int {
	return len(pi.params)
}

func (pi procedureInvocation) getPartitionParamValue(index int) driver.Value {
	return pi.params[index]
}

func (pi procedureInvocation) isAsync() bool {
	return pi.async
}
<commit_msg>document  procedureInvocation<commit_after>/* This file is part of VoltDB.
 * Copyright (C) 2008-2018 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
 */

package voltdbclient

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"time"
)

type procedureInvocation struct {

	// This is a unique integer that is used to identify the procedure invocation.
	handle  int64
	isQuery bool // as opposed to an exec.

	// The SQL query to be sent to voltdb
	query  string
	params []driver.Value

	// The channel on which response from voltdb will be sent. The node connection
	// thwt will write on this channel is stored in the conn field.
	responseCh chan voltResponse
	timeout    time.Duration
	arc        AsyncResponseConsumer
	async      bool
	slen       int // length of pi once serialized

	// This is the connection that received the invocation request. It is through
	// this connection that the response to the procedure invocation will be sent.
	conn *nodeConn
}

func newSyncProcedureInvocation(handle int64, isQuery bool, query string, params []driver.Value, responseCh chan voltResponse, timeout time.Duration) *procedureInvocation {
	return &procedureInvocation{
		handle:     handle,
		isQuery:    isQuery,
		query:      query,
		params:     params,
		responseCh: responseCh,
		timeout:    timeout,
		async:      false,
		slen:       -1,
	}
}

func newAsyncProcedureInvocation(handle int64, isQuery bool, query string, params []driver.Value, timeout time.Duration, arc AsyncResponseConsumer) *procedureInvocation {
	return &procedureInvocation{
		handle:  handle,
		isQuery: isQuery,
		query:   query,
		params:  params,
		timeout: timeout,
		arc:     arc,
		async:   true,
		slen:    -1,
	}
}

// a procedure invocation that will be processed based on its handle.
func newProcedureInvocationByHandle(handle int64, isQuery bool, query string, params []driver.Value) *procedureInvocation {
	return &procedureInvocation{
		handle:  handle,
		isQuery: isQuery,
		query:   query,
		params:  params,
		async:   true,
		slen:    -1,
	}
}

func (pi *procedureInvocation) getLen() int {
	if pi.slen == -1 {
		pi.slen = pi.calcLen()
	}
	return pi.slen
}

func (pi *procedureInvocation) calcLen() int {
	// fixed - 1 for batch timeout type, 4 for str length (proc name),
	// 8 for handle, 2 for paramCount
	var slen = 15
	slen += len(pi.query)
	for _, param := range pi.params {
		slen += pi.calcParamLen(param)
	}
	return slen
}

func (pi *procedureInvocation) calcParamLen(param interface{}) int {
	// add one to each because the type itself takes one byte
	// nil is 1
	if param == nil {
		return 1
	}
	v := reflect.ValueOf(param)
	switch v.Kind() {
	case reflect.Bool:
		return 2
	case reflect.Int8:
		return 2
	case reflect.Int16:
		return 3
	case reflect.Int32:
		return 5
	case reflect.Int64:
		return 9
	case reflect.Float64:
		return 9
	case reflect.String:
		return 5 + v.Len()
	case reflect.Slice:
		return 5 + v.Len()
	case reflect.Struct:
		if _, ok := v.Interface().(time.Time); ok {
			return 9
		}
		panic("Can't determine length of struct")

	case reflect.Ptr:
		panic("Can't marshal a pointer")
	default:
		panic(fmt.Sprintf("Can't marshal %v-type parameters", v.Kind()))
	}
}

func (pi procedureInvocation) getPassedParamCount() int {
	return len(pi.params)
}

func (pi procedureInvocation) getPartitionParamValue(index int) driver.Value {
	return pi.params[index]
}

func (pi procedureInvocation) isAsync() bool {
	return pi.async
}
<|endoftext|>


<commit_before>package main

/*
This could probably be smarter, but it'll do for now.
*/

const HTML_STOPPED = `

The instances for this service are currently powered down.

Click here to start.

	`

const HTML_STARTING = `

Your service is starting, please wait.

	`

const HTML_STOPPING = `

The instances for this service are being powered down.

	`

const HTML_UNHEALTHY = `

The instances for this service appear to be in an unhealthy or inconsistent state.

	`

const HTML_ERROR = `

An error occured processing your request: %v

	`
<commit_msg>Add auto page refresh when starting/stopping<commit_after>package main

/*
This could probably be smarter, but it'll do for now.
*/

const HTML_STOPPED = `

The instances for this service are currently powered down.

Click here to start.

	`

const HTML_STARTING = `

Your service is starting, please wait.

	`

const HTML_STOPPING = `

The instances for this service are being powered down.

	`

const HTML_UNHEALTHY = `

The instances for this service appear to be in an unhealthy or inconsistent state.

	`

const HTML_ERROR = `

An error occured processing your request: %v

	`
<|endoftext|>


<commit_before>package main

import (
    "log"
    "strconv"
    "net/http"
    "huabot-sched/db"
    "github.com/go-martini/martini"
    "github.com/martini-contrib/render"
    "github.com/martini-contrib/binding"
)

const API = ""

func StartHttpServer(addr string) {
    mart := martini.Classic()
    mart.Use(render.Renderer(render.Options{
        Directory: "templates",
        Layout: "layout",
        Extensions: []string{".tmpl", ".html"},
        Charset: "UTF-8",
        IndentJSON: true,
        IndentXML: true,
        HTMLContentType: "application/xhtml+xml",
    }))

    api(mart)

    mart.RunOnAddr(addr)
}

type JobForm struct {
    Name    string `form:"name" binding:"required"`
    Timeout int    `form:"timeout"`
    SchedAt int    `form:"sched_at" binding:"required"`
}

func api(mart *martini.ClassicMartini) {

    mart.Post(API + "/jobs/", binding.Bind(JobForm{}), func(j JobForm, r render.Render) {
        job := db.Job{
            Name: j.Name,
            Timeout: j.Timeout,
            SchedAt: j.SchedAt,
            Status: "ready",
        }
        err := job.Save()
        if err != nil {
            r.JSON(http.StatusInternalServerError, map[string]interface{}{"err": err.Error()})
            return
        }
        r.JSON(http.StatusOK, map[string]db.Job{"job": job})
    })

    mart.Get(API + "/jobs/(?P<job_id>[0-9]+)", func(params martini.Params, r render.Render) {
        jobId, _ := strconv.Atoi(params["job_id"])
        job, err := db.GetJob(jobId)
        if err != nil {
            r.JSON(http.StatusInternalServerError, map[string]interface{}{"err": err.Error()})
            return
        }
        r.JSON(http.StatusOK, map[string]db.Job{"job": job})
    })

    mart.Get(API + "/jobs/(?P<status>ready|doing)/", func(params martini.Params, req *http.Request, r render.Render) {
        status := params["status"]
        qs := req.URL.Query()
        var start, limit, stop int
        var err error
        if start, err = strconv.Atoi(qs.Get("start")); err != nil {
            start = 0
        }
        if limit, err = strconv.Atoi(qs.Get("limit")); err != nil {
            limit = 10
        }
        stop = start + limit
        var jobs []db.Job
        var count int
        jobs, err = db.RangeSchedJob(status, start, stop)
        count, _ = db.CountSchedJob(status)
        if err != nil {
            log.Printf("Error: RangeSchedJob error %s\n", err)
            r.JSON(http.StatusOK, map[string]interface{}{"jobs": "[]", "total": count, "current": start})
            return
        }
        r.JSON(http.StatusOK, map[string]interface{}{"jobs": jobs, "total": count, "current": start})
    })

    mart.Get(API + "/jobs/", func(req *http.Request, r render.Render) {
        qs := req.URL.Query()
        var start, limit, stop int
        var err error
        if start, err = strconv.Atoi(qs.Get("start")); err != nil {
            start = 0
        }
        if limit, err = strconv.Atoi(qs.Get("limit")); err != nil {
            limit = 10
        }
        stop = start + limit
        var jobs []db.Job
        var count int
        jobs, err = db.RangeJob(start, stop)
        count, _ = db.CountJob()
        if err != nil {
            log.Printf("Error: RangeSchedJob error %s\n", err)
            r.JSON(http.StatusOK, map[string]interface{}{"jobs": "[]", "total": count, "current": start})
            return
        }
        r.JSON(http.StatusOK, map[string]interface{}{"jobs": jobs, "total": count, "current": start})
    })

    mart.Delete(API + "/jobs/(?P<job_id>[0-9]+)", func(params martini.Params, r render.Render) {
        id, _ := strconv.Atoi(params["job_id"])
        err := db.DelJob(id)
        if err != nil {
            log.Printf("Error: DelJob error %s\n", err)
            r.JSON(http.StatusOK, map[string]interface{}{"err": err.Error()})
            return
        }
        r.JSON(http.StatusNotFound, map[string]interface{}{})
    })
}
<commit_msg>update log<commit_after>package main

import (
    "log"
    "strconv"
    "net/http"
    "huabot-sched/db"
    "github.com/go-martini/martini"
    "github.com/martini-contrib/render"
    "github.com/martini-contrib/binding"
)

const API = ""

func StartHttpServer(addr string) {
    mart := martini.Classic()
    mart.Use(render.Renderer(render.Options{
        Directory: "templates",
        Layout: "layout",
        Extensions: []string{".tmpl", ".html"},
        Charset: "UTF-8",
        IndentJSON: true,
        IndentXML: true,
        HTMLContentType: "application/xhtml+xml",
    }))

    api(mart)

    mart.RunOnAddr(addr)
}

type JobForm struct {
    Name    string `form:"name" binding:"required"`
    Timeout int    `form:"timeout"`
    SchedAt int    `form:"sched_at" binding:"required"`
}

func api(mart *martini.ClassicMartini) {

    mart.Post(API + "/jobs/", binding.Bind(JobForm{}), func(j JobForm, r render.Render) {
        job := db.Job{
            Name: j.Name,
            Timeout: j.Timeout,
            SchedAt: j.SchedAt,
            Status: "ready",
        }
        err := job.Save()
        if err != nil {
            r.JSON(http.StatusInternalServerError, map[string]interface{}{"err": err.Error()})
            return
        }
        r.JSON(http.StatusOK, map[string]db.Job{"job": job})
    })

    mart.Get(API + "/jobs/(?P<job_id>[0-9]+)", func(params martini.Params, r render.Render) {
        jobId, _ := strconv.Atoi(params["job_id"])
        job, err := db.GetJob(jobId)
        if err != nil {
            r.JSON(http.StatusInternalServerError, map[string]interface{}{"err": err.Error()})
            return
        }
        r.JSON(http.StatusOK, map[string]db.Job{"job": job})
    })

    mart.Get(API + "/jobs/(?P<status>ready|doing)/", func(params martini.Params, req *http.Request, r render.Render) {
        status := params["status"]
        qs := req.URL.Query()
        var start, limit, stop int
        var err error
        if start, err = strconv.Atoi(qs.Get("start")); err != nil {
            start = 0
        }
        if limit, err = strconv.Atoi(qs.Get("limit")); err != nil {
            limit = 10
        }
        stop = start + limit
        var jobs []db.Job
        var count int
        jobs, err = db.RangeSchedJob(status, start, stop)
        count, _ = db.CountSchedJob(status)
        if err != nil {
            log.Printf("Error: RangeSchedJob error %s\n", err)
            r.JSON(http.StatusOK, map[string]interface{}{"jobs": "[]", "total": count, "current": start})
            return
        }
        r.JSON(http.StatusOK, map[string]interface{}{"jobs": jobs, "total": count, "current": start})
    })

    mart.Get(API + "/jobs/", func(req *http.Request, r render.Render) {
        qs := req.URL.Query()
        var start, limit, stop int
        var err error
        if start, err = strconv.Atoi(qs.Get("start")); err != nil {
            start = 0
        }
        if limit, err = strconv.Atoi(qs.Get("limit")); err != nil {
            limit = 10
        }
        stop = start + limit
        var jobs []db.Job
        var count int
        jobs, err = db.RangeJob(start, stop)
        count, _ = db.CountJob()
        if err != nil {
            log.Printf("Error: RangeJob error %s\n", err)
            r.JSON(http.StatusOK, map[string]interface{}{"jobs": "[]", "total": count, "current": start})
            return
        }
        r.JSON(http.StatusOK, map[string]interface{}{"jobs": jobs, "total": count, "current": start})
    })

    mart.Delete(API + "/jobs/(?P<job_id>[0-9]+)", func(params martini.Params, r render.Render) {
        id, _ := strconv.Atoi(params["job_id"])
        err := db.DelJob(id)
        if err != nil {
            log.Printf("Error: DelJob error %s\n", err)
            r.JSON(http.StatusOK, map[string]interface{}{"err": err.Error()})
            return
        }
        r.JSON(http.StatusNotFound, map[string]interface{}{})
    })
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/base64"
	"errors"
	"github.com/heroku/slog"
	"io"
	"net/http"
	"strings"
	"sync"
	"time"
)

type Payload struct {
	SourceAddr string
	RequestId  string
	Body       []byte
	WaitCh     chan bool
}

type FixerFunc func(io.Reader, slog.Context, string, string) ([]byte, error)

type HttpServer struct {
	Config         *IssConfig
	FixerFunc      FixerFunc
	Outlet         chan *Payload
	InFlightWg     sync.WaitGroup
	ShutdownCh     ShutdownCh
	isShuttingDown bool
}

func NewHttpServer(config *IssConfig, fixerFunc FixerFunc, outlet chan *Payload) *HttpServer {
	return &HttpServer{
		Config:         config,
		FixerFunc:      fixerFunc,
		Outlet:         outlet,
		ShutdownCh:     make(chan int),
		isShuttingDown: false,
	}
}

func (s *HttpServer) Run() error {
	go s.awaitShutdown()

	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		ctx := slog.Context{}
		defer func() { LogContext(ctx) }()

		if s.isShuttingDown {
			http.Error(w, "Shutting down", 503)
			return
		}

		// check outlet depth?
		ctx.Count("log-iss.http.health.get", 1)
	})

	http.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		ctx := slog.Context{}
		defer func() { LogContext(ctx) }()

		if s.Config.EnforceSsl && r.Header.Get("X-Forwarded-Proto") != "https" {
			http.Error(w, "Only SSL requests accepted", 400)
			return
		}

		if s.isShuttingDown {
			http.Error(w, "Shutting down", 503)
			return
		}

		if r.Method != "POST" {
			http.Error(w, "Only POST is accepted", 400)
			return
		}

		if r.Header.Get("Content-Type") != "application/logplex-1" {
			http.Error(w, "Only Content-Type application/logplex-1 is accepted", 400)
			return
		}

		if err := s.checkAuth(r); err != nil {
			http.Error(w, err.Error(), 401)
			return
		}

		remoteAddr := r.Header.Get("X-Forwarded-For")
		if remoteAddr == "" {
			remoteAddrParts := strings.Split(r.RemoteAddr, ":")
			remoteAddr = strings.Join(remoteAddrParts[:len(remoteAddrParts)-1], ":")
		}
		ctx.Add("remote_addr", remoteAddr)

		requestId := r.Header.Get("X-Request-Id")
		ctx.Add("request_id", requestId)

		logplexDrainToken := r.Header.Get("Logplex-Drain-Token")
		ctx.Add("logdrain_token", logplexDrainToken)

		defer func() {
			ctx.Measure("log-iss.http.logs.post.duration", time.Since(start))
		}()

		if err, status := s.process(r.Body, ctx, remoteAddr, requestId, logplexDrainToken); err != nil {
			http.Error(w, err.Error(), status)
			ctx.Count("log-iss.http.logs.post.error", 1)
			ctx.Add("post.error", err)
			ctx.Add("status", status)
			return
		}

		ctx.Add("log-iss.http.logs.post.success", 1)
	})

	if err := http.ListenAndServe(":"+s.Config.HttpPort, nil); err != nil {
		return err
	}

	return nil
}

func (s *HttpServer) awaitShutdown() {
	<-s.ShutdownCh
	Logf("ns=http at=shutdown")
	s.isShuttingDown = true
}

func (s *HttpServer) checkAuth(r *http.Request) error {
	header := r.Header.Get("Authorization")
	if header == "" {
		return errors.New("Authorization required")
	}
	headerParts := strings.SplitN(header, " ", 2)
	if len(headerParts) != 2 {
		return errors.New("Authorization header is malformed")
	}

	method := headerParts[0]
	if method != "Basic" {
		return errors.New("Only Basic Authorization is accepted")
	}

	encodedUserPass := headerParts[1]
	decodedUserPass, err := base64.StdEncoding.DecodeString(encodedUserPass)
	if err != nil {
		return errors.New("Authorization header is malformed")
	}

	userPassParts := bytes.SplitN(decodedUserPass, []byte{':'}, 2)
	if len(userPassParts) != 2 {
		return errors.New("Authorization header is malformed")
	}

	user := userPassParts[0]
	pass := userPassParts[1]
	token, ok := s.Config.Tokens[string(user)]
	if !ok {
		return errors.New("Unknown user")
	}
	if token != string(pass) {
		return errors.New("Incorrect token")
	}

	return nil
}

func (s *HttpServer) process(r io.Reader, ctx slog.Context, remoteAddr string, requestId string, logplexDrainToken string) (error, int) {
	s.InFlightWg.Add(1)
	defer s.InFlightWg.Done()

	var start time.Time

	start = time.Now()
	fixedBody, err := s.FixerFunc(r, ctx, remoteAddr, logplexDrainToken)
	if err != nil {
		return errors.New("Problem processing body"), 400
	}
	ctx.Measure("log-iss.http.logs.fixer-func.duration", time.Since(start))

	waitCh := make(chan bool, 1)
	deadlineCh := time.After(time.Duration(5) * time.Second)

	start = time.Now()
	select {
	case s.Outlet <- &Payload{remoteAddr, requestId, fixedBody, waitCh}:
	case <-deadlineCh:
		ctx.Count("log-iss.http.logs.send.error", 1)
		return errors.New("Timeout delivering message"), 504
	}
	ctx.Measure("log-iss.http.logs.send.duration", time.Since(start))

	start = time.Now()
	select {
	case <-waitCh:
	case <-deadlineCh:
		ctx.Count("log-iss.http.logs.wait.error", 1)
		return errors.New("Timeout delivering message"), 504
	}
	ctx.Measure("log-iss.http.logs.wait.duration", time.Since(start))

	return nil, 200
}
<commit_msg>No need to wrap this in a func()<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"errors"
	"github.com/heroku/slog"
	"io"
	"net/http"
	"strings"
	"sync"
	"time"
)

type Payload struct {
	SourceAddr string
	RequestId  string
	Body       []byte
	WaitCh     chan bool
}

type FixerFunc func(io.Reader, slog.Context, string, string) ([]byte, error)

type HttpServer struct {
	Config         *IssConfig
	FixerFunc      FixerFunc
	Outlet         chan *Payload
	InFlightWg     sync.WaitGroup
	ShutdownCh     ShutdownCh
	isShuttingDown bool
}

func NewHttpServer(config *IssConfig, fixerFunc FixerFunc, outlet chan *Payload) *HttpServer {
	return &HttpServer{
		Config:         config,
		FixerFunc:      fixerFunc,
		Outlet:         outlet,
		ShutdownCh:     make(chan int),
		isShuttingDown: false,
	}
}

func (s *HttpServer) Run() error {
	go s.awaitShutdown()

	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		ctx := slog.Context{}
		defer func() { LogContext(ctx) }()

		if s.isShuttingDown {
			http.Error(w, "Shutting down", 503)
			return
		}

		// check outlet depth?
		ctx.Count("log-iss.http.health.get", 1)
	})

	http.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		ctx := slog.Context{}
		defer func() { LogContext(ctx) }()

		if s.Config.EnforceSsl && r.Header.Get("X-Forwarded-Proto") != "https" {
			http.Error(w, "Only SSL requests accepted", 400)
			return
		}

		if s.isShuttingDown {
			http.Error(w, "Shutting down", 503)
			return
		}

		if r.Method != "POST" {
			http.Error(w, "Only POST is accepted", 400)
			return
		}

		if r.Header.Get("Content-Type") != "application/logplex-1" {
			http.Error(w, "Only Content-Type application/logplex-1 is accepted", 400)
			return
		}

		if err := s.checkAuth(r); err != nil {
			http.Error(w, err.Error(), 401)
			return
		}

		remoteAddr := r.Header.Get("X-Forwarded-For")
		if remoteAddr == "" {
			remoteAddrParts := strings.Split(r.RemoteAddr, ":")
			remoteAddr = strings.Join(remoteAddrParts[:len(remoteAddrParts)-1], ":")
		}
		ctx.Add("remote_addr", remoteAddr)

		requestId := r.Header.Get("X-Request-Id")
		ctx.Add("request_id", requestId)

		logplexDrainToken := r.Header.Get("Logplex-Drain-Token")
		ctx.Add("logdrain_token", logplexDrainToken)

		defer ctx.Measure("log-iss.http.logs.post.duration", time.Since(start))

		if err, status := s.process(r.Body, ctx, remoteAddr, requestId, logplexDrainToken); err != nil {
			http.Error(w, err.Error(), status)
			ctx.Count("log-iss.http.logs.post.error", 1)
			ctx.Add("post.error", err)
			ctx.Add("status", status)
			return
		}

		ctx.Add("log-iss.http.logs.post.success", 1)
	})

	if err := http.ListenAndServe(":"+s.Config.HttpPort, nil); err != nil {
		return err
	}

	return nil
}

func (s *HttpServer) awaitShutdown() {
	<-s.ShutdownCh
	Logf("ns=http at=shutdown")
	s.isShuttingDown = true
}

func (s *HttpServer) checkAuth(r *http.Request) error {
	header := r.Header.Get("Authorization")
	if header == "" {
		return errors.New("Authorization required")
	}
	headerParts := strings.SplitN(header, " ", 2)
	if len(headerParts) != 2 {
		return errors.New("Authorization header is malformed")
	}

	method := headerParts[0]
	if method != "Basic" {
		return errors.New("Only Basic Authorization is accepted")
	}

	encodedUserPass := headerParts[1]
	decodedUserPass, err := base64.StdEncoding.DecodeString(encodedUserPass)
	if err != nil {
		return errors.New("Authorization header is malformed")
	}

	userPassParts := bytes.SplitN(decodedUserPass, []byte{':'}, 2)
	if len(userPassParts) != 2 {
		return errors.New("Authorization header is malformed")
	}

	user := userPassParts[0]
	pass := userPassParts[1]
	token, ok := s.Config.Tokens[string(user)]
	if !ok {
		return errors.New("Unknown user")
	}
	if token != string(pass) {
		return errors.New("Incorrect token")
	}

	return nil
}

func (s *HttpServer) process(r io.Reader, ctx slog.Context, remoteAddr string, requestId string, logplexDrainToken string) (error, int) {
	s.InFlightWg.Add(1)
	defer s.InFlightWg.Done()

	var start time.Time

	start = time.Now()
	fixedBody, err := s.FixerFunc(r, ctx, remoteAddr, logplexDrainToken)
	if err != nil {
		return errors.New("Problem processing body"), 400
	}
	ctx.Measure("log-iss.http.logs.fixer-func.duration", time.Since(start))

	waitCh := make(chan bool, 1)
	deadlineCh := time.After(time.Duration(5) * time.Second)

	start = time.Now()
	select {
	case s.Outlet <- &Payload{remoteAddr, requestId, fixedBody, waitCh}:
	case <-deadlineCh:
		ctx.Count("log-iss.http.logs.send.error", 1)
		return errors.New("Timeout delivering message"), 504
	}
	ctx.Measure("log-iss.http.logs.send.duration", time.Since(start))

	start = time.Now()
	select {
	case <-waitCh:
	case <-deadlineCh:
		ctx.Count("log-iss.http.logs.wait.error", 1)
		return errors.New("Timeout delivering message"), 504
	}
	ctx.Measure("log-iss.http.logs.wait.duration", time.Since(start))

	return nil, 200
}
<|endoftext|>


<commit_before>package _tests_test

import (
	. "github.com/onsi/ginkgo"
	// . "github.com/onsi/gomega"
)

var _ = Describe("Config", func() {
	Context("with a deployed app", func() {

		XIt("can list environment variables", func() {
			// "deis config:set FOO=bar--app=%s"
			// "deis run env --app=%s"
			// "deis config:list --app=%s"
		})

		XIt("can set an integer environment variable", func() {
			// "deis config:set FOO=10 --app=%s"
			// "deis run env --app=%s"
		})

		XIt("can set an environment variable containing spaces", func() {
			// "config:set POWERED_BY=\"the Deis team\" --app={{.AppName}}"
			// "deis run env --app=%s"
		})

		XIt("can set a multi-line environment variable", func() {
			// `deis config:set FOO="This is a
			//multiline string" --app={{.AppName}}`
			// "deis run env --app=%s"
		})

		XIt("can set an environment variable with multibyte chars", func() {
			// "deis config:set FOO=讲台 --app=%s"
			// "deis run env --app=%s"
		})

		XIt("can unset an environment variable", func() {
			// "deis config:set FOO=bar --app=%s"
			// "deis run env --app=%s"
			// "deis config:unset FOO --app=%s"
			// "deis run env --app=%s"
		})

		XIt("can pull the configuration to an .env file", func() {

		})

		XIt("can push the configuration from an .env file", func() {

		})
	})
})
<commit_msg>feat(_tests/config_test.go): add more config tests<commit_after>package _tests_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Config", func() {
	Context("with a deployed app", func() {
		appName := getRandAppName()
		BeforeEach(func() {
			login(url, testUser, testPassword)
		})

		It("can create a new app", func() {
			output, err := execute("deis apps:create %s", appName)
			Expect(err).NotTo(HaveOccurred())
			Expect(output).To(SatisfyAll(
				ContainSubstring("Creating Application... done, created %s", appName),
				ContainSubstring("Git remote deis added"),
				ContainSubstring("remote available at ")))
		})

		It("can list environment variables", func() {
			out, err := execute("deis config:set FOO=bar -a=%s", appName)
			Expect(err).NotTo(HaveOccurred())
			Expect(out).To(SatisfyAll(
				ContainSubstring("Creating config... done"),
				ContainSubstring("FOO      bar"),
				ContainSubstring("=== %s Config", appName),
			))
			out, err = execute("deis config:list --a=%s", appName)
			Expect(err).NotTo(HaveOccurred())
			Expect(out).To(SatisfyAll(
				ContainSubstring("=== %s Config", appName),
				ContainSubstring("FOO      bar"),
			))
			// TODO: the following won't work as-is because there is no app running
			// "deis run env --app=%s"

		})

		XIt("can set an integer environment variable", func() {
			// "deis config:set FOO=10 --app=%s"
			// "deis run env --app=%s"
		})

		XIt("can set an environment variable containing spaces", func() {
			// "config:set POWERED_BY=\"the Deis team\" --app={{.AppName}}"
			// "deis run env --app=%s"
		})

		XIt("can set a multi-line environment variable", func() {
			// `deis config:set FOO="This is a
			//multiline string" --app={{.AppName}}`
			// "deis run env --app=%s"
		})

		XIt("can set an environment variable with multibyte chars", func() {
			// "deis config:set FOO=讲台 --app=%s"
			// "deis run env --app=%s"
		})

		XIt("can unset an environment variable", func() {
			// "deis config:set FOO=bar --app=%s"
			// "deis run env --app=%s"
			// "deis config:unset FOO --app=%s"
			// "deis run env --app=%s"
		})

		XIt("can pull the configuration to an .env file", func() {

		})

		XIt("can push the configuration from an .env file", func() {

		})
	})
})
<|endoftext|>


<commit_before>package tools

import (
	"log"
	"net/http"
)

type LogResponseWriter struct {
	ct     []byte
	status int
	w      http.ResponseWriter
}

type HandlerInfo struct {
	Method  string
	Handler http.HandlerFunc
}

type LogMux struct {
	mux    *http.ServeMux
	lw     *LogResponseWriter
	logger *log.Logger
	mp     map[string]HandlerInfo
}

func NewLogMux(fn, prefix string) *LogMux {
	lm := &LogMux{}
	lm.mux = http.NewServeMux()
	lm.lw = &LogResponseWriter{}
	lm.logger = InitLog(fn, prefix)
	if lm.logger == nil {
		return nil
	}
	lm.mp = make(map[string]HandlerInfo)
	return lm
}

// implement http.ResponseWriter

func (lw *LogResponseWriter) Header() http.Header {
	return lw.w.Header()
}

func (lw *LogResponseWriter) Write(bt []byte) (int, error) {
	lw.ct = bt
	return lw.w.Write(bt)
}

func (lw *LogResponseWriter) WriteHeader(statusCode int) {
	lw.status = statusCode
	lw.w.WriteHeader(statusCode)
}

// implement http.ResponseWriter end

func (l *LogMux) GET(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var hi HandlerInfo
	hi.Handler = handler
	hi.Method = "GET"
	l.mp[pattern] = hi
	l.mux.HandleFunc(pattern, handler)
}

func (l *LogMux) POST(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var hi HandlerInfo
	hi.Handler = handler
	hi.Method = "POST"
	l.mp[pattern] = hi
	l.mux.HandleFunc(pattern, handler)
}

func (l *LogMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h, p := l.mux.Handler(r)
	hi, ok := l.mp[p]
	if ok == false {
		http.NotFound(w, r)
		return
	}
	if r.Method != hi.Method {
		w.Write([]byte("Not allowed"))
		return
	}
	l.lw.w = w
	h.ServeHTTP(l.lw, r)
	l.logger.Println(r.URL.Path+r.URL.RawQuery, string(l.lw.ct))
}
<commit_msg>add log<commit_after>package tools

import (
	"log"
	"net/http"
)

type LogResponseWriter struct {
	ct     []byte
	status int
	w      http.ResponseWriter
}

type HandlerInfo struct {
	Method  string
	Handler http.HandlerFunc
}

type LogMux struct {
	mux    *http.ServeMux
	lw     *LogResponseWriter
	logger *log.Logger
	mp     map[string]HandlerInfo
}

func NewLogMux(fn, prefix string) *LogMux {
	lm := &LogMux{}
	lm.mux = http.NewServeMux()
	lm.lw = &LogResponseWriter{}
	lm.logger = InitLog(fn, prefix)
	if lm.logger == nil {
		return nil
	}
	lm.mp = make(map[string]HandlerInfo)
	return lm
}

// implement http.ResponseWriter

func (lw *LogResponseWriter) Header() http.Header {
	return lw.w.Header()
}

func (lw *LogResponseWriter) Write(bt []byte) (int, error) {
	lw.ct = bt
	return lw.w.Write(bt)
}

func (lw *LogResponseWriter) WriteHeader(statusCode int) {
	lw.status = statusCode
	lw.w.WriteHeader(statusCode)
}

// implement http.ResponseWriter end

func (l *LogMux) GET(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var hi HandlerInfo
	hi.Handler = handler
	hi.Method = "GET"
	l.mp[pattern] = hi
	l.mux.HandleFunc(pattern, handler)
}

func (l *LogMux) POST(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	var hi HandlerInfo
	hi.Handler = handler
	hi.Method = "POST"
	l.mp[pattern] = hi
	l.mux.HandleFunc(pattern, handler)
}

func (l *LogMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h, p := l.mux.Handler(r)
	hi, ok := l.mp[p]
	if ok == false {
		http.NotFound(w, r)
		return
	}
	if r.Method != hi.Method {
		w.Write([]byte("Not allowed"))
		return
	}
	l.lw.w = w
	h.ServeHTTP(l.lw, r)
	l.logger.Println(r.URL.Path+r.URL.RawQuery, string(l.lw.ct), h, p, r)
    l.logger.Println(h, p)
}
<|endoftext|>