<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 etcdv3

import (
	"github.com/golang/protobuf/proto"
	"github.com/ligato/cn-infra/db/keyval"
)

// ProtoWrapperEtcd is decorator that allows to read/write proto file modelled data.
// It marshals/unmarshals go structures to slice of bytes and vice versa behind the scenes.
type ProtoWrapperEtcd struct {
	broker     *BytesConnectionEtcd
	serializer keyval.Serializer
}

type protoBrokerEtcd struct {
	broker     keyval.BytesBroker
	serializer keyval.Serializer
}

type protoWatcherEtcd struct {
	watcher    keyval.BytesWatcher
	serializer keyval.Serializer
}

// protoKeyValIterator is an iterator returned by ListValues call
type protoKeyValIterator struct {
	ctx        keyval.BytesKeyValIterator
	serializer keyval.Serializer
}

// protoKeyIterator is an iterator returned by ListKeys call
type protoKeyIterator struct {
	ctx keyval.BytesKeyIterator
}

// protoKeyVal represents single key-value pair
type protoKeyVal struct {
	pair       keyval.BytesKeyVal
	serializer keyval.Serializer
}

// NewProtoWrapperEtcd initializes proto decorator. The default serializer is used - SerializerProto.
func NewProtoWrapperEtcd(db *BytesConnectionEtcd) *ProtoWrapperEtcd {
	return &ProtoWrapperEtcd{db, &keyval.SerializerProto{}}
}

// NewProtoWrapperWithSerializer initializes proto decorator with the specified serializer.
func NewProtoWrapperWithSerializer(db *BytesConnectionEtcd, serializer keyval.Serializer) *ProtoWrapperEtcd {
	return &ProtoWrapperEtcd{db, serializer}
}

// Close closes underlying connection to etcd. Beware: if the connection is shared among multiple instances this might
// unintentionally cancel the connection.
func (db *ProtoWrapperEtcd) Close() error {
	return db.broker.Close()
}

// NewBroker creates a new instance of the proxy that shares the underlying connection
// and allows to read/edit key-value pairs.
func (db *ProtoWrapperEtcd) NewBroker(prefix string) keyval.ProtoBroker {
	return &protoBrokerEtcd{db.broker.NewBroker(prefix), db.serializer}
}

// NewWatcher creates a new instance of the proxy that shares the underlying connection
// and allows to subscribe for watching of the changes.
func (db *ProtoWrapperEtcd) NewWatcher(prefix string) keyval.ProtoWatcher {
	return &protoWatcherEtcd{db.broker.NewWatcher(prefix), db.serializer}
}

// NewTxn creates a new Data Broker transaction. A transaction can
// holds multiple operations that are all committed to the data
// store together. After a transaction has been created, one or
// more operations (put or delete) can be added to the transaction
// before it is committed.
func (db *ProtoWrapperEtcd) NewTxn() keyval.ProtoTxn {
	return &protoTxn{txn: db.broker.NewTxn(), serializer: db.serializer}
}

// NewTxn creates a new Data Broker transaction. A transaction can
// holds multiple operations that are all committed to the data
// store together. After a transaction has been created, one or
// more operations (put or delete) can be added to the transaction
// before it is committed.
func (pdb *protoBrokerEtcd) NewTxn() keyval.ProtoTxn {
	return &protoTxn{txn: pdb.broker.NewTxn(), serializer: pdb.serializer}
}

// Put writes the provided key-value item into the data store.
//
// Returns an error if the item could not be written, ok otherwise.
func (db *ProtoWrapperEtcd) Put(key string, value proto.Message, opts ...keyval.PutOption) error {
	return putProtoInternal(db.broker, db.serializer, key, value, opts...)
}

// Put writes the provided key-value item into the data store.
//
// Returns an error if the item could not be written, ok otherwise.
func (pdb *protoBrokerEtcd) Put(key string, value proto.Message, opts ...keyval.PutOption) error {
	return putProtoInternal(pdb.broker, pdb.serializer, key, value, opts...)
}

func putProtoInternal(broker keyval.BytesBroker, serializer keyval.Serializer, key string, value proto.Message, opts ...keyval.PutOption) error {
	// Marshal value to protobuf
	binData, err := serializer.Marshal(value)
	if err != nil {
		return err
	}
	broker.Put(key, binData, opts...)
	return nil
}

// Delete removes from data store key-value items stored under key.
func (db *ProtoWrapperEtcd) Delete(key string) (existed bool, err error) {
	return db.broker.Delete(key)
}

// Delete removes from data store key-value items stored under key.
func (pdb *protoBrokerEtcd) Delete(key string) (existed bool, err error) {
	return pdb.broker.Delete(key)
}

// Watch subscribes for changes in Data Store associated with the key. respChannel is used for delivery watch events
func (db *ProtoWrapperEtcd) Watch(resp chan keyval.ProtoWatchResp, keys ...string) error {
	byteCh := make(chan keyval.BytesWatchResp, 0)
	err := db.broker.Watch(byteCh, keys...)
	if err != nil {
		return err
	}
	go func() {
		for msg := range byteCh {
			resp <- NewWatchResp(db.serializer, msg)
		}
	}()
	return nil
}

// GetValue retrieves one key-value item from the data store. The item
// is identified by the provided key.
//
// If the item was found, its value is unmarshaled and placed in
// the `reqObj` message buffer and the function returns found=true.
// If the object was not found, the function returns found=false.
// Function returns revision=revision of the latest modification
// If an error was encountered, the function returns an error.
func (db *ProtoWrapperEtcd) GetValue(key string, reqObj proto.Message) (found bool, revision int64, err error) {
	return getValueProtoInternal(db.broker, db.serializer, key, reqObj)
}

// GetValue retrieves one key-value item from the data store. The item
// is identified by the provided key.
//
// If the item was found, its value is unmarshaled and placed in
// the `reqObj` message buffer and the function returns found=true.
// If the object was not found, the function returns found=false.
// Function returns revision=revision of the latest modification
// If an error was encountered, the function returns an error.
func (pdb *protoBrokerEtcd) GetValue(key string, reqObj proto.Message) (found bool, revision int64, err error) {
	return getValueProtoInternal(pdb.broker, pdb.serializer, key, reqObj)
}

func getValueProtoInternal(broker keyval.BytesBroker, serializer keyval.Serializer, key string, reqObj proto.Message) (found bool, revision int64, err error) {
	// get data from etcdv3
	resp, found, rev, err := broker.GetValue(key)
	if err != nil {
		return false, 0, err
	}

	if !found {
		return false, 0, nil
	}

	err = serializer.Unmarshal(resp, reqObj)
	if err != nil {
		return false, 0, err
	}
	return true, rev, nil
}

// ListValues retrieves an iterator for elements stored under the provided key.
func (db *ProtoWrapperEtcd) ListValues(key string) (keyval.ProtoKeyValIterator, error) {
	return listValuesProtoInternal(db.broker, db.serializer, key)
}

// ListValues retrieves an iterator for elements stored under the provided key.
func (pdb *protoBrokerEtcd) ListValues(key string) (keyval.ProtoKeyValIterator, error) {
	return listValuesProtoInternal(pdb.broker, pdb.serializer, key)
}

func listValuesProtoInternal(broker keyval.BytesBroker, serializer keyval.Serializer, key string) (keyval.ProtoKeyValIterator, error) {
	// get data from etcdv3
	ctx, err := broker.ListValues(key)
	if err != nil {
		return nil, err
	}
	return &protoKeyValIterator{ctx, serializer}, nil
}

// ListKeys is similar to the ListValues the difference is that values are not fetched
func (db *ProtoWrapperEtcd) ListKeys(prefix string) (keyval.ProtoKeyIterator, error) {
	return listKeysProtoInternal(db.broker, prefix)
}

// ListKeys is similar to the ListValues the difference is that values are not fetched
func (pdb *protoBrokerEtcd) ListKeys(prefix string) (keyval.ProtoKeyIterator, error) {
	return listKeysProtoInternal(pdb.broker, prefix)
}

func listKeysProtoInternal(broker keyval.BytesBroker, prefix string) (keyval.ProtoKeyIterator, error) {
	// get data from etcdv3
	ctx, err := broker.ListKeys(prefix)
	if err != nil {
		return nil, err
	}
	return &protoKeyIterator{ctx}, nil
}

// ListValuesRange retrieves an iterator for elements stored in specified range.
func (db *ProtoWrapperEtcd) ListValuesRange(fromPrefix string, toPrefix string) (keyval.ProtoKeyValIterator, error) {

	ctx, err := db.broker.ListValuesRange(fromPrefix, toPrefix)
	if err != nil {
		return nil, err
	}
	return &protoKeyValIterator{ctx, db.serializer}, nil
}

// GetNext returns the following item from the result set. If data was returned found is set to true.
func (ctx *protoKeyValIterator) GetNext() (kv keyval.ProtoKeyVal, lastReceived bool) {
	pair, allReceived := ctx.ctx.GetNext()
	if allReceived {
		return nil, allReceived
	}

	return &protoKeyVal{pair, ctx.serializer}, allReceived
}

// GetNext returns the following item from the result set. If data was returned found is set to true.
func (ctx *protoKeyIterator) GetNext() (key string, rev int64, lastReceived bool) {
	return ctx.ctx.GetNext()
}

// Watch for changes in Data Store respChannel is used for receiving watch events
func (pdb *protoWatcherEtcd) Watch(resp chan keyval.ProtoWatchResp, keys ...string) error {
	byteCh := make(chan keyval.BytesWatchResp, 0)
	err := pdb.watcher.Watch(byteCh, keys...)
	if err != nil {
		return err
	}
	go func() {
		for msg := range byteCh {
			resp <- NewWatchResp(pdb.serializer, msg)
		}
	}()
	return nil
}

// GetValue returns the value of the pair
func (kv *protoKeyVal) GetValue(msg proto.Message) error {
	err := kv.serializer.Unmarshal(kv.pair.GetValue(), msg)
	if err != nil {
		return err
	}
	return nil
}

// GetKey returns the key of the pair
func (kv *protoKeyVal) GetKey() string {
	return kv.pair.GetKey()
}

// GetRevision returns the revision associated with the pair
func (kv *protoKeyVal) GetRevision() int64 {
	return kv.pair.GetRevision()
}
<commit_msg>Update proto_broker_impl.go<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 etcdv3

import (
	"github.com/golang/protobuf/proto"
	"github.com/ligato/cn-infra/db/keyval"
)

// ProtoWrapperEtcd is a decorator which allows to read/write proto file modelled data.
// It marshals/unmarshals go structures to slice off bytes and vice versa behind the scenes.
type ProtoWrapperEtcd struct {
	broker     *BytesConnectionEtcd
	serializer keyval.Serializer
}

type protoBrokerEtcd struct {
	broker     keyval.BytesBroker
	serializer keyval.Serializer
}

type protoWatcherEtcd struct {
	watcher    keyval.BytesWatcher
	serializer keyval.Serializer
}

// protoKeyValIterator is an iterator returned by ListValues call
type protoKeyValIterator struct {
	ctx        keyval.BytesKeyValIterator
	serializer keyval.Serializer
}

// protoKeyIterator is an iterator returned by ListKeys call
type protoKeyIterator struct {
	ctx keyval.BytesKeyIterator
}

// protoKeyVal represents single key-value pair
type protoKeyVal struct {
	pair       keyval.BytesKeyVal
	serializer keyval.Serializer
}

// NewProtoWrapperEtcd initializes proto decorator. The default serializer is used - SerializerProto.
func NewProtoWrapperEtcd(db *BytesConnectionEtcd) *ProtoWrapperEtcd {
	return &ProtoWrapperEtcd{db, &keyval.SerializerProto{}}
}

