<commit_before>package natscrypto

// Tests for the PGPEncrypter
//
// TODO Missing tests are :
// - encrypt errors
// - decrypt errors
// - signature errors

import (
	"fmt"
	"io"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/packet"
)

func TestPGPEncrypterAccessors(t *testing.T) {
	ec := NewPGPEncrypter()
	assert.NotNil(t, ec.Identities)

	e := GetEntity(t)

	id := string(e.PrimaryKey.Fingerprint[:20])

	assert.Panics(t, func() { ec.AddEntity(nil) })

	ec.AddEntity(e)

	assert.Equal(t, openpgp.EntityList{e}, ec.AllEntities)
	assert.Equal(t, 1, len(ec.Identities))
	assert.Equal(t, e, ec.Identities[id])

	ec.RemoveID(id)

	assert.Equal(t, openpgp.EntityList{}, ec.AllEntities)
	assert.Equal(t, 0, len(ec.Identities))

	e.PrimaryKey = nil
	assert.Panics(t, func() { ec.AddEntity(e) })
}

func TestEncryptDecryptData(t *testing.T) {
	signer := GetEntity(t)
	signerID := string(signer.PrimaryKey.Fingerprint[:20])

	recipient := GetEntity(t)
	recipientID := string(recipient.PrimaryKey.Fingerprint[:20])
	recipient.PrivateKey = nil

	ec := NewPGPEncrypter()
	ec.AddEntity(signer, recipient)

	data, err := ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, "unknown")
	assert.Nil(t, data)
	assert.Equal(t, "Unknown signer id: unknown", err.Error())

	data, err = ec.EncryptData([]byte("Bonjour !"), []string{"jojo"}, signerID)
	assert.Nil(t, data)
	assert.Equal(t, "Unknown recipient id: jojo", err.Error())

	data, err = ec.EncryptData([]byte("Bonjour !"), []string{signerID}, recipientID)
	assert.Nil(t, data)
	assert.Equal(t, "No private key for this identity: "+recipientID, err.Error())

	data, err = ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.Nil(t, err)
	assert.NotEqual(t, "", data)

	decData, decRecipients, decSigner, decErr := ec.DecryptData(data)
	assert.Nil(t, decErr)
	assert.Equal(t, "Bonjour !", string(decData))
	assert.Equal(t, signerID, decSigner)
	assert.Equal(t, []string{recipientID}, decRecipients)
}

func TestOneShotEntity(t *testing.T) {
	entity := GetEntity(t)
	entityID := string(entity.PrimaryKey.Fingerprint[:20])

	ec := NewPGPEncrypter()
	ec.AddOneShotEntity(entity)

	assert.Equal(t, entity, ec.getEntity(entityID))
	assert.Nil(t, ec.getEntity(entityID))
}

func failingEncrypt(io.Writer, []*openpgp.Entity, *openpgp.Entity, *openpgp.FileHints, *packet.Config) (io.WriteCloser, error) {
	return nil, fmt.Errorf("Some error")
}

type WriteCloserWithFailingWrite struct{}

func (WriteCloserWithFailingWrite) Write([]byte) (int, error) { return 0, fmt.Errorf("Write failed") }
func (WriteCloserWithFailingWrite) Close() error              { return nil }

type WriteCloserWithFailingClose struct{}

func (WriteCloserWithFailingClose) Write([]byte) (int, error) { return 0, nil }
func (WriteCloserWithFailingClose) Close() error              { return fmt.Errorf("Close failed") }

func failingWriterWrite(io.Writer, []*openpgp.Entity, *openpgp.Entity, *openpgp.FileHints, *packet.Config) (io.WriteCloser, error) {
	return WriteCloserWithFailingWrite{}, nil
}
func failingWriterClose(io.Writer, []*openpgp.Entity, *openpgp.Entity, *openpgp.FileHints, *packet.Config) (io.WriteCloser, error) {
	return WriteCloserWithFailingClose{}, nil
}

func TestEncryptErrors(t *testing.T) {
	openpgpEncrypt = failingEncrypt
	defer func() { openpgpEncrypt = openpgp.Encrypt }()

	signer := GetEntity(t)
	signerID := string(signer.PrimaryKey.Fingerprint[:20])

	recipient := GetEntity(t)
	recipientID := string(recipient.PrimaryKey.Fingerprint[:20])
	recipient.PrivateKey = nil

	ec := NewPGPEncrypter()
	ec.AddEntity(signer, recipient)

	_, err := ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.EqualError(t, err, "Some error")

	openpgpEncrypt = failingWriterWrite
	_, err = ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.EqualError(t, err, "Write failed")

	openpgpEncrypt = failingWriterClose
	_, err = ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.EqualError(t, err, "Close failed")
}

var readMessageEntity = GetEntity(nil)

type failingReader struct{}

func (failingReader) Read([]byte) (int, error) {
	return 0, fmt.Errorf("Read failed")
}

func failingReadMessage(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return nil, fmt.Errorf("ReadMessage failed")
}

func readMessageReturnsFaultyBodyReader(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{UnverifiedBody: failingReader{}}, nil
}

func readMessageUnsigned(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{
		UnverifiedBody: strings.NewReader(""),
		DecryptedWith:  openpgp.Key{Entity: readMessageEntity},
	}, nil
}

func readMessageUnknownSigner(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{
		UnverifiedBody: strings.NewReader(""),
		DecryptedWith:  openpgp.Key{Entity: readMessageEntity},
		IsSigned:       true,
	}, nil
}

func readMessageSignatureError(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{
		UnverifiedBody: strings.NewReader(""),
		DecryptedWith:  openpgp.Key{Entity: readMessageEntity},
		IsSigned:       true,
		SignatureError: fmt.Errorf("Signature Error"),
	}, nil
}

func TestDecryptionErrors(t *testing.T) {
	defer func() { openpgpEncrypt = openpgp.Encrypt }()
	signer := GetEntity(t)

	recipient := GetEntity(t)
	recipient.PrivateKey = nil

	ec := NewPGPEncrypter()
	ec.AddEntity(signer, recipient)

	openpgpReadMessage = failingReadMessage
	_, _, _, err := ec.DecryptData([]byte(""))
	assert.EqualError(t, err, "Error decrypting message: ReadMessage failed")

	openpgpReadMessage = readMessageReturnsFaultyBodyReader
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.EqualError(t, err, "Read failed")

	openpgpReadMessage = readMessageUnsigned
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.Equal(t, err, ErrUnsignedMessage)

	openpgpReadMessage = readMessageUnknownSigner
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.Equal(t, err, ErrUnknownSigner)

	openpgpReadMessage = readMessageSignatureError
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.Equal(t, "Signature Error", err.Error())
}
<commit_msg>Remove outdated TODO<commit_after>package natscrypto

// Tests for the PGPEncrypter

import (
	"fmt"
	"io"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/packet"
)

func TestPGPEncrypterAccessors(t *testing.T) {
	ec := NewPGPEncrypter()
	assert.NotNil(t, ec.Identities)

	e := GetEntity(t)

	id := string(e.PrimaryKey.Fingerprint[:20])

	assert.Panics(t, func() { ec.AddEntity(nil) })

	ec.AddEntity(e)

	assert.Equal(t, openpgp.EntityList{e}, ec.AllEntities)
	assert.Equal(t, 1, len(ec.Identities))
	assert.Equal(t, e, ec.Identities[id])

	ec.RemoveID(id)

	assert.Equal(t, openpgp.EntityList{}, ec.AllEntities)
	assert.Equal(t, 0, len(ec.Identities))

	e.PrimaryKey = nil
	assert.Panics(t, func() { ec.AddEntity(e) })
}

func TestEncryptDecryptData(t *testing.T) {
	signer := GetEntity(t)
	signerID := string(signer.PrimaryKey.Fingerprint[:20])

	recipient := GetEntity(t)
	recipientID := string(recipient.PrimaryKey.Fingerprint[:20])
	recipient.PrivateKey = nil

	ec := NewPGPEncrypter()
	ec.AddEntity(signer, recipient)

	data, err := ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, "unknown")
	assert.Nil(t, data)
	assert.Equal(t, "Unknown signer id: unknown", err.Error())

	data, err = ec.EncryptData([]byte("Bonjour !"), []string{"jojo"}, signerID)
	assert.Nil(t, data)
	assert.Equal(t, "Unknown recipient id: jojo", err.Error())

	data, err = ec.EncryptData([]byte("Bonjour !"), []string{signerID}, recipientID)
	assert.Nil(t, data)
	assert.Equal(t, "No private key for this identity: "+recipientID, err.Error())

	data, err = ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.Nil(t, err)
	assert.NotEqual(t, "", data)

	decData, decRecipients, decSigner, decErr := ec.DecryptData(data)
	assert.Nil(t, decErr)
	assert.Equal(t, "Bonjour !", string(decData))
	assert.Equal(t, signerID, decSigner)
	assert.Equal(t, []string{recipientID}, decRecipients)
}

func TestOneShotEntity(t *testing.T) {
	entity := GetEntity(t)
	entityID := string(entity.PrimaryKey.Fingerprint[:20])

	ec := NewPGPEncrypter()
	ec.AddOneShotEntity(entity)

	assert.Equal(t, entity, ec.getEntity(entityID))
	assert.Nil(t, ec.getEntity(entityID))
}

func failingEncrypt(io.Writer, []*openpgp.Entity, *openpgp.Entity, *openpgp.FileHints, *packet.Config) (io.WriteCloser, error) {
	return nil, fmt.Errorf("Some error")
}

type WriteCloserWithFailingWrite struct{}

func (WriteCloserWithFailingWrite) Write([]byte) (int, error) { return 0, fmt.Errorf("Write failed") }
func (WriteCloserWithFailingWrite) Close() error              { return nil }

type WriteCloserWithFailingClose struct{}

func (WriteCloserWithFailingClose) Write([]byte) (int, error) { return 0, nil }
func (WriteCloserWithFailingClose) Close() error              { return fmt.Errorf("Close failed") }

func failingWriterWrite(io.Writer, []*openpgp.Entity, *openpgp.Entity, *openpgp.FileHints, *packet.Config) (io.WriteCloser, error) {
	return WriteCloserWithFailingWrite{}, nil
}
func failingWriterClose(io.Writer, []*openpgp.Entity, *openpgp.Entity, *openpgp.FileHints, *packet.Config) (io.WriteCloser, error) {
	return WriteCloserWithFailingClose{}, nil
}

func TestEncryptErrors(t *testing.T) {
	openpgpEncrypt = failingEncrypt
	defer func() { openpgpEncrypt = openpgp.Encrypt }()

	signer := GetEntity(t)
	signerID := string(signer.PrimaryKey.Fingerprint[:20])

	recipient := GetEntity(t)
	recipientID := string(recipient.PrimaryKey.Fingerprint[:20])
	recipient.PrivateKey = nil

	ec := NewPGPEncrypter()
	ec.AddEntity(signer, recipient)

	_, err := ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.EqualError(t, err, "Some error")

	openpgpEncrypt = failingWriterWrite
	_, err = ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.EqualError(t, err, "Write failed")

	openpgpEncrypt = failingWriterClose
	_, err = ec.EncryptData([]byte("Bonjour !"), []string{recipientID}, signerID)
	assert.EqualError(t, err, "Close failed")
}

