<commit_before>package idp

import (
	"crypto/rsa"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gorilla/sessions"
	hclient "github.com/ory-am/hydra/client"
	hjwk "github.com/ory-am/hydra/jwk"
	hoauth2 "github.com/ory-am/hydra/oauth2"
	hydra "github.com/ory-am/hydra/sdk"
	"github.com/patrickmn/go-cache"
)

const (
	VerifyPublicKey   = "VerifyPublic"
	ConsentPrivateKey = "ConsentPrivate"
)

func ClientInfoKey(clientID string) string {
	return "ClientInfo:" + clientID
}

var encryptionkey = "something-very-secret"

// Identity Provider's options
type IDPConfig struct {
	// Client id issued by Hydra
	ClientID string `yaml:"client_id"`

	// Client secret issued by Hydra
	ClientSecret string `yaml:"client_secret"`

	// Hydra's address
	ClusterURL string `yaml:"hydra_address"`

	// Expiration time of internal key cache
	KeyCacheExpiration time.Duration `yaml:"key_cache_expiration"`

	// Expiration time of internal clientid cache
	ClientCacheExpiration time.Duration `yaml:"client_cache_expiration"`

	// Internal cache cleanup interval
	CacheCleanupInterval time.Duration `yaml:"cache_cleanup_interval"`

	// Gorilla sessions Store for storing the Challenge.
	ChallengeStore sessions.Store
}

// Identity Provider helper
type IDP struct {
	config *IDPConfig

	// Communication with Hydra
	hc *hydra.Client

	// Http client for communicating with Hydra
	client *http.Client

	// Cache for all private and public keys
	cache *cache.Cache

	// Prepared cookie options for creating and deleting cookies
	// TODO: Is this the best way to do this?
	createChallengeCookieOptions *sessions.Options
	deleteChallengeCookieOptions *sessions.Options
}

// Create the Identity Provider helper
func NewIDP(config *IDPConfig) *IDP {
	var idp = new(IDP)
	idp.config = config

	// TODO: Pass TTL and refresh period from config
	idp.cache = cache.New(config.KeyCacheExpiration, config.CacheCleanupInterval)
	idp.cache.OnEvicted(func(key string, value interface{}) { idp.refreshCache(key) })

	idp.createChallengeCookieOptions = new(sessions.Options)
	idp.createChallengeCookieOptions.Path = "/"      // TODO: More specific?
	idp.createChallengeCookieOptions.MaxAge = 60 * 5 // 5min
	idp.createChallengeCookieOptions.Secure = false  // TODO: Change to true
	idp.createChallengeCookieOptions.HttpOnly = false

	idp.deleteChallengeCookieOptions = new(sessions.Options)
	idp.deleteChallengeCookieOptions.Path = "/"     // TODO: More specific?
	idp.deleteChallengeCookieOptions.MaxAge = -1    // Mark for deletion
	idp.deleteChallengeCookieOptions.Secure = false // TODO: Change to true
	idp.deleteChallengeCookieOptions.HttpOnly = false

	return idp
}

func (idp *IDP) cacheConsentKey() error {
	consentKey, err := idp.downloadConsentKey()

	duration := cache.DefaultExpiration
	if err != nil {
		// re-cache the result even if there's an error, but
		// do it with a shorter timeout. This will ensure we
		// try to refresh the key once that timeout expires,
		// otherwise we'll _never_ refresh the key again.
		duration = idp.config.CacheCleanupInterval
	}

	idp.cache.Set(ConsentPrivateKey, consentKey, duration)
	return err
}

func (idp *IDP) cacheVerificationKey() error {
	verifyKey, err := idp.downloadVerificationKey()

	duration := cache.DefaultExpiration
	if err != nil {
		// re-cache the result even if there's an error, but
		// do it with a shorter timeout. This will ensure we
		// try to refresh the key once that timeout expires,
		// otherwise we'll _never_ refresh the key again.
		duration = idp.config.CacheCleanupInterval
	}

	idp.cache.Set(VerifyPublicKey, verifyKey, duration)
	return err
}

// Called when any key expires
func (idp *IDP) refreshCache(key string) {
	switch key {
	case VerifyPublicKey:
		idp.cacheVerificationKey()
		return

	case ConsentPrivateKey:
		idp.cacheConsentKey()
		return

	default:
		// Will get here for client IDs.
		// Fine to just let them expire, the next request from that
		// client will trigger a refresh
		return
	}
}

// Downloads the hydra's public key
func (idp *IDP) downloadVerificationKey() (*rsa.PublicKey, error) {

	jwk, err := idp.hc.JWK.GetKey(hoauth2.ConsentChallengeKey, "public")
	if err != nil {
		return nil, err
	}

	rsaKey, ok := hjwk.First(jwk.Keys).Key.(*rsa.PublicKey)
	if !ok {
		return nil, ErrorBadPublicKey
	}

	return rsaKey, nil
}

// Downloads the private key used for signing the consent
func (idp *IDP) downloadConsentKey() (*rsa.PrivateKey, error) {
	jwk, err := idp.hc.JWK.GetKey(hoauth2.ConsentEndpointKey, "private")
	if err != nil {
		return nil, err
	}

	rsaKey, ok := hjwk.First(jwk.Keys).Key.(*rsa.PrivateKey)
	if !ok {
		return nil, ErrorBadPrivateKey
	}

	return rsaKey, nil
}

// Connect to Hydra
func (idp *IDP) Connect() error {
	var err error
	idp.hc, err = hydra.Connect(
		hydra.ClientID(idp.config.ClientID),
		hydra.ClientSecret(idp.config.ClientSecret),
		hydra.ClusterURL(idp.config.ClusterURL),
		// TODO: Remove
		hydra.SkipTLSVerify(),
	)

	if err != nil {
		return err
	}

	err = idp.cacheVerificationKey()
	if err != nil {
		return err
	}

	err = idp.cacheConsentKey()
	if err != nil {
		return err
	}

	return nil
}

// Parse and verify the challenge JWT
func (idp *IDP) getChallengeToken(challengeString string) (*jwt.Token, error) {
	token, err := jwt.Parse(challengeString, func(token *jwt.Token) (interface{}, error) {
		_, ok := token.Method.(*jwt.SigningMethodRSA)
		if !ok {
			return nil, ErrorBadSigningMethod
		}

		return idp.getVerificationKey()
	})

	if err != nil {
		return nil, err
	}

	if !token.Valid {
		return nil, ErrorInvalidToken
	}

	return token, nil
}

func (idp *IDP) getConsentKey() (*rsa.PrivateKey, error) {
	data, ok := idp.cache.Get(ConsentPrivateKey)
	if !ok {
		return nil, ErrorNotInCache
	}

	key, ok := data.(*rsa.PrivateKey)
	if !ok {
		return nil, ErrorBadKey
	}

	return key, nil
}

func (idp *IDP) getVerificationKey() (*rsa.PublicKey, error) {
	data, ok := idp.cache.Get(VerifyPublicKey)
	if !ok {
		return nil, ErrorNotInCache
	}

	key, ok := data.(*rsa.PublicKey)
	if !ok {
		return nil, ErrorBadKey
	}

	return key, nil
}

func (idp *IDP) getClient(clientID string) (*hclient.Client, error) {
	clientKey := ClientInfoKey(clientID)
	data, ok := idp.cache.Get(clientKey)
	if ok {
		if data != nil {
			client := data.(*hclient.Client)
			return client, nil
		}
		return nil, ErrorNoSuchClient
	}

	client, err := idp.hc.Client.GetClient(clientID)
	if err != nil {
		// Either the client isn't registered in hydra, or maybe hydra is
		// having some problem. Either way, ensure we don't hit hydra again
		// for this client if someone (maybe an attacker) retries quickly.
		idp.cache.Set(clientKey, nil, idp.config.ClientCacheExpiration)
		return nil, err
	}

	c := client.(*hclient.Client)
	idp.cache.Set(clientKey, client, idp.config.ClientCacheExpiration)
	return c, nil
}

// Create a new Challenge. The request will contain all the necessary information from Hydra, passed in the URL.
func (idp *IDP) NewChallenge(r *http.Request, user string) (challenge *Challenge, err error) {
	tokenStr := r.FormValue("challenge")
	if tokenStr == "" {
		// No challenge token
		err = ErrorBadRequest
		return
	}

	token, err := idp.getChallengeToken(tokenStr)
	if err != nil {
		// Most probably, token can't be verified or parsed
		return
	}
	claims := token.Claims.(jwt.MapClaims)

	challenge = new(Challenge)
	challenge.Expires = time.Unix(int64(claims["exp"].(float64)), 0)
	if challenge.Expires.Before(time.Now()) {
		challenge = nil
		err = ErrorChallengeExpired
		return
	}

	// Get data from the challenge jwt
	challenge.Client, err = idp.getClient(claims["aud"].(string))
	if err != nil {
		return nil, err
	}

	challenge.Redirect = claims["redir"].(string)
	challenge.User = user
	challenge.idp = idp

	scopes := claims["scp"].([]interface{})
	challenge.Scopes = make([]string, len(scopes), len(scopes))
	for i, scope := range scopes {
		challenge.Scopes[i] = scope.(string)
	}

	return
}

// Get the Challenge from a cookie, using Gorilla sessions
func (idp *IDP) GetChallenge(r *http.Request) (*Challenge, error) {
	session, err := idp.config.ChallengeStore.Get(r, SessionCookieName)
	if err != nil {
		return nil, err
	}

	challenge, ok := session.Values[SessionCookieName].(*Challenge)
	if !ok {
		return nil, ErrorBadChallengeCookie
	}

	if challenge.Expires.Before(time.Now()) {
		return nil, ErrorChallengeExpired
	}

	challenge.idp = idp

	return challenge, nil
}

// Closes connection to Hydra, cleans cache etc.
func (idp *IDP) Close() {
	idp.client = nil

	// Removes all keys from the cache
	idp.cache.Flush()
}
<commit_msg>Optionally skip TLS verification<commit_after>package idp

import (
	"crypto/rsa"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gorilla/sessions"
	hclient "github.com/ory-am/hydra/client"
	hjwk "github.com/ory-am/hydra/jwk"
	hoauth2 "github.com/ory-am/hydra/oauth2"
	hydra "github.com/ory-am/hydra/sdk"
	"github.com/patrickmn/go-cache"
)

const (
	VerifyPublicKey   = "VerifyPublic"
	ConsentPrivateKey = "ConsentPrivate"
)

func ClientInfoKey(clientID string) string {
	return "ClientInfo:" + clientID
}

var encryptionkey = "something-very-secret"

// Identity Provider's options
type IDPConfig struct {
	// Client id issued by Hydra
	ClientID string `yaml:"client_id"`

	// Client secret issued by Hydra
	ClientSecret string `yaml:"client_secret"`

	// Hydra's address
	ClusterURL string `yaml:"hydra_address"`

	// Expiration time of internal key cache
	KeyCacheExpiration time.Duration `yaml:"key_cache_expiration"`

	// Expiration time of internal clientid cache
	ClientCacheExpiration time.Duration `yaml:"client_cache_expiration"`

	// Internal cache cleanup interval
	CacheCleanupInterval time.Duration `yaml:"cache_cleanup_interval"`

	// Gorilla sessions Store for storing the Challenge.
	ChallengeStore sessions.Store
}

// Identity Provider helper
type IDP struct {
	config *IDPConfig

	// Communication with Hydra
	hc *hydra.Client

	// Http client for communicating with Hydra
	client *http.Client

	// Cache for all private and public keys
	cache *cache.Cache

	// Prepared cookie options for creating and deleting cookies
	// TODO: Is this the best way to do this?
	createChallengeCookieOptions *sessions.Options
	deleteChallengeCookieOptions *sessions.Options
}

// Create the Identity Provider helper
func NewIDP(config *IDPConfig) *IDP {
	var idp = new(IDP)
	idp.config = config

	// TODO: Pass TTL and refresh period from config
	idp.cache = cache.New(config.KeyCacheExpiration, config.CacheCleanupInterval)
	idp.cache.OnEvicted(func(key string, value interface{}) { idp.refreshCache(key) })

	idp.createChallengeCookieOptions = new(sessions.Options)
	idp.createChallengeCookieOptions.Path = "/"      // TODO: More specific?
	idp.createChallengeCookieOptions.MaxAge = 60 * 5 // 5min
	idp.createChallengeCookieOptions.Secure = false  // TODO: Change to true
	idp.createChallengeCookieOptions.HttpOnly = false

	idp.deleteChallengeCookieOptions = new(sessions.Options)
	idp.deleteChallengeCookieOptions.Path = "/"     // TODO: More specific?
	idp.deleteChallengeCookieOptions.MaxAge = -1    // Mark for deletion
	idp.deleteChallengeCookieOptions.Secure = false // TODO: Change to true
	idp.deleteChallengeCookieOptions.HttpOnly = false

	return idp
}