// NewProtoWrapperWithSerializer initializes proto decorator with the specified serializer.
func NewProtoWrapperWithSerializer(db *BytesConnectionEtcd, serializer keyval.Serializer) *ProtoWrapperEtcd {
	return &ProtoWrapperEtcd{db, serializer}
}

// Close closes underlying connection to etcd. Beware: if the connection is shared among multiple instances this might
// unintentionally cancel the connection.
func (db *ProtoWrapperEtcd) Close() error {
	return db.broker.Close()
}

// NewBroker creates a new instance of the proxy that shares the underlying connection
// and allows to read/edit key-value pairs.
func (db *ProtoWrapperEtcd) NewBroker(prefix string) keyval.ProtoBroker {
	return &protoBrokerEtcd{db.broker.NewBroker(prefix), db.serializer}
}

// NewWatcher creates a new instance of the proxy that shares the underlying connection
// and allows to subscribe for watching of the changes.
func (db *ProtoWrapperEtcd) NewWatcher(prefix string) keyval.ProtoWatcher {
	return &protoWatcherEtcd{db.broker.NewWatcher(prefix), db.serializer}
}

// NewTxn creates a new Data Broker transaction. A transaction can
// holds multiple operations that are all committed to the data
// store together. After a transaction has been created, one or
// more operations (put or delete) can be added to the transaction
// before it is committed.
func (db *ProtoWrapperEtcd) NewTxn() keyval.ProtoTxn {
	return &protoTxn{txn: db.broker.NewTxn(), serializer: db.serializer}
}

// NewTxn creates a new Data Broker transaction. A transaction can
// hold multiple operations that are all committed to the data
// store together. After a transaction has been created, one or
// more operations (put or delete) can be added to the transaction
// before it is committed.
func (pdb *protoBrokerEtcd) NewTxn() keyval.ProtoTxn {
	return &protoTxn{txn: pdb.broker.NewTxn(), serializer: pdb.serializer}
}

// Put writes the provided key-value item into the data store.
//
// Returns an error if the item could not be written, ok otherwise.
func (db *ProtoWrapperEtcd) Put(key string, value proto.Message, opts ...keyval.PutOption) error {
	return putProtoInternal(db.broker, db.serializer, key, value, opts...)
}

// Put writes the provided key-value item into the data store.
//
// Returns an error if the item could not be written, ok otherwise.
func (pdb *protoBrokerEtcd) Put(key string, value proto.Message, opts ...keyval.PutOption) error {
	return putProtoInternal(pdb.broker, pdb.serializer, key, value, opts...)
}

func putProtoInternal(broker keyval.BytesBroker, serializer keyval.Serializer, key string, value proto.Message, opts ...keyval.PutOption) error {
	// Marshal value to protobuf
	binData, err := serializer.Marshal(value)
	if err != nil {
		return err
	}
	broker.Put(key, binData, opts...)
	return nil
}

// Delete removes from datastore key-value items stored under key.
func (db *ProtoWrapperEtcd) Delete(key string) (existed bool, err error) {
	return db.broker.Delete(key)
}

// Delete removes from datastore key-value items stored under key.
func (pdb *protoBrokerEtcd) Delete(key string) (existed bool, err error) {
	return pdb.broker.Delete(key)
}

// Watch subscribes for changes in datastore associated with the key. respChannel is used for delivery watch events
func (db *ProtoWrapperEtcd) Watch(resp chan keyval.ProtoWatchResp, keys ...string) error {
	byteCh := make(chan keyval.BytesWatchResp, 0)
	err := db.broker.Watch(byteCh, keys...)
	if err != nil {
		return err
	}
	go func() {
		for msg := range byteCh {
			resp <- NewWatchResp(db.serializer, msg)
		}
	}()
	return nil
}

// GetValue retrieves one key-value item from the datastore. The item
// is identified by the provided key.
//
// If the item was found, its value is unmarshaled and placed in
// the `reqObj` message buffer and the function returns found=true.
// If the object was not found, the function returns found=false.
// Function returns revision=revision of the latest modification
// If an error was encountered, the function returns an error.
func (db *ProtoWrapperEtcd) GetValue(key string, reqObj proto.Message) (found bool, revision int64, err error) {
	return getValueProtoInternal(db.broker, db.serializer, key, reqObj)
}

// GetValue retrieves one key-value item from the datastore. The item
// is identified by the provided key.
//
// If the item was found, its value is unmarshaled and placed in
// the `reqObj` message buffer and the function returns found=true.
// If the object was not found, the function returns found=false.
// Function returns revision=revision of the latest modification
// If an error was encountered, the function returns an error.
func (pdb *protoBrokerEtcd) GetValue(key string, reqObj proto.Message) (found bool, revision int64, err error) {
	return getValueProtoInternal(pdb.broker, pdb.serializer, key, reqObj)
}

func getValueProtoInternal(broker keyval.BytesBroker, serializer keyval.Serializer, key string, reqObj proto.Message) (found bool, revision int64, err error) {
	// get data from etcdv3
	resp, found, rev, err := broker.GetValue(key)
	if err != nil {
		return false, 0, err
	}

	if !found {
		return false, 0, nil
	}

	err = serializer.Unmarshal(resp, reqObj)
	if err != nil {
		return false, 0, err
	}
	return true, rev, nil
}

// ListValues retrieves an iterator for elements stored under the provided key.
func (db *ProtoWrapperEtcd) ListValues(key string) (keyval.ProtoKeyValIterator, error) {
	return listValuesProtoInternal(db.broker, db.serializer, key)
}

// ListValues retrieves an iterator for elements stored under the provided key.
func (pdb *protoBrokerEtcd) ListValues(key string) (keyval.ProtoKeyValIterator, error) {
	return listValuesProtoInternal(pdb.broker, pdb.serializer, key)
}

func listValuesProtoInternal(broker keyval.BytesBroker, serializer keyval.Serializer, key string) (keyval.ProtoKeyValIterator, error) {
	// get data from etcdv3
	ctx, err := broker.ListValues(key)
	if err != nil {
		return nil, err
	}
	return &protoKeyValIterator{ctx, serializer}, nil
}

// ListKeys is similar to the ListValues the difference is that values are not fetched
func (db *ProtoWrapperEtcd) ListKeys(prefix string) (keyval.ProtoKeyIterator, error) {
	return listKeysProtoInternal(db.broker, prefix)
}

// ListKeys is similar to the ListValues the difference is that values are not fetched
func (pdb *protoBrokerEtcd) ListKeys(prefix string) (keyval.ProtoKeyIterator, error) {
	return listKeysProtoInternal(pdb.broker, prefix)
}

func listKeysProtoInternal(broker keyval.BytesBroker, prefix string) (keyval.ProtoKeyIterator, error) {
	// get data from etcdv3
	ctx, err := broker.ListKeys(prefix)
	if err != nil {
		return nil, err
	}
	return &protoKeyIterator{ctx}, nil
}

// ListValuesRange retrieves an iterator for elements stored in specified range.
func (db *ProtoWrapperEtcd) ListValuesRange(fromPrefix string, toPrefix string) (keyval.ProtoKeyValIterator, error) {

	ctx, err := db.broker.ListValuesRange(fromPrefix, toPrefix)
	if err != nil {
		return nil, err
	}
	return &protoKeyValIterator{ctx, db.serializer}, nil
}

// GetNext returns the following item from the result set. If data was returned, found is set to true.
func (ctx *protoKeyValIterator) GetNext() (kv keyval.ProtoKeyVal, lastReceived bool) {
	pair, allReceived := ctx.ctx.GetNext()
	if allReceived {
		return nil, allReceived
	}

	return &protoKeyVal{pair, ctx.serializer}, allReceived
}

// GetNext returns the following item from the result set. If data was returned, found is set to true.
func (ctx *protoKeyIterator) GetNext() (key string, rev int64, lastReceived bool) {
	return ctx.ctx.GetNext()
}

// Watch for changes in datastore respChannel is used for receiving watch events
func (pdb *protoWatcherEtcd) Watch(resp chan keyval.ProtoWatchResp, keys ...string) error {
	byteCh := make(chan keyval.BytesWatchResp, 0)
	err := pdb.watcher.Watch(byteCh, keys...)
	if err != nil {
		return err
	}
	go func() {
		for msg := range byteCh {
			resp <- NewWatchResp(pdb.serializer, msg)
		}
	}()
	return nil
}

// GetValue returns the value of the pair
func (kv *protoKeyVal) GetValue(msg proto.Message) error {
	err := kv.serializer.Unmarshal(kv.pair.GetValue(), msg)
	if err != nil {
		return err
	}
	return nil
}

// GetKey returns the key of the pair
func (kv *protoKeyVal) GetKey() string {
	return kv.pair.GetKey()
}

// GetRevision returns the revision associated with the pair
func (kv *protoKeyVal) GetRevision() int64 {
	return kv.pair.GetRevision()
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go Authors.
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
// Licensed under the same terms as Go itself:
// https://code.google.com/p/go/source/browse/LICENSE

package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os/exec"
	"runtime"
	"time"

	"camlistore.org/pkg/googlestorage"
	"github.com/bradfitz/http2"
)

var (
	openFirefox = flag.Bool("openff", false, "Open Firefox")
	prod        = flag.Bool("prod", false, "Whether to configure itself to be the production http2.golang.org server.")
)

func oldHTTPHandler(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, `

Go + HTTP/2

Welcome to the Go language's HTTP/2 demo & interop server.

Unfortunately, you're not using HTTP/2 right now.

See code & instructions for connecting at https://github.com/bradfitz/http2.

`)
}

func registerHandlers() {
	mux := http.NewServeMux()
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.TLS == nil {
			http.Redirect(w, r, "https://http2.golang.org/", http.StatusFound)
			return
		}
		if r.ProtoMajor == 1 {
			oldHTTPHandler(w, r)
			return
		}
		mux.ServeHTTP(w, r)
	})
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// TODO: more
		io.WriteString(w, "
Hello, world!
Greetings from Go's HTTP/2 server. You're speaking HTTP/2.")
	})
}

func serveProdTLS() error {
	c, err := googlestorage.NewServiceClient()
	if err != nil {
		return err
	}
	slurp := func(key string) ([]byte, error) {
		const bucket = "http2-demo-server-tls"
		rc, _, err := c.GetObject(&googlestorage.Object{
			Bucket: bucket,
			Key:    key,
		})
		if err != nil {
			return nil, fmt.Errorf("Error fetching GCS object %q in bucket %q: %v", key, bucket, err)
		}
		defer rc.Close()
		return ioutil.ReadAll(rc)
	}
	certPem, err := slurp("http2.golang.org.chained.pem")
	if err != nil {
		return err
	}
	keyPem, err := slurp("http2.golang.org.key")
	if err != nil {
		return err
	}
	cert, err := tls.X509KeyPair(certPem, keyPem)
	if err != nil {
		return err
	}
	srv := &http.Server{
		TLSConfig: &tls.Config{
			Certificates: []tls.Certificate{cert},
		},
	}
	http2.ConfigureServer(srv, &http2.Server{})
	ln, err := net.Listen("tcp", ":443")
	if err != nil {
		return err
	}
	return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig))
}

type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

func serveProd() error {
	errc := make(chan error, 2)
	go func() { errc <- http.ListenAndServe(":80", nil) }()
	go func() { errc <- serveProdTLS() }()
	return <-errc
}