var readMessageEntity = GetEntity(nil)

type failingReader struct{}

func (failingReader) Read([]byte) (int, error) {
	return 0, fmt.Errorf("Read failed")
}

func failingReadMessage(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return nil, fmt.Errorf("ReadMessage failed")
}

func readMessageReturnsFaultyBodyReader(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{UnverifiedBody: failingReader{}}, nil
}

func readMessageUnsigned(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{
		UnverifiedBody: strings.NewReader(""),
		DecryptedWith:  openpgp.Key{Entity: readMessageEntity},
	}, nil
}

func readMessageUnknownSigner(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{
		UnverifiedBody: strings.NewReader(""),
		DecryptedWith:  openpgp.Key{Entity: readMessageEntity},
		IsSigned:       true,
	}, nil
}

func readMessageSignatureError(io.Reader, openpgp.KeyRing, openpgp.PromptFunction, *packet.Config) (*openpgp.MessageDetails, error) {
	return &openpgp.MessageDetails{
		UnverifiedBody: strings.NewReader(""),
		DecryptedWith:  openpgp.Key{Entity: readMessageEntity},
		IsSigned:       true,
		SignatureError: fmt.Errorf("Signature Error"),
	}, nil
}

func TestDecryptionErrors(t *testing.T) {
	defer func() { openpgpEncrypt = openpgp.Encrypt }()
	signer := GetEntity(t)

	recipient := GetEntity(t)
	recipient.PrivateKey = nil

	ec := NewPGPEncrypter()
	ec.AddEntity(signer, recipient)

	openpgpReadMessage = failingReadMessage
	_, _, _, err := ec.DecryptData([]byte(""))
	assert.EqualError(t, err, "Error decrypting message: ReadMessage failed")

	openpgpReadMessage = readMessageReturnsFaultyBodyReader
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.EqualError(t, err, "Read failed")

	openpgpReadMessage = readMessageUnsigned
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.Equal(t, err, ErrUnsignedMessage)

	openpgpReadMessage = readMessageUnknownSigner
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.Equal(t, err, ErrUnknownSigner)

	openpgpReadMessage = readMessageSignatureError
	_, _, _, err = ec.DecryptData([]byte(""))
	assert.Equal(t, "Signature Error", err.Error())
}
<|endoftext|>


<commit_before>package session

import (
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"time"
)

type MongoStore struct {
	c         *mgo.Collection
	maxAge    time.Duration
	gcTimer   *time.Timer
	closeChan chan bool
}

func NewMongoStore(c *mgo.Collection, maxAge int) *MongoStore {
	duration := time.Second * time.Duration(maxAge)
	store := &MongoStore{
		c:         c,
		maxAge:    duration,
		gcTimer:   time.NewTimer(duration),
		closeChan: make(chan bool),
	}

	err := c.EnsureIndex(mgo.Index{
		Key:        []string{"id"},
		Unique:     true,
		DropDups:   false,
		Background: true, // See notes.
		Sparse:     true,
	})
	if err != nil {
		log.Printf("[session.MongoStore] ensure index error: %s", err)
	}

	go func() {
		//fmt.Println("gc enter")
	loop:
		for {
			select {
			case <-store.gcTimer.C:
				//fmt.Println("gc...")
				s := bson.M{
					"expires_at": bson.M{
						"$lt": time.Now(),
					},
				}
				if err := c.Remove(s); err != nil {
					if err != mgo.ErrNotFound {
						log.Printf("[session.MongoStore] clean up error: %s\n", err)
					}
				}
				store.gcTimer.Reset(duration)
			case <-store.closeChan:
				store.closeChan <- true
				break loop
			}
		}
		//fmt.Println("gc exit")
	}()

	return store
}

func (s *MongoStore) Load(id string) (*Session, error) {
	doc := struct {
		ExpiresAt time.Time
		Data      map[string]interface{}
	}{}
	err := s.c.Find(bson.M{"id": id}).One(&doc)
	if err != nil {
		return nil, fmt.Errorf("[session.MongoStore] get: %s", err)
	}
	sess := NewSession(id)
	sess.expiresAt = doc.ExpiresAt
	sess.data = doc.Data
	return sess, nil
}

func (s *MongoStore) Has(id string) (bool, error) {
	n, err := s.c.Find(bson.M{"id": id}).Count()
	if err != nil {
		return false, fmt.Errorf("[session.MongoStore] count: %s", err)
	}
	return n > 0, nil
}

func (s *MongoStore) Revoke(id string) error {
	err := s.c.Remove(bson.M{"id": id})
	if err != nil {
		return fmt.Errorf("[session.MongoStore] remove: %s", err)
	}
	return nil
}

func (s *MongoStore) Renew(session *Session) error {
	return s.Save(session)
}

func (s *MongoStore) Save(session *Session) error {
	expiry := time.Now().Add(s.maxAge)
	session.expiresAt = expiry
	_, err := s.c.Upsert(bson.M{"id": session.id}, bson.M{
		"$set": bson.M{
			"expires_at": expiry,
			"data":       session.data,
		},
		"$setOnInsert": bson.M{
			"id": session.id,
		},
	})
	return err
}

func (s *MongoStore) Close() error {
	s.gcTimer.Stop()
	s.closeChan <- true
	<-s.closeChan
	//fmt.Println("Closed")
	return nil
}
<commit_msg>check mgo.ErrorNotFound<commit_after>package session

import (
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"time"
)

type MongoStore struct {
	c         *mgo.Collection
	maxAge    time.Duration
	gcTimer   *time.Timer
	closeChan chan bool
}

func NewMongoStore(c *mgo.Collection, maxAge int) *MongoStore {
	duration := time.Second * time.Duration(maxAge)
	store := &MongoStore{
		c:         c,
		maxAge:    duration,
		gcTimer:   time.NewTimer(duration),
		closeChan: make(chan bool),
	}

	err := c.EnsureIndex(mgo.Index{
		Key:        []string{"id"},
		Unique:     true,
		DropDups:   false,
		Background: true, // See notes.
		Sparse:     true,
	})
	if err != nil {
		log.Printf("[session.MongoStore] ensure index error: %s", err)
	}

	go func() {
		//fmt.Println("gc enter")
	loop:
		for {
			select {
			case <-store.gcTimer.C:
				//fmt.Println("gc...")
				s := bson.M{
					"expires_at": bson.M{
						"$lt": time.Now(),
					},
				}
				if err := c.Remove(s); err != nil {
					if err != mgo.ErrNotFound {
						log.Printf("[session.MongoStore] clean up error: %s\n", err)
					}
				}
				store.gcTimer.Reset(duration)
			case <-store.closeChan:
				store.closeChan <- true
				break loop
			}
		}
		//fmt.Println("gc exit")
	}()

	return store
}

func (s *MongoStore) Load(id string) (*Session, error) {
	doc := struct {
		ExpiresAt time.Time
		Data      map[string]interface{}
	}{}
	err := s.c.Find(bson.M{"id": id}).One(&doc)
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, nil
		}
		return nil, fmt.Errorf("[session.MongoStore] get: %s", err)
	}
	sess := NewSession(id)
	sess.expiresAt = doc.ExpiresAt
	sess.data = doc.Data
	return sess, nil
}

func (s *MongoStore) Has(id string) (bool, error) {
	n, err := s.c.Find(bson.M{"id": id}).Count()
	if err != nil {
		return false, fmt.Errorf("[session.MongoStore] count: %s", err)
	}
	return n > 0, nil
}

func (s *MongoStore) Revoke(id string) error {
	err := s.c.Remove(bson.M{"id": id})
	if err != nil {
		return fmt.Errorf("[session.MongoStore] remove: %s", err)
	}
	return nil
}

func (s *MongoStore) Renew(session *Session) error {
	return s.Save(session)
}

func (s *MongoStore) Save(session *Session) error {
	expiry := time.Now().Add(s.maxAge)
	session.expiresAt = expiry
	_, err := s.c.Upsert(bson.M{"id": session.id}, bson.M{
		"$set": bson.M{
			"expires_at": expiry,
			"data":       session.data,
		},
		"$setOnInsert": bson.M{
			"id": session.id,
		},
	})
	return err
}

func (s *MongoStore) Close() error {
	s.gcTimer.Stop()
	s.closeChan <- true
	<-s.closeChan
	//fmt.Println("Closed")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

const alwaysMonkeyingAround = 1.0

func TestItMonkeysWithStatusCodes(t *testing.T) {
	notFoundBehaviour := new(behaviour)
	notFoundBehaviour.Frequency = alwaysMonkeyingAround
	notFoundBehaviour.Status = http.StatusNotFound

	testServer, request := makeTestServerAndRequest()

	monkeyServer := NewMonkeyServer(testServer.Config.Handler, []behaviour{*notFoundBehaviour})

	w := httptest.NewRecorder()

	monkeyServer.ServeHTTP(w, request)

	if w.Code != notFoundBehaviour.Status {
		t.Error("Server shouldve returned a 404 because of monkey override")
	}
}

func makeTestServerAndRequest() (*httptest.Server, *http.Request) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	}))
	request, _ := http.NewRequest("GET", server.URL, nil)

	return server, request
}
<commit_msg>Garbage test<commit_after>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

const alwaysMonkeyingAround = 1.0
const cannedResponse = "hello, world"

func TestItMonkeysWithStatusCodesAndBodies(t *testing.T) {
	monkeyBehaviour := new(behaviour)
	monkeyBehaviour.Frequency = alwaysMonkeyingAround
	monkeyBehaviour.Status = http.StatusNotFound
	monkeyBehaviour.Body = "hello, monkey"

	testServer, request := makeTestServerAndRequest()

	monkeyServer := NewMonkeyServer(testServer.Config.Handler, []behaviour{*monkeyBehaviour})

	w := httptest.NewRecorder()

	monkeyServer.ServeHTTP(w, request)

	if w.Code != monkeyBehaviour.Status {
		t.Error("Server shouldve returned a 404 because of monkey override")
	}

	if w.Body.String() != monkeyBehaviour.Body {
		t.Error("Server should've returned a different body because of monkey override")
	}
}

func TestItReturnsGarbage(t *testing.T) {
	monkeyBehaviour := new(behaviour)
	monkeyBehaviour.Frequency = alwaysMonkeyingAround
	monkeyBehaviour.Garbage = 1984

	testServer, request := makeTestServerAndRequest()

	monkeyServer := NewMonkeyServer(testServer.Config.Handler, []behaviour{*monkeyBehaviour})

	w := httptest.NewRecorder()

	monkeyServer.ServeHTTP(w, request)

	if len(w.Body.Bytes()) != monkeyBehaviour.Garbage {
		t.Error("Server shouldve returned garbage")
	}
}

func makeTestServerAndRequest() (*httptest.Server, *http.Request) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(cannedResponse))
	}))
	request, _ := http.NewRequest("GET", server.URL, nil)

	return server, request
}
<|endoftext|>