func (idp *IDP) cacheConsentKey() error {
	consentKey, err := idp.downloadConsentKey()

	duration := cache.DefaultExpiration
	if err != nil {
		// re-cache the result even if there's an error, but
		// do it with a shorter timeout. This will ensure we
		// try to refresh the key once that timeout expires,
		// otherwise we'll _never_ refresh the key again.
		duration = idp.config.CacheCleanupInterval
	}

	idp.cache.Set(ConsentPrivateKey, consentKey, duration)
	return err
}

func (idp *IDP) cacheVerificationKey() error {
	verifyKey, err := idp.downloadVerificationKey()

	duration := cache.DefaultExpiration
	if err != nil {
		// re-cache the result even if there's an error, but
		// do it with a shorter timeout. This will ensure we
		// try to refresh the key once that timeout expires,
		// otherwise we'll _never_ refresh the key again.
		duration = idp.config.CacheCleanupInterval
	}

	idp.cache.Set(VerifyPublicKey, verifyKey, duration)
	return err
}

// Called when any key expires
func (idp *IDP) refreshCache(key string) {
	switch key {
	case VerifyPublicKey:
		idp.cacheVerificationKey()
		return

	case ConsentPrivateKey:
		idp.cacheConsentKey()
		return

	default:
		// Will get here for client IDs.
		// Fine to just let them expire, the next request from that
		// client will trigger a refresh
		return
	}
}

// Downloads the hydra's public key
func (idp *IDP) downloadVerificationKey() (*rsa.PublicKey, error) {

	jwk, err := idp.hc.JWK.GetKey(hoauth2.ConsentChallengeKey, "public")
	if err != nil {
		return nil, err
	}

	rsaKey, ok := hjwk.First(jwk.Keys).Key.(*rsa.PublicKey)
	if !ok {
		return nil, ErrorBadPublicKey
	}

	return rsaKey, nil
}

// Downloads the private key used for signing the consent
func (idp *IDP) downloadConsentKey() (*rsa.PrivateKey, error) {
	jwk, err := idp.hc.JWK.GetKey(hoauth2.ConsentEndpointKey, "private")
	if err != nil {
		return nil, err
	}

	rsaKey, ok := hjwk.First(jwk.Keys).Key.(*rsa.PrivateKey)
	if !ok {
		return nil, ErrorBadPrivateKey
	}

	return rsaKey, nil
}

// Connect to Hydra
func (idp *IDP) Connect(skipTLSVerify bool) error {
	var err error
	if skipTLSVerify {
		idp.hc, err = hydra.Connect(
			hydra.ClientID(idp.config.ClientID),
			hydra.ClientSecret(idp.config.ClientSecret),
			hydra.ClusterURL(idp.config.ClusterURL),
			hydra.SkipTLSVerify(),
		)
	} else {
		idp.hc, err = hydra.Connect(
			hydra.ClientID(idp.config.ClientID),
			hydra.ClientSecret(idp.config.ClientSecret),
			hydra.ClusterURL(idp.config.ClusterURL),
		)
	}

	if err != nil {
		return err
	}

	err = idp.cacheVerificationKey()
	if err != nil {
		return err
	}

	err = idp.cacheConsentKey()
	if err != nil {
		return err
	}

	return nil
}

// Parse and verify the challenge JWT
func (idp *IDP) getChallengeToken(challengeString string) (*jwt.Token, error) {
	token, err := jwt.Parse(challengeString, func(token *jwt.Token) (interface{}, error) {
		_, ok := token.Method.(*jwt.SigningMethodRSA)
		if !ok {
			return nil, ErrorBadSigningMethod
		}

		return idp.getVerificationKey()
	})

	if err != nil {
		return nil, err
	}

	if !token.Valid {
		return nil, ErrorInvalidToken
	}

	return token, nil
}

func (idp *IDP) getConsentKey() (*rsa.PrivateKey, error) {
	data, ok := idp.cache.Get(ConsentPrivateKey)
	if !ok {
		return nil, ErrorNotInCache
	}

	key, ok := data.(*rsa.PrivateKey)
	if !ok {
		return nil, ErrorBadKey
	}

	return key, nil
}

func (idp *IDP) getVerificationKey() (*rsa.PublicKey, error) {
	data, ok := idp.cache.Get(VerifyPublicKey)
	if !ok {
		return nil, ErrorNotInCache
	}

	key, ok := data.(*rsa.PublicKey)
	if !ok {
		return nil, ErrorBadKey
	}

	return key, nil
}

func (idp *IDP) getClient(clientID string) (*hclient.Client, error) {
	clientKey := ClientInfoKey(clientID)
	data, ok := idp.cache.Get(clientKey)
	if ok {
		if data != nil {
			client := data.(*hclient.Client)
			return client, nil
		}
		return nil, ErrorNoSuchClient
	}

	client, err := idp.hc.Client.GetClient(clientID)
	if err != nil {
		// Either the client isn't registered in hydra, or maybe hydra is
		// having some problem. Either way, ensure we don't hit hydra again
		// for this client if someone (maybe an attacker) retries quickly.
		idp.cache.Set(clientKey, nil, idp.config.ClientCacheExpiration)
		return nil, err
	}

	c := client.(*hclient.Client)
	idp.cache.Set(clientKey, client, idp.config.ClientCacheExpiration)
	return c, nil
}

// Create a new Challenge. The request will contain all the necessary information from Hydra, passed in the URL.
func (idp *IDP) NewChallenge(r *http.Request, user string) (challenge *Challenge, err error) {
	tokenStr := r.FormValue("challenge")
	if tokenStr == "" {
		// No challenge token
		err = ErrorBadRequest
		return
	}

	token, err := idp.getChallengeToken(tokenStr)
	if err != nil {
		// Most probably, token can't be verified or parsed
		return
	}
	claims := token.Claims.(jwt.MapClaims)

	challenge = new(Challenge)
	challenge.Expires = time.Unix(int64(claims["exp"].(float64)), 0)
	if challenge.Expires.Before(time.Now()) {
		challenge = nil
		err = ErrorChallengeExpired
		return
	}

	// Get data from the challenge jwt
	challenge.Client, err = idp.getClient(claims["aud"].(string))
	if err != nil {
		return nil, err
	}

	challenge.Redirect = claims["redir"].(string)
	challenge.User = user
	challenge.idp = idp

	scopes := claims["scp"].([]interface{})
	challenge.Scopes = make([]string, len(scopes), len(scopes))
	for i, scope := range scopes {
		challenge.Scopes[i] = scope.(string)
	}

	return
}

// Get the Challenge from a cookie, using Gorilla sessions
func (idp *IDP) GetChallenge(r *http.Request) (*Challenge, error) {
	session, err := idp.config.ChallengeStore.Get(r, SessionCookieName)
	if err != nil {
		return nil, err
	}

	challenge, ok := session.Values[SessionCookieName].(*Challenge)
	if !ok {
		return nil, ErrorBadChallengeCookie
	}

	if challenge.Expires.Before(time.Now()) {
		return nil, ErrorChallengeExpired
	}

	challenge.idp = idp

	return challenge, nil
}

// Closes connection to Hydra, cleans cache etc.
func (idp *IDP) Close() {
	idp.client = nil

	// Removes all keys from the cache
	idp.cache.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/tcgl/redis"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
)

// The maximal size for an image is 5MB
const maxSize = 5 * (1 << 20)

// The directory for caching files
var directory string

// The connection to redis
var connection *redis.Database

func urlStatus(uri string) error {
	res := connection.Command("hexists", "img/"+uri, "created_at")
	if !res.IsOK() {
		return res.Error()
	}

	ok, err := res.ValueAsBool()
	if err != nil {
		return err
	}

	if !ok {
		return errors.New("Invalid URL")
	}

	return nil
}

// Generate a key for cache from a string
func generateKeyForCache(s string) string {
	h := sha1.New()
	io.WriteString(h, s)
	key := h.Sum(nil)

	// Use 3 levels of hasing to avoid having too many files in the same directory
	return fmt.Sprintf("%s/%x/%x/%x/%x", directory, key[0:1], key[1:2], key[2:3], key[3:])
}

// Fetch image from cache
func fetchImageFromCache(uri string) (contentType string, body []byte, ok bool) {
	ok = false

	res := connection.Command("hget", "img/"+uri, "type")
	if !res.IsOK() {
		return
	}
	contentType = res.String()

	filename := generateKeyForCache(uri)
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	ok = true
	return
}

// Save the body and the content-type header in cache
func saveImageInCache(uri string, contentType string, body []byte) {
	filename := generateKeyForCache(uri)
	dirname := path.Dir(filename)
	err := os.MkdirAll(dirname, 0755)
	if err != nil {
		return
	}

	// Save the body on disk
	err = ioutil.WriteFile(filename, body, 0644)
	if err != nil {
		log.Printf("Error while writing %s\n", filename)
		return
	}

	// And other infos in redis
	connection.Command("hset", "img/"+uri, "type", contentType)
}

// Fetch the image from the distant server
func fetchImageFromServer(uri string) (contentType string, body []byte, err error) {
	res, err := http.Get(uri)
	if err != nil {
		return
	}
	if res.StatusCode != 200 {
		log.Printf("Status code of %s is: %d\n", uri, res.StatusCode)
		err = errors.New("Unexpected status code")
		return
	}

	defer res.Body.Close()
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}
	// TODO keep errors in cache with a small TTL
	if res.ContentLength > maxSize {
		log.Printf("Exceeded max size for %s: %d\n", uri, res.ContentLength)
		err = errors.New("Exceeded max size")
		return
	}
	contentType = res.Header.Get("Content-Type")
	if contentType[0:5] != "image" {
		log.Printf("%s has an invalid content-type: %s\n", uri, contentType)
		err = errors.New("Invalid content-type")
		return
	}
	log.Printf("Fetch %s (%s)\n", uri, contentType)

	if urlStatus(uri) == nil {
		go saveImageInCache(uri, contentType, body)
	}
	return
}

// Fetch image from cache if available, or from the server
func fetchImage(uri string) (contentType string, body []byte, err error) {
	err = urlStatus(uri)
	if err != nil {
		return
	}

	contentType, body, ok := fetchImageFromCache(uri)
	if !ok {
		contentType, body, err = fetchImageFromServer(uri)
	}

	return
}

// Receive an HTTP request for an image and respond with it
func Img(w http.ResponseWriter, r *http.Request) {
	encoded_url := r.URL.Query().Get(":encoded_url")
	chars, err := hex.DecodeString(encoded_url)
	if err != nil {
		log.Printf("Invalid URL %s\n", encoded_url)
		http.Error(w, "Invalid parameters", 400)
		return
	}
	uri := string(chars)

	contentType, body, err := fetchImage(uri)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	w.Header().Add("Content-Type", contentType)
	w.Write(body)
}

// Returns 200 OK if the server is running (for monitoring)
func Status(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "OK")
}

func main() {
	// Parse the command-line
	var addr string
	var logs string
	var conn string
	flag.StringVar(&addr, "a", "127.0.0.1:8000", "Bind to this address:port")
	flag.StringVar(&logs, "l", "-", "Use this file for logs")
	flag.StringVar(&conn, "-r", "localhost:6379/0", "The redis database to use for caching meta")
	flag.StringVar(&directory, "d", "cache", "The directory for the caching files")
	flag.Parse()

	// Logging
	if logs != "-" {
		f, err := os.OpenFile(logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal("OpenFile: ", err)
		}
		syscall.Dup2(int(f.Fd()), int(os.Stdout.Fd()))
		syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd()))
	}

	// Redis
	parts := strings.Split(conn, "/")
	host := parts[0]
	db := 0
	if len(parts) >= 2 {
		db, _ = strconv.Atoi(parts[1])
	}
	cfg := redis.Configuration{Database: db, Address: host, PoolSize: 4, LogCommands: false}
	connection = redis.Connect(cfg)

	// Routing
	m := pat.New()
	m.Get("/status", http.HandlerFunc(Status))
	m.Get("/img/:encoded_url", http.HandlerFunc(Img))
	http.Handle("/", m)

	// Start the HTTP server
	log.Printf("Listening on http://%s/\n", addr)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Also save errors in redis for 10 minutes<commit_after>package main

import (
	"code.google.com/p/tcgl/redis"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
)

// The maximal size for an image is 5MB
const maxSize = 5 * (1 << 20)

// The directory for caching files
var directory string

// The connection to redis
var connection *redis.Database

func urlStatus(uri string) error {
	res := connection.Command("hexists", "img/"+uri, "created_at")
	if !res.IsOK() {
		return res.Error()
	}

	ok, err := res.ValueAsBool()
	if err != nil {
		return err
	}

	if !ok {
		return errors.New("Invalid URL")
	}

	res = connection.Command("get", "img/err/"+uri)
	if !res.IsOK() {
		return nil
	}
	str := res.ValueAsString()
	if err == nil {
		fmt.Printf("*** str = %s ***\n", str)
		return errors.New(str)
	}

	return nil
}

// Generate a key for cache from a string
func generateKeyForCache(s string) string {
	h := sha1.New()
	io.WriteString(h, s)
	key := h.Sum(nil)

	// Use 3 levels of hasing to avoid having too many files in the same directory
	return fmt.Sprintf("%s/%x/%x/%x/%x", directory, key[0:1], key[1:2], key[2:3], key[3:])
}