func main() {
	var srv http.Server
	flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.")
	flag.StringVar(&srv.Addr, "addr", "localhost:4430", "host:port to listen on ")
	flag.Parse()

	registerHandlers()

	if *prod {
		log.Fatal(serveProd())
	}

	url := "https://" + srv.Addr + "/"
	log.Printf("Listening on " + url)
	http2.ConfigureServer(&srv, &http2.Server{})

	go func() {
		log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
	}()
	if *openFirefox && runtime.GOOS == "darwin" {
		time.Sleep(250 * time.Millisecond)
		exec.Command("open", "-b", "org.mozilla.nightly", "https://localhost:4430/").Run()
	}
	select {}
}
<commit_msg>More demo handlers.<commit_after>// Copyright 2014 The Go Authors.
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
// Licensed under the same terms as Go itself:
// https://code.google.com/p/go/source/browse/LICENSE

package main

import (
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os/exec"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"

	"camlistore.org/pkg/googlestorage"
	"camlistore.org/pkg/singleflight"
	"github.com/bradfitz/http2"
)

var (
	openFirefox = flag.Bool("openff", false, "Open Firefox")
	prod        = flag.Bool("prod", false, "Whether to configure itself to be the production http2.golang.org server.")
)

func homeOldHTTP(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, `

Go + HTTP/2

Welcome to the Go language's HTTP/2 demo & interop server.

Unfortunately, you're not using HTTP/2 right now.

See code & instructions for connecting at https://github.com/bradfitz/http2.

`)
}

func home(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, `

Go + HTTP/2

Welcome to the Go language's HTTP/2 demo & interop server.

Congratulations, you're using HTTP/2 right now.

This server exists for others in the HTTP/2 community to test their HTTP/2 client implementations and point out flaws in our server.

 The code is currently at github.com/bradfitz/http2
but will move to the Go standard library at some point in the future
(enabled by default, without users needing to change their code).

Contact info: bradfitz@golang.org, or file a bug.

Handlers for testing

GET /reqinfo to dump the request + headers received

GET /clockstream streams the current time every second

GET /file/gopher.png for a small file (does If-Modified-Since, Content-Range, etc)

GET /file/go.src.tar.gz for a larger file (~10 MB)

GET /redirect to redirect back to / (this page)

PUT something to /crc32 to get a count of number of bytes and its CRC-32

`)
}

func reqInfoHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "Method: %s\n", r.Method)
	fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
	fmt.Fprintf(w, "Host: %s\n", r.Host)
	fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
	fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI)
	fmt.Fprintf(w, "URL: %#v\n", r.URL)
	fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength)
	fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close)
	fmt.Fprintf(w, "TLS: %#v\n", r.TLS)
	fmt.Fprintf(w, "\nHeaders:\n")
	r.Header.Write(w)
}

func crcHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "PUT" {
		http.Error(w, "PUT required.", 400)
		return
	}
	crc := crc32.NewIEEE()
	n, err := io.Copy(crc, r.Body)
	if err == nil {
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprintf(w, "bytes=%d, CRC32=%x", n, crc.Sum(nil))
	}
}

var (
	fsGrp   singleflight.Group
	fsMu    sync.Mutex // guards fsCache
	fsCache = map[string]http.Handler{}
)

// fileServer returns a file-serving handler that proxies URL.
// It lazily fetches URL on the first access and caches its contents forever.
func fileServer(url string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		hi, err := fsGrp.Do(url, func() (interface{}, error) {
			fsMu.Lock()
			if h, ok := fsCache[url]; ok {
				fsMu.Unlock()
				return h, nil
			}
			fsMu.Unlock()

			res, err := http.Get(url)
			if err != nil {
				return nil, err
			}
			defer res.Body.Close()
			slurp, err := ioutil.ReadAll(res.Body)
			if err != nil {
				return nil, err
			}

			modTime := time.Now()
			var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				http.ServeContent(w, r, path.Base(url), modTime, bytes.NewReader(slurp))
			})
			fsMu.Lock()
			fsCache[url] = h
			fsMu.Unlock()
			return h, nil
		})
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		hi.(http.Handler).ServeHTTP(w, r)
	})
}

func clockStreamHandler(w http.ResponseWriter, r *http.Request) {
	clientGone := w.(http.CloseNotifier).CloseNotify()
	w.Header().Set("Content-Type", "text/plain")
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	fmt.Fprintf(w, "# ~1KB of junk to force browsers to start rendering immediately: \n")
	io.WriteString(w, strings.Repeat("# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 13))

	for {
		fmt.Fprintf(w, "%v\n", time.Now())
		w.(http.Flusher).Flush()
		select {
		case <-ticker.C:
		case <-clientGone:
			log.Printf("Client %v disconnected from the clock", r.RemoteAddr)
			return
		}
	}
}

func registerHandlers() {
	mux2 := http.NewServeMux()
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.TLS == nil {
			http.Redirect(w, r, "https://http2.golang.org/", http.StatusFound)
			return
		}
		if r.ProtoMajor == 1 {
			if r.URL.Path == "/reqinfo" {
				reqInfoHandler(w, r)
			} else {
				homeOldHTTP(w, r)
			}
			return
		}
		mux2.ServeHTTP(w, r)
	})
	mux2.HandleFunc("/", home)
	mux2.Handle("/file/gopher.png", fileServer("https://golang.org/doc/gopher/frontpage.png"))
	mux2.Handle("/file/go.src.tar.gz", fileServer("https://storage.googleapis.com/golang/go1.4rc1.src.tar.gz"))
	mux2.HandleFunc("/reqinfo", reqInfoHandler)
	mux2.HandleFunc("/crc32", crcHandler)
	mux2.HandleFunc("/clockstream", clockStreamHandler)
	mux2.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})
}

func serveProdTLS() error {
	c, err := googlestorage.NewServiceClient()
	if err != nil {
		return err
	}
	slurp := func(key string) ([]byte, error) {
		const bucket = "http2-demo-server-tls"
		rc, _, err := c.GetObject(&googlestorage.Object{
			Bucket: bucket,
			Key:    key,
		})
		if err != nil {
			return nil, fmt.Errorf("Error fetching GCS object %q in bucket %q: %v", key, bucket, err)
		}
		defer rc.Close()
		return ioutil.ReadAll(rc)
	}
	certPem, err := slurp("http2.golang.org.chained.pem")
	if err != nil {
		return err
	}
	keyPem, err := slurp("http2.golang.org.key")
	if err != nil {
		return err
	}
	cert, err := tls.X509KeyPair(certPem, keyPem)
	if err != nil {
		return err
	}
	srv := &http.Server{
		TLSConfig: &tls.Config{
			Certificates: []tls.Certificate{cert},
		},
	}
	http2.ConfigureServer(srv, &http2.Server{})
	ln, err := net.Listen("tcp", ":443")
	if err != nil {
		return err
	}
	return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig))
}

type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

func serveProd() error {
	errc := make(chan error, 2)
	go func() { errc <- http.ListenAndServe(":80", nil) }()
	go func() { errc <- serveProdTLS() }()
	return <-errc
}

func main() {
	var srv http.Server
	flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.")
	flag.StringVar(&srv.Addr, "addr", "localhost:4430", "host:port to listen on ")
	flag.Parse()

	registerHandlers()

	if *prod {
		log.Fatal(serveProd())
	}

	url := "https://" + srv.Addr + "/"
	log.Printf("Listening on " + url)
	http2.ConfigureServer(&srv, &http2.Server{})

	go func() {
		log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
	}()
	if *openFirefox && runtime.GOOS == "darwin" {
		time.Sleep(250 * time.Millisecond)
		exec.Command("open", "-b", "org.mozilla.nightly", "https://localhost:4430/").Run()
	}
	select {}
}
<|endoftext|>


<commit_before>package h2quic

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	quic "github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/qerr"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"
)

type streamCreator interface {
	quic.Session
	GetOrOpenStream(protocol.StreamID) (quic.Stream, error)
}

type remoteCloser interface {
	CloseRemote(protocol.ByteCount)
}

// allows mocking of quic.Listen and quic.ListenAddr
var (
	quicListen     = quic.Listen
	quicListenAddr = quic.ListenAddr
)

// Server is a HTTP2 server listening for QUIC connections.
type Server struct {
	*http.Server

	// By providing a quic.Config, it is possible to set parameters of the QUIC connection.
	// If nil, it uses reasonable default values.
	QuicConfig *quic.Config

	// Private flag for demo, do not use
	CloseAfterFirstRequest bool

	port uint32 // used atomically

	listenerMutex sync.Mutex
	listener      quic.Listener

	supportedVersionsAsString string
}

// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServe() error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	return s.serveImpl(s.TLSConfig, nil)
}

// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}
	return s.serveImpl(config, nil)
}

// Serve an existing UDP connection.
func (s *Server) Serve(conn net.PacketConn) error {
	return s.serveImpl(s.TLSConfig, conn)
}

func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	s.listenerMutex.Lock()
	if s.listener != nil {
		s.listenerMutex.Unlock()
		return errors.New("ListenAndServe may only be called once")
	}

	var ln quic.Listener
	var err error
	if conn == nil {
		ln, err = quicListenAddr(s.Addr, tlsConfig, s.QuicConfig)
	} else {
		ln, err = quicListen(conn, tlsConfig, s.QuicConfig)
	}
	if err != nil {
		s.listenerMutex.Unlock()
		return err
	}
	s.listener = ln
	s.listenerMutex.Unlock()

	for {
		sess, err := ln.Accept()
		if err != nil {
			return err
		}
		go s.handleHeaderStream(sess.(streamCreator))
	}
}

func (s *Server) handleHeaderStream(session streamCreator) {
	stream, err := session.AcceptStream()
	if err != nil {
		session.Close(qerr.Error(qerr.InvalidHeadersStreamData, err.Error()))
		return
	}

	hpackDecoder := hpack.NewDecoder(4096, nil)
	h2framer := http2.NewFramer(nil, stream)

	var headerStreamMutex sync.Mutex // Protects concurrent calls to Write()
	for {
		if err := s.handleRequest(session, stream, &headerStreamMutex, hpackDecoder, h2framer); err != nil {
			// QuicErrors must originate from stream.Read() returning an error.
			// In this case, the session has already logged the error, so we don't
			// need to log it again.
			if _, ok := err.(*qerr.QuicError); !ok {
				utils.Errorf("error handling h2 request: %s", err.Error())
			}
			session.Close(err)
			return
		}
	}
}