<commit_before>package aero

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/OneOfOne/xxhash"
	"github.com/aerogo/session"
	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
	"github.com/tomasen/realip"
	"github.com/valyala/fasthttp"
)

// This should be close to the MTU size of a TCP packet.
// Regarding performance it makes no sense to compress smaller files.
// Bandwidth can be saved however the savings are minimal for small files
// and the overhead of compressing can lead up to a 75% reduction
// in server speed under high load. Therefore in this case
// we're trying to optimize for performance, not bandwidth.
const gzipThreshold = 1450

const (
	serverHeader                  = "Server"
	server                        = "Aero"
	cacheControlHeader            = "Cache-Control"
	cacheControlAlwaysValidate    = "must-revalidate"
	cacheControlMedia             = "public, max-age=864000"
	contentTypeOptionsHeader      = "X-Content-Type-Options"
	contentTypeOptions            = "nosniff"
	xssProtectionHeader           = "X-XSS-Protection"
	xssProtection                 = "1; mode=block"
	etagHeader                    = "ETag"
	contentTypeHeader             = "Content-Type"
	contentTypeHTML               = "text/html; charset=utf-8"
	contentTypeJavaScript         = "application/javascript; charset=utf-8"
	contentTypeJSON               = "application/json; charset=utf-8"
	contentTypePlainText          = "text/plain; charset=utf-8"
	contentEncodingHeader         = "Content-Encoding"
	contentEncodingGzip           = "gzip"
	contentLengthHeader           = "Content-Length"
	responseTimeHeader            = "X-Response-Time"
	ifNoneMatchHeader             = "If-None-Match"
	xFrameOptionsHeader           = "X-Frame-Options"
	xFrameOptions                 = "SAMEORIGIN"
	referrerPolicyHeader          = "Referrer-Policy"
	referrerPolicySameOrigin      = "no-referrer"
	strictTransportSecurityHeader = "Strict-Transport-Security"
	strictTransportSecurity       = "max-age=31536000; includeSubDomains; preload"
	contentSecurityPolicyHeader   = "Content-Security-Policy"
)

// Context represents a single request & response.
type Context struct {
	// net/http
	request  *http.Request
	response http.ResponseWriter
	params   httprouter.Params

	// A pointer to the application this request occured on.
	App *Application

	// Status code
	StatusCode int

	// Custom data
	Data interface{}

	// User session
	session *session.Session
}

// Request returns the HTTP request.
func (ctx *Context) Request() Request {
	return Request{
		inner: ctx.request,
	}
}

// Response returns the HTTP response.
func (ctx *Context) Response() Response {
	return Response{
		inner: ctx.response,
	}
}

// Session returns the session of the context or creates and caches a new session.
func (ctx *Context) Session() *session.Session {
	// Return cached session if available.
	if ctx.session != nil {
		return ctx.session
	}

	// Check if the client has a session cookie already.
	cookie, err := ctx.request.Cookie("sid")

	if err == nil {
		sid := cookie.Value

		if session.IsValidID(sid) {
			ctx.session, err = ctx.App.Sessions.Store.Get(sid)

			if err != nil {
				color.Red(err.Error())
			}

			if ctx.session != nil {
				return ctx.session
			}
		}
	}

	// Create a new session
	ctx.session = ctx.App.Sessions.New()

	// Create a session cookie in the client
	ctx.createSessionCookie()

	return ctx.session
}

// createSessionCookie creates a session cookie in the client.
func (ctx *Context) createSessionCookie() {
	sessionCookie := http.Cookie{
		Name:     "sid",
		Value:    ctx.session.ID(),
		HttpOnly: true,
		Secure:   true,
		MaxAge:   ctx.App.Sessions.Duration,
		Path:     "/",
	}

	http.SetCookie(ctx.response, &sessionCookie)

	// HACK: Add SameSite attribute
	// Remove this once it's available inside http.Cookie
	// cookieData := ctx.response.Header().Get("Set-Cookie")
	// cookieData += "; SameSite=lax"
	// ctx.response.Header().Set("Set-Cookie", cookieData)
}

// HasSession indicates whether the client has a valid session or not.
func (ctx *Context) HasSession() bool {
	if ctx.session != nil {
		return true
	}

	cookie, err := ctx.request.Cookie("sid")

	if err != nil || !session.IsValidID(cookie.Value) {
		return false
	}

	ctx.session, err = ctx.App.Sessions.Store.Get(cookie.Value)

	if err != nil {
		return false
	}

	return ctx.session != nil
}

// JSON encodes the object to a JSON string and responds.
func (ctx *Context) JSON(value interface{}) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypeJSON)

	bytes, err := json.Marshal(value)

	if err != nil {
		ctx.StatusCode = http.StatusInternalServerError
		return `{"error": "Could not encode object to JSON"}`
	}

	return string(bytes)
}

// HTML sends a HTML string.
func (ctx *Context) HTML(html string) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypeHTML)
	ctx.response.Header().Set(contentTypeOptionsHeader, contentTypeOptions)
	ctx.response.Header().Set(xssProtectionHeader, xssProtection)
	// ctx.response.Header().Set(xFrameOptionsHeader, xFrameOptions)
	ctx.response.Header().Set(referrerPolicyHeader, referrerPolicySameOrigin)

	if ctx.App.Security.Certificate != "" {
		ctx.response.Header().Set(strictTransportSecurityHeader, strictTransportSecurity)
		ctx.response.Header().Set(contentSecurityPolicyHeader, ctx.App.ContentSecurityPolicy.String())
	}

	return html
}

// Text sends a plain text string.
func (ctx *Context) Text(text string) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypePlainText)
	return text
}

// JavaScript sends a script.
func (ctx *Context) JavaScript(code string) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypeJavaScript)
	return code
}

// File sends the contents of a local file and determines its mime type by extension.
func (ctx *Context) File(file string) string {
	extension := filepath.Ext(file)
	mimeType := mime.TypeByExtension(extension)
	data, _ := ioutil.ReadFile(file)

	if mimeType == "" {
		mimeType = http.DetectContentType(data)
	}

	ctx.response.Header().Set(contentTypeHeader, mimeType)
	return string(data)
}

// Error should be used for sending error messages to the user.
func (ctx *Context) Error(statusCode int, explanation string, err error) string {
	ctx.StatusCode = statusCode
	ctx.response.Header().Set(contentTypeHeader, contentTypeHTML)

	if err != nil {
		detailed := err.Error()
		color.Red(detailed)
		return fmt.Sprintf("%s (%s)", explanation, detailed)
	}

	return explanation
}

// URI returns the relative path, e.g. /blog/post/123.
func (ctx *Context) URI() string {
	return ctx.request.URL.Path
}

// SetURI sets the relative path, e.g. /blog/post/123.
func (ctx *Context) SetURI(b string) {
	ctx.request.URL.Path = b
}

// Get retrieves an URL parameter.
func (ctx *Context) Get(param string) string {
	return ctx.params.ByName(param)
}

// GetInt retrieves an URL parameter as an integer.
func (ctx *Context) GetInt(param string) (int, error) {
	return strconv.Atoi(ctx.Get(param))
}

// RealIP tries to determine the real IP address of the request.
func (ctx *Context) RealIP() string {
	return realip.RealIP(ctx.request)
}

// UserAgent retrieves the user agent for the given request.
func (ctx *Context) UserAgent() string {
	ctx.request.URL.Query()
	return ctx.request.UserAgent()
}

// Query retrieves the value for the given URL query parameter.
func (ctx *Context) Query(param string) string {
	return ctx.request.URL.Query().Get(param)
}

// Redirect redirects to the given URL using status code 302.
func (ctx *Context) Redirect(url string) string {
	ctx.StatusCode = http.StatusFound
	ctx.response.Header().Set("Location", url)
	return ""
}

// RedirectPermanently redirects to the given URL and indicates that this is a permanent change using status code 301.
func (ctx *Context) RedirectPermanently(url string) string {
	ctx.StatusCode = http.StatusPermanentRedirect
	ctx.response.Header().Set("Location", url)
	return ""
}

// IsMediaType returns whether the given content type is a media type.
func IsMediaType(contentType string) bool {
	return strings.HasPrefix(contentType, "image/") || strings.HasPrefix(contentType, "video/") || strings.HasPrefix(contentType, "audio/")
}

// pushResources will push the given resources to the HTTP response.
func (ctx *Context) pushResources() {
	// Check if all the conditions for a push are met
	for _, pushCondition := range ctx.App.pushConditions {
		if !pushCondition(ctx) {
			return
		}
	}

	// OnPush callbacks
	for _, callback := range ctx.App.onPush {
		callback(ctx)
	}

	// Check if we can push
	pusher, ok := ctx.response.(http.Pusher)

	if !ok {
		return
	}

	// Push every resource defined in config.json
	for _, resource := range ctx.App.Config.Push {
		if err := pusher.Push(resource, nil); err != nil {
			log.Printf("Failed to push %s: %v", resource, err)
		}
	}
}

// respond responds either with raw code or gzipped if the
// code length is greater than the gzip threshold.
func (ctx *Context) respond(code string) {
	ctx.respondBytes(StringToBytesUnsafe(code))
}