// Fetch image from cache
func fetchImageFromCache(uri string) (contentType string, body []byte, ok bool) {
	ok = false

	res := connection.Command("hget", "img/"+uri, "type")
	if !res.IsOK() {
		return
	}
	contentType = res.ValueAsString()

	filename := generateKeyForCache(uri)
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	ok = true
	return
}

// Save the body and the content-type header in cache
func saveImageInCache(uri string, contentType string, body []byte) {
	go func() {
		filename := generateKeyForCache(uri)
		dirname := path.Dir(filename)
		err := os.MkdirAll(dirname, 0755)
		if err != nil {
			return
		}

		// Save the body on disk
		err = ioutil.WriteFile(filename, body, 0644)
		if err != nil {
			log.Printf("Error while writing %s\n", filename)
			return
		}

		// And other infos in redis
		connection.Command("hset", "img/"+uri, "type", contentType)
	}()
}

func saveErrorInCache(uri string, err error) {
	go func() {
		connection.Command("set", "img/err/"+uri, err.Error())
		connection.Command("expire", "img/err/"+uri, 600)
	}()
}

// Fetch the image from the distant server
func fetchImageFromServer(uri string) (contentType string, body []byte, err error) {
	res, err := http.Get(uri)
	if err != nil {
		return
	}
	if res.StatusCode != 200 {
		log.Printf("Status code of %s is: %d\n", uri, res.StatusCode)
		err = errors.New("Unexpected status code")
		saveErrorInCache(uri, err)
		return
	}

	defer res.Body.Close()
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}
	if res.ContentLength > maxSize {
		log.Printf("Exceeded max size for %s: %d\n", uri, res.ContentLength)
		err = errors.New("Exceeded max size")
		saveErrorInCache(uri, err)
		return
	}
	contentType = res.Header.Get("Content-Type")
	if contentType[0:5] != "image" {
		log.Printf("%s has an invalid content-type: %s\n", uri, contentType)
		err = errors.New("Invalid content-type")
		saveErrorInCache(uri, err)
		return
	}
	log.Printf("Fetch %s (%s)\n", uri, contentType)

	if urlStatus(uri) == nil {
		saveImageInCache(uri, contentType, body)
	}
	return
}

// Fetch image from cache if available, or from the server
func fetchImage(uri string) (contentType string, body []byte, err error) {
	err = urlStatus(uri)
	if err != nil {
		return
	}

	contentType, body, ok := fetchImageFromCache(uri)
	if !ok {
		contentType, body, err = fetchImageFromServer(uri)
	}

	return
}

// Receive an HTTP request for an image and respond with it
func Img(w http.ResponseWriter, r *http.Request) {
	encoded_url := r.URL.Query().Get(":encoded_url")
	chars, err := hex.DecodeString(encoded_url)
	if err != nil {
		log.Printf("Invalid URL %s\n", encoded_url)
		http.Error(w, "Invalid parameters", 400)
		return
	}
	uri := string(chars)

	contentType, body, err := fetchImage(uri)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	w.Header().Add("Content-Type", contentType)
	w.Write(body)
}

// Returns 200 OK if the server is running (for monitoring)
func Status(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "OK")
}

func main() {
	// Parse the command-line
	var addr string
	var logs string
	var conn string
	flag.StringVar(&addr, "a", "127.0.0.1:8000", "Bind to this address:port")
	flag.StringVar(&logs, "l", "-", "Use this file for logs")
	flag.StringVar(&conn, "-r", "localhost:6379/0", "The redis database to use for caching meta")
	flag.StringVar(&directory, "d", "cache", "The directory for the caching files")
	flag.Parse()

	// Logging
	if logs != "-" {
		f, err := os.OpenFile(logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal("OpenFile: ", err)
		}
		syscall.Dup2(int(f.Fd()), int(os.Stdout.Fd()))
		syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd()))
	}

	// Redis
	parts := strings.Split(conn, "/")
	host := parts[0]
	db := 0
	if len(parts) >= 2 {
		db, _ = strconv.Atoi(parts[1])
	}
	cfg := redis.Configuration{Database: db, Address: host, PoolSize: 4}
	connection = redis.Connect(cfg)

	// Routing
	m := pat.New()
	m.Get("/status", http.HandlerFunc(Status))
	m.Get("/img/:encoded_url", http.HandlerFunc(Img))
	http.Handle("/", m)

	// Start the HTTP server
	log.Printf("Listening on http://%s/\n", addr)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"os"
)

type (
    neuron []float64
    nLayer []neuron
    neuralNet []nLayer
)

func randWeight() float64 {
	return rand.Float64() - rand.Float64()
}

func NewNeuron(numInputs int64) neuron {
	var n neuron
	for i := int64(0); i < numInputs; i++ {
		n = append(n, randWeight())
	}
	return n
}

func NewNeuronLayer(numNeurons int64, numInputs int64) nLayer {
	var layer nLayer
	for i := int64(0); i < numNeurons; i++ {
		layer = append(layer, NewNeuron(numInputs))
	}
	return layer
}

func NewNeuralNet(numInputs int64, numOutputs int64, numHiddenLayers int64, numNeuronsPerLayer int64) neuralNet {
    var net neuralNet
	if numHiddenLayers > 0 {
		net = append(net, NewNeuronLayer(numNeuronsPerLayer, numInputs))
		for i := int64(0); i < numHiddenLayers-1; i++ {
			net = append(net, NewNeuronLayer(numNeuronsPerLayer, numNeuronsPerLayer))
		}
		net = append(net, NewNeuronLayer(numOutputs, numNeuronsPerLayer))
	} else {
		net = append(net, NewNeuronLayer(numOutputs, numInputs))
	}
	return net
}

func (net neuralNet) getWeights() []float64 {
	weights := make([]float64, 0)

	for _, layer := range net {
		for _, neuron := range layer {
			for _, weight := range neuron {
				weights = append(weights, weight)
			}
		}
	}
	return weights
}

func (net neuralNet) setWeights(newWeights []float64) {
	index := 0
	for l, _ := range net {
		for n, _ := range net[l] {
			for e, _ := range net[l][n] {
				net[l][n][e] = newWeights[index]
				index++
			}
		}
	}
}

func (net neuralNet) printNet() {
	for c, layer := range net {
		fmt.Printf("Layer: %d (%d neurons)\n", c, len(layer))
		for i, neuron := range layer {
			fmt.Printf("Neuron: %d\n", i)
			for _, weight := range neuron {
				fmt.Println(weight)
			}
		}
	}
}

func (net neuralNet) update(inputs []float64) []float64 {
	outputs := make([]float64, 1)
	if len(inputs) != len(net) {
		return outputs
	}

	return outputs
}

func seedRand() {
	f, err := os.Open("/dev/urandom")
	if err != nil {
		fmt.Printf("Failed to open random source: %s\n", err)
		os.Exit(1)
	}
	seed := make([]byte, 8)
	i, err := f.Read(seed)
	if err != nil {
		fmt.Printf("Failed to read random source: %s\n", err)
		os.Exit(1)
	}
	if i != 8 {
		fmt.Printf("Was expecting to read 8 bytes of seeds, read %d bytes instead\n", i)
		os.Exit(1)
	}
	seed64 := int64(binary.LittleEndian.Uint64(seed))
	rand.Seed(seed64)
}

func main() {
	seedRand()

	net := NewNeuralNet(1, 1, 3, 10)
	net.printNet()

	w := net.getWeights()

	derp := make([]float64, 0)
	for i := 0; i < len(w); i++ {
		derp = append(derp, float64(i))
	}
	net.setWeights(derp)
	net.printNet()
	net.setWeights(w)
	net.printNet()
}
<commit_msg>Add update code<commit_after>package main

import (
	"encoding/binary"
	"fmt"
    "math"
	"math/rand"
	"os"
)

type (
    neuron []float64
    nLayer []neuron
    neuralNet []nLayer
)

func randWeight() float64 {
	return rand.Float64() - rand.Float64()
}

func NewNeuron(numInputs int64) neuron {
	var n neuron
	for i := int64(0); i < numInputs; i++ {
		n = append(n, randWeight())
	}
	return n
}

func NewNeuronLayer(numNeurons int64, numInputs int64) nLayer {
	var layer nLayer
	for i := int64(0); i < numNeurons; i++ {
		layer = append(layer, NewNeuron(numInputs))
	}
	return layer
}

func NewNeuralNet(numInputs int64, numOutputs int64, numHiddenLayers int64, numNeuronsPerLayer int64) neuralNet {
    var net neuralNet
	if numHiddenLayers > 0 {
		net = append(net, NewNeuronLayer(numNeuronsPerLayer, numInputs))
		for i := int64(0); i < numHiddenLayers-1; i++ {
			net = append(net, NewNeuronLayer(numNeuronsPerLayer, numNeuronsPerLayer))
		}
		net = append(net, NewNeuronLayer(numOutputs, numNeuronsPerLayer))
	} else {
		net = append(net, NewNeuronLayer(numOutputs, numInputs))
	}
	return net
}

func (net neuralNet) getWeights() []float64 {
	weights := make([]float64, 0)

	for _, layer := range net {
		for _, neuron := range layer {
			for _, weight := range neuron {
				weights = append(weights, weight)
			}
		}
	}
	return weights
}

func (net neuralNet) setWeights(newWeights []float64) {
	index := 0
	for l, _ := range net {
		for n, _ := range net[l] {
			for e, _ := range net[l][n] {
				net[l][n][e] = newWeights[index]
				index++
			}
		}
	}
}

func (net neuralNet) printNet() {
	for c, layer := range net {
		fmt.Printf("Layer: %d (%d neurons)\n", c, len(layer))
		for i, neuron := range layer {
			fmt.Printf("Neuron: %d\n", i)
			for _, weight := range neuron {
				fmt.Println(weight)
			}
		}
	}
}

func sigmoid(input float64) float64 {
    p := 1.0
    return 1 / ( 1 + math.Exp(-1*input/p))
}

func (n neuron) updateNeuron(inputs []float64) float64 {
    var output float64 = 0.0
    for i, weight := range n {
        output += weight * inputs[i]
    }
    return sigmoid(output)
}

func (layer nLayer) updateLayer(inputs []float64) []float64 {
    var output []float64
    for _, n := range layer {
        output = append(output, n.updateNeuron(inputs))
    }
    return output
}

func (net neuralNet) update(inputs []float64) []float64 {
	var outputs []float64

    for _, layer := range net {
        outputs = layer.updateLayer(inputs)
        inputs = outputs
    }

	return outputs
}

func seedRand() {
	f, err := os.Open("/dev/urandom")
	if err != nil {
		fmt.Printf("Failed to open random source: %s\n", err)
		os.Exit(1)
	}
	seed := make([]byte, 8)
	i, err := f.Read(seed)
	if err != nil {
		fmt.Printf("Failed to read random source: %s\n", err)
		os.Exit(1)
	}
	if i != 8 {
		fmt.Printf("Was expecting to read 8 bytes of seeds, read %d bytes instead\n", i)
		os.Exit(1)
	}
	seed64 := int64(binary.LittleEndian.Uint64(seed))
	rand.Seed(seed64)
}

func main() {
	seedRand()

	net := NewNeuralNet(1, 1, 3, 10)
	net.printNet()

	w := net.getWeights()

	derp := make([]float64, 0)
	for i := 0; i < len(w); i++ {
		derp = append(derp, float64(i))
	}
	net.setWeights(derp)
	net.printNet()
	net.setWeights(w)
	net.printNet()
}
<|endoftext|>


<commit_before>package irg

import (
	"fmt"
	"bufio"
	"regexp"
	"net"
)

type Irc struct {
	Reader *bufio.Reader
	Writer *bufio.Writer
	R      chan string
	W      chan string
}

type Bot struct {
	Nick, Name string
	Events     chan string
	Conn       *Irc
}

func Connect(server string) *Bot {
	conn, err := net.Dial("tcp", server)
	if err != nil {
		panic(err)
	}

	reader := bufio.NewReader(conn)
	writer := bufio.NewWriter(conn)
	in := make(chan string, 1000)
	out := make(chan string, 1000)
	events := make(chan string, 1000)
	irc := &Irc{reader, writer, in, out}
	bot := &Bot{"GoBot", "GoBot", events, irc}

	go func() {
		for {
			ln, err := bot.Conn.Reader.ReadString(byte('\n'))
			if err != nil {
				fmt.Println(err)
			}
			bot.Conn.R <- ln
		}
	}()

	go func() {
		for {
			str := <-bot.Conn.W
			_, err := bot.Conn.Writer.WriteString(str + "\r\n")
			if err != nil {
				fmt.Println(err)
			}

			writer.Flush()
		}
	}()

	return bot
}

func (b *Bot) Join(ch string) {
	b.Conn.W <- "JOIN " + ch
}