func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream, headerStreamMutex *sync.Mutex, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error {
	h2frame, err := h2framer.ReadFrame()
	if err != nil {
		return qerr.Error(qerr.HeadersStreamDataDecompressFailure, "cannot read frame")
	}
	h2headersFrame, ok := h2frame.(*http2.HeadersFrame)
	if !ok {
		return qerr.Error(qerr.InvalidHeadersStreamData, "expected a header frame")
	}
	if !h2headersFrame.HeadersEnded() {
		return errors.New("http2 header continuation not implemented")
	}
	headers, err := hpackDecoder.DecodeFull(h2headersFrame.HeaderBlockFragment())
	if err != nil {
		utils.Errorf("invalid http2 headers encoding: %s", err.Error())
		return err
	}

	req, err := requestFromHeaders(headers)
	if err != nil {
		return err
	}

	req.RemoteAddr = session.RemoteAddr().String()

	if utils.Debug() {
		utils.Infof("%s %s%s, on data stream %d", req.Method, req.Host, req.RequestURI, h2headersFrame.StreamID)
	} else {
		utils.Infof("%s %s%s", req.Method, req.Host, req.RequestURI)
	}

	dataStream, err := session.GetOrOpenStream(protocol.StreamID(h2headersFrame.StreamID))
	if err != nil {
		return err
	}
	// this can happen if the client immediately closes the data stream after sending the request and the runtime processes the reset before the request
	if dataStream == nil {
		return nil
	}

	var streamEnded bool
	if h2headersFrame.StreamEnded() {
		dataStream.(remoteCloser).CloseRemote(0)
		streamEnded = true
		_, _ = dataStream.Read([]byte{0}) // read the eof
	}

	req = req.WithContext(dataStream.Context())
	reqBody := newRequestBody(dataStream)
	req.Body = reqBody

	responseWriter := newResponseWriter(headerStream, headerStreamMutex, dataStream, protocol.StreamID(h2headersFrame.StreamID))

	go func() {
		handler := s.Handler
		if handler == nil {
			handler = http.DefaultServeMux
		}
		panicked := false
		func() {
			defer func() {
				if p := recover(); p != nil {
					// Copied from net/http/server.go
					const size = 64 << 10
					buf := make([]byte, size)
					buf = buf[:runtime.Stack(buf, false)]
					utils.Errorf("http: panic serving: %v\n%s", p, buf)
					panicked = true
				}
			}()
			handler.ServeHTTP(responseWriter, req)
		}()
		if panicked {
			responseWriter.WriteHeader(500)
		} else {
			responseWriter.WriteHeader(200)
		}
		if responseWriter.dataStream != nil {
			if !streamEnded && !reqBody.requestRead {
				responseWriter.dataStream.Reset(nil)
			}
			responseWriter.dataStream.Close()
		}
		if s.CloseAfterFirstRequest {
			time.Sleep(100 * time.Millisecond)
			session.Close(nil)
		}
	}()

	return nil
}

// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients.
// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) Close() error {
	s.listenerMutex.Lock()
	defer s.listenerMutex.Unlock()
	if s.listener != nil {
		err := s.listener.Close()
		s.listener = nil
		return err
	}
	return nil
}

// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete.
// CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) CloseGracefully(timeout time.Duration) error {
	// TODO: implement
	return nil
}

// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC.
// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443):
//  Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30"
func (s *Server) SetQuicHeaders(hdr http.Header) error {
	port := atomic.LoadUint32(&s.port)

	if port == 0 {
		// Extract port from s.Server.Addr
		_, portStr, err := net.SplitHostPort(s.Server.Addr)
		if err != nil {
			return err
		}
		portInt, err := net.LookupPort("tcp", portStr)
		if err != nil {
			return err
		}
		port = uint32(portInt)
		atomic.StoreUint32(&s.port, port)
	}

	if s.supportedVersionsAsString == "" {
		var versions []string
		for _, v := range protocol.SupportedVersions {
			versions = append(versions, v.ToAltSvc())
		}
		s.supportedVersionsAsString = strings.Join(versions, ",")
	}

	hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, port, s.supportedVersionsAsString))

	return nil
}

// ListenAndServeQUIC listens on the UDP network address addr and calls the
// handler for HTTP/2 requests on incoming connections. http.DefaultServeMux is
// used when handler is nil.
func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error {
	server := &Server{
		Server: &http.Server{
			Addr:    addr,
			Handler: handler,
		},
	}
	return server.ListenAndServeTLS(certFile, keyFile)
}

// ListenAndServe listens on the given network address for both, TLS and QUIC
// connetions in parallel. It returns if one of the two returns an error.
// http.DefaultServeMux is used when handler is nil.
// The correct Alt-Svc headers for QUIC are set.
func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error {
	// Load certs
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}

	// Open the listeners
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return err
	}
	udpConn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
		return err
	}
	defer udpConn.Close()

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	tcpConn, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}
	defer tcpConn.Close()

	tlsConn := tls.NewListener(tcpConn, config)
	defer tlsConn.Close()

	// Start the servers
	httpServer := &http.Server{
		Addr:      addr,
		TLSConfig: config,
	}

	quicServer := &Server{
		Server: httpServer,
	}

	if handler == nil {
		handler = http.DefaultServeMux
	}
	httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		quicServer.SetQuicHeaders(w.Header())
		handler.ServeHTTP(w, r)
	})

	hErr := make(chan error)
	qErr := make(chan error)
	go func() {
		hErr <- httpServer.Serve(tlsConn)
	}()
	go func() {
		qErr <- quicServer.Serve(udpConn)
	}()

	select {
	case err := <-hErr:
		quicServer.Close()
		return err
	case err := <-qErr:
		// Cannot close the HTTP server or wait for requests to complete properly :/
		return err
	}
}
<commit_msg>shorten blocking call of Server.handleRequest<commit_after>package h2quic

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	quic "github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/qerr"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"
)

type streamCreator interface {
	quic.Session
	GetOrOpenStream(protocol.StreamID) (quic.Stream, error)
}

type remoteCloser interface {
	CloseRemote(protocol.ByteCount)
}

// allows mocking of quic.Listen and quic.ListenAddr
var (
	quicListen     = quic.Listen
	quicListenAddr = quic.ListenAddr
)

// Server is a HTTP2 server listening for QUIC connections.
type Server struct {
	*http.Server

	// By providing a quic.Config, it is possible to set parameters of the QUIC connection.
	// If nil, it uses reasonable default values.
	QuicConfig *quic.Config

	// Private flag for demo, do not use
	CloseAfterFirstRequest bool

	port uint32 // used atomically

	listenerMutex sync.Mutex
	listener      quic.Listener

	supportedVersionsAsString string
}

// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServe() error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	return s.serveImpl(s.TLSConfig, nil)
}

// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}
	return s.serveImpl(config, nil)
}

// Serve an existing UDP connection.
func (s *Server) Serve(conn net.PacketConn) error {
	return s.serveImpl(s.TLSConfig, conn)
}

func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	s.listenerMutex.Lock()
	if s.listener != nil {
		s.listenerMutex.Unlock()
		return errors.New("ListenAndServe may only be called once")
	}

	var ln quic.Listener
	var err error
	if conn == nil {
		ln, err = quicListenAddr(s.Addr, tlsConfig, s.QuicConfig)
	} else {
		ln, err = quicListen(conn, tlsConfig, s.QuicConfig)
	}
	if err != nil {
		s.listenerMutex.Unlock()
		return err
	}
	s.listener = ln
	s.listenerMutex.Unlock()

	for {
		sess, err := ln.Accept()
		if err != nil {
			return err
		}
		go s.handleHeaderStream(sess.(streamCreator))
	}
}

func (s *Server) handleHeaderStream(session streamCreator) {
	stream, err := session.AcceptStream()
	if err != nil {
		session.Close(qerr.Error(qerr.InvalidHeadersStreamData, err.Error()))
		return
	}

	hpackDecoder := hpack.NewDecoder(4096, nil)
	h2framer := http2.NewFramer(nil, stream)

	var headerStreamMutex sync.Mutex // Protects concurrent calls to Write()
	for {
		if err := s.handleRequest(session, stream, &headerStreamMutex, hpackDecoder, h2framer); err != nil {
			// QuicErrors must originate from stream.Read() returning an error.
			// In this case, the session has already logged the error, so we don't
			// need to log it again.
			if _, ok := err.(*qerr.QuicError); !ok {
				utils.Errorf("error handling h2 request: %s", err.Error())
			}
			session.Close(err)
			return
		}
	}
}

func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream, headerStreamMutex *sync.Mutex, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error {
	h2frame, err := h2framer.ReadFrame()
	if err != nil {
		return qerr.Error(qerr.HeadersStreamDataDecompressFailure, "cannot read frame")
	}
	h2headersFrame, ok := h2frame.(*http2.HeadersFrame)
	if !ok {
		return qerr.Error(qerr.InvalidHeadersStreamData, "expected a header frame")
	}
	if !h2headersFrame.HeadersEnded() {
		return errors.New("http2 header continuation not implemented")
	}
	headers, err := hpackDecoder.DecodeFull(h2headersFrame.HeaderBlockFragment())
	if err != nil {
		utils.Errorf("invalid http2 headers encoding: %s", err.Error())
		return err
	}

	req, err := requestFromHeaders(headers)
	if err != nil {
		return err
	}

	if utils.Debug() {
		utils.Infof("%s %s%s, on data stream %d", req.Method, req.Host, req.RequestURI, h2headersFrame.StreamID)
	} else {
		utils.Infof("%s %s%s", req.Method, req.Host, req.RequestURI)
	}

	dataStream, err := session.GetOrOpenStream(protocol.StreamID(h2headersFrame.StreamID))
	if err != nil {
		return err
	}
	// this can happen if the client immediately closes the data stream after sending the request and the runtime processes the reset before the request
	if dataStream == nil {
		return nil
	}

	// handleRequest should be as non-blocking as possible to minimize
	// head-of-line blocking. Potentially blocking code is run in a separate
	// goroutine, enabling handleRequest to return before the code is executed.
	go func() {
		streamEnded := h2headersFrame.StreamEnded()
		if streamEnded {
			dataStream.(remoteCloser).CloseRemote(0)
			streamEnded = true
			_, _ = dataStream.Read([]byte{0}) // read the eof
		}

		req = req.WithContext(dataStream.Context())
		reqBody := newRequestBody(dataStream)
		req.Body = reqBody

		req.RemoteAddr = session.RemoteAddr().String()

		responseWriter := newResponseWriter(headerStream, headerStreamMutex, dataStream, protocol.StreamID(h2headersFrame.StreamID))

		handler := s.Handler
		if handler == nil {
			handler = http.DefaultServeMux
		}
		panicked := false
		func() {
			defer func() {
				if p := recover(); p != nil {
					// Copied from net/http/server.go
					const size = 64 << 10
					buf := make([]byte, size)
					buf = buf[:runtime.Stack(buf, false)]
					utils.Errorf("http: panic serving: %v\n%s", p, buf)
					panicked = true
				}
			}()
			handler.ServeHTTP(responseWriter, req)
		}()
		if panicked {
			responseWriter.WriteHeader(500)
		} else {
			responseWriter.WriteHeader(200)
		}
		if responseWriter.dataStream != nil {
			if !streamEnded && !reqBody.requestRead {
				responseWriter.dataStream.Reset(nil)
			}
			responseWriter.dataStream.Close()
		}
		if s.CloseAfterFirstRequest {
			time.Sleep(100 * time.Millisecond)
			session.Close(nil)
		}
	}()

	return nil
}

// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients.
// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) Close() error {
	s.listenerMutex.Lock()
	defer s.listenerMutex.Unlock()
	if s.listener != nil {
		err := s.listener.Close()
		s.listener = nil
		return err
	}
	return nil
}

// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete.
// CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) CloseGracefully(timeout time.Duration) error {
	// TODO: implement
	return nil
}

// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC.
// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443):
//  Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30"
func (s *Server) SetQuicHeaders(hdr http.Header) error {
	port := atomic.LoadUint32(&s.port)

	if port == 0 {
		// Extract port from s.Server.Addr
		_, portStr, err := net.SplitHostPort(s.Server.Addr)
		if err != nil {
			return err
		}
		portInt, err := net.LookupPort("tcp", portStr)
		if err != nil {
			return err
		}
		port = uint32(portInt)
		atomic.StoreUint32(&s.port, port)
	}

	if s.supportedVersionsAsString == "" {
		var versions []string
		for _, v := range protocol.SupportedVersions {
			versions = append(versions, v.ToAltSvc())
		}
		s.supportedVersionsAsString = strings.Join(versions, ",")
	}

	hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, port, s.supportedVersionsAsString))

	return nil
}