// respondBytes responds either with raw code or gzipped if the
// code length is greater than the gzip threshold. Requires a byte slice.
func (ctx *Context) respondBytes(b []byte) {
	response := ctx.response
	header := response.Header()
	contentType := header.Get(contentTypeHeader)
	isMedia := IsMediaType(contentType)

	// Push
	if contentType == contentTypeHTML {
		header.Set(serverHeader, server)
		ctx.pushResources()
	}

	// Cache control header
	if isMedia {
		header.Set(cacheControlHeader, cacheControlMedia)
	} else {
		header.Set(cacheControlHeader, cacheControlAlwaysValidate)
	}

	// Small response
	if len(b) < gzipThreshold {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// ETag generation
	h := xxhash.NewS64(0)
	h.Write(b)
	etag := strconv.FormatUint(h.Sum64(), 16)

	// If client cache is up to date, send 304 with no response body.
	clientETag := ctx.request.Header.Get(ifNoneMatchHeader)

	if etag == clientETag {
		response.WriteHeader(304)
		return
	}

	// Set ETag
	header.Set(etagHeader, etag)

	// No GZip?
	if !ctx.App.Config.GZip || isMedia {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// GZip
	header.Set(contentEncodingHeader, contentEncodingGzip)

	if ctx.App.Config.GZipCache {
		cachedResponse, found := ctx.App.gzipCache.Get(etag)

		if found {
			cachedResponseBytes := cachedResponse.([]byte)
			header.Set(contentLengthHeader, strconv.Itoa(len(cachedResponseBytes)))
			response.WriteHeader(ctx.StatusCode)
			response.Write(cachedResponseBytes)
			return
		}
	}

	var buffer bytes.Buffer
	writer := bufio.NewWriter(&buffer)
	fasthttp.WriteGzipLevel(writer, b, 9)
	writer.Flush()
	gzippedBytes := buffer.Bytes()

	header.Set(contentLengthHeader, strconv.Itoa(len(gzippedBytes)))
	response.WriteHeader(ctx.StatusCode)
	response.Write(gzippedBytes)

	if ctx.App.Config.GZipCache {
		ctx.App.gzipCache.Set(etag, gzippedBytes, cache.DefaultExpiration)
	}
}
<commit_msg>Remove brackets from RealIP<commit_after>package aero

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/OneOfOne/xxhash"
	"github.com/aerogo/session"
	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
	"github.com/tomasen/realip"
	"github.com/valyala/fasthttp"
)

// This should be close to the MTU size of a TCP packet.
// Regarding performance it makes no sense to compress smaller files.
// Bandwidth can be saved however the savings are minimal for small files
// and the overhead of compressing can lead up to a 75% reduction
// in server speed under high load. Therefore in this case
// we're trying to optimize for performance, not bandwidth.
const gzipThreshold = 1450

const (
	serverHeader                  = "Server"
	server                        = "Aero"
	cacheControlHeader            = "Cache-Control"
	cacheControlAlwaysValidate    = "must-revalidate"
	cacheControlMedia             = "public, max-age=864000"
	contentTypeOptionsHeader      = "X-Content-Type-Options"
	contentTypeOptions            = "nosniff"
	xssProtectionHeader           = "X-XSS-Protection"
	xssProtection                 = "1; mode=block"
	etagHeader                    = "ETag"
	contentTypeHeader             = "Content-Type"
	contentTypeHTML               = "text/html; charset=utf-8"
	contentTypeJavaScript         = "application/javascript; charset=utf-8"
	contentTypeJSON               = "application/json; charset=utf-8"
	contentTypePlainText          = "text/plain; charset=utf-8"
	contentEncodingHeader         = "Content-Encoding"
	contentEncodingGzip           = "gzip"
	contentLengthHeader           = "Content-Length"
	responseTimeHeader            = "X-Response-Time"
	ifNoneMatchHeader             = "If-None-Match"
	xFrameOptionsHeader           = "X-Frame-Options"
	xFrameOptions                 = "SAMEORIGIN"
	referrerPolicyHeader          = "Referrer-Policy"
	referrerPolicySameOrigin      = "no-referrer"
	strictTransportSecurityHeader = "Strict-Transport-Security"
	strictTransportSecurity       = "max-age=31536000; includeSubDomains; preload"
	contentSecurityPolicyHeader   = "Content-Security-Policy"
)

// Context represents a single request & response.
type Context struct {
	// net/http
	request  *http.Request
	response http.ResponseWriter
	params   httprouter.Params

	// A pointer to the application this request occured on.
	App *Application

	// Status code
	StatusCode int

	// Custom data
	Data interface{}

	// User session
	session *session.Session
}

// Request returns the HTTP request.
func (ctx *Context) Request() Request {
	return Request{
		inner: ctx.request,
	}
}

// Response returns the HTTP response.
func (ctx *Context) Response() Response {
	return Response{
		inner: ctx.response,
	}
}

// Session returns the session of the context or creates and caches a new session.
func (ctx *Context) Session() *session.Session {
	// Return cached session if available.
	if ctx.session != nil {
		return ctx.session
	}

	// Check if the client has a session cookie already.
	cookie, err := ctx.request.Cookie("sid")

	if err == nil {
		sid := cookie.Value

		if session.IsValidID(sid) {
			ctx.session, err = ctx.App.Sessions.Store.Get(sid)

			if err != nil {
				color.Red(err.Error())
			}

			if ctx.session != nil {
				return ctx.session
			}
		}
	}

	// Create a new session
	ctx.session = ctx.App.Sessions.New()

	// Create a session cookie in the client
	ctx.createSessionCookie()

	return ctx.session
}

// createSessionCookie creates a session cookie in the client.
func (ctx *Context) createSessionCookie() {
	sessionCookie := http.Cookie{
		Name:     "sid",
		Value:    ctx.session.ID(),
		HttpOnly: true,
		Secure:   true,
		MaxAge:   ctx.App.Sessions.Duration,
		Path:     "/",
	}

	http.SetCookie(ctx.response, &sessionCookie)

	// HACK: Add SameSite attribute
	// Remove this once it's available inside http.Cookie
	// cookieData := ctx.response.Header().Get("Set-Cookie")
	// cookieData += "; SameSite=lax"
	// ctx.response.Header().Set("Set-Cookie", cookieData)
}

// HasSession indicates whether the client has a valid session or not.
func (ctx *Context) HasSession() bool {
	if ctx.session != nil {
		return true
	}

	cookie, err := ctx.request.Cookie("sid")

	if err != nil || !session.IsValidID(cookie.Value) {
		return false
	}

	ctx.session, err = ctx.App.Sessions.Store.Get(cookie.Value)

	if err != nil {
		return false
	}

	return ctx.session != nil
}

// JSON encodes the object to a JSON string and responds.
func (ctx *Context) JSON(value interface{}) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypeJSON)

	bytes, err := json.Marshal(value)

	if err != nil {
		ctx.StatusCode = http.StatusInternalServerError
		return `{"error": "Could not encode object to JSON"}`
	}

	return string(bytes)
}

// HTML sends a HTML string.
func (ctx *Context) HTML(html string) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypeHTML)
	ctx.response.Header().Set(contentTypeOptionsHeader, contentTypeOptions)
	ctx.response.Header().Set(xssProtectionHeader, xssProtection)
	// ctx.response.Header().Set(xFrameOptionsHeader, xFrameOptions)
	ctx.response.Header().Set(referrerPolicyHeader, referrerPolicySameOrigin)

	if ctx.App.Security.Certificate != "" {
		ctx.response.Header().Set(strictTransportSecurityHeader, strictTransportSecurity)
		ctx.response.Header().Set(contentSecurityPolicyHeader, ctx.App.ContentSecurityPolicy.String())
	}

	return html
}

// Text sends a plain text string.
func (ctx *Context) Text(text string) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypePlainText)
	return text
}

// JavaScript sends a script.
func (ctx *Context) JavaScript(code string) string {
	ctx.response.Header().Set(contentTypeHeader, contentTypeJavaScript)
	return code
}

// File sends the contents of a local file and determines its mime type by extension.
func (ctx *Context) File(file string) string {
	extension := filepath.Ext(file)
	mimeType := mime.TypeByExtension(extension)
	data, _ := ioutil.ReadFile(file)

	if mimeType == "" {
		mimeType = http.DetectContentType(data)
	}

	ctx.response.Header().Set(contentTypeHeader, mimeType)
	return string(data)
}

// Error should be used for sending error messages to the user.
func (ctx *Context) Error(statusCode int, explanation string, err error) string {
	ctx.StatusCode = statusCode
	ctx.response.Header().Set(contentTypeHeader, contentTypeHTML)

	if err != nil {
		detailed := err.Error()
		color.Red(detailed)
		return fmt.Sprintf("%s (%s)", explanation, detailed)
	}

	return explanation
}

// URI returns the relative path, e.g. /blog/post/123.
func (ctx *Context) URI() string {
	return ctx.request.URL.Path
}

// SetURI sets the relative path, e.g. /blog/post/123.
func (ctx *Context) SetURI(b string) {
	ctx.request.URL.Path = b
}

// Get retrieves an URL parameter.
func (ctx *Context) Get(param string) string {
	return ctx.params.ByName(param)
}

// GetInt retrieves an URL parameter as an integer.
func (ctx *Context) GetInt(param string) (int, error) {
	return strconv.Atoi(ctx.Get(param))
}

// RealIP tries to determine the real IP address of the request.
func (ctx *Context) RealIP() string {
	return strings.Trim(realip.RealIP(ctx.request), "[]")
}

// UserAgent retrieves the user agent for the given request.
func (ctx *Context) UserAgent() string {
	ctx.request.URL.Query()
	return ctx.request.UserAgent()
}

// Query retrieves the value for the given URL query parameter.
func (ctx *Context) Query(param string) string {
	return ctx.request.URL.Query().Get(param)
}

// Redirect redirects to the given URL using status code 302.
func (ctx *Context) Redirect(url string) string {
	ctx.StatusCode = http.StatusFound
	ctx.response.Header().Set("Location", url)
	return ""
}

// RedirectPermanently redirects to the given URL and indicates that this is a permanent change using status code 301.
func (ctx *Context) RedirectPermanently(url string) string {
	ctx.StatusCode = http.StatusPermanentRedirect
	ctx.response.Header().Set("Location", url)
	return ""
}

// IsMediaType returns whether the given content type is a media type.
func IsMediaType(contentType string) bool {
	return strings.HasPrefix(contentType, "image/") || strings.HasPrefix(contentType, "video/") || strings.HasPrefix(contentType, "audio/")
}

// pushResources will push the given resources to the HTTP response.
func (ctx *Context) pushResources() {
	// Check if all the conditions for a push are met
	for _, pushCondition := range ctx.App.pushConditions {
		if !pushCondition(ctx) {
			return
		}
	}

	// OnPush callbacks
	for _, callback := range ctx.App.onPush {
		callback(ctx)
	}

	// Check if we can push
	pusher, ok := ctx.response.(http.Pusher)

	if !ok {
		return
	}

	// Push every resource defined in config.json
	for _, resource := range ctx.App.Config.Push {
		if err := pusher.Push(resource, nil); err != nil {
			log.Printf("Failed to push %s: %v", resource, err)
		}
	}
}

// respond responds either with raw code or gzipped if the
// code length is greater than the gzip threshold.
func (ctx *Context) respond(code string) {
	ctx.respondBytes(StringToBytesUnsafe(code))
}