func (b *Bot) Login(nick, name string) {
	b.setNickName(nick, name)
	b.Conn.W <- "NICK " + b.Nick
	b.Conn.W <- "USER " + b.Nick + " 0 * :" + b.Name
}

func (b *Bot) setNickName(nick, name string) {
	b.Nick = nick
	b.Name = name
}

func (b *Bot) RunLoop() {
	for {
		ln := <-b.Conn.R
		fmt.Print(ln)
		if regexp.MustCompile("^PING").Match([]byte(ln)) {
			b.Conn.W <- "PONG " + regexp.MustCompile(":.*").FindString(ln)
		}
	}
}
<commit_msg>move read and write handler into there own funcs<commit_after>package irg

import (
	"fmt"
	"bufio"
	"regexp"
	"net"
)

type Irc struct {
	Reader *bufio.Reader
	Writer *bufio.Writer
	R      chan string
	W      chan string
}

type Bot struct {
	Nick, Name string
	Events     chan string
	Conn       *Irc
}

func Connect(server string) *Bot {
	conn, err := net.Dial("tcp", server)
	if err != nil {
		panic(err)
	}

	reader := bufio.NewReader(conn)
	writer := bufio.NewWriter(conn)
	in := make(chan string, 1000)
	out := make(chan string, 1000)
	events := make(chan string, 1000)
	irc := &Irc{reader, writer, in, out}
	bot := &Bot{"GoBot", "GoBot", events, irc}

	go readHandler(bot)
	go writeHandler(bot)

	return bot
}

func writeHandler(bot *Bot) {
	for {
		str := <-bot.Conn.W
		_, err := bot.Conn.Writer.WriteString(str + "\r\n")
		if err != nil {
			fmt.Println(err)
		}

		writer.Flush()
	}
}

func readHandler(bot *Bot) {
	for {
		ln, err := bot.Conn.Reader.ReadString(byte('\n'))
		if err != nil {
			fmt.Println(err)
		}
		bot.Conn.R <- ln
	}
}

func (b *Bot) Join(ch string) {
	b.Conn.W <- "JOIN " + ch
}

func (b *Bot) Login(nick, name string) {
	b.setNickName(nick, name)
	b.Conn.W <- "NICK " + b.Nick
	b.Conn.W <- "USER " + b.Nick + " 0 * :" + b.Name
}

func (b *Bot) setNickName(nick, name string) {
	b.Nick = nick
	b.Name = name
}

func (b *Bot) RunLoop() {
	for {
		ln := <-b.Conn.R
		fmt.Print(ln)
		if regexp.MustCompile("^PING").Match([]byte(ln)) {
			b.Conn.W <- "PONG " + regexp.MustCompile(":.*").FindString(ln)
		}
	}
}
<|endoftext|>


<commit_before>package hdf5

// #include "hdf5.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"fmt"

	"reflect"
	"runtime"
	"unsafe"
)

type Dataset struct {
	Location
}

func newDataset(id C.hid_t) *Dataset {
	d := &Dataset{Location{Identifier{id}}}
	runtime.SetFinalizer(d, (*Dataset).finalizer)
	return d
}

func createDataset(id C.hid_t, name string, dtype *Datatype, dspace *Dataspace, dcpl *PropList) (*Dataset, error) {
	dtype, err := dtype.Copy() // For safety
	if err != nil {
		return nil, err
	}
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))
	hid := C.H5Dcreate2(id, c_name, dtype.id, dspace.id, P_DEFAULT.id, dcpl.id, P_DEFAULT.id)
	if err := checkID(hid); err != nil {
		return nil, err
	}
	return newDataset(hid), nil
}

func (s *Dataset) finalizer() {
	if err := s.Close(); err != nil {
		panic(fmt.Errorf("error closing dset: %s", err))
	}
}

// Close releases and terminates access to a dataset.
func (s *Dataset) Close() error {
	if s.id == 0 {
		return nil
	}
	err := h5err(C.H5Dclose(s.id))
	s.id = 0
	return err
}

// Space returns an identifier for a copy of the dataspace for a dataset.
func (s *Dataset) Space() *Dataspace {
	hid := C.H5Dget_space(s.id)
	if int(hid) > 0 {
		return newDataspace(hid)
	}
	return nil
}

// ReadSubset reads a subset of raw data from a dataset into a buffer.
func (s *Dataset) ReadSubset(data interface{}, memspace, filespace *Dataspace) error {
	dtype, err := s.Datatype()
	defer dtype.Close()
	if err != nil {
		return err
	}

	var addr unsafe.Pointer
	v := reflect.Indirect(reflect.ValueOf(data))

	switch v.Kind() {

	case reflect.Array:
		addr = unsafe.Pointer(v.UnsafeAddr())

	case reflect.Slice:
		slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(slice.Data)

	case reflect.String:
		str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		addr = unsafe.Pointer(v.Pointer())

	default:
		addr = unsafe.Pointer(v.UnsafeAddr())
	}

	var filespace_id, memspace_id C.hid_t = 0, 0
	if memspace != nil {
		memspace_id = memspace.id
	}
	if filespace != nil {
		filespace_id = filespace.id
	}
	rc := C.H5Dread(s.id, dtype.id, memspace_id, filespace_id, 0, addr)
	err = h5err(rc)
	return err
}

// Read reads raw data from a dataset into a buffer.
func (s *Dataset) Read(data interface{}) error {
	return s.ReadSubset(data, nil, nil)
}

// WriteSubset writes a subset of raw data from a buffer to a dataset.
func (s *Dataset) WriteSubset(data interface{}, memspace, filespace *Dataspace) error {
	dtype, err := s.Datatype()
	defer dtype.Close()
	if err != nil {
		return err
	}

	addr := unsafe.Pointer(nil)
	v := reflect.Indirect(reflect.ValueOf(data))

	switch v.Kind() {

	case reflect.Array:
		addr = unsafe.Pointer(v.UnsafeAddr())

	case reflect.Slice:
		slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(slice.Data)

	case reflect.String:
		str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		addr = unsafe.Pointer(v.Pointer())

	default:
		addr = unsafe.Pointer(v.UnsafeAddr())
	}

	var filespace_id, memspace_id C.hid_t = 0, 0
	if memspace != nil {
		memspace_id = memspace.id
	}
	if filespace != nil {
		filespace_id = filespace.id
	}
	rc := C.H5Dwrite(s.id, dtype.id, memspace_id, filespace_id, 0, addr)
	err = h5err(rc)
	return err
}

// Write writes raw data from a buffer to a dataset.
func (s *Dataset) Write(data interface{}) error {
	return s.WriteSubset(data, nil, nil)
}

// Creates a new attribute at this location.
func (s *Dataset) CreateAttribute(name string, dtype *Datatype, dspace *Dataspace) (*Attribute, error) {
	return createAttribute(s.id, name, dtype, dspace, P_DEFAULT)
}

// Creates a new attribute at this location.
func (s *Dataset) CreateAttributeWith(name string, dtype *Datatype, dspace *Dataspace, acpl *PropList) (*Attribute, error) {
	return createAttribute(s.id, name, dtype, dspace, acpl)
}

// Opens an existing attribute.
func (s *Dataset) OpenAttribute(name string) (*Attribute, error) {
	return openAttribute(s.id, name)
}

// Datatype returns the HDF5 Datatype of the Dataset
func (s *Dataset) Datatype() (*Datatype, error) {
	dtype_id := C.H5Dget_type(s.id)
	if dtype_id < 0 {
		return nil, fmt.Errorf("couldn't open Datatype from Dataset %q", s.Name())
	}
	return NewDatatype(dtype_id), nil
}
<commit_msg>avoid Go pointer to Go pointer using uintptr, to support Go1.6 or later<commit_after>package hdf5

/*
#include "hdf5.h"
#include <stdlib.h>
#include <string.h>

herr_t _H5Dwrite(
  hid_t dataset_id,
  hid_t mem_type_id,
  hid_t mem_space_id,
  hid_t file_space_id,
  hid_t xfer_plist_id,
  const uintptr_t buf
) {
  return H5Dwrite(dataset_id, mem_type_id, mem_space_id, file_space_id,
                  xfer_plist_id, (void*) buf);
}
*/
import "C"

import (
	"fmt"

	"reflect"
	"runtime"
	"unsafe"
)

type Dataset struct {
	Location
}

func newDataset(id C.hid_t) *Dataset {
	d := &Dataset{Location{Identifier{id}}}
	runtime.SetFinalizer(d, (*Dataset).finalizer)
	return d
}

func createDataset(id C.hid_t, name string, dtype *Datatype, dspace *Dataspace, dcpl *PropList) (*Dataset, error) {
	dtype, err := dtype.Copy() // For safety
	if err != nil {
		return nil, err
	}
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))
	hid := C.H5Dcreate2(id, c_name, dtype.id, dspace.id, P_DEFAULT.id, dcpl.id, P_DEFAULT.id)
	if err := checkID(hid); err != nil {
		return nil, err
	}
	return newDataset(hid), nil
}

func (s *Dataset) finalizer() {
	if err := s.Close(); err != nil {
		panic(fmt.Errorf("error closing dset: %s", err))
	}
}

// Close releases and terminates access to a dataset.
func (s *Dataset) Close() error {
	if s.id == 0 {
		return nil
	}
	err := h5err(C.H5Dclose(s.id))
	s.id = 0
	return err
}

// Space returns an identifier for a copy of the dataspace for a dataset.
func (s *Dataset) Space() *Dataspace {
	hid := C.H5Dget_space(s.id)
	if int(hid) > 0 {
		return newDataspace(hid)
	}
	return nil
}

// ReadSubset reads a subset of raw data from a dataset into a buffer.
func (s *Dataset) ReadSubset(data interface{}, memspace, filespace *Dataspace) error {
	dtype, err := s.Datatype()
	defer dtype.Close()
	if err != nil {
		return err
	}

	var addr unsafe.Pointer
	v := reflect.Indirect(reflect.ValueOf(data))

	switch v.Kind() {

	case reflect.Array:
		addr = unsafe.Pointer(v.UnsafeAddr())

	case reflect.Slice:
		slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(slice.Data)

	case reflect.String:
		str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		addr = unsafe.Pointer(v.Pointer())

	default:
		addr = unsafe.Pointer(v.UnsafeAddr())
	}

	var filespace_id, memspace_id C.hid_t = 0, 0
	if memspace != nil {
		memspace_id = memspace.id
	}
	if filespace != nil {
		filespace_id = filespace.id
	}
	rc := C.H5Dread(s.id, dtype.id, memspace_id, filespace_id, 0, addr)
	err = h5err(rc)
	return err
}

// Read reads raw data from a dataset into a buffer.
func (s *Dataset) Read(data interface{}) error {
	return s.ReadSubset(data, nil, nil)
}

// WriteSubset writes a subset of raw data from a buffer to a dataset.
func (s *Dataset) WriteSubset(data interface{}, memspace, filespace *Dataspace) error {
	dtype, err := s.Datatype()
	defer dtype.Close()
	if err != nil {
		return err
	}

	addr := unsafe.Pointer(nil)
	v := reflect.Indirect(reflect.ValueOf(data))

	switch v.Kind() {

	case reflect.Array:
		addr = unsafe.Pointer(v.UnsafeAddr())

	case reflect.Slice:
		slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(slice.Data)

	case reflect.String:
		str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr()))
		addr = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		addr = unsafe.Pointer(v.Pointer())

	default:
		addr = unsafe.Pointer(v.UnsafeAddr())
	}

	var filespace_id, memspace_id C.hid_t = 0, 0
	if memspace != nil {
		memspace_id = memspace.id
	}
	if filespace != nil {
		filespace_id = filespace.id
	}
	rc := C._H5Dwrite(s.id, dtype.id, memspace_id, filespace_id, 0,
		C.uintptr_t(uintptr(addr)))
	err = h5err(rc)
	return err
}

// Write writes raw data from a buffer to a dataset.
func (s *Dataset) Write(data interface{}) error {
	return s.WriteSubset(data, nil, nil)
}

// Creates a new attribute at this location.
func (s *Dataset) CreateAttribute(name string, dtype *Datatype, dspace *Dataspace) (*Attribute, error) {
	return createAttribute(s.id, name, dtype, dspace, P_DEFAULT)
}

// Creates a new attribute at this location.
func (s *Dataset) CreateAttributeWith(name string, dtype *Datatype, dspace *Dataspace, acpl *PropList) (*Attribute, error) {
	return createAttribute(s.id, name, dtype, dspace, acpl)
}

// Opens an existing attribute.
func (s *Dataset) OpenAttribute(name string) (*Attribute, error) {
	return openAttribute(s.id, name)
}

// Datatype returns the HDF5 Datatype of the Dataset
func (s *Dataset) Datatype() (*Datatype, error) {
	dtype_id := C.H5Dget_type(s.id)
	if dtype_id < 0 {
		return nil, fmt.Errorf("couldn't open Datatype from Dataset %q", s.Name())
	}
	return NewDatatype(dtype_id), nil
}
<|endoftext|>


<commit_before>package rogue