// ListenAndServeQUIC listens on the UDP network address addr and calls the
// handler for HTTP/2 requests on incoming connections. http.DefaultServeMux is
// used when handler is nil.
func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error {
	server := &Server{
		Server: &http.Server{
			Addr:    addr,
			Handler: handler,
		},
	}
	return server.ListenAndServeTLS(certFile, keyFile)
}

// ListenAndServe listens on the given network address for both, TLS and QUIC
// connetions in parallel. It returns if one of the two returns an error.
// http.DefaultServeMux is used when handler is nil.
// The correct Alt-Svc headers for QUIC are set.
func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error {
	// Load certs
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}

	// Open the listeners
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return err
	}
	udpConn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
		return err
	}
	defer udpConn.Close()

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	tcpConn, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}
	defer tcpConn.Close()

	tlsConn := tls.NewListener(tcpConn, config)
	defer tlsConn.Close()

	// Start the servers
	httpServer := &http.Server{
		Addr:      addr,
		TLSConfig: config,
	}

	quicServer := &Server{
		Server: httpServer,
	}

	if handler == nil {
		handler = http.DefaultServeMux
	}
	httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		quicServer.SetQuicHeaders(w.Header())
		handler.ServeHTTP(w, r)
	})

	hErr := make(chan error)
	qErr := make(chan error)
	go func() {
		hErr <- httpServer.Serve(tlsConn)
	}()
	go func() {
		qErr <- quicServer.Serve(udpConn)
	}()

	select {
	case err := <-hErr:
		quicServer.Close()
		return err
	case err := <-qErr:
		// Cannot close the HTTP server or wait for requests to complete properly :/
		return err
	}
}
<|endoftext|>


<commit_before>package anidb

import (
	"encoding/gob"
	"os"
	"path"
	"reflect"
	"testing"
)

type stringifyVec struct {
	result []string
	data   []interface{}
}

func TestStringify(T *testing.T) {
	T.Parallel()

	vec := []stringifyVec{
		stringifyVec{[]string{"a"}, []interface{}{"a"}},
	}
	for i, v := range vec {
		str := stringify(v.data...)
		if !reflect.DeepEqual(v.result, str) {
			T.Errorf("Vector #%d: Expected %v, got %v", i+1, v.result, str)
		}
	}
}

type cachePathVec struct {
	path string
	data []interface{}
}

var testDir = path.Join(os.TempDir(), "testing", "anidb")

func init() { SetCacheDir(testDir) }

func TestCachePath(T *testing.T) {
	T.Parallel()

	vec := []cachePathVec{
		cachePathVec{path.Join(testDir, "a"), []interface{}{"a"}},
		cachePathVec{path.Join(testDir, "b", "c", "d"), []interface{}{"b", "c", "d"}},
	}
	for i, v := range vec {
		str := cachePath(v.data...)

		if v.path != str {
			T.Errorf("Vector #%d: Expected %v, got %v", i+1, v.path, str)
		}
	}
}

type testString string

func (_ testString) Touch()        {}
func (_ testString) IsStale() bool { return false }

func init() {
	gob.Register(testString(""))
}

func TestCacheRoundtrip(T *testing.T) {
	T.Parallel()

	test := testString("some string")
	_, err := cache.Set(test, "test", "string")
	if err != nil {
		T.Fatalf("Error storing: %v", err)
	}

	var t2 testString
	err = cache.Get(&t2, "test", "string")
	if err != nil {
		T.Errorf("Error reading: %v", err)
	}

	if test != t2 {
		T.Errorf("Expected %q, got %q", test, t2)
	}
}
<commit_msg>anidb: Fix cache_test.go compilation<commit_after>package anidb

import (
	"encoding/gob"
	"os"
	"path"
	"reflect"
	"testing"
)

type stringifyVec struct {
	result []string
	data   []cacheKey
}

func TestStringify(T *testing.T) {
	T.Parallel()

	vec := []stringifyVec{
		stringifyVec{[]string{"a"}, []cacheKey{"a"}},
	}
	for i, v := range vec {
		str := stringify(v.data...)
		if !reflect.DeepEqual(v.result, str) {
			T.Errorf("Vector #%d: Expected %v, got %v", i+1, v.result, str)
		}
	}
}

type cachePathVec struct {
	path string
	data []cacheKey
}

var testDir = path.Join(os.TempDir(), "testing", "anidb")

func init() { SetCacheDir(testDir) }

func TestCachePath(T *testing.T) {
	T.Parallel()

	vec := []cachePathVec{
		cachePathVec{path.Join(testDir, "a"), []cacheKey{"a"}},
		cachePathVec{path.Join(testDir, "b", "c", "d"), []cacheKey{"b", "c", "d"}},
	}
	for i, v := range vec {
		str := cachePath(v.data...)

		if v.path != str {
			T.Errorf("Vector #%d: Expected %v, got %v", i+1, v.path, str)
		}
	}
}

type testString string

func (_ testString) Touch()        {}
func (_ testString) IsStale() bool { return false }

func init() {
	gob.Register(testString(""))
}

func TestCacheRoundtrip(T *testing.T) {
	T.Parallel()

	test := testString("some string")
	_, err := cache.Set(test, "test", "string")
	if err != nil {
		T.Fatalf("Error storing: %v", err)
	}

	var t2 testString
	err = cache.Get(&t2, "test", "string")
	if err != nil {
		T.Errorf("Error reading: %v", err)
	}

	if test != t2 {
		T.Errorf("Expected %q, got %q", test, t2)
	}
}
<|endoftext|>


<commit_before>package cache

import "testing"
import "time"

var (
	ok *Cache
)

func TestTest(t *testing.T) {
	toplel := CacheOptions{}
	toplel.BurnStrategy = BurnStrategyOldest
	toplel.MaxEntries = 2
	toplel.Upper = 2
	ok = NewCache(toplel)
	ok.Set("Test", "FOO BAR")
	if ok.Get("Test") != "FOO BAR" {
		t.Error("TestTest Failed")
	}
}

func TestRemove(t *testing.T) {
	ok.RemoveItem("Test")
	if ok.Get("Test") != nil || ok.Len() != 0 {
		t.Error("TestRemove Failed")
	}
}

func TestSetNilRemove(t *testing.T) {
	ok.Set("Test", "FOO BAR")
	if ok.Get("Test") != "FOO BAR" {
		t.Error("Setup for TestSetNilRemove Failed")
	}
	ok.Set("Test", nil)
	if ok.Len() != 0 {
		t.Error("TestSetNilRemove failed: did not remove item")
	}
}

func TestExpireTime(t *testing.T) {
	op := CacheOptions{}
	op.BurnStrategy = BurnStrategyRandom
	op.MaxEntries = 0
	op.Upper = 0
	op.ExpirationTime = time.Second * 2
	oj := NewCache(op)
	oj.Set("Tests", "FOO BAR")
	if oj.Get("Tests") != "FOO BAR" {
		t.Error("Setup for TestExpireTime failed")
		return
	}
	time.Sleep(time.Second * 3)
	if oj.Get("Tests") != nil {
		t.Error("TestExpireTime failed")
	}
}

func TestHits(t *testing.T) {
	toplel := CacheOptions{}
	toplel.BurnStrategy = BurnStrategyOldest
	toplel.MaxEntries = 2
	toplel.Upper = 2
	ok = NewCache(toplel)
	ok.Set("test", "lol")
	if ok.Get("test") != "lol" {
		t.Error("Setup")
		return
	}
	if ok.Hits() != 1 {
		t.Error("Fail")
	}
}

func TestMisses(t *testing.T) {
	if ok.Get("non") != nil {
		t.Error("Setup")
		return
	}
	if ok.Misses() != 1 {
		t.Error("Fail")
	}
}

func TestAutoBurnOnUpper(t *testing.T) {
	toplel := CacheOptions{}
	toplel.BurnStrategy = BurnStrategyOldest
	toplel.MaxEntries = 2
	toplel.Upper = 1
	ok = NewCache(toplel)
	ok.Set("first", 1)
	if ok.Get("first") != 1 {
		t.Error("setup")
	}
	ok.Set("second", 2)
	if ok.Get("first") != nil {
		t.Error("first not nil")
		return
	}
	if ok.Get("second") != 2 {
		t.Error("second not 2")
	}
}
<commit_msg>Add TestBump<commit_after>package cache

import "testing"
import "time"

var (
	ok *Cache
)

func TestTest(t *testing.T) {
	toplel := CacheOptions{}
	toplel.BurnStrategy = BurnStrategyOldest
	toplel.MaxEntries = 2
	toplel.Upper = 2
	ok = NewCache(toplel)
	ok.Set("Test", "FOO BAR")
	if ok.Get("Test") != "FOO BAR" {
		t.Error("TestTest Failed")
	}
}

func TestRemove(t *testing.T) {
	ok.RemoveItem("Test")
	if ok.Get("Test") != nil || ok.Len() != 0 {
		t.Error("TestRemove Failed")
	}
}

func TestSetNilRemove(t *testing.T) {
	ok.Set("Test", "FOO BAR")
	if ok.Get("Test") != "FOO BAR" {
		t.Error("Setup for TestSetNilRemove Failed")
	}
	ok.Set("Test", nil)
	if ok.Len() != 0 {
		t.Error("TestSetNilRemove failed: did not remove item")
	}
}

func TestExpireTime(t *testing.T) {
	op := CacheOptions{}
	op.BurnStrategy = BurnStrategyRandom
	op.MaxEntries = 0
	op.Upper = 0
	op.ExpirationTime = time.Second * 2
	oj := NewCache(op)
	oj.Set("Tests", "FOO BAR")
	if oj.Get("Tests") != "FOO BAR" {
		t.Error("Setup for TestExpireTime failed")
		return
	}
	time.Sleep(time.Second * 3)
	if oj.Get("Tests") != nil {
		t.Error("TestExpireTime failed")
	}
}

func TestHits(t *testing.T) {
	toplel := CacheOptions{}
	toplel.BurnStrategy = BurnStrategyOldest
	toplel.MaxEntries = 2
	toplel.Upper = 2
	ok = NewCache(toplel)
	ok.Set("test", "lol")
	if ok.Get("test") != "lol" {
		t.Error("Setup")
		return
	}
	if ok.Hits() != 1 {
		t.Error("Fail")
	}
}

func TestMisses(t *testing.T) {
	if ok.Get("non") != nil {
		t.Error("Setup")
		return
	}
	if ok.Misses() != 1 {
		t.Error("Fail")
	}
}

func TestAutoBurnOnUpper(t *testing.T) {
	toplel := CacheOptions{}
	toplel.BurnStrategy = BurnStrategyOldest
	toplel.MaxEntries = 2
	toplel.Upper = 1
	ok = NewCache(toplel)
	ok.Set("first", 1)
	if ok.Get("first") != 1 {
		t.Error("setup")
	}
	ok.Set("second", 2)
	if ok.Get("first") != nil {
		t.Error("first not nil")
		return
	}
	if ok.Get("second") != 2 {
		t.Error("second not 2")
	}
}

func TestBump(t *testing.T) {
	toplel := CacheOptions{}
	toplel.BurnStrategy = BurnStrategyOldest
	toplel.Upper = 500
	ok = NewCache(toplel)
	ok.Set("first", 1)
	ok.Set("Second", 2)
	ok.Bump("first")
	if ok.l.Front().Value.(*CachedItem).key != "first" {
		t.Error("Expected first got", ok.l.Front().Value.(*CachedItem).key)
	}
	if ok.l.Back().Value.(*CachedItem).key != "Second" {
		t.Error("Expected first got", ok.l.Back().Value.(*CachedItem).key)
	}
}
<|endoftext|>