// respondBytes responds either with raw code or gzipped if the
// code length is greater than the gzip threshold. Requires a byte slice.
func (ctx *Context) respondBytes(b []byte) {
	response := ctx.response
	header := response.Header()
	contentType := header.Get(contentTypeHeader)
	isMedia := IsMediaType(contentType)

	// Push
	if contentType == contentTypeHTML {
		header.Set(serverHeader, server)
		ctx.pushResources()
	}

	// Cache control header
	if isMedia {
		header.Set(cacheControlHeader, cacheControlMedia)
	} else {
		header.Set(cacheControlHeader, cacheControlAlwaysValidate)
	}

	// Small response
	if len(b) < gzipThreshold {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// ETag generation
	h := xxhash.NewS64(0)
	h.Write(b)
	etag := strconv.FormatUint(h.Sum64(), 16)

	// If client cache is up to date, send 304 with no response body.
	clientETag := ctx.request.Header.Get(ifNoneMatchHeader)

	if etag == clientETag {
		response.WriteHeader(304)
		return
	}

	// Set ETag
	header.Set(etagHeader, etag)

	// No GZip?
	if !ctx.App.Config.GZip || isMedia {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// GZip
	header.Set(contentEncodingHeader, contentEncodingGzip)

	if ctx.App.Config.GZipCache {
		cachedResponse, found := ctx.App.gzipCache.Get(etag)

		if found {
			cachedResponseBytes := cachedResponse.([]byte)
			header.Set(contentLengthHeader, strconv.Itoa(len(cachedResponseBytes)))
			response.WriteHeader(ctx.StatusCode)
			response.Write(cachedResponseBytes)
			return
		}
	}

	var buffer bytes.Buffer
	writer := bufio.NewWriter(&buffer)
	fasthttp.WriteGzipLevel(writer, b, 9)
	writer.Flush()
	gzippedBytes := buffer.Bytes()

	header.Set(contentLengthHeader, strconv.Itoa(len(gzippedBytes)))
	response.WriteHeader(ctx.StatusCode)
	response.Write(gzippedBytes)

	if ctx.App.Config.GZipCache {
		ctx.App.gzipCache.Set(etag, gzippedBytes, cache.DefaultExpiration)
	}
}
<|endoftext|>


<commit_before>package aero

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io/ioutil"
	"mime"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/OneOfOne/xxhash"
	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
	"github.com/tomasen/realip"
	"github.com/valyala/fasthttp"
)

// This should be close to the MTU size of a TCP packet.
// Regarding performance it makes no sense to compress smaller files.
// Bandwidth can be saved however the savings are minimal for small files
// and the overhead of compressing can lead up to a 75% reduction
// in server speed under high load. Therefore in this case
// we're trying to optimize for performance, not bandwidth.
const gzipThreshold = 1450

const (
	serverHeader                  = "Server"
	server                        = "Aero"
	cacheControlHeader            = "Cache-Control"
	cacheControlAlwaysValidate    = "must-revalidate"
	cacheControlMedia             = "max-age=864000"
	contentTypeOptionsHeader      = "X-Content-Type-Options"
	contentTypeOptions            = "nosniff"
	xssProtectionHeader           = "X-XSS-Protection"
	xssProtection                 = "1; mode=block"
	etagHeader                    = "ETag"
	contentTypeHeader             = "Content-Type"
	contentTypeHTML               = "text/html; charset=utf-8"
	contentTypeJSON               = "application/json; charset=utf-8"
	contentTypePlainText          = "text/plain; charset=utf-8"
	contentEncodingHeader         = "Content-Encoding"
	contentEncodingGzip           = "gzip"
	contentLengthHeader           = "Content-Length"
	responseTimeHeader            = "X-Response-Time"
	ifNoneMatchHeader             = "If-None-Match"
	xFrameOptionsHeader           = "X-Frame-Options"
	xFrameOptions                 = "SAMEORIGIN"
	referrerPolicyHeader          = "Referrer-Policy"
	referrerPolicySameOrigin      = "no-referrer"
	strictTransportSecurityHeader = "Strict-Transport-Security"
	strictTransportSecurity       = "max-age=31536000; includeSubDomains; preload"
	contentSecurityPolicyHeader   = "Content-Security-Policy"
)

// Context ...
type Context struct {
	// net/http
	request  *http.Request
	response http.ResponseWriter
	params   httprouter.Params

	// A pointer to the application this request occured on.
	App *Application

	// Status code
	StatusCode int

	// Start time
	start time.Time

	// User session
	session *Session
}

// Handle ...
type Handle func(*Context) string

// Session returns the session of the context or creates and caches a new session.
func (ctx *Context) Session() *Session {
	// Return cached session if available.
	if ctx.session != nil {
		return ctx.session
	}

	// Check if the client has a session cookie already.
	cookie, err := ctx.request.Cookie("sid")

	if err == nil {
		sid := cookie.Value

		if sid != "" {
			ctx.session = ctx.App.Sessions.Store.Get(sid)

			if ctx.session != nil {
				return ctx.session
			}
		}
	}

	// Create a new session
	ctx.session = ctx.App.Sessions.New()

	sessionCookie := http.Cookie{
		Name:     "sid",
		Value:    ctx.session.id,
		HttpOnly: true,
		Secure:   true,
	}

	http.SetCookie(ctx.response, &sessionCookie)

	return ctx.session
}

// HasSession indicates whether a session has been constructed for this context or not.
func (ctx *Context) HasSession() bool {
	return ctx.session != nil
}

// JSON encodes the object to a JSON string and responds.
func (ctx *Context) JSON(value interface{}) string {
	bytes, _ := json.Marshal(value)

	ctx.SetResponseHeader(contentTypeHeader, contentTypeJSON)
	return string(bytes)
}

// HTML sends a HTML string.
func (ctx *Context) HTML(html string) string {
	ctx.SetResponseHeader(contentTypeHeader, contentTypeHTML)
	ctx.SetResponseHeader(contentTypeOptionsHeader, contentTypeOptions)
	ctx.SetResponseHeader(xssProtectionHeader, xssProtection)
	ctx.SetResponseHeader(xFrameOptionsHeader, xFrameOptions)
	ctx.SetResponseHeader(referrerPolicyHeader, referrerPolicySameOrigin)

	if ctx.App.Security.Certificate != "" {
		ctx.SetResponseHeader(strictTransportSecurityHeader, strictTransportSecurity)
		ctx.SetResponseHeader(contentSecurityPolicyHeader, ctx.App.contentSecurityPolicy)
	}

	return html
}

// Text sends a plain text string.
func (ctx *Context) Text(text string) string {
	ctx.SetResponseHeader(contentTypeHeader, contentTypePlainText)
	return text
}

// File sends the contents of a local file and determines its mime type by extension.
func (ctx *Context) File(file string) string {
	extension := filepath.Ext(file)
	mimeType := mime.TypeByExtension(extension)
	data, _ := ioutil.ReadFile(file)

	if mimeType == "" {
		mimeType = http.DetectContentType(data)
	}

	ctx.SetResponseHeader(contentTypeHeader, mimeType)
	return string(data)
}

// Error should be used for sending error messages to the user.
func (ctx *Context) Error(statusCode int, explanation string, err error) string {
	ctx.StatusCode = statusCode
	ctx.SetResponseHeader(contentTypeHeader, contentTypeHTML)
	// ctx.App.Logger.Error(
	// 	color.RedString(explanation),
	// 	zap.String("error", err.Error()),
	// 	zap.String("url", ctx.request.RequestURI),
	// )
	color.Red(err.Error())
	return explanation
}

// GetRequestHeader retrieves the value for the request header.
func (ctx *Context) GetRequestHeader(header string) string {
	return ctx.request.Header.Get(header)
}

// SetResponseHeader sets response header to value.
func (ctx *Context) SetResponseHeader(header string, value string) {
	ctx.response.Header().Set(header, value)
}

// URI returns the relative path, e.g. /blog/post/123.
func (ctx *Context) URI() string {
	return ctx.request.URL.Path
}

// SetURI sets the relative path, e.g. /blog/post/123.
func (ctx *Context) SetURI(b string) {
	ctx.request.URL.Path = b
}

// Get retrieves an URL parameter.
func (ctx *Context) Get(param string) string {
	return ctx.params.ByName(param)
}

// GetInt retrieves an URL parameter as an integer.
func (ctx *Context) GetInt(param string) (int, error) {
	return strconv.Atoi(ctx.Get(param))
}

// RealIP tries to determine the real IP address of the request.
func (ctx *Context) RealIP() string {
	return realip.RealIP(ctx.request)
}

// UserAgent retrieves the user agent for the given request.
func (ctx *Context) UserAgent() string {
	ctx.request.URL.Query()
	return ctx.request.UserAgent()
}

// Query retrieves the value for the given URL query parameter.
func (ctx *Context) Query(param string) string {
	return ctx.request.URL.Query().Get(param)
}

// Redirect redirects to the given URL using status code 302.
func (ctx *Context) Redirect(url string) string {
	ctx.StatusCode = http.StatusFound
	ctx.SetResponseHeader("Location", url)
	return ""
}

// RedirectPermanently redirects to the given URL and indicates that this is a permanent change using status code 301.
func (ctx *Context) RedirectPermanently(url string) string {
	ctx.StatusCode = http.StatusPermanentRedirect
	ctx.SetResponseHeader("Location", url)
	return ""
}

// CanUseWebP checks the Accept header to find out if WebP is supported by the client's browser.
func (ctx *Context) CanUseWebP() bool {
	accept := ctx.GetRequestHeader("Accept")

	if strings.Index(accept, "image/webp") != -1 {
		return true
	}

	return false
}

// Respond responds either with raw code or gzipped if the
// code length is greater than the gzip threshold.
func (ctx *Context) Respond(code string) {
	ctx.RespondBytes(StringToBytesUnsafe(code))
}

// RespondBytes responds either with raw code or gzipped if the
// code length is greater than the gzip threshold. Requires a byte slice.
func (ctx *Context) RespondBytes(b []byte) {
	response := ctx.response
	header := response.Header()
	contentType := ctx.response.Header().Get(contentTypeHeader)
	isMedia := strings.HasPrefix(contentType, "image/") || strings.HasPrefix(contentType, "video/")

	// Headers
	if isMedia {
		header.Set(cacheControlHeader, cacheControlMedia)
	} else {
		header.Set(cacheControlHeader, cacheControlAlwaysValidate)
		header.Set(serverHeader, server)
		header.Set(responseTimeHeader, strconv.FormatInt(time.Since(ctx.start).Nanoseconds()/1000, 10)+" us")
	}

	// Small response
	if len(b) < gzipThreshold {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// ETag generation
	h := xxhash.NewS64(0)
	h.Write(b)
	etag := strconv.FormatUint(h.Sum64(), 16)

	// If client cache is up to date, send 304 with no response body.
	clientETag := ctx.request.Header.Get(ifNoneMatchHeader)

	if etag == clientETag {
		response.WriteHeader(304)
		return
	}

	// Set ETag
	header.Set(etagHeader, etag)

	// No GZip?
	if !ctx.App.Config.GZip || isMedia {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// GZip
	header.Set(contentEncodingHeader, contentEncodingGzip)

	if ctx.App.Config.GZipCache {
		cachedResponse, found := ctx.App.gzipCache.Get(etag)

		if found {
			cachedResponseBytes := cachedResponse.([]byte)
			header.Set(contentLengthHeader, strconv.Itoa(len(cachedResponseBytes)))
			response.WriteHeader(ctx.StatusCode)
			response.Write(cachedResponseBytes)
			return
		}
	}

	var buffer bytes.Buffer
	writer := bufio.NewWriter(&buffer)
	fasthttp.WriteGzipLevel(writer, b, 9)
	writer.Flush()
	gzippedBytes := buffer.Bytes()

	header.Set(contentLengthHeader, strconv.Itoa(len(gzippedBytes)))
	response.WriteHeader(ctx.StatusCode)
	response.Write(gzippedBytes)

	if ctx.App.Config.GZipCache {
		ctx.App.gzipCache.Set(etag, gzippedBytes, cache.DefaultExpiration)
	}
}
<commit_msg>Added RequestBody()<commit_after>package aero

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io/ioutil"
	"mime"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/OneOfOne/xxhash"
	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
	"github.com/tomasen/realip"
	"github.com/valyala/fasthttp"
)

// This should be close to the MTU size of a TCP packet.
// Regarding performance it makes no sense to compress smaller files.
// Bandwidth can be saved however the savings are minimal for small files
// and the overhead of compressing can lead up to a 75% reduction
// in server speed under high load. Therefore in this case
// we're trying to optimize for performance, not bandwidth.
const gzipThreshold = 1450

const (
	serverHeader                  = "Server"
	server                        = "Aero"
	cacheControlHeader            = "Cache-Control"
	cacheControlAlwaysValidate    = "must-revalidate"
	cacheControlMedia             = "max-age=864000"
	contentTypeOptionsHeader      = "X-Content-Type-Options"
	contentTypeOptions            = "nosniff"
	xssProtectionHeader           = "X-XSS-Protection"
	xssProtection                 = "1; mode=block"
	etagHeader                    = "ETag"
	contentTypeHeader             = "Content-Type"
	contentTypeHTML               = "text/html; charset=utf-8"
	contentTypeJSON               = "application/json; charset=utf-8"
	contentTypePlainText          = "text/plain; charset=utf-8"
	contentEncodingHeader         = "Content-Encoding"
	contentEncodingGzip           = "gzip"
	contentLengthHeader           = "Content-Length"
	responseTimeHeader            = "X-Response-Time"
	ifNoneMatchHeader             = "If-None-Match"
	xFrameOptionsHeader           = "X-Frame-Options"
	xFrameOptions                 = "SAMEORIGIN"
	referrerPolicyHeader          = "Referrer-Policy"
	referrerPolicySameOrigin      = "no-referrer"
	strictTransportSecurityHeader = "Strict-Transport-Security"
	strictTransportSecurity       = "max-age=31536000; includeSubDomains; preload"
	contentSecurityPolicyHeader   = "Content-Security-Policy"
)

// Context ...
type Context struct {
	// net/http
	request  *http.Request
	response http.ResponseWriter
	params   httprouter.Params

	// A pointer to the application this request occured on.
	App *Application

	// Status code
	StatusCode int

	// Start time
	start time.Time

	// User session
	session *Session
}

// Handle ...
type Handle func(*Context) string

// Session returns the session of the context or creates and caches a new session.
func (ctx *Context) Session() *Session {
	// Return cached session if available.
	if ctx.session != nil {
		return ctx.session
	}

	// Check if the client has a session cookie already.
	cookie, err := ctx.request.Cookie("sid")

	if err == nil {
		sid := cookie.Value

		if sid != "" {
			ctx.session = ctx.App.Sessions.Store.Get(sid)

			if ctx.session != nil {
				return ctx.session
			}
		}
	}

	// Create a new session
	ctx.session = ctx.App.Sessions.New()

	sessionCookie := http.Cookie{
		Name:     "sid",
		Value:    ctx.session.id,
		HttpOnly: true,
		Secure:   true,
	}

	http.SetCookie(ctx.response, &sessionCookie)

	return ctx.session
}

// HasSession indicates whether a session has been constructed for this context or not.
func (ctx *Context) HasSession() bool {
	return ctx.session != nil
}

// JSON encodes the object to a JSON string and responds.
func (ctx *Context) JSON(value interface{}) string {
	bytes, _ := json.Marshal(value)

	ctx.SetResponseHeader(contentTypeHeader, contentTypeJSON)
	return string(bytes)
}

// HTML sends a HTML string.
func (ctx *Context) HTML(html string) string {
	ctx.SetResponseHeader(contentTypeHeader, contentTypeHTML)
	ctx.SetResponseHeader(contentTypeOptionsHeader, contentTypeOptions)
	ctx.SetResponseHeader(xssProtectionHeader, xssProtection)
	ctx.SetResponseHeader(xFrameOptionsHeader, xFrameOptions)
	ctx.SetResponseHeader(referrerPolicyHeader, referrerPolicySameOrigin)

	if ctx.App.Security.Certificate != "" {
		ctx.SetResponseHeader(strictTransportSecurityHeader, strictTransportSecurity)
		ctx.SetResponseHeader(contentSecurityPolicyHeader, ctx.App.contentSecurityPolicy)
	}

	return html
}

// Text sends a plain text string.
func (ctx *Context) Text(text string) string {
	ctx.SetResponseHeader(contentTypeHeader, contentTypePlainText)
	return text
}

// File sends the contents of a local file and determines its mime type by extension.
func (ctx *Context) File(file string) string {
	extension := filepath.Ext(file)
	mimeType := mime.TypeByExtension(extension)
	data, _ := ioutil.ReadFile(file)

	if mimeType == "" {
		mimeType = http.DetectContentType(data)
	}

	ctx.SetResponseHeader(contentTypeHeader, mimeType)
	return string(data)
}

// Error should be used for sending error messages to the user.
func (ctx *Context) Error(statusCode int, explanation string, err error) string {
	ctx.StatusCode = statusCode
	ctx.SetResponseHeader(contentTypeHeader, contentTypeHTML)
	// ctx.App.Logger.Error(
	// 	color.RedString(explanation),
	// 	zap.String("error", err.Error()),
	// 	zap.String("url", ctx.request.RequestURI),
	// )
	color.Red(err.Error())
	return explanation
}

// GetRequestHeader retrieves the value for the request header.
func (ctx *Context) GetRequestHeader(header string) string {
	return ctx.request.Header.Get(header)
}

// SetResponseHeader sets response header to value.
func (ctx *Context) SetResponseHeader(header string, value string) {
	ctx.response.Header().Set(header, value)
}

// URI returns the relative path, e.g. /blog/post/123.
func (ctx *Context) URI() string {
	return ctx.request.URL.Path
}

// SetURI sets the relative path, e.g. /blog/post/123.
func (ctx *Context) SetURI(b string) {
	ctx.request.URL.Path = b
}

// Get retrieves an URL parameter.
func (ctx *Context) Get(param string) string {
	return ctx.params.ByName(param)
}

// GetInt retrieves an URL parameter as an integer.
func (ctx *Context) GetInt(param string) (int, error) {
	return strconv.Atoi(ctx.Get(param))
}

// RequestBody returns the request body as a string.
func (ctx *Context) RequestBody() []byte {
	body, err := ioutil.ReadAll(ctx.request.Body)

	if err != nil {
		panic(err)
	}

	return body
}

// RealIP tries to determine the real IP address of the request.
func (ctx *Context) RealIP() string {
	return realip.RealIP(ctx.request)
}

// UserAgent retrieves the user agent for the given request.
func (ctx *Context) UserAgent() string {
	ctx.request.URL.Query()
	return ctx.request.UserAgent()
}

// Query retrieves the value for the given URL query parameter.
func (ctx *Context) Query(param string) string {
	return ctx.request.URL.Query().Get(param)
}

// Redirect redirects to the given URL using status code 302.
func (ctx *Context) Redirect(url string) string {
	ctx.StatusCode = http.StatusFound
	ctx.SetResponseHeader("Location", url)
	return ""
}

// RedirectPermanently redirects to the given URL and indicates that this is a permanent change using status code 301.
func (ctx *Context) RedirectPermanently(url string) string {
	ctx.StatusCode = http.StatusPermanentRedirect
	ctx.SetResponseHeader("Location", url)
	return ""
}

// CanUseWebP checks the Accept header to find out if WebP is supported by the client's browser.
func (ctx *Context) CanUseWebP() bool {
	accept := ctx.GetRequestHeader("Accept")

	if strings.Index(accept, "image/webp") != -1 {
		return true
	}

	return false
}

// Respond responds either with raw code or gzipped if the
// code length is greater than the gzip threshold.
func (ctx *Context) Respond(code string) {
	ctx.RespondBytes(StringToBytesUnsafe(code))
}

// RespondBytes responds either with raw code or gzipped if the
// code length is greater than the gzip threshold. Requires a byte slice.
func (ctx *Context) RespondBytes(b []byte) {
	response := ctx.response
	header := response.Header()
	contentType := ctx.response.Header().Get(contentTypeHeader)
	isMedia := strings.HasPrefix(contentType, "image/") || strings.HasPrefix(contentType, "video/")

	// Headers
	if isMedia {
		header.Set(cacheControlHeader, cacheControlMedia)
	} else {
		header.Set(cacheControlHeader, cacheControlAlwaysValidate)
		header.Set(serverHeader, server)
		header.Set(responseTimeHeader, strconv.FormatInt(time.Since(ctx.start).Nanoseconds()/1000, 10)+" us")
	}

	// Small response
	if len(b) < gzipThreshold {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// ETag generation
	h := xxhash.NewS64(0)
	h.Write(b)
	etag := strconv.FormatUint(h.Sum64(), 16)

	// If client cache is up to date, send 304 with no response body.
	clientETag := ctx.request.Header.Get(ifNoneMatchHeader)

	if etag == clientETag {
		response.WriteHeader(304)
		return
	}

	// Set ETag
	header.Set(etagHeader, etag)

	// No GZip?
	if !ctx.App.Config.GZip || isMedia {
		header.Set(contentLengthHeader, strconv.Itoa(len(b)))
		response.WriteHeader(ctx.StatusCode)
		response.Write(b)
		return
	}

	// GZip
	header.Set(contentEncodingHeader, contentEncodingGzip)

	if ctx.App.Config.GZipCache {
		cachedResponse, found := ctx.App.gzipCache.Get(etag)

		if found {
			cachedResponseBytes := cachedResponse.([]byte)
			header.Set(contentLengthHeader, strconv.Itoa(len(cachedResponseBytes)))
			response.WriteHeader(ctx.StatusCode)
			response.Write(cachedResponseBytes)
			return
		}
	}

	var buffer bytes.Buffer
	writer := bufio.NewWriter(&buffer)
	fasthttp.WriteGzipLevel(writer, b, 9)
	writer.Flush()
	gzippedBytes := buffer.Bytes()

	header.Set(contentLengthHeader, strconv.Itoa(len(gzippedBytes)))
	response.WriteHeader(ctx.StatusCode)
	response.Write(gzippedBytes)

	if ctx.App.Config.GZipCache {
		ctx.App.gzipCache.Set(etag, gzippedBytes, cache.DefaultExpiration)
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/google/go-github/github"
	"github.com/k8s-community/cicd"
	userManClient "github.com/k8s-community/user-manager/client"
	"github.com/takama/router"
	githubhook "gopkg.in/rjz/githubhook.v0"
	"github.com/AlekSi/pointer"
	"github.com/k8s-community/github-integration/models"
	"gopkg.in/reform.v1"
)

// WebHookHandler is common handler for web hooks (installation, repositories installation, push)
func (h *Handler) WebHookHandler(c *router.Control) {
	secret := []byte(h.Env["GITHUBINT_TOKEN"])

	hook, err := githubhook.Parse(secret, c.Request)
	if err != nil {
		h.Errlog.Printf("cannot parse hook (ID %s): %s", hook.Id, err)
		return
	}

	switch hook.Event {
	case "integration_installation":
		// Triggered when an integration has been installed or uninstalled by user.
		h.Infolog.Printf("initialization web hook (ID %s)", hook.Id)
		err = h.saveInstallation(hook)

	case "integration_installation_repositories":
		// Triggered when a repository is added or removed from an installation.
		h.Infolog.Printf("initialization web hook for user repositories (ID %s)", hook.Id)
		err = h.initialUserManagement(hook)

	case "push":
		// Any Git push to a Repository, including editing tags or branches.
		// Commits via API actions that update references are also counted. This is the default event.
		h.Infolog.Printf("push hook (ID %s)", hook.Id)
		err = h.processPush(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}

	case "create":
		h.Infolog.Printf("create hook (ID %s)", hook.Id)
		err = h.processPush(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}

	default:
		h.Infolog.Printf("Warning! Don't know how to process hook (ID %s), event = %s", hook.Id, hook.Event)
		c.Code(http.StatusOK).Body(nil)
		return
	}

	if err != nil {
		h.Errlog.Printf("cannot process hook (ID %s): %s", hook.Id, err)
		c.Code(http.StatusInternalServerError).Body(nil)
		return
	}

	h.Infolog.Printf("finished to process hook (ID %s)", hook.Id)
	c.Code(http.StatusOK).Body(nil)
}

// initialUserManagement is used for user activation in k8s system
func (h *Handler) initialUserManagement(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationRepositoriesEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	userManagerURL := h.Env["USERMAN_BASE_URL"]

	client, err := userManClient.NewClient(nil, userManagerURL)
	if err != nil {
		return err
	}

	h.Infolog.Print("Try to activate (sync) user in k8s system: ", *evt.Sender.Login)

	user := userManClient.NewUser(*evt.Installation.Account.Login)

	code, err := client.User.Sync(user)
	if err != nil {
		return err
	}

	h.Infolog.Printf("Service user-man, method sync, returned code: %d", code)

	return nil
}

// processPush is used for start CI/CD process for some repository from push hook
func (h *Handler) processPush(c *router.Control, hook *githubhook.Hook) error {
	evt := github.PushEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// ToDO: process somehow kind of hooks without HeadCommit
	if evt.HeadCommit == nil {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - no HeadCommit inside", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	prefix := "refs/heads/" + h.Env["GITHUBINT_BRANCH"]
	if !strings.HasPrefix(*evt.Ref, prefix) {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - branch %s", hook.Id, *evt.Ref)
		return nil
	}

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	version := strings.Trim(*evt.Ref, prefix)
	commitID := *evt.HeadCommit.ID
	version += "_" + commitID[0:5]

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.HeadCommit.ID,
		Task: cicd.TaskDeploy,
		Version: &version,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

func (h *Handler) processCreate(c *router.Control, hook *githubhook.Hook) error {
	evt := github.CreateEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// Process only tags
	if evt.RefType == nil || *evt.RefType != "tag" {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - not a tag", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.Ref,
		Task: cicd.TaskDeploy,
		Version: evt.Ref,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

// saveInstallation saves installation in memory
func (h *Handler) saveInstallation(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	h.Infolog.Printf("save installation for user %s (installation ID = %d)", *evt.Sender.Login, *evt.Installation.ID)

	// save installation for commit status update
	err = h.setInstallationID(*evt.Installation.Account.Login, *evt.Installation.ID)
	if err != nil {
		h.Errlog.Printf("Couldn't save installation: %+v", err)
	}

	return nil
}

// installationID gets installation from DB
func (h *Handler) installationID(username string) (*int, error) {
	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil {
		return nil, err
	}
	inst := st.(*models.Installation)

	return pointer.ToInt(inst.InstallationID), nil
}

func (h *Handler) setInstallationID(username string, instID int) error {
	var inst = &models.Installation{}

	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil && err != reform.ErrNoRows {
		return err
	}

	if err == nil {
		inst = st.(*models.Installation)
	}

	inst.InstallationID = instID
	inst.Username = username

	err = h.DB.Save(inst)

	return err
}
<commit_msg>Typo fix<commit_after>package handlers

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/google/go-github/github"
	"github.com/k8s-community/cicd"
	userManClient "github.com/k8s-community/user-manager/client"
	"github.com/takama/router"
	githubhook "gopkg.in/rjz/githubhook.v0"
	"github.com/AlekSi/pointer"
	"github.com/k8s-community/github-integration/models"
	"gopkg.in/reform.v1"
)

// WebHookHandler is common handler for web hooks (installation, repositories installation, push)
func (h *Handler) WebHookHandler(c *router.Control) {
	secret := []byte(h.Env["GITHUBINT_TOKEN"])

	hook, err := githubhook.Parse(secret, c.Request)
	if err != nil {
		h.Errlog.Printf("cannot parse hook (ID %s): %s", hook.Id, err)
		return
	}

	switch hook.Event {
	case "integration_installation":
		// Triggered when an integration has been installed or uninstalled by user.
		h.Infolog.Printf("initialization web hook (ID %s)", hook.Id)
		err = h.saveInstallation(hook)

	case "integration_installation_repositories":
		// Triggered when a repository is added or removed from an installation.
		h.Infolog.Printf("initialization web hook for user repositories (ID %s)", hook.Id)
		err = h.initialUserManagement(hook)

	case "push":
		// Any Git push to a Repository, including editing tags or branches.
		// Commits via API actions that update references are also counted. This is the default event.
		h.Infolog.Printf("push hook (ID %s)", hook.Id)
		err = h.processPush(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}

	case "create":
		h.Infolog.Printf("create hook (ID %s)", hook.Id)
		err = h.processCreate(c, hook)
		if err != nil {
			h.Infolog.Printf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
			c.Code(http.StatusBadRequest).Body(nil)
			return
		}

	default:
		h.Infolog.Printf("Warning! Don't know how to process hook (ID %s), event = %s", hook.Id, hook.Event)
		c.Code(http.StatusOK).Body(nil)
		return
	}

	if err != nil {
		h.Errlog.Printf("cannot process hook (ID %s): %s", hook.Id, err)
		c.Code(http.StatusInternalServerError).Body(nil)
		return
	}

	h.Infolog.Printf("finished to process hook (ID %s)", hook.Id)
	c.Code(http.StatusOK).Body(nil)
}

// initialUserManagement is used for user activation in k8s system
func (h *Handler) initialUserManagement(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationRepositoriesEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	userManagerURL := h.Env["USERMAN_BASE_URL"]

	client, err := userManClient.NewClient(nil, userManagerURL)
	if err != nil {
		return err
	}

	h.Infolog.Print("Try to activate (sync) user in k8s system: ", *evt.Sender.Login)

	user := userManClient.NewUser(*evt.Installation.Account.Login)

	code, err := client.User.Sync(user)
	if err != nil {
		return err
	}

	h.Infolog.Printf("Service user-man, method sync, returned code: %d", code)

	return nil
}

// processPush is used for start CI/CD process for some repository from push hook
func (h *Handler) processPush(c *router.Control, hook *githubhook.Hook) error {
	evt := github.PushEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// ToDO: process somehow kind of hooks without HeadCommit
	if evt.HeadCommit == nil {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - no HeadCommit inside", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	prefix := "refs/heads/" + h.Env["GITHUBINT_BRANCH"]
	if !strings.HasPrefix(*evt.Ref, prefix) {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - branch %s", hook.Id, *evt.Ref)
		return nil
	}

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	version := strings.Trim(*evt.Ref, prefix)
	commitID := *evt.HeadCommit.ID
	version += "_" + commitID[0:5]

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.HeadCommit.ID,
		Task: cicd.TaskDeploy,
		Version: &version,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

func (h *Handler) processCreate(c *router.Control, hook *githubhook.Hook) error {
	evt := github.CreateEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	// Process only tags
	if evt.RefType == nil || *evt.RefType != "tag" {
		h.Infolog.Printf("Warning! Don't know how to process hook %s - not a tag", hook.Id)
		return nil
	}

	h.setInstallationID(*evt.Repo.Owner.Name, *evt.Installation.ID)

	ciCdURL := h.Env["CICD_BASE_URL"]

	client := cicd.NewClient(ciCdURL)

	// run CICD process
	req := &cicd.BuildRequest{
		Username:   *evt.Repo.Owner.Name,
		Repository: *evt.Repo.Name,
		CommitHash: *evt.Ref,
		Task: cicd.TaskDeploy,
		Version: evt.Ref,
	}

	_, err = client.Build(req)
	if err != nil {
		return fmt.Errorf("cannot run ci/cd process for hook (ID %s): %s", hook.Id, err)
	}

	return nil
}

// saveInstallation saves installation in memory
func (h *Handler) saveInstallation(hook *githubhook.Hook) error {
	evt := github.IntegrationInstallationEvent{}

	err := hook.Extract(&evt)
	if err != nil {
		return err
	}

	h.Infolog.Printf("save installation for user %s (installation ID = %d)", *evt.Sender.Login, *evt.Installation.ID)

	// save installation for commit status update
	err = h.setInstallationID(*evt.Installation.Account.Login, *evt.Installation.ID)
	if err != nil {
		h.Errlog.Printf("Couldn't save installation: %+v", err)
	}

	return nil
}

// installationID gets installation from DB
func (h *Handler) installationID(username string) (*int, error) {
	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil {
		return nil, err
	}
	inst := st.(*models.Installation)

	return pointer.ToInt(inst.InstallationID), nil
}

func (h *Handler) setInstallationID(username string, instID int) error {
	var inst = &models.Installation{}

	st, err := h.DB.FindOneFrom(models.InstallationTable, "username", username)
	if err != nil && err != reform.ErrNoRows {
		return err
	}

	if err == nil {
		inst = st.(*models.Installation)
	}

	inst.InstallationID = instID
	inst.Username = username

	err = h.DB.Save(inst)

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"text/template"
	"time"
)

const downloadDir1 = "//home//ubuntu//upload//"

const maxMemory = 1 * 1024 * 1024

// upload logic
func multiUploadHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("upload 1 method:", r.Method)
	if r.Method == "GET" {
		fmt.Println("upload 1 GET:")
		crutime := time.Now().Unix()
		h := md5.New()
		io.WriteString(h, strconv.FormatInt(crutime, 10))
		token := fmt.Sprintf("%x", h.Sum(nil))

		t, _ := template.ParseFiles("upload1.html")
		t.Execute(w, token)
	} else {
		if err := r.ParseMultipartForm(maxMemory); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusForbidden)
		}

		for key, value := range r.MultipartForm.Value {
			fmt.Fprintf(w, "%s:%s ", key, value)
			log.Printf("%s:%s", key, value)
		}

		for _, fileHeaders := range r.MultipartForm.File {
			for _, fileHeader := range fileHeaders {
				file, _ := fileHeader.Open()
				path := fmt.Sprintf("%s/%s", downloadDir1, fileHeader.Filename)
				buf, _ := ioutil.ReadAll(file)
				ioutil.WriteFile(path, buf, os.ModePerm)
			}
		}
	}

}
<commit_msg>fix bug<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"text/template"
	"time"
)

const downloadDir1 = "//home//ubuntu//upload//"

const maxMemory = 1 * 1024 * 1024

// upload logic
func multiUploadHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("upload 1 method:", r.Method)
	if r.Method == "GET" {
		fmt.Println("upload 1 GET:")
		crutime := time.Now().Unix()
		h := md5.New()
		io.WriteString(h, strconv.FormatInt(crutime, 10))
		token := fmt.Sprintf("%x", h.Sum(nil))

		t, _ := template.ParseFiles("multiupload.html")
		t.Execute(w, token)
	} else {
		if err := r.ParseMultipartForm(maxMemory); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusForbidden)
		}

		for key, value := range r.MultipartForm.Value {
			fmt.Fprintf(w, "%s:%s ", key, value)
			log.Printf("%s:%s", key, value)
		}

		for _, fileHeaders := range r.MultipartForm.File {
			for _, fileHeader := range fileHeaders {
				file, _ := fileHeader.Open()
				path := fmt.Sprintf("%s/%s", downloadDir1, fileHeader.Filename)
				buf, _ := ioutil.ReadAll(file)
				ioutil.WriteFile(path, buf, os.ModePerm)
			}
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/gob"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
	"time"

	linkpkg "github.com/cloudfoundry-incubator/warden-linux/iodaemon/link"
	"github.com/cloudfoundry-incubator/warden-linux/ptyutil"
	"github.com/kr/pty"
)

func spawn(socketPath string, argv []string, timeout time.Duration, withTty bool, windowColumns int, windowRows int, debug bool) {
	err := os.MkdirAll(filepath.Dir(socketPath), 0755)
	if err != nil {
		fatal(err)
	}

	if debug {
		ownPid := os.Getpid()

		traceOut, err := os.Create(socketPath + ".trace")
		if err != nil {
			fatal(err)
		}

		strace := exec.Command("strace", "-f", "-p", strconv.Itoa(ownPid))
		strace.Stdout = traceOut
		strace.Stderr = traceOut

		err = strace.Start()
		if err != nil {
			fatal(err)
		}
	}

	listener, err := net.Listen("unix", socketPath)
	if err != nil {
		fatal(err)
	}

	bin, err := exec.LookPath(argv[0])
	if err != nil {
		fatal(err)
	}

	cmd := child(bin, argv)

	// stderr will not be assigned in the case of a tty, so make
	// a dummy pipe to send across instead
	stderrR, stderrW, err := os.Pipe()
	if err != nil {
		fatal(err)
	}

	var stdinW, stdoutR *os.File
	var stdinR, stdoutW *os.File

	if withTty {
		pty, tty, err := pty.Open()
		if err != nil {
			fatal(err)
		}

		// do NOT assign stderrR to pty; the receiving end should only receive one
		// pty output stream, as they're both the same fd

		stdinW = pty
		stdoutR = pty

		stdinR = tty
		stdoutW = tty
		stderrW = tty

		ptyutil.SetWinSize(stdinW, windowColumns, windowRows)

		cmd.SysProcAttr.Setctty = true
		cmd.SysProcAttr.Setsid = true
	} else {
		stdinR, stdinW, err = os.Pipe()
		if err != nil {
			fatal(err)
		}

		stdoutR, stdoutW, err = os.Pipe()
		if err != nil {
			fatal(err)
		}
	}

	cmd.Stdin = stdinR
	cmd.Stdout = stdoutW
	cmd.Stderr = stderrW

	statusR, statusW, err := os.Pipe()
	if err != nil {
		fatal(err)
	}

	fmt.Println("ready")

	started := false

	for {
		conn, err := listener.Accept()
		if err != nil {
			fatal(err)
			break
		}

		rights := syscall.UnixRights(
			int(stdoutR.Fd()),
			int(stderrR.Fd()),
			int(statusR.Fd()),
		)

		_, _, err = conn.(*net.UnixConn).WriteMsgUnix([]byte{}, rights, nil)
		if err != nil {
			break
		}

		if !started {
			err := cmd.Start()
			if err != nil {
				fatal(err)
			}

			// close no longer relevant pipe ends
			// this closes tty 3 times but that's OK
			stdinR.Close()
			stdoutW.Close()
			stderrW.Close()

			fmt.Println("pid:", cmd.Process.Pid)

			go func() {
				cmd.Wait()

				if cmd.ProcessState != nil {
					fmt.Fprintf(statusW, "%d\n", cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus())
				}

				os.Exit(0)
			}()

			// detach from parent process
			os.Stdin.Close()
			os.Stdout.Close()
			os.Stderr.Close()

			started = true
		}

		decoder := gob.NewDecoder(conn)

		for {
			var input linkpkg.Input
			err := decoder.Decode(&input)
			if err != nil {
				break
			}

			if input.WindowSize != nil {
				ptyutil.SetWinSize(stdinW, input.WindowSize.Columns, input.WindowSize.Rows)
				cmd.Process.Signal(syscall.SIGWINCH)
			} else if input.EOF {
				err := stdinW.Close()
				if err != nil {
					conn.Close()
					break
				}
			} else {
				_, err := stdinW.Write(input.Data)
				if err != nil {
					conn.Close()
					break
				}
			}
		}
	}
}

func fatal(err error) {
	println("fatal: " + err.Error())
	os.Exit(1)
}
<commit_msg>when stracing, capture longer string lengths<commit_after>package main

import (
	"encoding/gob"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
	"time"

	linkpkg "github.com/cloudfoundry-incubator/warden-linux/iodaemon/link"
	"github.com/cloudfoundry-incubator/warden-linux/ptyutil"
	"github.com/kr/pty"
)

func spawn(socketPath string, argv []string, timeout time.Duration, withTty bool, windowColumns int, windowRows int, debug bool) {
	err := os.MkdirAll(filepath.Dir(socketPath), 0755)
	if err != nil {
		fatal(err)
	}

	if debug {
		ownPid := os.Getpid()

		traceOut, err := os.Create(socketPath + ".trace")
		if err != nil {
			fatal(err)
		}

		strace := exec.Command("strace", "-f", "-s", "10240", "-p", strconv.Itoa(ownPid))
		strace.Stdout = traceOut
		strace.Stderr = traceOut

		err = strace.Start()
		if err != nil {
			fatal(err)
		}
	}

	listener, err := net.Listen("unix", socketPath)
	if err != nil {
		fatal(err)
	}

	bin, err := exec.LookPath(argv[0])
	if err != nil {
		fatal(err)
	}

	cmd := child(bin, argv)

	// stderr will not be assigned in the case of a tty, so make
	// a dummy pipe to send across instead
	stderrR, stderrW, err := os.Pipe()
	if err != nil {
		fatal(err)
	}

	var stdinW, stdoutR *os.File
	var stdinR, stdoutW *os.File

	if withTty {
		pty, tty, err := pty.Open()
		if err != nil {
			fatal(err)
		}

		// do NOT assign stderrR to pty; the receiving end should only receive one
		// pty output stream, as they're both the same fd

		stdinW = pty
		stdoutR = pty

		stdinR = tty
		stdoutW = tty
		stderrW = tty

		ptyutil.SetWinSize(stdinW, windowColumns, windowRows)

		cmd.SysProcAttr.Setctty = true
		cmd.SysProcAttr.Setsid = true
	} else {
		stdinR, stdinW, err = os.Pipe()
		if err != nil {
			fatal(err)
		}

		stdoutR, stdoutW, err = os.Pipe()
		if err != nil {
			fatal(err)
		}
	}

	cmd.Stdin = stdinR
	cmd.Stdout = stdoutW
	cmd.Stderr = stderrW

	statusR, statusW, err := os.Pipe()
	if err != nil {
		fatal(err)
	}

	fmt.Println("ready")

	started := false

	for {
		conn, err := listener.Accept()
		if err != nil {
			fatal(err)
			break
		}

		rights := syscall.UnixRights(
			int(stdoutR.Fd()),
			int(stderrR.Fd()),
			int(statusR.Fd()),
		)

		_, _, err = conn.(*net.UnixConn).WriteMsgUnix([]byte{}, rights, nil)
		if err != nil {
			break
		}

		if !started {
			err := cmd.Start()
			if err != nil {
				fatal(err)
			}

			// close no longer relevant pipe ends
			// this closes tty 3 times but that's OK
			stdinR.Close()
			stdoutW.Close()
			stderrW.Close()

			fmt.Println("pid:", cmd.Process.Pid)

			go func() {
				cmd.Wait()

				if cmd.ProcessState != nil {
					fmt.Fprintf(statusW, "%d\n", cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus())
				}

				os.Exit(0)
			}()

			// detach from parent process
			os.Stdin.Close()
			os.Stdout.Close()
			os.Stderr.Close()

			started = true
		}

		decoder := gob.NewDecoder(conn)

		for {
			var input linkpkg.Input
			err := decoder.Decode(&input)
			if err != nil {
				break
			}

			if input.WindowSize != nil {
				ptyutil.SetWinSize(stdinW, input.WindowSize.Columns, input.WindowSize.Rows)
				cmd.Process.Signal(syscall.SIGWINCH)
			} else if input.EOF {
				err := stdinW.Close()
				if err != nil {
					conn.Close()
					break
				}
			} else {
				_, err := stdinW.Write(input.Data)
				if err != nil {
					conn.Close()
					break
				}
			}
		}
	}
}

func fatal(err error) {
	println("fatal: " + err.Error())
	os.Exit(1)
}
<|endoftext|>


<commit_before>package mux

import (
	"errors"
	"fmt"
	"strings"
)

// Converts patterns like "/users/:id" to "/users/(?P<id>\d+)"
func convertSimplePatternToRegexp(pattern string) string {
	parts := strings.Split(pattern, "/")

	for i, part := range parts {
		if len(part) != 0 && part[0] == ':' {
			parts[i] = fmt.Sprintf(`(?P<%s>[a-f\d]{24})`, part[1:])
		}
	}

	pattern = strings.Join(parts, `\/`)
	pattern = fmt.Sprintf("^%s$", pattern)

	return pattern
}

// Return path relative to "base"
func relativePath(base string, absolute string) (string, error) {
	baseLen := len(base)
	absoluteLen := len(absolute)

	if absoluteLen < baseLen {
		return "", errors.New("absolute len shorter than base len")
	}

	if absolute[:baseLen] != base {
		return "", errors.New("absolute path doesn't start with base path")
	}

	return absolute[baseLen:], nil
}
<commit_msg>Remove unnecessary complex operations in path params regexp builder.<commit_after>package mux

import (
	"errors"
	"fmt"
	"regexp"
)

var paramRegexp = regexp.MustCompile(`:([[:lower:]]|_)+`)

// Converts patterns like "/users/:id" to "/users/(?P<id>\d+)"
func convertSimplePatternToRegexp(pattern string) string {
	patternWithParams := paramRegexp.ReplaceAllStringFunc(pattern, func(param string) string {
		return fmt.Sprintf(`(?P<%s>[[:xdigit:]]{24})`, param[1:])
	})

	return fmt.Sprintf("^%s$", patternWithParams)
}

// Return path relative to "base"
func relativePath(base string, absolute string) (string, error) {
	baseLen := len(base)
	absoluteLen := len(absolute)

	if absoluteLen < baseLen {
		return "", errors.New("absolute len shorter than base len")
	}

	if absolute[:baseLen] != base {
		return "", errors.New("absolute path doesn't start with base path")
	}

	return absolute[baseLen:], nil
}
<|endoftext|>