import (
	"time"

	"github.com/dgrijalva/jwt-go"
)

func newJwt() string {

	token := jwt.New(jwt.SigningMethodHS256)

	signingKey := []byte("top secret")

	//token.Claims["foo"] = "bar"
	token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()

	tokenString, err := token.SignedString(signingKey)
	if err != nil {
		panic(err)
	}

	return tokenString
}
<commit_msg>jwt: upgrade to jwt-go v3 and use gokpg.in path to avoid breakage<commit_after>package rogue

import (
	"time"

	jwt "gopkg.in/dgrijalva/jwt-go.v3"
)

func newJwt() string {

	signingKey := []byte("top secret")

	// Create the Claims
	claims := &jwt.StandardClaims{
		ExpiresAt: time.Now().Add(time.Hour * 72).Unix(),
		Issuer:    "test",
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	tokenString, err := token.SignedString(signingKey)
	if err != nil {
		panic(err)
	}

	return tokenString
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"unicode"
	"utf8"
)

type itemType int

const eof = 0

const (
	itemEOF        itemType = iota
	itemIdentifier
	itemNumber
	itemSemi
	itemOperator
	itemKindDecl
	itemKeyword
)

type kind struct {
	names []string
	def   string
}

type tok struct {
	val    string
	line   int
	off    int
	yyKind int
	kind   itemType
}

type stateFn func (*calcLex) stateFn

type calcLex struct {
	s     string           // the string being scanned
	pos   int              // current position in the input
	start int              // start of this token
	width int              // width of the last rune
	items chan tok         // channel of scanned items
	state stateFn
	semi  bool
}

func (l *calcLex) Lex(lval *calcSymType) int {
	for {
		select {
		case item := <-l.items:
			lval.tok = item
			return item.yyKind
		default:
			l.state = l.state(l)
		}
	}
	panic("unreachable")
}

func newCalcLex(input string) calcLexer {
	return &calcLex{
		s:     input,
		items: make(chan tok, 2),
		state: lexStatement,
	}
}

func (l *calcLex) Error(s string) {
	panic(s)
	fmt.Printf("syntax error: %s\n", s)
}

func (l *calcLex) next() int {
	if l.pos >= len(l.s) {
		return 0
	}
	rune, width := utf8.DecodeRuneInString(l.s[l.pos:])
	l.pos += width
	l.width = width
	return rune
}

func (l *calcLex) backup() {
	l.pos -= l.width
}

func (l *calcLex) peek() int {
	peek := l.next()
	l.backup()
	return peek
}

func (l *calcLex) ignore() {
	l.start = l.pos
}

func (l *calcLex) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

func (l *calcLex) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {}
	l.backup()
}

func (l *calcLex) emit(yyTy int, ty itemType) {
	t := tok{val:l.s[l.start:l.pos], yyKind: yyTy, kind: ty}
	log.Printf("t: %#v\n", t)
	l.items <- t
	l.ignore()

	switch {
	case ty == itemIdentifier || ty == itemNumber || ty == itemKindDecl:
		l.semi = true
	default:
		l.semi = false
	}
}

func (l *calcLex) errorf(format string, args ...interface{}) stateFn {
	fmt.Printf(format, args...)
	log.Printf("a")
	l.emit(eof, itemEOF)
	return nil
}

func lexStatement(l *calcLex) stateFn {
	switch r := l.next(); {
	case r == eof:
		log.Printf("b")
		l.emit(eof, itemEOF)
	case r == '/':
		if l.peek() == '/' {
			l.next()
			return lexComment
		}
		log.Printf("d")
		l.emit(r, itemOperator)
	case r == '`':
		return lexType
	case r == ';':
		l.emit(r, itemSemi)
	case unicode.IsSpace(r):
		if r == '\n' && l.semi {
			l.emit(';', itemSemi)
		}
//		log.Print("1 ignoring:", l.s[l.start:l.pos])
		l.ignore()
	case unicode.IsDigit(r):
		l.backup()
		return lexNumber
	case isIdentifierStart(r):
		l.backup()
		return lexIdentifier
	case isOperator(r):
		log.Printf("e")
		l.emit(r, itemOperator)
	default:
		return l.errorf("unrecognized char: %#U\n", r)
	}
	return lexStatement
}

func lexComment(l *calcLex) stateFn {
	// skip everything until the end of the line, or the end of
	// the file, whichever is first
	for r := l.next(); r != '\n' && r != eof; r = l.next() {}
	l.backup()
//	log.Print("2 ignoring:", l.s[l.start:l.pos])
	l.ignore()
	return lexStatement
}

func lexType(l *calcLex) stateFn {
	log.Print("3 ignoring:", l.s[l.start:l.pos])
	l.ignore()
	for r := l.next(); r != '`' && r != eof; r = l.next() {}
	l.backup()

	if l.peek() != '`' {
		return l.errorf("unexpected EOF")
	}
	l.emit(KIND_DECL, itemKindDecl)
	l.next()
	log.Print("4 ignoring:", l.s[l.start:l.pos])
	l.ignore()
	return lexStatement
}

func lexNumber(l *calcLex) stateFn {
	l.acceptRun("0123456789")
	log.Printf("g")
	l.emit(NUMBER, itemNumber)
	return lexStatement
}

func lexIdentifier(l *calcLex) stateFn {
	for isAlphaNumeric(l.next()) {}
	l.backup()
	switch id := l.s[l.start:l.pos]; {
	case id == "kind":
		l.emit(KIND, itemKeyword)
	default:
		l.emit(ID, itemIdentifier)
	}
	return lexStatement
}

func isOperator(rune int) bool {
	return bytes.IndexRune([]byte(",+-*/|&="), rune) > -1
}

func isIdentifierStart(r int) bool {
	return !(unicode.IsDigit(r) || unicode.IsSpace(r) || isOperator(r))
}

// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
func isAlphaNumeric(r int) bool {
	return !(unicode.IsSpace(r) || isOperator(r))
}
<commit_msg>disable debug logging & gofmt<commit_after>package main

import (
	"bytes"
	"fmt"
	"strings"
	"unicode"
	"utf8"
)

type itemType int

const eof = 0

const (
	itemEOF itemType = iota
	itemIdentifier
	itemNumber
	itemSemi
	itemOperator
	itemKindDecl
	itemKeyword
)

type kind struct {
	names []string
	def   string
}

type tok struct {
	val    string
	line   int
	off    int
	yyKind int
	kind   itemType
}

type stateFn func(*calcLex) stateFn

type calcLex struct {
	s     string   // the string being scanned
	pos   int      // current position in the input
	start int      // start of this token
	width int      // width of the last rune
	items chan tok // channel of scanned items
	state stateFn
	semi  bool
}

func (l *calcLex) Lex(lval *calcSymType) int {
	for {
		select {
		case item := <-l.items:
			lval.tok = item
			return item.yyKind
		default:
			l.state = l.state(l)
		}
	}
	panic("unreachable")
}

func newCalcLex(input string) calcLexer {
	return &calcLex{
		s:     input,
		items: make(chan tok, 2),
		state: lexStatement,
	}
}

func (l *calcLex) Error(s string) {
	panic(s)
	fmt.Printf("syntax error: %s\n", s)
}

func (l *calcLex) next() int {
	if l.pos >= len(l.s) {
		return 0
	}
	rune, width := utf8.DecodeRuneInString(l.s[l.pos:])
	l.pos += width
	l.width = width
	return rune
}

func (l *calcLex) backup() {
	l.pos -= l.width
}

func (l *calcLex) peek() int {
	peek := l.next()
	l.backup()
	return peek
}

func (l *calcLex) ignore() {
	l.start = l.pos
}

func (l *calcLex) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

func (l *calcLex) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

func (l *calcLex) emit(yyTy int, ty itemType) {
	t := tok{val: l.s[l.start:l.pos], yyKind: yyTy, kind: ty}
	//	log.Printf("t: %#v\n", t)
	l.items <- t
	l.ignore()

	switch {
	case ty == itemIdentifier || ty == itemNumber || ty == itemKindDecl:
		l.semi = true
	default:
		l.semi = false
	}
}

func (l *calcLex) errorf(format string, args ...interface{}) stateFn {
	fmt.Printf(format, args...)
	l.emit(eof, itemEOF)
	return nil
}

func lexStatement(l *calcLex) stateFn {
	switch r := l.next(); {
	case r == eof:
		l.emit(eof, itemEOF)
	case r == '/':
		if l.peek() == '/' {
			l.next()
			return lexComment
		}
		l.emit(r, itemOperator)
	case r == '`':
		return lexType
	case r == ';':
		l.emit(r, itemSemi)
	case unicode.IsSpace(r):
		if r == '\n' && l.semi {
			l.emit(';', itemSemi)
		}
		//		log.Print("1 ignoring:", l.s[l.start:l.pos])
		l.ignore()
	case unicode.IsDigit(r):
		l.backup()
		return lexNumber
	case isIdentifierStart(r):
		l.backup()
		return lexIdentifier
	case isOperator(r):
		l.emit(r, itemOperator)
	default:
		return l.errorf("unrecognized char: %#U\n", r)
	}
	return lexStatement
}

func lexComment(l *calcLex) stateFn {
	// skip everything until the end of the line, or the end of
	// the file, whichever is first
	for r := l.next(); r != '\n' && r != eof; r = l.next() {
	}
	l.backup()
	//	log.Print("2 ignoring:", l.s[l.start:l.pos])
	l.ignore()
	return lexStatement
}

func lexType(l *calcLex) stateFn {
	l.ignore()
	for r := l.next(); r != '`' && r != eof; r = l.next() {
	}
	l.backup()

	if l.peek() != '`' {
		return l.errorf("unexpected EOF")
	}
	l.emit(KIND_DECL, itemKindDecl)
	l.next()
	l.ignore()
	return lexStatement
}

func lexNumber(l *calcLex) stateFn {
	l.acceptRun("0123456789")
	l.emit(NUMBER, itemNumber)
	return lexStatement
}

func lexIdentifier(l *calcLex) stateFn {
	for isAlphaNumeric(l.next()) {
	}
	l.backup()
	switch id := l.s[l.start:l.pos]; {
	case id == "kind":
		l.emit(KIND, itemKeyword)
	default:
		l.emit(ID, itemIdentifier)
	}
	return lexStatement
}

func isOperator(rune int) bool {
	return bytes.IndexRune([]byte(",+-*/|&="), rune) > -1
}

func isIdentifierStart(r int) bool {
	return !(unicode.IsDigit(r) || unicode.IsSpace(r) || isOperator(r))
}

// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
func isAlphaNumeric(r int) bool {
	return !(unicode.IsSpace(r) || isOperator(r))
}
<|endoftext|>


<commit_before>package gohm