<commit_before>package tunnel

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

	"github.com/gorilla/websocket"
	"github.com/looplab/fsm"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/util"
	"github.com/sirupsen/logrus"
)

const stateInitial = "initial"
const stateConnecting = "connecting"
const stateOpen = "open"
const stateClosed = "closed"

const evConnect = "connect"
const evConnected = "connected"
const evError = "error"
const evClose = "close"

// Connection -- WebSocket connection
//
// internal state machine:
// - initial (OpenWebsocket() sets the state to 'connecting' pretty soon after creating the state machine, so this state isn't all too relevant)
// - connecting: event: "connect", from: initial
// - open: event: "connected", from: connecting
// - closed:
//	events: "close", "error"
//	from: initial, connecting, open
type Connection struct {
	ws           *websocket.Conn
	stateMachine *fsm.FSM

	CloseListeners    []func()
	ErrorListeners    []func(err util.APIError)
	MessageListerners []func(int, []byte)

	writeLock sync.Mutex
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...config.Auth) util.APIError {
	if c.stateMachine != nil {
		// TODO this is not thread safe
		panic("OpenWebsocket() called twice on a single Connection!")
	}
	c.stateMachine = fsm.NewFSM(stateInitial, fsm.Events{
		{Name: "connect", Src: []string{stateInitial}, Dst: stateConnecting},
		{Name: evConnected, Src: []string{stateConnecting}, Dst: stateOpen},
		{Name: evError, Src: []string{stateInitial, stateConnecting, stateOpen}, Dst: stateClosed},
		{Name: evClose, Src: []string{stateInitial, stateConnecting, stateOpen}, Dst: stateClosed},
	}, fsm.Callbacks{
		"after_" + evError:     c._onError,
		"enter_" + stateClosed: c._onClose,
		"enter_state":          c._onStateChange,
	})
	c.done = make(chan struct{})

	hdr := http.Header{}

	var auth config.Auth
	if len(auths) == 0 {
		var err error
		if auth, err = config.LoadAuth().GetClientAuth(); err != nil {
			return util.NewAPIError(util.OtherError, err.Error())
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())
	hdr.Add("User-agent", fmt.Sprintf("ondevice v%s", config.GetVersion()))

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	logrus.Debugf("opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	c.stateMachine.Event(evConnect)
	websocket.DefaultDialer.HandshakeTimeout = 60 * time.Second
	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return util.NewAPIError(resp.StatusCode, "API server authentication failed")
			}
			return util.NewAPIError(resp.StatusCode, "Error opening websocket: ", err)
		}
		return util.NewAPIError(util.OtherError, "Error opening websocket: ", err)
	}

	c.ws = ws
	c.MessageListerners = append(c.MessageListerners, onMessage)

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c *Connection) Close() {
	if err := c.stateMachine.Event(evClose); err != nil {
		if _, ok := err.(fsm.NoTransitionError); ok {
			// already in closed state
		} else {
			logrus.WithError(err).Error("failed to close connection")
		}
	}
}

// IsClosed -- Returns true for closed connections (either being closed normally or due to an error/timeout)
func (c *Connection) IsClosed() bool {
	if s := c.stateMachine; s != nil {
		return s.Is(stateClosed)
	}
	return false
}

func (c *Connection) receive() {
	defer c.Close()

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			if e, ok := err.(*websocket.CloseError); ok {
				if e.Code == 1000 {
					// normal close
				} else {
					logrus.WithError(err).Error("websocket closed abnormally")
				}
			} else {
				if !c.IsClosed() {
					logrus.WithError(err).Errorf("read error (type: %s)", reflect.TypeOf(err))
					c._error(util.NewAPIError(util.OtherError, err.Error()))
				} else {
					logrus.WithError(err).Debug("connetion.receive() interrupted by error: ", reflect.TypeOf(err))
				}
			}
			return
		}
		for _, cb := range c.MessageListerners {
			cb(msgType, msg)
		}
	}
}

// SendBinary -- Send binary WebSocket message
func (c *Connection) SendBinary(data []byte) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.BinaryMessage, data)
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c *Connection) SendJSON(value interface{}) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteJSON(value)
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c *Connection) SendText(msg string) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
}

// Wait -- Wait for the connection to close
func (c *Connection) Wait() {
	<-c.done
}

// _error -- Puts the connection into the 'error' state and closes it
func (c *Connection) _error(err util.APIError) {
	c.stateMachine.Event(evError, err)
}

func (c *Connection) _onClose(ev *fsm.Event) {
	if c.ws != nil { // could be nil if a goroutine called us before we are connected
		c.ws.Close()
	}

	close(c.done)
	go func() { // do this asynchronously (we could end up in a deadlock (if one of the callbacks calls Close() again))
		for _, cb := range c.CloseListeners {
			cb()
		}
	}()
}

func (c *Connection) _onError(ev *fsm.Event) {
	err, ok := ev.Args[0].(util.APIError)
	if !ok {
		panic("Connection._onError() expects an APIError parameter!")
	}
	for _, cb := range c.ErrorListeners {
		cb(err)
	}
}

func (c *Connection) _onStateChange(ev *fsm.Event) {
	logrus.Debug("connection state changed: ", ev.Src, " -> ", ev.Dst)
}
<commit_msg>tunnel.Connection: fixed .MessageListerner typo<commit_after>package tunnel

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

	"github.com/gorilla/websocket"
	"github.com/looplab/fsm"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/util"
	"github.com/sirupsen/logrus"
)

const stateInitial = "initial"
const stateConnecting = "connecting"
const stateOpen = "open"
const stateClosed = "closed"

const evConnect = "connect"
const evConnected = "connected"
const evError = "error"
const evClose = "close"

// Connection -- WebSocket connection
//
// internal state machine:
// - initial (OpenWebsocket() sets the state to 'connecting' pretty soon after creating the state machine, so this state isn't all too relevant)
// - connecting: event: "connect", from: initial
// - open: event: "connected", from: connecting
// - closed:
//	events: "close", "error"
//	from: initial, connecting, open
type Connection struct {
	ws           *websocket.Conn
	stateMachine *fsm.FSM

	CloseListeners   []func()
	ErrorListeners   []func(err util.APIError)
	MessageListeners []func(int, []byte)

	writeLock sync.Mutex
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...config.Auth) util.APIError {
	if c.stateMachine != nil {
		// TODO this is not thread safe
		panic("OpenWebsocket() called twice on a single Connection!")
	}
	c.stateMachine = fsm.NewFSM(stateInitial, fsm.Events{
		{Name: "connect", Src: []string{stateInitial}, Dst: stateConnecting},
		{Name: evConnected, Src: []string{stateConnecting}, Dst: stateOpen},
		{Name: evError, Src: []string{stateInitial, stateConnecting, stateOpen}, Dst: stateClosed},
		{Name: evClose, Src: []string{stateInitial, stateConnecting, stateOpen}, Dst: stateClosed},
	}, fsm.Callbacks{
		"after_" + evError:     c._onError,
		"enter_" + stateClosed: c._onClose,
		"enter_state":          c._onStateChange,
	})
	c.done = make(chan struct{})

	hdr := http.Header{}

	var auth config.Auth
	if len(auths) == 0 {
		var err error
		if auth, err = config.LoadAuth().GetClientAuth(); err != nil {
			return util.NewAPIError(util.OtherError, err.Error())
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())
	hdr.Add("User-agent", fmt.Sprintf("ondevice v%s", config.GetVersion()))

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	logrus.Debugf("opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	c.stateMachine.Event(evConnect)
	websocket.DefaultDialer.HandshakeTimeout = 60 * time.Second
	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return util.NewAPIError(resp.StatusCode, "API server authentication failed")
			}
			return util.NewAPIError(resp.StatusCode, "Error opening websocket: ", err)
		}
		return util.NewAPIError(util.OtherError, "Error opening websocket: ", err)
	}

	c.ws = ws
	c.MessageListeners = append(c.MessageListeners, onMessage)

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c *Connection) Close() {
	if err := c.stateMachine.Event(evClose); err != nil {
		if _, ok := err.(fsm.NoTransitionError); ok {
			// already in closed state
		} else {
			logrus.WithError(err).Error("failed to close connection")
		}
	}
}

// IsClosed -- Returns true for closed connections (either being closed normally or due to an error/timeout)
func (c *Connection) IsClosed() bool {
	if s := c.stateMachine; s != nil {
		return s.Is(stateClosed)
	}
	return false
}

func (c *Connection) receive() {
	defer c.Close()

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			if e, ok := err.(*websocket.CloseError); ok {
				if e.Code == 1000 {
					// normal close
				} else {
					logrus.WithError(err).Error("websocket closed abnormally")
				}
			} else {
				if !c.IsClosed() {
					logrus.WithError(err).Errorf("read error (type: %s)", reflect.TypeOf(err))
					c._error(util.NewAPIError(util.OtherError, err.Error()))
				} else {
					logrus.WithError(err).Debug("connetion.receive() interrupted by error: ", reflect.TypeOf(err))
				}
			}
			return
		}
		for _, cb := range c.MessageListeners {
			cb(msgType, msg)
		}
	}
}

// SendBinary -- Send binary WebSocket message
func (c *Connection) SendBinary(data []byte) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.BinaryMessage, data)
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c *Connection) SendJSON(value interface{}) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteJSON(value)
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c *Connection) SendText(msg string) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
}

// Wait -- Wait for the connection to close
func (c *Connection) Wait() {
	<-c.done
}

// _error -- Puts the connection into the 'error' state and closes it
func (c *Connection) _error(err util.APIError) {
	c.stateMachine.Event(evError, err)
}

func (c *Connection) _onClose(ev *fsm.Event) {
	if c.ws != nil { // could be nil if a goroutine called us before we are connected
		c.ws.Close()
	}

	close(c.done)
	go func() { // do this asynchronously (we could end up in a deadlock (if one of the callbacks calls Close() again))
		for _, cb := range c.CloseListeners {
			cb()
		}
	}()
}

func (c *Connection) _onError(ev *fsm.Event) {
	err, ok := ev.Args[0].(util.APIError)
	if !ok {
		panic("Connection._onError() expects an APIError parameter!")
	}
	for _, cb := range c.ErrorListeners {
		cb(err)
	}
}

func (c *Connection) _onStateChange(ev *fsm.Event) {
	logrus.Debug("connection state changed: ", ev.Src, " -> ", ev.Dst)
}
<|endoftext|>


<commit_before>package common

import (
	"bufio"
	"fmt"
	"github.com/n0rad/go-erlog/data"
	"github.com/n0rad/go-erlog/errs"
	"github.com/n0rad/go-erlog/logs"
	"strings"
)

const rktSupportedVersion Version = "1.0.0"

type RktConfig struct {
	Path               string   `yaml:"path"`
	InsecureOptions    []string `yaml:"insecureOptions"`
	dir                string   `yaml:"dir"`
	LocalConfig        string   `yaml:"localConfig"`
	SystemConfig       string   `yaml:"systemConfig"`
	UserConfig         string   `yaml:"userConfig"`
	TrustKeysFromHttps bool     `yaml:"trustKeysFromHttps"`
	NoStore            bool     `yaml:"noStore"`
	StoreOnly          bool     `yaml:"storeOnly"`
}

type RktClient struct {
	config     RktConfig
	globalArgs []string
	fields     data.Fields
}

func NewRktClient(config RktConfig) (*RktClient, error) {
	if len(config.InsecureOptions) == 0 {
		config.InsecureOptions = []string{"ondisk", "image"}
	}

	rkt := &RktClient{
		fields:     data.WithField("config", config),
		config:     config,
		globalArgs: config.prepareGlobalArgs(),
	}

	v, err := rkt.Version()
	if err != nil {
		return nil, err
	}
	if v.LessThan(rktSupportedVersion) {
		return nil, errs.WithF(rkt.fields.WithField("current", v).WithField("required", ">="+rktSupportedVersion), "Unsupported version of rkt")
	}

	logs.WithField("version", v).WithField("args", rkt.globalArgs).Debug("New rkt client")
	return rkt, nil
}

func (rktCfg *RktConfig) prepareGlobalArgs() []string {
	args := []string{}

	cmd := "rkt"
	if rktCfg.Path != "" {
		cmd = rktCfg.Path
	}
	args = append(args, cmd)

	if logs.IsDebugEnabled() {
		args = append(args, "--debug")
	}
	if rktCfg.TrustKeysFromHttps {
		args = append(args, "--trust-keys-from-https")
	}
	if rktCfg.UserConfig != "" {
		args = append(args, "--user-config="+rktCfg.UserConfig)
	}
	if rktCfg.LocalConfig != "" {
		args = append(args, "--local-config="+rktCfg.LocalConfig)
	}
	if rktCfg.SystemConfig != "" {
		args = append(args, "--system-config="+rktCfg.SystemConfig)
	}
	if rktCfg.dir != "" {
		args = append(args, "--rkt.dir="+rktCfg.dir)
	}
	args = append(args, "--insecure-options="+strings.Join(rktCfg.InsecureOptions, ","))
	return args
}

func (rkt *RktClient) argsStore(cmd []string, cmdArgs ...string) []string {
	args := rkt.globalArgs[1:]
	args = append(args, cmd...)
	if rkt.config.NoStore {
		args = append(args, "--no-store")
	}
	if rkt.config.StoreOnly {
		args = append(args, "--store-only")
	}
	args = append(args, cmdArgs...)
	return args
}

func (rkt *RktClient) GetPath() (string, error) {
	if rkt.config.Path != "" {
		return rkt.config.Path, nil
	}
	return ExecCmdGetOutput("/bin/bash", "-c", "command -v rkt")
}

func (rkt *RktClient) Version() (Version, error) {
	output, err := ExecCmdGetOutput(rkt.globalArgs[0], "version")
	if err != nil {
		return "", errs.WithEF(err, rkt.fields, "Failed to get rkt Version")
	}

	scanner := bufio.NewScanner(strings.NewReader(output))
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "rkt Version:") {
			var versionString string
			if read, err := fmt.Sscanf(line, "rkt Version: %s", &versionString); read != 1 || err != nil {
				return "", errs.WithEF(err, rkt.fields.WithField("cpntent", line), "Failed to read rkt version")
			}
			version := Version(versionString)
			return version, nil
		}
	}
	return "", errs.WithF(rkt.fields.WithField("content", output), "Cannot found rkt version from rkt call")
}

func (rkt *RktClient) Fetch(image string) (string, error) {
	hash, err := ExecCmdGetOutput(rkt.globalArgs[0], rkt.argsStore([]string{"fetch"}, "--full", image)...)
	if err != nil {
		return "", errs.WithEF(err, rkt.fields.WithField("image", image), "Failed to fetch image")
	}
	return hash, err
}

func (rkt *RktClient) CatManifest(image string) (string, error) {
	content, err := ExecCmdGetOutput(rkt.globalArgs[0], append(rkt.globalArgs[1:], "image", "cat-manifest", image)...)
	if err != nil {
		return "", errs.WithEF(err, rkt.fields.WithField("image", image), "Failed to cat manifest")
	}
	return content, err
}

func (rkt *RktClient) ImageRm(images string) error {
	stdout, stderr, err := ExecCmdGetStdoutAndStderr(rkt.globalArgs[0], append(rkt.globalArgs[1:], "image", "rm", images)...)
	if err != nil {
		return errs.WithEF(err, rkt.fields.WithField("images", images).WithField("stdout", stdout).WithField("stderr", stderr), "Failed to cat manifest")
	}
	return err
}

func (rkt *RktClient) RmFromFile(path string) (string, string, error) {
	out, stderr, err := ExecCmdGetStdoutAndStderr(rkt.globalArgs[0], append(rkt.globalArgs[1:], "rm", "--uuid-file", path)...)
	if err != nil {
		return "", "", errs.WithEF(err, rkt.fields.WithField("path", path).
			WithField("stdout", out).
			WithField("stderr", stderr), "Failed to remove containers")
	}
	return out, stderr, err
}

func (rkt *RktClient) Rm(uuids string) (string, string, error) {
	out, stderr, err := ExecCmdGetStdoutAndStderr(rkt.globalArgs[0], append(rkt.globalArgs[1:], "rm", uuids)...)
	if err != nil {
		return "", "", errs.WithEF(err, rkt.fields.WithField("uuids", uuids).
			WithField("stdout", out).
			WithField("stderr", stderr), "Failed to remove containers")
	}
	return out, stderr, err
}

func (rkt *RktClient) Run(args []string) error {
	if err := ExecCmd(rkt.globalArgs[0], append(append(rkt.globalArgs[1:], "run"), args...)...); err != nil {
		return errs.WithEF(err, rkt.fields, "Run failed")
	}
	return nil
}
<commit_msg>set rkt 1.4.0 as minimal version required<commit_after>package common

import (
	"bufio"
	"fmt"
	"github.com/n0rad/go-erlog/data"
	"github.com/n0rad/go-erlog/errs"
	"github.com/n0rad/go-erlog/logs"
	"strings"
)

const rktSupportedVersion Version = "1.4.0"

type RktConfig struct {
	Path               string   `yaml:"path"`
	InsecureOptions    []string `yaml:"insecureOptions"`
	dir                string   `yaml:"dir"`
	LocalConfig        string   `yaml:"localConfig"`
	SystemConfig       string   `yaml:"systemConfig"`
	UserConfig         string   `yaml:"userConfig"`
	TrustKeysFromHttps bool     `yaml:"trustKeysFromHttps"`
	NoStore            bool     `yaml:"noStore"`
	StoreOnly          bool     `yaml:"storeOnly"`
}

type RktClient struct {
	config     RktConfig
	globalArgs []string
	fields     data.Fields
}

func NewRktClient(config RktConfig) (*RktClient, error) {
	if len(config.InsecureOptions) == 0 {
		config.InsecureOptions = []string{"ondisk", "image"}
	}

	rkt := &RktClient{
		fields:     data.WithField("config", config),
		config:     config,
		globalArgs: config.prepareGlobalArgs(),
	}

	v, err := rkt.Version()
	if err != nil {
		return nil, err
	}
	if v.LessThan(rktSupportedVersion) {
		return nil, errs.WithF(rkt.fields.WithField("current", v).WithField("required", ">="+rktSupportedVersion), "Unsupported version of rkt")
	}

	logs.WithField("version", v).WithField("args", rkt.globalArgs).Debug("New rkt client")
	return rkt, nil
}

func (rktCfg *RktConfig) prepareGlobalArgs() []string {
	args := []string{}

	cmd := "rkt"
	if rktCfg.Path != "" {
		cmd = rktCfg.Path
	}
	args = append(args, cmd)

	if logs.IsDebugEnabled() {
		args = append(args, "--debug")
	}
	if rktCfg.TrustKeysFromHttps {
		args = append(args, "--trust-keys-from-https")
	}
	if rktCfg.UserConfig != "" {
		args = append(args, "--user-config="+rktCfg.UserConfig)
	}
	if rktCfg.LocalConfig != "" {
		args = append(args, "--local-config="+rktCfg.LocalConfig)
	}
	if rktCfg.SystemConfig != "" {
		args = append(args, "--system-config="+rktCfg.SystemConfig)
	}
	if rktCfg.dir != "" {
		args = append(args, "--rkt.dir="+rktCfg.dir)
	}
	args = append(args, "--insecure-options="+strings.Join(rktCfg.InsecureOptions, ","))
	return args
}

func (rkt *RktClient) argsStore(cmd []string, cmdArgs ...string) []string {
	args := rkt.globalArgs[1:]
	args = append(args, cmd...)
	if rkt.config.NoStore {
		args = append(args, "--no-store")
	}
	if rkt.config.StoreOnly {
		args = append(args, "--store-only")
	}
	args = append(args, cmdArgs...)
	return args
}

func (rkt *RktClient) GetPath() (string, error) {
	if rkt.config.Path != "" {
		return rkt.config.Path, nil
	}
	return ExecCmdGetOutput("/bin/bash", "-c", "command -v rkt")
}

func (rkt *RktClient) Version() (Version, error) {
	output, err := ExecCmdGetOutput(rkt.globalArgs[0], "version")
	if err != nil {
		return "", errs.WithEF(err, rkt.fields, "Failed to get rkt Version")
	}

	scanner := bufio.NewScanner(strings.NewReader(output))
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "rkt Version:") {
			var versionString string
			if read, err := fmt.Sscanf(line, "rkt Version: %s", &versionString); read != 1 || err != nil {
				return "", errs.WithEF(err, rkt.fields.WithField("cpntent", line), "Failed to read rkt version")
			}
			version := Version(versionString)
			return version, nil
		}
	}
	return "", errs.WithF(rkt.fields.WithField("content", output), "Cannot found rkt version from rkt call")
}

func (rkt *RktClient) Fetch(image string) (string, error) {
	hash, err := ExecCmdGetOutput(rkt.globalArgs[0], rkt.argsStore([]string{"fetch"}, "--full", image)...)
	if err != nil {
		return "", errs.WithEF(err, rkt.fields.WithField("image", image), "Failed to fetch image")
	}
	return hash, err
}

func (rkt *RktClient) CatManifest(image string) (string, error) {
	content, err := ExecCmdGetOutput(rkt.globalArgs[0], append(rkt.globalArgs[1:], "image", "cat-manifest", image)...)
	if err != nil {
		return "", errs.WithEF(err, rkt.fields.WithField("image", image), "Failed to cat manifest")
	}
	return content, err
}

func (rkt *RktClient) ImageRm(images string) error {
	stdout, stderr, err := ExecCmdGetStdoutAndStderr(rkt.globalArgs[0], append(rkt.globalArgs[1:], "image", "rm", images)...)
	if err != nil {
		return errs.WithEF(err, rkt.fields.WithField("images", images).WithField("stdout", stdout).WithField("stderr", stderr), "Failed to cat manifest")
	}
	return err
}

func (rkt *RktClient) RmFromFile(path string) (string, string, error) {
	out, stderr, err := ExecCmdGetStdoutAndStderr(rkt.globalArgs[0], append(rkt.globalArgs[1:], "rm", "--uuid-file", path)...)
	if err != nil {
		return "", "", errs.WithEF(err, rkt.fields.WithField("path", path).
			WithField("stdout", out).
			WithField("stderr", stderr), "Failed to remove containers")
	}
	return out, stderr, err
}

func (rkt *RktClient) Rm(uuids string) (string, string, error) {
	out, stderr, err := ExecCmdGetStdoutAndStderr(rkt.globalArgs[0], append(rkt.globalArgs[1:], "rm", uuids)...)
	if err != nil {
		return "", "", errs.WithEF(err, rkt.fields.WithField("uuids", uuids).
			WithField("stdout", out).
			WithField("stderr", stderr), "Failed to remove containers")
	}
	return out, stderr, err
}

func (rkt *RktClient) Run(args []string) error {
	if err := ExecCmd(rkt.globalArgs[0], append(append(rkt.globalArgs[1:], "run"), args...)...); err != nil {
		return errs.WithEF(err, rkt.fields, "Run failed")
	}
	return nil
}
<|endoftext|>