import (
	"bytes"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// ApacheCommonLogFormat (CLF) is the default log line format for Apache Web
// Server.  It is included here for users of this library that would like to
// easily specify log lines out to be emitted using the Apache Common Log Format
// (CLR), by setting `LogFormat` to `gohm.ApackeCommonLogFormat`.
const ApacheCommonLogFormat = "{client-ip} - - [{begin}] \"{method} {uri} {proto}\" {status} {bytes}"

const apacheTimeFormat = "02/Jan/2006:15:04:05 -0700"

// NOTE: Apache Common Log Format size excludes HTTP headers
// "%h %l %u %t \"%r\" %>s %b"
// "{remote-hostname} {remote-logname} {remote-user} {begin-time} \"{first-line-of-request}\" {status} {bytes}"
// "{remote-ip} - - {begin-time} \"{first-line-of-request}\" {status} {bytes}"

// DefaultLogFormat is the default log line format used by this library.
const DefaultLogFormat = "{client-ip} [{begin-iso8601}] \"{method} {uri} {proto}\" {status} {bytes} {duration} {error}"

// LogStatus1xx used to log HTTP requests which have a 1xx response
const LogStatus1xx uint32 = 1

// LogStatus2xx used to log HTTP requests which have a 2xx response
const LogStatus2xx uint32 = 2

// LogStatus3xx used to log HTTP requests which have a 3xx response
const LogStatus3xx uint32 = 4

// LogStatus4xx used to log HTTP requests which have a 4xx response
const LogStatus4xx uint32 = 8

// LogStatus5xx used to log HTTP requests which have a 5xx response
const LogStatus5xx uint32 = 16

// LogStatusAll used to log all HTTP requests
const LogStatusAll uint32 = 1 | 2 | 4 | 8 | 16

// LogStatusErrors used to log HTTP requests which have 4xx or 5xx response
const LogStatusErrors uint32 = 8 | 16

// compileFormat converts the format string into a slice of functions to invoke
// when creating a log line.  It's implemented as a state machine that
// alternates between 2 states: consuming runes to create a constant string to
// emit, and consuming runes to create a token that is intended to match one of
// the pre-defined format specifier tokens, or an undefined format specifier
// token that begins with "http-".
func compileFormat(format string) []func(*responseWriter, *http.Request, *[]byte) {
	// build slice of emitter functions, each will emit the requested
	// information
	var emitters []func(*responseWriter, *http.Request, *[]byte)

	// state machine alternating between two states: either capturing runes for
	// the next constant buffer, or capturing runes for the next token
	var buf, token bytes.Buffer
	var capturingToken bool  // false, because start off capturing buffer runes
	var nextRuneEscaped bool // true when next rune has been escaped

	for _, rune := range format {
		if nextRuneEscaped {
			// when this rune has been escaped, then just write it out to
			// whichever buffer we're collecting to right now
			if capturingToken {
				token.WriteRune(rune)
			} else {
				buf.WriteRune(rune)
			}
			nextRuneEscaped = false
			continue
		}
		if rune == '\\' {
			// Format specifies that next rune ought to be escaped.  Handy when
			// extra curly braces are desired in the log line format.
			nextRuneEscaped = true
			continue
		}
		if rune == '{' {
			// Stop capturing buf, and begin capturing token.
			// NOTE: undefined behavior if open curly brace when previous open
			// curly brace has not yet been closed.
			emitters = append(emitters, makeStringEmitter(buf.String()))
			buf.Reset()
			capturingToken = true
		} else if rune == '}' {
			// Stop capturing token, and begin capturing buffer.
			// NOTE: undefined behavior if close curly brace when not capturing
			// runes for a token.
			switch tok := token.String(); tok {
			case "begin":
				emitters = append(emitters, beginEmitter)
			case "begin-epoch":
				emitters = append(emitters, beginEpochEmitter)
			case "begin-iso8601":
				emitters = append(emitters, beginISO8601Emitter)
			case "bytes":
				emitters = append(emitters, bytesEmitter)
			case "client":
				emitters = append(emitters, clientEmitter)
			case "client-ip":
				emitters = append(emitters, clientIPEmitter)
			case "client-port":
				emitters = append(emitters, clientPortEmitter)
			case "duration":
				emitters = append(emitters, durationEmitter)
			case "end":
				emitters = append(emitters, endEmitter)
			case "end-epoch":
				emitters = append(emitters, endEpochEmitter)
			case "end-iso8601":
				emitters = append(emitters, endISO8601Emitter)
			case "error":
				emitters = append(emitters, errorMessageEmitter)
			case "method":
				emitters = append(emitters, methodEmitter)
			case "proto":
				emitters = append(emitters, protoEmitter)
			case "status":
				emitters = append(emitters, statusEmitter)
			case "status-text":
				emitters = append(emitters, statusTextEmitter)
			case "uri":
				emitters = append(emitters, uriEmitter)
			default:
				if strings.HasPrefix(tok, "http-") {
					// emit value of specified HTTP request header
					emitters = append(emitters, makeHeaderEmitter(tok[5:]))
				} else {
					// unknown token, so just append to buf
					buf.WriteRune('{')
					buf.WriteString(tok)
					buf.WriteRune(rune)
				}
			}
			token.Reset()
			capturingToken = false
		} else {
			// emit to either token or buffer
			if capturingToken {
				token.WriteRune(rune)
			} else {
				buf.WriteRune(rune)
			}
		}
	}
	if capturingToken {
		buf.WriteRune('{')
		buf.Write(token.Bytes())
	}
	buf.WriteRune('\n')
	emitters = append(emitters, makeStringEmitter(buf.String()))

	return emitters
}

func makeStringEmitter(value string) func(*responseWriter, *http.Request, *[]byte) {
	return func(_ *responseWriter, _ *http.Request, bb *[]byte) {
		*bb = append(*bb, value...)
	}
}

func beginEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.begin.UTC().Format(apacheTimeFormat)...)
}

func beginEpochEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(lrw.begin.UTC().Unix(), 10)...)
}

func beginISO8601Emitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.begin.UTC().Format(time.RFC3339)...)
}

func bytesEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(lrw.size, 10)...)
}

func clientEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.RemoteAddr...)
}

func clientIPEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	value := r.RemoteAddr // ip:port
	if colon := strings.LastIndex(value, ":"); colon != -1 {
		value = value[:colon]
	}
	*bb = append(*bb, value...)
}

func clientPortEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	value := r.RemoteAddr // ip:port
	if colon := strings.LastIndex(value, ":"); colon != -1 {
		value = value[colon+1:]
	}
	*bb = append(*bb, value...)
}

func durationEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	// 6 decimal places: microsecond precision
	*bb = append(*bb, strconv.FormatFloat(lrw.end.Sub(lrw.begin).Seconds(), 'f', 6, 64)...)
}

func endEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.end.UTC().Format(apacheTimeFormat)...)
}

func endEpochEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(lrw.end.UTC().Unix(), 10)...)
}

func endISO8601Emitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.end.UTC().Format(time.RFC3339)...)
}

func errorMessageEmitter(rw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, rw.errorMessage...)
}

func methodEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.Method...)
}

func protoEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.Proto...)
}

func statusEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(int64(lrw.status), 10)...)
}

func statusTextEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, http.StatusText(lrw.status)...)
}

func uriEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.RequestURI...)
}

func makeHeaderEmitter(headerName string) func(*responseWriter, *http.Request, *[]byte) {
	return func(_ *responseWriter, r *http.Request, bb *[]byte) {
		value := r.Header.Get(headerName)
		if value == "" {
			value = "-"
		}
		*bb = append(*bb, value...)
	}
}
<commit_msg>log format compiler uses byte slices rather than bytes.Buffer instances<commit_after>package gohm

import (
	"net/http"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

// ApacheCommonLogFormat (CLF) is the default log line format for Apache Web
// Server.  It is included here for users of this library that would like to
// easily specify log lines out to be emitted using the Apache Common Log Format
// (CLR), by setting `LogFormat` to `gohm.ApackeCommonLogFormat`.
const ApacheCommonLogFormat = "{client-ip} - - [{begin}] \"{method} {uri} {proto}\" {status} {bytes}"

const apacheTimeFormat = "02/Jan/2006:15:04:05 -0700"

// NOTE: Apache Common Log Format size excludes HTTP headers
// "%h %l %u %t \"%r\" %>s %b"
// "{remote-hostname} {remote-logname} {remote-user} {begin-time} \"{first-line-of-request}\" {status} {bytes}"
// "{remote-ip} - - {begin-time} \"{first-line-of-request}\" {status} {bytes}"

// DefaultLogFormat is the default log line format used by this library.
const DefaultLogFormat = "{client-ip} [{begin-iso8601}] \"{method} {uri} {proto}\" {status} {bytes} {duration} {error}"

// LogStatus1xx used to log HTTP requests which have a 1xx response
const LogStatus1xx uint32 = 1

// LogStatus2xx used to log HTTP requests which have a 2xx response
const LogStatus2xx uint32 = 2

// LogStatus3xx used to log HTTP requests which have a 3xx response
const LogStatus3xx uint32 = 4

// LogStatus4xx used to log HTTP requests which have a 4xx response
const LogStatus4xx uint32 = 8

// LogStatus5xx used to log HTTP requests which have a 5xx response
const LogStatus5xx uint32 = 16

// LogStatusAll used to log all HTTP requests
const LogStatusAll uint32 = 1 | 2 | 4 | 8 | 16

// LogStatusErrors used to log HTTP requests which have 4xx or 5xx response
const LogStatusErrors uint32 = 8 | 16

// compileFormat converts the format string into a slice of functions to invoke
// when creating a log line.  It's implemented as a state machine that
// alternates between 2 states: consuming runes to create a constant string to
// emit, and consuming runes to create a token that is intended to match one of
// the pre-defined format specifier tokens, or an undefined format specifier
// token that begins with "http-".
func compileFormat(format string) []func(*responseWriter, *http.Request, *[]byte) {
	// build slice of emitter functions, each will emit the requested
	// information
	var emitters []func(*responseWriter, *http.Request, *[]byte)

	// state machine alternating between two states: either capturing runes for
	// the next constant buffer, or capturing runes for the next token
	var buf, token []byte
	var capturingToken bool  // false, because start off capturing buffer runes
	var nextRuneEscaped bool // true when next rune has been escaped

	for _, rune := range format {
		if nextRuneEscaped {
			// when this rune has been escaped, then just write it out to
			// whichever buffer we're collecting to right now
			if capturingToken {
				appendRune(&token, rune)
			} else {
				appendRune(&buf, rune)
			}
			nextRuneEscaped = false
			continue
		}
		if rune == '\\' {
			// Format specifies that next rune ought to be escaped.  Handy when
			// extra curly braces are desired in the log line format.
			nextRuneEscaped = true
			continue
		}
		if rune == '{' {
			// Stop capturing buf, and begin capturing token.
			// NOTE: undefined behavior if open curly brace when previous open
			// curly brace has not yet been closed.
			emitters = append(emitters, makeStringEmitter(string(buf)))
			buf = buf[:0]
			capturingToken = true
		} else if rune == '}' {
			// Stop capturing token, and begin capturing buffer.
			// NOTE: undefined behavior if close curly brace when not capturing
			// runes for a token.
			switch tok := string(token); tok {
			case "begin":
				emitters = append(emitters, beginEmitter)
			case "begin-epoch":
				emitters = append(emitters, beginEpochEmitter)
			case "begin-iso8601":
				emitters = append(emitters, beginISO8601Emitter)
			case "bytes":
				emitters = append(emitters, bytesEmitter)
			case "client":
				emitters = append(emitters, clientEmitter)
			case "client-ip":
				emitters = append(emitters, clientIPEmitter)
			case "client-port":
				emitters = append(emitters, clientPortEmitter)
			case "duration":
				emitters = append(emitters, durationEmitter)
			case "end":
				emitters = append(emitters, endEmitter)
			case "end-epoch":
				emitters = append(emitters, endEpochEmitter)
			case "end-iso8601":
				emitters = append(emitters, endISO8601Emitter)
			case "error":
				emitters = append(emitters, errorMessageEmitter)
			case "method":
				emitters = append(emitters, methodEmitter)
			case "proto":
				emitters = append(emitters, protoEmitter)
			case "status":
				emitters = append(emitters, statusEmitter)
			case "status-text":
				emitters = append(emitters, statusTextEmitter)
			case "uri":
				emitters = append(emitters, uriEmitter)
			default:
				if strings.HasPrefix(tok, "http-") {
					// emit value of specified HTTP request header
					emitters = append(emitters, makeHeaderEmitter(tok[5:]))
				} else {
					// unknown token: just append to buf, wrapped in curly
					// braces
					buf = append(buf, '{')
					buf = append(buf, tok...)
					buf = append(buf, '}')
				}
			}
			token = token[:0]
			capturingToken = false
		} else {
			// append to either token or buffer
			if capturingToken {
				appendRune(&token, rune)
			} else {
				appendRune(&buf, rune)
			}
		}
	}
	if capturingToken {
		buf = append(buf, '{') // token started with left curly brace, so it needs to precede the token
		buf = append(buf, token...)
	}
	buf = append(buf, '\n') // each log line terminated by newline byte
	emitters = append(emitters, makeStringEmitter(string(buf)))

	return emitters
}

func makeStringEmitter(value string) func(*responseWriter, *http.Request, *[]byte) {
	return func(_ *responseWriter, _ *http.Request, bb *[]byte) {
		*bb = append(*bb, value...)
	}
}

func beginEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.begin.UTC().Format(apacheTimeFormat)...)
}

func beginEpochEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(lrw.begin.UTC().Unix(), 10)...)
}

func beginISO8601Emitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.begin.UTC().Format(time.RFC3339)...)
}

func bytesEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(lrw.size, 10)...)
}

func clientEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.RemoteAddr...)
}

func clientIPEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	value := r.RemoteAddr // ip:port
	if colon := strings.LastIndex(value, ":"); colon != -1 {
		value = value[:colon]
	}
	*bb = append(*bb, value...)
}

func clientPortEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	value := r.RemoteAddr // ip:port
	if colon := strings.LastIndex(value, ":"); colon != -1 {
		value = value[colon+1:]
	}
	*bb = append(*bb, value...)
}

func durationEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	// 6 decimal places: microsecond precision
	*bb = append(*bb, strconv.FormatFloat(lrw.end.Sub(lrw.begin).Seconds(), 'f', 6, 64)...)
}

func endEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.end.UTC().Format(apacheTimeFormat)...)
}

func endEpochEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(lrw.end.UTC().Unix(), 10)...)
}

func endISO8601Emitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, lrw.end.UTC().Format(time.RFC3339)...)
}

func errorMessageEmitter(rw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, rw.errorMessage...)
}

func methodEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.Method...)
}

func protoEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.Proto...)
}

func statusEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, strconv.FormatInt(int64(lrw.status), 10)...)
}

func statusTextEmitter(lrw *responseWriter, _ *http.Request, bb *[]byte) {
	*bb = append(*bb, http.StatusText(lrw.status)...)
}

func uriEmitter(_ *responseWriter, r *http.Request, bb *[]byte) {
	*bb = append(*bb, r.RequestURI...)
}