<commit_before>package cas

import (
	"net/http"
)

func (err *CASServerError) Error() string { return err.msg }

// Error declarations
var (
	// Input errors (error codes 100-199)
	InvalidEmailAddressError = CASServerError{
		msg:        "An error occurred finding a user with that email address.. Please wait a while and try again",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 100,
	}

	InvalidCredentialsError = CASServerError{
		msg:        "Invalid email/password combination",
		httpCode:   http.StatusUnauthorized,
		casErrCode: 101,
	}
	FailedToFindServiceError = CASServerError{
		msg:        "Failed to find matching service",
		httpCode:   http.StatusNotImplemented,
		casErrCode: 102,
	}
	FailedToFindTicketError = CASServerError{
		msg:        "Failed to find matching ticket",
		httpCode:   http.StatusBadRequest,
		casErrCode: 103,
	}
	SSOAuthenticatedUserRenewError = CASServerError{
		msg:        "Failed to validate ticket, renew option specified and user was SSO authenticated",
		httpCode:   http.StatusNotImplemented,
		casErrCode: 103,
	}
	EmailAlreadyTakenError = CASServerError{
		msg:        "Looks like that email address is already taken. If you've forgotten your password, please contact the administrator",
		httpCode:   http.StatusBadRequest,
		casErrCode: 104,
	}
	FailedToFindServiceByUrlError = CASServerError{
		msg:        "Failed to find service with given URL",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 208,
	}

	// Internal Server errors (error codes 200 - 299)
	FailedToSaveSessionError = CASServerError{
		msg:        "Failed to save session",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 200,
	}
	FailedToDeleteSessionError = CASServerError{
		msg:        "Failed to delete session",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 201,
	}
	FailedToCreateNewAuthTicketError = CASServerError{
		msg:        "Failed to create new authentication ticket",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 202,
	}
	AuthMethodNotSupportedError = CASServerError{
		msg:        "Failed to create new authentication ticket",
		httpCode:   http.StatusMethodNotAllowed,
		casErrCode: 203,
	}
	FailedToCreateUserError = CASServerError{
		msg:        "An error occurred while creating your account.. Please verify fields and try again",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 204,
	}
	FailedToTeardownDatabaseError = CASServerError{
		msg:        "Failed to tear down database",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 205,
	}
	FailedToSetupDatabaseError = CASServerError{
		msg:        "Failed to setup database",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 206,
	}
	FailedToLoadJSONFixtureError = CASServerError{
		msg:        "Failed to import database information from file",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 207,
	}
	FailedToLookupServiceByUrlError = CASServerError{
		msg:        "An error occurred while searching for service with given URL",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 208,
	}
	FailedToCreateTicketError = CASServerError{
		msg:        "Failed to create ticket",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 209,
	}
	FailedToDeleteTicketsForUserError = CASServerError{
		msg:        "Failed to delete tickets for user",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 210,
	}

	// Other (error codes 300 - 399)
	UnsupportedFeatureError = CASServerError{
		msg:        "Feature not supported by CASGO",
		httpCode:   http.StatusNotImplemented,
		casErrCode: 300,
	}
)
<commit_msg>Add error code for failure to find user by email<commit_after>package cas

import (
	"net/http"
)

func (err *CASServerError) Error() string { return err.msg }

// Error declarations
var (
	// Input errors (error codes 100-199)
	InvalidEmailAddressError = CASServerError{
		msg:        "An error occurred finding a user with that email address.. Please wait a while and try again",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 100,
	}

	InvalidCredentialsError = CASServerError{
		msg:        "Invalid email/password combination",
		httpCode:   http.StatusUnauthorized,
		casErrCode: 101,
	}
	FailedToFindServiceError = CASServerError{
		msg:        "Failed to find matching service",
		httpCode:   http.StatusNotImplemented,
		casErrCode: 102,
	}
	FailedToFindTicketError = CASServerError{
		msg:        "Failed to find matching ticket",
		httpCode:   http.StatusBadRequest,
		casErrCode: 103,
	}
	SSOAuthenticatedUserRenewError = CASServerError{
		msg:        "Failed to validate ticket, renew option specified and user was SSO authenticated",
		httpCode:   http.StatusNotImplemented,
		casErrCode: 103,
	}
	EmailAlreadyTakenError = CASServerError{
		msg:        "Looks like that email address is already taken. If you've forgotten your password, please contact the administrator",
		httpCode:   http.StatusBadRequest,
		casErrCode: 104,
	}
	FailedToFindServiceByUrlError = CASServerError{
		msg:        "Failed to find service with given URL",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 208,
	}
	FailedToFindUserByEmailError = CASServerError{
		msg:        "Failed to find user with given email address",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 209,
	}

	// Internal Server errors (error codes 200 - 299)
	FailedToSaveSessionError = CASServerError{
		msg:        "Failed to save session",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 200,
	}
	FailedToDeleteSessionError = CASServerError{
		msg:        "Failed to delete session",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 201,
	}
	FailedToCreateNewAuthTicketError = CASServerError{
		msg:        "Failed to create new authentication ticket",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 202,
	}
	AuthMethodNotSupportedError = CASServerError{
		msg:        "Failed to create new authentication ticket",
		httpCode:   http.StatusMethodNotAllowed,
		casErrCode: 203,
	}
	FailedToCreateUserError = CASServerError{
		msg:        "An error occurred while creating your account.. Please verify fields and try again",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 204,
	}
	FailedToTeardownDatabaseError = CASServerError{
		msg:        "Failed to tear down database",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 205,
	}
	FailedToSetupDatabaseError = CASServerError{
		msg:        "Failed to setup database",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 206,
	}
	FailedToLoadJSONFixtureError = CASServerError{
		msg:        "Failed to import database information from file",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 207,
	}
	FailedToLookupServiceByUrlError = CASServerError{
		msg:        "An error occurred while searching for service with given URL",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 208,
	}
	FailedToCreateTicketError = CASServerError{
		msg:        "Failed to create ticket",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 209,
	}
	FailedToDeleteTicketsForUserError = CASServerError{
		msg:        "Failed to delete tickets for user",
		httpCode:   http.StatusInternalServerError,
		casErrCode: 210,
	}

	// Other (error codes 300 - 399)
	UnsupportedFeatureError = CASServerError{
		msg:        "Feature not supported by CASGO",
		httpCode:   http.StatusNotImplemented,
		casErrCode: 300,
	}
)
<|endoftext|>


<commit_before>package main

import (
  "os"
  "database/sql"
  _ "github.com/bmizerany/pq"
  "github.com/pilu/traffic"
  "github.com/pilu/cerebellum/models"
)

const VERSION = "0.1.0"

var (
  router *traffic.Router
  DB     *sql.DB
)

func initDatabase() {
  var err error
  DB, err = sql.Open("postgres", os.Getenv("DB"))
  if err != nil {
    panic(err)
  }

  err = DB.Ping()
  if err != nil {
    panic(err)
  }

  models.DB = DB
}

func init() {
  initDatabase()

  router = traffic.New()
  router.NotFoundHandler = NotFoundHandler
  router.ErrorHandler    = ErrorHandler
  router.AddBeforeFilter(SetDefaultHeaders)

  router.Get("/", RootHandler)
  // Artist:
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56
  router.Get("/artists/:gid", ArtistHandler)

  // Release Group:
  //   /release-groups/aa997ea0-2936-40bd-884d-3af8a0e064dc
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/release-groups/aa997ea0-2936-40bd-884d-3af8a0e064dc
  router.Get("(/artists/:artist_gid)?/release-groups/:gid", ReleaseGroupHandler).
    AddBeforeFilter(CheckArtistFilter)

  // Release:
  //   /releases/79215cdf-4764-4dee-b0b9-fec1643df7c5
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/releases/79215cdf-4764-4dee-b0b9-fec1643df7c5
  router.Get("(/artists/:artist_gid)?/releases/:gid", ReleaseHandler)

  // Recordings:
  //   /releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings
  router.Get("(/artists/:artist_gid)?/releases/:release_gid/recordings", RecordingsHandler).
    AddBeforeFilter(CheckArtistFilter).
    AddBeforeFilter(CheckReleaseFilter)

  // Release Groups:
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/release-groups
  router.Get("/artists/:artist_gid/release-groups", ReleaseGroupsHandler).
    AddBeforeFilter(CheckArtistFilter)

  // Recordings:
  //   /recordings/0c871a4a-efdf-47f8-98c2-cc277f806d2f
  //   /releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings/0c871a4a-efdf-47f8-98c2-cc277f806d2f
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings/833f00e1-781f-4edd-90e4-e52712618862
  router.Get("((/artists/:artist_gid)?/releases/:release_gid)?/recordings/:gid", RecordingHandler).
    AddBeforeFilter(CheckArtistFilter).
    AddBeforeFilter(CheckReleaseFilter)
}

func main() {
  router.Run()
}
<commit_msg>optional trailing slashes<commit_after>package main

import (
  "os"
  "database/sql"
  _ "github.com/bmizerany/pq"
  "github.com/pilu/traffic"
  "github.com/pilu/cerebellum/models"
)

const VERSION = "0.1.0"

var (
  router *traffic.Router
  DB     *sql.DB
)

func initDatabase() {
  var err error
  DB, err = sql.Open("postgres", os.Getenv("DB"))
  if err != nil {
    panic(err)
  }

  err = DB.Ping()
  if err != nil {
    panic(err)
  }

  models.DB = DB
}

func init() {
  initDatabase()

  router = traffic.New()
  router.NotFoundHandler = NotFoundHandler
  router.ErrorHandler    = ErrorHandler
  router.AddBeforeFilter(SetDefaultHeaders)

  router.Get("/", RootHandler)
  // Artist:
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56
  router.Get("/artists/:gid/?", ArtistHandler)

  // Release Group:
  //   /release-groups/aa997ea0-2936-40bd-884d-3af8a0e064dc
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/release-groups/aa997ea0-2936-40bd-884d-3af8a0e064dc
  router.Get("(/artists/:artist_gid)?/release-groups/:gid/?", ReleaseGroupHandler).
    AddBeforeFilter(CheckArtistFilter)

  // Release:
  //   /releases/79215cdf-4764-4dee-b0b9-fec1643df7c5
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/releases/79215cdf-4764-4dee-b0b9-fec1643df7c5
  router.Get("(/artists/:artist_gid)?/releases/:gid/?", ReleaseHandler)

  // Recordings:
  //   /releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings
  router.Get("(/artists/:artist_gid)?/releases/:release_gid/recordings/?", RecordingsHandler).
    AddBeforeFilter(CheckArtistFilter).
    AddBeforeFilter(CheckReleaseFilter)

  // Release Groups:
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/release-groups
  router.Get("/artists/:artist_gid/release-groups/?", ReleaseGroupsHandler).
    AddBeforeFilter(CheckArtistFilter)

  // Recordings:
  //   /recordings/0c871a4a-efdf-47f8-98c2-cc277f806d2f
  //   /releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings/0c871a4a-efdf-47f8-98c2-cc277f806d2f
  //   /artists/056e4f3e-d505-4dad-8ec1-d04f521cbb56/releases/79215cdf-4764-4dee-b0b9-fec1643df7c5/recordings/833f00e1-781f-4edd-90e4-e52712618862
  router.Get("((/artists/:artist_gid)?/releases/:release_gid)?/recordings/:gid/?", RecordingHandler).
    AddBeforeFilter(CheckArtistFilter).
    AddBeforeFilter(CheckReleaseFilter)
}

func main() {
  router.Run()
}
<|endoftext|>