func makeHeaderEmitter(headerName string) func(*responseWriter, *http.Request, *[]byte) {
	return func(_ *responseWriter, r *http.Request, bb *[]byte) {
		value := r.Header.Get(headerName)
		if value == "" {
			value = "-"
		}
		*bb = append(*bb, value...)
	}
}

func appendRune(buf *[]byte, r rune) {
	if r < utf8.RuneSelf {
		*buf = append(*buf, byte(r))
		return
	}
	olen := len(*buf)
	*buf = append(*buf, 0, 0, 0, 0)              // grow buf large enough to accommodate largest possible UTF8 sequence
	n := utf8.EncodeRune((*buf)[olen:olen+4], r) // encode rune into newly allocated buf space
	*buf = (*buf)[:olen+n]                       // trim buf to actual size used by rune addition
}
<|endoftext|>


<commit_before>package jet

import (
	"container/list"
	"crypto/sha1"
	"database/sql"
	"sync"
)

type lru struct {
	m        sync.Mutex
	maxItems int
	keys     map[string]*list.Element
	list     *list.List
}

type lruItem struct {
	key  string
	stmt *sql.Stmt
}

func newLru() *lru {
	return &lru{
		maxItems: 500,
		keys:     make(map[string]*list.Element),
		list:     list.New(),
	}
}

func (c *lru) get(k string) (*sql.Stmt, bool) {
	c.m.Lock()
	defer c.m.Unlock()
	k = makeKey(k)
	e, ok := c.keys[k]
	if ok {
		return e.Value.(*lruItem).stmt, ok
	}
	return nil, false
}

func (c *lru) put(k string, stmt *sql.Stmt) {
	c.m.Lock()
	defer c.m.Unlock()
	k = makeKey(k)
	e, ok := c.keys[k]
	if ok {
		c.list.MoveToFront(e)
	} else {
		c.keys[k] = c.list.PushFront(&lruItem{
			key:  k,
			stmt: stmt,
		})
	}
	c.clean()
}

func (c *lru) del(k string) {
	c.m.Lock()
	defer c.m.Unlock()
	k = makeKey(k)
	e, ok := c.keys[k]
	if ok {
		c.delElem(e)
	}
}

func (c *lru) delElem(e *list.Element) {
	item := c.list.Remove(e).(*lruItem)
	defer item.stmt.Close()
	delete(c.keys, item.key)
}

func (c *lru) clean() {
	n := c.list.Len() - c.maxItems
	if n > 0 {
		for i := 0; i < n; i++ {
			c.delElem(c.list.Back())
		}
	}
}

// makeKey hashes the key to save some bytes
func makeKey(k string) string {
	b := sha1.Sum([]byte(k))
	return string(b[:])
}
<commit_msg>Fixed invalid hashing code.<commit_after>package jet

import (
	"container/list"
	"crypto/sha1"
	"database/sql"
	"sync"
)

type lru struct {
	m        sync.Mutex
	maxItems int
	keys     map[string]*list.Element
	list     *list.List
}

type lruItem struct {
	key  string
	stmt *sql.Stmt
}

func newLru() *lru {
	return &lru{
		maxItems: 500,
		keys:     make(map[string]*list.Element),
		list:     list.New(),
	}
}

func (c *lru) get(k string) (*sql.Stmt, bool) {
	c.m.Lock()
	defer c.m.Unlock()
	k = makeKey(k)
	e, ok := c.keys[k]
	if ok {
		return e.Value.(*lruItem).stmt, ok
	}
	return nil, false
}

func (c *lru) put(k string, stmt *sql.Stmt) {
	c.m.Lock()
	defer c.m.Unlock()
	k = makeKey(k)
	e, ok := c.keys[k]
	if ok {
		c.list.MoveToFront(e)
	} else {
		c.keys[k] = c.list.PushFront(&lruItem{
			key:  k,
			stmt: stmt,
		})
	}
	c.clean()
}

func (c *lru) del(k string) {
	c.m.Lock()
	defer c.m.Unlock()
	k = makeKey(k)
	e, ok := c.keys[k]
	if ok {
		c.delElem(e)
	}
}

func (c *lru) delElem(e *list.Element) {
	item := c.list.Remove(e).(*lruItem)
	defer item.stmt.Close()
	delete(c.keys, item.key)
}

func (c *lru) clean() {
	n := c.list.Len() - c.maxItems
	if n > 0 {
		for i := 0; i < n; i++ {
			c.delElem(c.list.Back())
		}
	}
}

// makeKey hashes the key to save some bytes
func makeKey(k string) string {
	buffer := sha1.New()
	buffer.Write([]byte(k))
	return string(buffer.Sum(nil))
}
<|endoftext|>


<commit_before>package lru

import (
	"container/list"
	"encoding/base64"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"os"
	"sync"
	"time"
)

type object struct {
	key        string
	size       uint64
	accessTime time.Time
}

type Cache struct {
	sync.Mutex

	list  *list.List
	table map[string]*list.Element
	size  uint64

	maxSize uint64
	path    string
}

func hashCacheKey(data string) string {
	hash := fnv.New64a()
	hash.Write([]byte(data))

	return base64.URLEncoding.EncodeToString(hash.Sum(nil))
}

func New(maxSize uint64, path string, clearCacheOnBoot bool) *Cache {
	fmt.Printf("lru: new cache of size %d\n", maxSize)

	cache := Cache{
		list:    list.New(),
		table:   make(map[string]*list.Element),
		maxSize: maxSize,
		path:    path,
	}

	if clearCacheOnBoot {
		fmt.Println("lru: clear cache on boot")
		os.RemoveAll(cache.path)
		os.MkdirAll(cache.path, 0755)
	}

	return &cache
}

func (cache *Cache) FilePath(key string) string {
	return cache.path + "/" + hashCacheKey(key)
}

func (cache *Cache) Get(key string) ([]byte, bool) {
	cache.Lock()
	defer cache.Unlock()

	element := cache.table[key]

	if element == nil {
		return nil, false
	}

	cache.moveToFront(element)

	value, err := ioutil.ReadFile(cache.FilePath(element.Value.(*object).key))

	if err != nil {
		return nil, false
	}

	return value, true
}

func (cache *Cache) Set(key string, value []byte) {
	cache.Lock()
	defer cache.Unlock()

	if element := cache.table[key]; element != nil {
		cache.moveToFront(element)
	} else {
		cache.addNew(key, value)
	}
}

func (cache *Cache) Delete(key string) bool {
	cache.Lock()
	defer cache.Unlock()

	element := cache.table[key]

	if element == nil {
		return false
	}

	err := os.Remove(cache.FilePath(key))

	if err != nil {
		return false
	}

	cache.list.Remove(element)
	delete(cache.table, key)

	cache.size -= element.Value.(*object).size

	return true
}

func (cache *Cache) Clear() {
	cache.Lock()
	defer cache.Unlock()

	cache.clearFiles()
	cache.list.Init()
	cache.table = make(map[string]*list.Element)
	cache.size = 0
}

func (cache *Cache) Size() uint64 {
	cache.Lock()
	defer cache.Unlock()

	return cache.size
}

func (cache *Cache) MaxSize() uint64 {
	cache.Lock()
	defer cache.Unlock()

	return cache.maxSize
}

func (cache *Cache) Oldest() (oldest time.Time) {
	cache.Lock()
	defer cache.Unlock()

	if lastElem := cache.list.Back(); lastElem != nil {
		oldest = lastElem.Value.(*object).accessTime
	}

	return
}

func (cache *Cache) keys() []string {
	keys := make([]string, 0, cache.list.Len())

	for element := cache.list.Front(); element != nil; element = element.Next() {
		keys = append(keys, element.Value.(*object).key)
	}

	return keys
}

func (cache *Cache) moveToFront(element *list.Element) {
	cache.list.MoveToFront(element)
	element.Value.(*object).accessTime = time.Now()
}

func (cache *Cache) addNew(key string, value []byte) {
	size := uint64(len(value))

	fmt.Printf("lru: new object of size %d\n", size)

	if size > cache.maxSize {
		fmt.Println("lru: file is too large")
		return
	}

	newObject := &object{key, size, time.Now()}

	cache.trim(cache.size + newObject.size)

	if _, err := os.Stat(cache.FilePath(key)); os.IsNotExist(err) {
		err := ioutil.WriteFile(cache.FilePath(key), value, 0644)

		if err != nil {
			fmt.Println("lru: " + err.Error())
			return
		}

		element := cache.list.PushFront(newObject)
		cache.table[key] = element
		cache.size += (*newObject).size
		fmt.Printf("lru: added %d, new size is %d\n", (*newObject).size, cache.size)
	} else {
		fmt.Println("lru: file already exist")
	}
}

func (cache *Cache) trim(futureSize uint64) {
	for futureSize > cache.maxSize {
		element := cache.list.Back()

		if element == nil {
			fmt.Println("lru: file is too large")
			return
		}

		value := cache.list.Remove(element).(*object)

		fmt.Printf("lru: deleting %s\n", cache.FilePath(value.key))

		if err := os.RemoveAll(cache.FilePath(value.key)); err != nil {
			fmt.Printf("lru: couldn't delete %s\n", cache.FilePath(value.key))
		}

		delete(cache.table, value.key)

		cache.size -= value.size
		futureSize -= value.size
	}
}

func (cache *Cache) clearFiles() {
	for _, key := range cache.keys() {
		os.RemoveAll(cache.FilePath(key))
	}
}
<commit_msg>Add debug logging option<commit_after>package lru

import (
	"container/list"
	"encoding/base64"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"os"
	"sync"
	"time"
)

type object struct {
	key        string
	size       uint64
	accessTime time.Time
}

type Cache struct {
	sync.Mutex

	list  *list.List
	table map[string]*list.Element
	size  uint64

	maxSize uint64
	path    string

	ShowDebug bool
}

func hashCacheKey(data string) string {
	hash := fnv.New64a()
	hash.Write([]byte(data))

	return base64.URLEncoding.EncodeToString(hash.Sum(nil))
}

func New(maxSize uint64, path string, clearCacheOnBoot bool) *Cache {
	cache := Cache{
		list:    list.New(),
		table:   make(map[string]*list.Element),
		maxSize: maxSize,
		path:    path,
	}

	cache.Debug(fmt.Sprintf("new cache of size %d", maxSize))

	if clearCacheOnBoot {
		cache.Debug("clearing cache on boot")
		os.RemoveAll(cache.path)
		os.MkdirAll(cache.path, 0755)
	}

	return &cache
}

func (cache *Cache) Debug(msg string) {
	if cache.ShowDebug {
		fmt.Println("[lru]", msg)
	}
}

func (cache *Cache) FilePath(key string) string {
	return cache.path + "/" + hashCacheKey(key)
}

func (cache *Cache) Get(key string) ([]byte, bool) {
	cache.Lock()
	defer cache.Unlock()

	element := cache.table[key]

	if element == nil {
		return nil, false
	}

	cache.moveToFront(element)

	value, err := ioutil.ReadFile(cache.FilePath(element.Value.(*object).key))

	if err != nil {
		return nil, false
	}

	return value, true
}

func (cache *Cache) Set(key string, value []byte) {
	cache.Lock()
	defer cache.Unlock()

	if element := cache.table[key]; element != nil {
		cache.moveToFront(element)
	} else {
		cache.addNew(key, value)
	}
}

func (cache *Cache) Delete(key string) bool {
	cache.Lock()
	defer cache.Unlock()

	element := cache.table[key]

	if element == nil {
		return false
	}

	err := os.Remove(cache.FilePath(key))

	if err != nil {
		return false
	}

	cache.list.Remove(element)
	delete(cache.table, key)

	cache.size -= element.Value.(*object).size

	return true
}

func (cache *Cache) Clear() {
	cache.Lock()
	defer cache.Unlock()

	cache.clearFiles()
	cache.list.Init()
	cache.table = make(map[string]*list.Element)
	cache.size = 0
}

func (cache *Cache) Size() uint64 {
	cache.Lock()
	defer cache.Unlock()

	return cache.size
}

func (cache *Cache) MaxSize() uint64 {
	cache.Lock()
	defer cache.Unlock()

	return cache.maxSize
}

func (cache *Cache) Oldest() (oldest time.Time) {
	cache.Lock()
	defer cache.Unlock()

	if lastElem := cache.list.Back(); lastElem != nil {
		oldest = lastElem.Value.(*object).accessTime
	}

	return
}

func (cache *Cache) keys() []string {
	keys := make([]string, 0, cache.list.Len())

	for element := cache.list.Front(); element != nil; element = element.Next() {
		keys = append(keys, element.Value.(*object).key)
	}

	return keys
}

func (cache *Cache) moveToFront(element *list.Element) {
	cache.list.MoveToFront(element)
	element.Value.(*object).accessTime = time.Now()
}

func (cache *Cache) addNew(key string, value []byte) {
	size := uint64(len(value))

	cache.Debug(fmt.Sprintf("new object of size %d", size))

	if size > cache.maxSize {
		cache.Debug("file is too large")
		return
	}

	newObject := &object{key, size, time.Now()}

	cache.trim(cache.size + newObject.size)

	if _, err := os.Stat(cache.FilePath(key)); os.IsNotExist(err) {
		err := ioutil.WriteFile(cache.FilePath(key), value, 0644)

		if err != nil {
			cache.Debug(err.Error())
			return
		}

		element := cache.list.PushFront(newObject)
		cache.table[key] = element
		cache.size += (*newObject).size
		cache.Debug(fmt.Sprintf("added %d, new size is %d", (*newObject).size, cache.size))
	} else {
		cache.Debug("file already exist")
	}
}

func (cache *Cache) trim(futureSize uint64) {
	for futureSize > cache.maxSize {
		element := cache.list.Back()

		if element == nil {
			cache.Debug("file is too large")
			return
		}

		value := cache.list.Remove(element).(*object)

		cache.Debug(fmt.Sprintf("deleting %s", cache.FilePath(value.key)))

		if err := os.RemoveAll(cache.FilePath(value.key)); err != nil {
			cache.Debug(fmt.Sprintf("couldn't delete %s", cache.FilePath(value.key)))
		}

		delete(cache.table, value.key)

		cache.size -= value.size
		futureSize -= value.size
	}
}

func (cache *Cache) clearFiles() {
	for _, key := range cache.keys() {
		os.RemoveAll(cache.FilePath(key))
	}
}
<|endoftext|>


<commit_before>package jwt

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"strings"
)

const (
	typ = "JWT"
	alg = "HS256"
)

var (
	ErrMissingConfig      = errors.New("missing configuration")
	ErrMissingSecret      = errors.New("please provide a shared secret")
	ErrMissingAuthFunc    = errors.New("please provide an auth function")
	ErrMissingClaimsFunc  = errors.New("please provide a claims function")
	ErrEncoding           = errors.New("error encoding value")
	ErrDecoding           = errors.New("error decoding value")
	ErrMissingToken       = errors.New("please provide a token")
	ErrMalformedToken     = errors.New("please provide a valid token")
	ErrInvalidSignature   = errors.New("signature could not be verified")
	ErrParsingCredentials = errors.New("error parsing credentials")
)

type Config struct {
	Secret string
	Auth   AuthFunc
	Claims ClaimsFunc
}

type AuthFunc func(string, string) error

type ClaimsFunc func(string) (map[string]interface{}, error)

type VerifyClaimsFunc func([]byte) error

type JWTMiddleware struct {
	secret string
	auth   AuthFunc
	claims ClaimsFunc
}

func NewMiddleware(c *Config) (*JWTMiddleware, error) {
	if c == nil {
		return nil, ErrMissingConfig
	}
	if c.Secret == "" {
		return nil, ErrMissingSecret
	}
	if c.Auth == nil {
		return nil, ErrMissingAuthFunc
	}
	if c.Claims == nil {
		return nil, ErrMissingClaimsFunc
	}
	m := &JWTMiddleware{
		secret: c.Secret,
		auth:   c.Auth,
		claims: c.Claims,
	}
	return m, nil
}

func (m *JWTMiddleware) Secure(h http.Handler, v VerifyClaimsFunc) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		authHeader := r.Header.Get("Authorization")
		if authHeader == "" {
			http.Error(w, ErrMissingToken.Error(), http.StatusUnauthorized)
			return
		}
		token := strings.Split(authHeader, " ")[1]
		tokenParts := strings.Split(token, ".")
		if len(tokenParts) != 3 {
			http.Error(w, ErrMalformedToken.Error(), http.StatusUnauthorized)
			return
		}

		// First, verify JOSE header
		var t struct {
			Typ string
			Alg string
		}
		header, err := decode(tokenParts[0])
		if err != nil {
			log.Printf("error (%v) while decoding header (%v)", err, tokenParts[0])
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		err = json.Unmarshal(header, &t)
		if err != nil {
			log.Printf("error (%v) while unmarshalling header (%s)", err, header)
			http.Error(w, ErrMalformedToken.Error(), http.StatusInternalServerError)
			return
		}

		// Then, verify signature
		mac := hmac.New(sha256.New, []byte(m.secret))
		message := []byte(strings.Join([]string{tokenParts[0], tokenParts[1]}, "."))
		mac.Write(message)
		expectedMac, err := encode(mac.Sum(nil))
		if err != nil {
			panic(err)
			return
		}
		if !hmac.Equal([]byte(tokenParts[2]), []byte(expectedMac)) {
			log.Printf("invalid signature: %v", tokenParts[2])
			http.Error(w, ErrInvalidSignature.Error(), http.StatusUnauthorized)
			return
		}

		// Finally, check claims
		claimSet, err := decode(tokenParts[1])
		if err != nil {
			log.Printf("error (%v) while decoding claims", err)
			http.Error(w, ErrDecoding.Error(), http.StatusInternalServerError)
			return
		}
		err = v(claimSet)
		if err != nil {
			log.Printf("claims handler error: %v", err)
			http.Error(w, err.Error(), http.StatusUnauthorized)
			return
		}

		// If we make it this far, process the downstream handler
		h.ServeHTTP(w, r)
	})
}

func (m *JWTMiddleware) GenerateToken(w http.ResponseWriter, r *http.Request) {
	var b map[string]string
	err := json.NewDecoder(r.Body).Decode(&b)
	if err != nil {
		log.Printf("error (%v) while parsing authorization", err)
		http.Error(w, ErrParsingCredentials.Error(), http.StatusInternalServerError)
		return
	}
	err = m.auth(b["email"], b["password"])
	if err != nil {
		log.Printf("error (%v) while performing authorization", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// For now, the header will be static
	header, err := encode(fmt.Sprintf(`{"typ":%q,"alg":%q}`, typ, alg))
	if err != nil {
		log.Printf("error (%v) while encoding header", err)
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	// Generate claims for user
	claims, err := m.claims(b["email"])
	if err != nil {
		log.Printf("error (%v) while generating claims", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	claimsJson, err := json.Marshal(claims)
	if err != nil {
		log.Printf("error (%v) while marshalling claims")
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	claimsSet, err := encode(claimsJson)
	if err != nil {
		log.Printf("error (%v) while encoding claims")
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	toSig := strings.Join([]string{header, claimsSet}, ".")

	h := hmac.New(sha256.New, []byte(m.secret))
	h.Write([]byte(toSig))
	sig, err := encode(h.Sum(nil))
	if err != nil {
		log.Printf("error (%v) while encoding signature")
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	response := strings.Join([]string{toSig, sig}, ".")
	w.Write([]byte(response))
}

func encode(s interface{}) (string, error) {
	var r []byte
	switch v := s.(type) {
	case string:
		r = []byte(v)
	case []byte:
		r = v
	default:
		return "", ErrEncoding
	}
	return base64.StdEncoding.EncodeToString(r), nil
}

func decode(s string) ([]byte, error) {
	return base64.StdEncoding.DecodeString(s)
}
<commit_msg>Added private error handler middleware<commit_after>package jwt

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"strings"
)

const (
	typ = "JWT"
	alg = "HS256"
)

var (
	ErrMissingConfig      = errors.New("missing configuration")
	ErrMissingSecret      = errors.New("please provide a shared secret")
	ErrMissingAuthFunc    = errors.New("please provide an auth function")
	ErrMissingClaimsFunc  = errors.New("please provide a claims function")
	ErrEncoding           = errors.New("error encoding value")
	ErrDecoding           = errors.New("error decoding value")
	ErrMissingToken       = errors.New("please provide a token")
	ErrMalformedToken     = errors.New("please provide a valid token")
	ErrInvalidSignature   = errors.New("signature could not be verified")
	ErrParsingCredentials = errors.New("error parsing credentials")
)

type Config struct {
	Secret string
	Auth   AuthFunc
	Claims ClaimsFunc
}

type AuthFunc func(string, string) error

type ClaimsFunc func(string) (map[string]interface{}, error)

type VerifyClaimsFunc func([]byte) error

type JWTMiddleware struct {
	secret string
	auth   AuthFunc
	claims ClaimsFunc
}

func NewMiddleware(c *Config) (*JWTMiddleware, error) {
	if c == nil {
		return nil, ErrMissingConfig
	}
	if c.Secret == "" {
		return nil, ErrMissingSecret
	}
	if c.Auth == nil {
		return nil, ErrMissingAuthFunc
	}
	if c.Claims == nil {
		return nil, ErrMissingClaimsFunc
	}
	m := &JWTMiddleware{
		secret: c.Secret,
		auth:   c.Auth,
		claims: c.Claims,
	}
	return m, nil
}

type jwtError struct {
	status  int
	err     error
	message string
}

type errorHandler func(http.ResponseWriter, *http.Request) *jwtError

func (e errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if err := e(w, r); err != nil {
		if err.message != "" {
			log.Printf("error (%v) while %s", err.err, err.message)
		}
		http.Error(w, err.err.Error(), err.status)
	}
}

func (m *JWTMiddleware) Secure(h http.Handler, v VerifyClaimsFunc) http.Handler {
	secureHandler := func(w http.ResponseWriter, r *http.Request) *jwtError {
		authHeader := r.Header.Get("Authorization")
		if authHeader == "" {
			return &jwtError{status: http.StatusUnauthorized, err: ErrMissingToken}
		}
		token := strings.Split(authHeader, " ")[1]
		tokenParts := strings.Split(token, ".")
		if len(tokenParts) != 3 {
			return &jwtError{status: http.StatusUnauthorized, err: ErrMalformedToken}
		}

		// First, verify JOSE header
		var t struct {
			Typ string
			Alg string
		}
		header, err := decode(tokenParts[0])
		if err != nil {
			return &jwtError{
				status:  http.StatusInternalServerError,
				err:     err,
				message: fmt.Sprintf("decoding header (%v)", tokenParts[0]),
			}
		}
		err = json.Unmarshal(header, &t)
		if err != nil {
			return &jwtError{
				status:  http.StatusInternalServerError,
				err:     ErrMalformedToken,
				message: fmt.Sprintf("unmarshalling header (%s)", header),
			}
		}

		// Then, verify signature
		mac := hmac.New(sha256.New, []byte(m.secret))
		message := []byte(strings.Join([]string{tokenParts[0], tokenParts[1]}, "."))
		mac.Write(message)
		expectedMac, err := encode(mac.Sum(nil))
		if err != nil {
			return &jwtError{status: http.StatusInternalServerError, err: err}
		}
		if !hmac.Equal([]byte(tokenParts[2]), []byte(expectedMac)) {
			return &jwtError{
				status:  http.StatusUnauthorized,
				err:     ErrInvalidSignature,
				message: fmt.Sprintf("checking signature (%v)", tokenParts[2]),
			}
		}

		// Finally, check claims
		claimSet, err := decode(tokenParts[1])
		if err != nil {
			return &jwtError{
				status:  http.StatusInternalServerError,
				err:     ErrDecoding,
				message: "decoding claims",
			}
		}
		err = v(claimSet)
		if err != nil {
			return &jwtError{
				status:  http.StatusUnauthorized,
				err:     err,
				message: "handling claims callback",
			}
		}

		// If we make it this far, process the downstream handler
		h.ServeHTTP(w, r)
		return nil
	}

	return errorHandler(secureHandler)
}

func (m *JWTMiddleware) GenerateToken(w http.ResponseWriter, r *http.Request) {
	var b map[string]string
	err := json.NewDecoder(r.Body).Decode(&b)
	if err != nil {
		log.Printf("error (%v) while parsing authorization", err)
		http.Error(w, ErrParsingCredentials.Error(), http.StatusInternalServerError)
		return
	}
	err = m.auth(b["email"], b["password"])
	if err != nil {
		log.Printf("error (%v) while performing authorization", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// For now, the header will be static
	header, err := encode(fmt.Sprintf(`{"typ":%q,"alg":%q}`, typ, alg))
	if err != nil {
		log.Printf("error (%v) while encoding header", err)
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	// Generate claims for user
	claims, err := m.claims(b["email"])
	if err != nil {
		log.Printf("error (%v) while generating claims", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	claimsJson, err := json.Marshal(claims)
	if err != nil {
		log.Printf("error (%v) while marshalling claims")
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	claimsSet, err := encode(claimsJson)
	if err != nil {
		log.Printf("error (%v) while encoding claims")
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	toSig := strings.Join([]string{header, claimsSet}, ".")

	h := hmac.New(sha256.New, []byte(m.secret))
	h.Write([]byte(toSig))
	sig, err := encode(h.Sum(nil))
	if err != nil {
		log.Printf("error (%v) while encoding signature")
		http.Error(w, ErrEncoding.Error(), http.StatusInternalServerError)
		return
	}

	response := strings.Join([]string{toSig, sig}, ".")
	w.Write([]byte(response))
}

func encode(s interface{}) (string, error) {
	var r []byte
	switch v := s.(type) {
	case string:
		r = []byte(v)
	case []byte:
		r = v
	default:
		return "", ErrEncoding
	}
	return base64.StdEncoding.EncodeToString(r), nil
}

func decode(s string) ([]byte, error) {
	return base64.StdEncoding.DecodeString(s)
}
<|endoftext|>