<commit_before>/*
 * Copyright (c) 2014 Michael Wendland
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 *	Authors:
 * 		Michael Wendland <michael@michiwend.com>
 */

package gomusicbrainz

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

var (
	mux    *http.ServeMux
	server *httptest.Server
	client GoMusicBrainz
)

// Init multiplexer and httptest server
func setupHttpTesting() {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	host, _ := url.Parse(server.URL)
	client = GoMusicBrainz{WS2RootURL: host}
}

// handleFunc passes response to the http client.
func handleFunc(url string, response *string, t *testing.T) {
	mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, *response)
	})
}

// serveTestFile responses to the http client with content of a test file
// located in ./testdata
func serveTestFile(url string, testfile string, t *testing.T) {

	//TODO check request URL if it matches one of the following patterns
	//lookup:   /<ENTITY>/<MBID>?inc=<INC>
	//browse:   /<ENTITY>?<ENTITY>=<MBID>&limit=<LIMIT>&offset=<OFFSET>&inc=<INC>
	//search:   /<ENTITY>?query=<QUERY>&limit=<LIMIT>&offset=<OFFSET>

	mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		if testing.Verbose() {
			fmt.Println("GET request:", r.URL.String())
		}

		http.ServeFile(w, r, "./testdata/"+testfile)
	})
}

func TestSearchArtist(t *testing.T) {

	want := []Artist{
		{
			Id:             "some-artist-id",
			Type:           "Group",
			Name:           "Gopher And Friends",
			Disambiguation: "Some crazy pocket gophers",
			SortName:       "0Gopher And Friends",
			CountryCode:    "DE",
			Lifespan: Lifespan{
				Ended: false,
				Begin: BrainzTime{time.Date(2007, 9, 21, 0, 0, 0, 0, time.UTC)},
				End:   BrainzTime{time.Time{}},
			},
			Aliases: []Alias{
				{
					Name:     "Mr. Gopher and Friends",
					SortName: "0Mr. Gopher and Friends",
				},
				{
					Name:     "Mr Gopher and Friends",
					SortName: "0Mr Gopher and Friends",
				},
			},
		},
	}

	setupHttpTesting()
	defer server.Close()
	serveTestFile("/artist", "SearchArtist.xml", t)

	returned, err := client.SearchArtist("Gopher", -1, -1)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(*returned, want) {
		t.Errorf("Artists returned: %+v, want: %+v", returned, want)
	}
}

func TestSearchRelease(t *testing.T) {

	want := []Release{
		{
			Id:     "9ab1b03e-6722-4ab8-bc7f-a8722f0d34c1",
			Title:  "Fred Schneider & The Shake Society",
			Status: "official",
			TextRepresentation: TextRepresentation{
				Language: "eng",
				Script:   "latn",
			},
			ArtistCredit: ArtistCredit{
				NameCredit{
					Artist{
						Id:       "43bcca8b-9edc-4997-8343-122350e790bf",
						Name:     "Fred Schneider",
						SortName: "Schneider, Fred",
					},
				},
			},
			ReleaseGroup: ReleaseGroup{
				Type: "Album",
			},
			Date:        BrainzTime{time.Date(1991, 4, 30, 0, 0, 0, 0, time.UTC)},
			CountryCode: "us",
			Barcode:     "075992659222",
			Asin:        "075992659222",
			LabelInfos: []LabelInfo{
				{
					CatalogNumber: "9 26592-2",
					Label: Label{
						Name: "Reprise Records",
					},
				},
			},
			Mediums: []Medium{
				{
					Format: "cd",
				},
			},
		},
	}

	setupHttpTesting()
	defer server.Close()
	serveTestFile("/release", "SearchRelease.xml", t)

	returned, err := client.SearchRelease("Fred", -1, -1)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(*returned, want) {
		t.Errorf("Releases returned: %+v, want: %+v", returned, want)
	}
}

func TestSearchReleaseGroup(t *testing.T) {

	want := []ReleaseGroup{
		{
			Id:          "70664047-2545-4e46-b75f-4556f2a7b83e",
			Type:        "Single",
			Title:       "Main Tenance",
			PrimaryType: "Single",
			ArtistCredit: ArtistCredit{
				NameCredit{
					Artist{
						Id:             "a8fa58d8-f60b-4b83-be7c-aea1af11596b",
						Name:           "Fred Giannelli",
						SortName:       "Giannelli, Fred",
						Disambiguation: "US electronic artist",
					},
				},
			},
			Releases: []Release{
				{
					Id:    "9168f4cc-a852-4ba5-bf85-602996625651",
					Title: "Main Tenance",
				},
			},
		},
	}

	setupHttpTesting()
	defer server.Close()
	serveTestFile("/release-group", "SearchReleaseGroup.xml", t)

	returned, err := client.SearchReleaseGroup("Tenance", -1, -1)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(*returned, want) {
		t.Errorf("ReleaseGroups returned: %+v, want: %+v", returned, want)
	}
}
<commit_msg>print struct, not the pointer<commit_after>/*
 * Copyright (c) 2014 Michael Wendland
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 *	Authors:
 * 		Michael Wendland <michael@michiwend.com>
 */

package gomusicbrainz

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

var (
	mux    *http.ServeMux
	server *httptest.Server
	client GoMusicBrainz
)

// Init multiplexer and httptest server
func setupHttpTesting() {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	host, _ := url.Parse(server.URL)
	client = GoMusicBrainz{WS2RootURL: host}
}

// handleFunc passes response to the http client.
func handleFunc(url string, response *string, t *testing.T) {
	mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, *response)
	})
}

// serveTestFile responses to the http client with content of a test file
// located in ./testdata
func serveTestFile(url string, testfile string, t *testing.T) {

	//TODO check request URL if it matches one of the following patterns
	//lookup:   /<ENTITY>/<MBID>?inc=<INC>
	//browse:   /<ENTITY>?<ENTITY>=<MBID>&limit=<LIMIT>&offset=<OFFSET>&inc=<INC>
	//search:   /<ENTITY>?query=<QUERY>&limit=<LIMIT>&offset=<OFFSET>

	mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		if testing.Verbose() {
			fmt.Println("GET request:", r.URL.String())
		}

		http.ServeFile(w, r, "./testdata/"+testfile)
	})
}

func TestSearchArtist(t *testing.T) {

	want := []Artist{
		{
			Id:             "some-artist-id",
			Type:           "Group",
			Name:           "Gopher And Friends",
			Disambiguation: "Some crazy pocket gophers",
			SortName:       "0Gopher And Friends",
			CountryCode:    "DE",
			Lifespan: Lifespan{
				Ended: false,
				Begin: BrainzTime{time.Date(2007, 9, 21, 0, 0, 0, 0, time.UTC)},
				End:   BrainzTime{time.Time{}},
			},
			Aliases: []Alias{
				{
					Name:     "Mr. Gopher and Friends",
					SortName: "0Mr. Gopher and Friends",
				},
				{
					Name:     "Mr Gopher and Friends",
					SortName: "0Mr Gopher and Friends",
				},
			},
		},
	}

	setupHttpTesting()
	defer server.Close()
	serveTestFile("/artist", "SearchArtist.xml", t)

	returned, err := client.SearchArtist("Gopher", -1, -1)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(*returned, want) {
		t.Errorf("Artists returned: %+v, want: %+v", *returned, want)
	}
}

func TestSearchRelease(t *testing.T) {

	want := []Release{
		{
			Id:     "9ab1b03e-6722-4ab8-bc7f-a8722f0d34c1",
			Title:  "Fred Schneider & The Shake Society",
			Status: "official",
			TextRepresentation: TextRepresentation{
				Language: "eng",
				Script:   "latn",
			},
			ArtistCredit: ArtistCredit{
				NameCredit{
					Artist{
						Id:       "43bcca8b-9edc-4997-8343-122350e790bf",
						Name:     "Fred Schneider",
						SortName: "Schneider, Fred",
					},
				},
			},
			ReleaseGroup: ReleaseGroup{
				Type: "Album",
			},
			Date:        BrainzTime{time.Date(1991, 4, 30, 0, 0, 0, 0, time.UTC)},
			CountryCode: "us",
			Barcode:     "075992659222",
			Asin:        "075992659222",
			LabelInfos: []LabelInfo{
				{
					CatalogNumber: "9 26592-2",
					Label: Label{
						Name: "Reprise Records",
					},
				},
			},
			Mediums: []Medium{
				{
					Format: "cd",
				},
			},
		},
	}

	setupHttpTesting()
	defer server.Close()
	serveTestFile("/release", "SearchRelease.xml", t)

	returned, err := client.SearchRelease("Fred", -1, -1)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(*returned, want) {
		t.Errorf("Releases returned: %+v, want: %+v", *returned, want)
	}
}

func TestSearchReleaseGroup(t *testing.T) {

	want := []ReleaseGroup{
		{
			Id:          "70664047-2545-4e46-b75f-4556f2a7b83e",
			Type:        "Single",
			Title:       "Main Tenance",
			PrimaryType: "Single",
			ArtistCredit: ArtistCredit{
				NameCredit{
					Artist{
						Id:             "a8fa58d8-f60b-4b83-be7c-aea1af11596b",
						Name:           "Fred Giannelli",
						SortName:       "Giannelli, Fred",
						Disambiguation: "US electronic artist",
					},
				},
			},
			Releases: []Release{
				{
					Id:    "9168f4cc-a852-4ba5-bf85-602996625651",
					Title: "Main Tenance",
				},
			},
		},
	}

	setupHttpTesting()
	defer server.Close()
	serveTestFile("/release-group", "SearchReleaseGroup.xml", t)

	returned, err := client.SearchReleaseGroup("Tenance", -1, -1)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(*returned, want) {
		t.Errorf("ReleaseGroups returned: %+v, want: %+v", *returned, want)
	}
}
<|endoftext|>


<commit_before>// +build !appengine

package google

import (
	"net/http"
	"strings"

	"github.com/golang/oauth2"
	"google.golang.org/appengine"
)

// AppEngineConfig represents a configuration for an
// App Engine application's Google service account.
type AppEngineConfig struct {
	context appengine.Context
	scopes  []string
}

// NewAppEngineConfig creates a new AppEngineConfig for the
// provided auth scopes.
func NewAppEngineConfig(context appengine.Context, scopes []string) *AppEngineConfig {
	return &AppEngineConfig{context: context, scopes: scopes}
}

// NewTransport returns a transport that authorizes
// the requests with the application's service account.
func (c *AppEngineConfig) NewTransport() oauth2.Transport {
	return oauth2.NewAuthorizedTransport(http.DefaultTransport, c, nil)
}

// FetchToken fetches a new access token for the provided scopes.
func (c *AppEngineConfig) FetchToken(existing *oauth2.Token) (*oauth2.Token, error) {
	token, expiry, err := appengine.AccessToken(c.context, strings.Join(c.scopes, " "))
	if err != nil {
		return nil, err
	}
	return &oauth2.Token{
		AccessToken: token,
		Expiry:      expiry,
	}, nil
}
<commit_msg>App Engine Managed VMs should depend on url fetcher.<commit_after>// +build !appengine

package google

import (
	"strings"

	"github.com/golang/oauth2"

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"
)

// AppEngineConfig represents a configuration for an
// App Engine application's Google service account.
type AppEngineConfig struct {
	context appengine.Context
	scopes  []string
}

// NewAppEngineConfig creates a new AppEngineConfig for the
// provided auth scopes.
func NewAppEngineConfig(context appengine.Context, scopes []string) *AppEngineConfig {
	return &AppEngineConfig{context: context, scopes: scopes}
}

// NewTransport returns a transport that authorizes
// the requests with the application's service account.
func (c *AppEngineConfig) NewTransport() oauth2.Transport {
	transport := &urlfetch.Transport{
		Context:                       c.context,
		Deadline:                      0,
		AllowInvalidServerCertificate: false,
	}
	return oauth2.NewAuthorizedTransport(transport, c, nil)
}

// FetchToken fetches a new access token for the provided scopes.
func (c *AppEngineConfig) FetchToken(existing *oauth2.Token) (*oauth2.Token, error) {
	token, expiry, err := appengine.AccessToken(c.context, strings.Join(c.scopes, " "))
	if err != nil {
		return nil, err
	}
	return &oauth2.Token{
		AccessToken: token,
		Expiry:      expiry,
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/Seklfreak/Robyul2/helpers"
    "github.com/Seklfreak/Robyul2/metrics"
    "github.com/Seklfreak/Robyul2/migrations"
    "github.com/Seklfreak/Robyul2/version"
    "github.com/bwmarrin/discordgo"
    "github.com/getsentry/raven-go"
    "math/rand"
    "os"
    "os/signal"
    "time"
    "github.com/go-redis/redis"
    "github.com/Seklfreak/Robyul2/cache"
    "github.com/emicklei/go-restful"
    "net/http"
    "github.com/Seklfreak/Robyul2/rest"
    "github.com/Sirupsen/logrus"
)

// Entrypoint
func main() {
    log := logrus.New()
    log.Out = os.Stdout
    log.Level = logrus.InfoLevel
    log.Formatter = &logrus.TextFormatter{ForceColors: true}
    cache.SetLogger(log)

    log.WithField("module", "launcher").Info("Booting Robyul...")

    // Read config
    helpers.LoadConfig("config.json")
    config := helpers.GetConfig()

    // Read i18n
    helpers.LoadTranslations()

    // Show version
    version.DumpInfo()

    // Start metric server
    metrics.Init()

    // Make the randomness more random
    rand.Seed(time.Now().UTC().UnixNano())

    // Check if the bot is being debugged
    if config.Path("debug").Data().(bool) {
        helpers.DEBUG_MODE = true
        log.Level = logrus.DebugLevel
    }

    // Print UA
    log.WithField("module", "launcher").Info("USERAGENT: '"+helpers.DEFAULT_UA+"'")

    // Call home
    log.WithField("module", "launcher").Info("[SENTRY] Calling home...")
    err := raven.SetDSN(config.Path("sentry").Data().(string))
    if err != nil {
        panic(err)
    }
    if version.BOT_VERSION != "UNSET" {
        raven.SetRelease(version.BOT_VERSION)
    }
    log.WithField("module", "launcher").Info("[SENTRY] Someone picked up the phone \\^-^/")

    // Connect to DB
    log.WithField("module", "launcher").Info("Opening database connection...")
    helpers.ConnectDB(
        config.Path("rethink.url").Data().(string),
        config.Path("rethink.db").Data().(string),
    )

    // Close DB when main dies
    defer helpers.GetDB().Close()

    // Run migrations
    migrations.Run()

    // Connecting to redis
    log.WithField("module", "launcher").Info("Connecting to redis...")
    redisClient := redis.NewClient(&redis.Options{
        Addr:     config.Path("redis.address").Data().(string),
        Password: "", // no password set
        DB:       0,  // use default DB
    })
    cache.SetRedisClient(redisClient)

    // Connect and add event handlers
    log.WithField("module", "launcher").Info("Connecting to discord...")
    discord, err := discordgo.New("Bot " + config.Path("discord.token").Data().(string))
    if err != nil {
        panic(err)
    }

    discord.Lock()
    discord.Debug = false
    discord.LogLevel = discordgo.LogInformational
    discord.StateEnabled = true
    discord.Unlock()

    discord.AddHandler(BotOnReady)
    discord.AddHandler(BotOnMessageCreate)
    discord.AddHandler(BotOnGuildMemberAdd)
    discord.AddHandler(BotOnGuildMemberRemove)
    discord.AddHandler(BotOnReactionAdd)
    discord.AddHandler(BotOnReactionRemove)
    discord.AddHandler(BotOnGuildBanAdd)
    discord.AddHandler(BotOnGuildBanRemove)
    discord.AddHandler(metrics.OnReady)
    discord.AddHandler(metrics.OnMessageCreate)

    // Connect to discord
    err = discord.Open()
    if err != nil {
        raven.CaptureErrorAndWait(err, nil)
        panic(err)
    }

    // Open REST API
    for _, service := range rest.NewRestServices() {
        restful.Add(service)
    }
    log.Fatal(http.ListenAndServe("localhost:2021", nil))
    log.WithField("module", "launcher").Info("REST API listening on localhost:2021")

    // Make a channel that waits for a os signal
    channel := make(chan os.Signal, 1)
    signal.Notify(channel, os.Interrupt, os.Kill)

    // Wait until the os wants us to shutdown
    <-channel

    log.WithField("module", "launcher").Info("The OS is killing me :c")
    log.WithField("module", "launcher").Info("Disconnecting...")
    discord.Close()
}
<commit_msg>enable debug output by default<commit_after>package main

import (
    "github.com/Seklfreak/Robyul2/helpers"
    "github.com/Seklfreak/Robyul2/metrics"
    "github.com/Seklfreak/Robyul2/migrations"
    "github.com/Seklfreak/Robyul2/version"
    "github.com/bwmarrin/discordgo"
    "github.com/getsentry/raven-go"
    "math/rand"
    "os"
    "os/signal"
    "time"
    "github.com/go-redis/redis"
    "github.com/Seklfreak/Robyul2/cache"
    "github.com/emicklei/go-restful"
    "net/http"
    "github.com/Seklfreak/Robyul2/rest"
    "github.com/Sirupsen/logrus"
)

// Entrypoint
func main() {
    log := logrus.New()
    log.Out = os.Stdout
    log.Level = logrus.DebugLevel
    log.Formatter = &logrus.TextFormatter{ForceColors: true}
    cache.SetLogger(log)

    log.WithField("module", "launcher").Info("Booting Robyul...")

    // Read config
    helpers.LoadConfig("config.json")
    config := helpers.GetConfig()

    // Read i18n
    helpers.LoadTranslations()

    // Show version
    version.DumpInfo()

    // Start metric server
    metrics.Init()

    // Make the randomness more random
    rand.Seed(time.Now().UTC().UnixNano())

    // Check if the bot is being debugged
    if config.Path("debug").Data().(bool) {
        helpers.DEBUG_MODE = true
    }

    // Print UA
    log.WithField("module", "launcher").Info("USERAGENT: '"+helpers.DEFAULT_UA+"'")

    // Call home
    log.WithField("module", "launcher").Info("[SENTRY] Calling home...")
    err := raven.SetDSN(config.Path("sentry").Data().(string))
    if err != nil {
        panic(err)
    }
    if version.BOT_VERSION != "UNSET" {
        raven.SetRelease(version.BOT_VERSION)
    }
    log.WithField("module", "launcher").Info("[SENTRY] Someone picked up the phone \\^-^/")

    // Connect to DB
    log.WithField("module", "launcher").Info("Opening database connection...")
    helpers.ConnectDB(
        config.Path("rethink.url").Data().(string),
        config.Path("rethink.db").Data().(string),
    )

    // Close DB when main dies
    defer helpers.GetDB().Close()

    // Run migrations
    migrations.Run()

    // Connecting to redis
    log.WithField("module", "launcher").Info("Connecting to redis...")
    redisClient := redis.NewClient(&redis.Options{
        Addr:     config.Path("redis.address").Data().(string),
        Password: "", // no password set
        DB:       0,  // use default DB
    })
    cache.SetRedisClient(redisClient)

    // Connect and add event handlers
    log.WithField("module", "launcher").Info("Connecting to discord...")
    discord, err := discordgo.New("Bot " + config.Path("discord.token").Data().(string))
    if err != nil {
        panic(err)
    }

    discord.Lock()
    discord.Debug = false
    discord.LogLevel = discordgo.LogInformational
    discord.StateEnabled = true
    discord.Unlock()

    discord.AddHandler(BotOnReady)
    discord.AddHandler(BotOnMessageCreate)
    discord.AddHandler(BotOnGuildMemberAdd)
    discord.AddHandler(BotOnGuildMemberRemove)
    discord.AddHandler(BotOnReactionAdd)
    discord.AddHandler(BotOnReactionRemove)
    discord.AddHandler(BotOnGuildBanAdd)
    discord.AddHandler(BotOnGuildBanRemove)
    discord.AddHandler(metrics.OnReady)
    discord.AddHandler(metrics.OnMessageCreate)

    // Connect to discord
    err = discord.Open()
    if err != nil {
        raven.CaptureErrorAndWait(err, nil)
        panic(err)
    }

    // Open REST API
    for _, service := range rest.NewRestServices() {
        restful.Add(service)
    }
    log.Fatal(http.ListenAndServe("localhost:2021", nil))
    log.WithField("module", "launcher").Info("REST API listening on localhost:2021")

    // Make a channel that waits for a os signal
    channel := make(chan os.Signal, 1)
    signal.Notify(channel, os.Interrupt, os.Kill)

    // Wait until the os wants us to shutdown
    <-channel

    log.WithField("module", "launcher").Info("The OS is killing me :c")
    log.WithField("module", "launcher").Info("Disconnecting...")
    discord.Close()
}
<|endoftext|>


<commit_before>package mtree

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"fmt"
	"hash"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ripemd160"
)

// KeywordFunc is the type of a function called on each file to be included in
// a DirectoryHierarchy, that will produce the string output of the keyword to
// be included for the file entry. Otherwise, empty string.
type KeywordFunc func(path string, info os.FileInfo) (string, error)

// KeyVal is a "keyword=value"
type KeyVal string

// Keyword is the mapping to the available keywords
func (kv KeyVal) Keyword() string {
	if !strings.Contains(string(kv), "=") {
		return ""
	}
	chunks := strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[0]
	if !strings.Contains(chunks, ".") {
		return chunks
	}
	return strings.SplitN(chunks, ".", 2)[0]
}

// KeywordSuffix is really only used for xattr, as the keyword is a prefix to
// the xattr "namespace.key"
func (kv KeyVal) KeywordSuffix() string {
	if !strings.Contains(string(kv), "=") {
		return ""
	}
	chunks := strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[0]
	if !strings.Contains(chunks, ".") {
		return ""
	}
	return strings.SplitN(chunks, ".", 2)[1]
}

// Value is the data/value portion of "keyword=value"
func (kv KeyVal) Value() string {
	if !strings.Contains(string(kv), "=") {
		return ""
	}
	return strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[1]
}

// keywordSelector takes an array of "keyword=value" and filters out that only the set of words
func keywordSelector(keyval, words []string) []string {
	retList := []string{}
	for _, kv := range keyval {
		if inSlice(KeyVal(kv).Keyword(), words) {
			retList = append(retList, kv)
		}
	}
	return retList
}

// NewKeyVals constructs a list of KeyVal from the list of strings, like "keyword=value"
func NewKeyVals(keyvals []string) KeyVals {
	kvs := make(KeyVals, len(keyvals))
	for i := range keyvals {
		kvs[i] = KeyVal(keyvals[i])
	}
	return kvs
}

// KeyVals is a list of KeyVal
type KeyVals []KeyVal

// Has the "keyword" present in the list of KeyVal, and returns the
// corresponding KeyVal, else an empty string.
func (kvs KeyVals) Has(keyword string) KeyVal {
	for i := range kvs {
		if kvs[i].Keyword() == keyword {
			return kvs[i]
		}
	}
	return emptyKV
}

var emptyKV = KeyVal("")

// MergeSet takes the current setKeyVals, and then applies the entryKeyVals
// such that the entry's values win. The union is returned.
func MergeSet(setKeyVals, entryKeyVals []string) KeyVals {
	retList := NewKeyVals(append([]string{}, setKeyVals...))
	eKVs := NewKeyVals(entryKeyVals)
	seenKeywords := []string{}
	for i := range retList {
		word := retList[i].Keyword()
		if ekv := eKVs.Has(word); ekv != emptyKV {
			retList[i] = ekv
		}
		seenKeywords = append(seenKeywords, word)
	}
	for i := range eKVs {
		if !inSlice(eKVs[i].Keyword(), seenKeywords) {
			retList = append(retList, eKVs[i])
		}
	}
	return retList
}

var (
	// DefaultKeywords has the several default keyword producers (uid, gid,
	// mode, nlink, type, size, mtime)
	DefaultKeywords = []string{
		"size",
		"type",
		"uid",
		"gid",
		"mode",
		"link",
		"nlink",
		"time",
	}
	// SetKeywords is the default set of keywords calculated for a `/set` SpecialType
	SetKeywords = []string{
		"uid",
		"gid",
	}
	// KeywordFuncs is the map of all keywords (and the functions to produce them)
	KeywordFuncs = map[string]KeywordFunc{
		"size":            sizeKeywordFunc,                                     // The size, in bytes, of the file
		"type":            typeKeywordFunc,                                     // The type of the file
		"time":            timeKeywordFunc,                                     // The last modification time of the file
		"link":            linkKeywordFunc,                                     // The target of the symbolic link when type=link
		"uid":             uidKeywordFunc,                                      // The file owner as a numeric value
		"gid":             gidKeywordFunc,                                      // The file group as a numeric value
		"nlink":           nlinkKeywordFunc,                                    // The number of hard links the file is expected to have
		"uname":           unameKeywordFunc,                                    // The file owner as a symbolic name
		"mode":            modeKeywordFunc,                                     // The current file's permissions as a numeric (octal) or symbolic value
		"cksum":           cksumKeywordFunc,                                    // The checksum of the file using the default algorithm specified by the cksum(1) utility
		"md5":             hasherKeywordFunc("md5", md5.New),                   // The MD5 message digest of the file
		"md5digest":       hasherKeywordFunc("md5digest", md5.New),             // A synonym for `md5`
		"rmd160":          hasherKeywordFunc("rmd160", ripemd160.New),          // The RIPEMD160 message digest of the file
		"rmd160digest":    hasherKeywordFunc("rmd160digest", ripemd160.New),    // A synonym for `rmd160`
		"ripemd160digest": hasherKeywordFunc("ripemd160digest", ripemd160.New), // A synonym for `rmd160`
		"sha1":            hasherKeywordFunc("sha1", sha1.New),                 // The SHA1 message digest of the file
		"sha1digest":      hasherKeywordFunc("sha1digest", sha1.New),           // A synonym for `sha1`
		"sha256":          hasherKeywordFunc("sha256", sha256.New),             // The SHA256 message digest of the file
		"sha256digest":    hasherKeywordFunc("sha256digest", sha256.New),       // A synonym for `sha256`
		"sha384":          hasherKeywordFunc("sha384", sha512.New384),          // The SHA384 message digest of the file
		"sha384digest":    hasherKeywordFunc("sha384digest", sha512.New384),    // A synonym for `sha384`
		"sha512":          hasherKeywordFunc("sha512", sha512.New),             // The SHA512 message digest of the file
		"sha512digest":    hasherKeywordFunc("sha512digest", sha512.New),       // A synonym for `sha512`

		// This is not an upstreamed keyword, but a needed attribute for file validation.
		// The pattern for this keyword key is prefixed by "xattr." followed by the extended attribute "namespace.key".
		// The keyword value is the SHA1 digest of the extended attribute's value.
		// In this way, the order of the keys does not matter, and the contents of the value is not revealed.
		"xattr": xattrKeywordFunc,
	}
)

var (
	modeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		return fmt.Sprintf("mode=%#o", info.Mode().Perm()), nil
	}
	sizeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		return fmt.Sprintf("size=%d", info.Size()), nil
	}
	cksumKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		if !info.Mode().IsRegular() {
			return "", nil
		}

		fh, err := os.Open(path)
		if err != nil {
			return "", err
		}
		defer fh.Close()
		sum, _, err := cksum(fh)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("cksum=%d", sum), nil
	}
	hasherKeywordFunc = func(name string, newHash func() hash.Hash) KeywordFunc {
		return func(path string, info os.FileInfo) (string, error) {
			if !info.Mode().IsRegular() {
				return "", nil
			}

			fh, err := os.Open(path)
			if err != nil {
				return "", err
			}
			defer fh.Close()

			h := newHash()
			if _, err := io.Copy(h, fh); err != nil {
				return "", err
			}
			return fmt.Sprintf("%s=%x", name, h.Sum(nil)), nil
		}
	}
	timeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		t := info.ModTime().UnixNano()
		if t == 0 {
			return "time=0.000000000", nil
		}
		return fmt.Sprintf("time=%d.%9.9d", (t / 1e9), (t % (t / 1e9))), nil
	}
	linkKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		if info.Mode()&os.ModeSymlink != 0 {
			str, err := os.Readlink(path)
			if err != nil {
				return "", err
			}
			return fmt.Sprintf("link=%s", str), nil
		}
		return "", nil
	}
	typeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		if info.Mode().IsDir() {
			return "type=dir", nil
		}
		if info.Mode().IsRegular() {
			return "type=file", nil
		}
		if info.Mode()&os.ModeSocket != 0 {
			return "type=socket", nil
		}
		if info.Mode()&os.ModeSymlink != 0 {
			return "type=link", nil
		}
		if info.Mode()&os.ModeNamedPipe != 0 {
			return "type=fifo", nil
		}
		if info.Mode()&os.ModeDevice != 0 {
			if info.Mode()&os.ModeCharDevice != 0 {
				return "type=char", nil
			}
			return "type=device", nil
		}
		return "", nil
	}
)
<commit_msg>keywords: obtain all permission bits<commit_after>package mtree

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"fmt"
	"hash"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ripemd160"
)

// KeywordFunc is the type of a function called on each file to be included in
// a DirectoryHierarchy, that will produce the string output of the keyword to
// be included for the file entry. Otherwise, empty string.
type KeywordFunc func(path string, info os.FileInfo) (string, error)

// KeyVal is a "keyword=value"
type KeyVal string

// Keyword is the mapping to the available keywords
func (kv KeyVal) Keyword() string {
	if !strings.Contains(string(kv), "=") {
		return ""
	}
	chunks := strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[0]
	if !strings.Contains(chunks, ".") {
		return chunks
	}
	return strings.SplitN(chunks, ".", 2)[0]
}

// KeywordSuffix is really only used for xattr, as the keyword is a prefix to
// the xattr "namespace.key"
func (kv KeyVal) KeywordSuffix() string {
	if !strings.Contains(string(kv), "=") {
		return ""
	}
	chunks := strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[0]
	if !strings.Contains(chunks, ".") {
		return ""
	}
	return strings.SplitN(chunks, ".", 2)[1]
}

// Value is the data/value portion of "keyword=value"
func (kv KeyVal) Value() string {
	if !strings.Contains(string(kv), "=") {
		return ""
	}
	return strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[1]
}

// keywordSelector takes an array of "keyword=value" and filters out that only the set of words
func keywordSelector(keyval, words []string) []string {
	retList := []string{}
	for _, kv := range keyval {
		if inSlice(KeyVal(kv).Keyword(), words) {
			retList = append(retList, kv)
		}
	}
	return retList
}

// NewKeyVals constructs a list of KeyVal from the list of strings, like "keyword=value"
func NewKeyVals(keyvals []string) KeyVals {
	kvs := make(KeyVals, len(keyvals))
	for i := range keyvals {
		kvs[i] = KeyVal(keyvals[i])
	}
	return kvs
}

// KeyVals is a list of KeyVal
type KeyVals []KeyVal

// Has the "keyword" present in the list of KeyVal, and returns the
// corresponding KeyVal, else an empty string.
func (kvs KeyVals) Has(keyword string) KeyVal {
	for i := range kvs {
		if kvs[i].Keyword() == keyword {
			return kvs[i]
		}
	}
	return emptyKV
}

var emptyKV = KeyVal("")

// MergeSet takes the current setKeyVals, and then applies the entryKeyVals
// such that the entry's values win. The union is returned.
func MergeSet(setKeyVals, entryKeyVals []string) KeyVals {
	retList := NewKeyVals(append([]string{}, setKeyVals...))
	eKVs := NewKeyVals(entryKeyVals)
	seenKeywords := []string{}
	for i := range retList {
		word := retList[i].Keyword()
		if ekv := eKVs.Has(word); ekv != emptyKV {
			retList[i] = ekv
		}
		seenKeywords = append(seenKeywords, word)
	}
	for i := range eKVs {
		if !inSlice(eKVs[i].Keyword(), seenKeywords) {
			retList = append(retList, eKVs[i])
		}
	}
	return retList
}

var (
	// DefaultKeywords has the several default keyword producers (uid, gid,
	// mode, nlink, type, size, mtime)
	DefaultKeywords = []string{
		"size",
		"type",
		"uid",
		"gid",
		"mode",
		"link",
		"nlink",
		"time",
	}
	// SetKeywords is the default set of keywords calculated for a `/set` SpecialType
	SetKeywords = []string{
		"uid",
		"gid",
	}
	// KeywordFuncs is the map of all keywords (and the functions to produce them)
	KeywordFuncs = map[string]KeywordFunc{
		"size":            sizeKeywordFunc,                                     // The size, in bytes, of the file
		"type":            typeKeywordFunc,                                     // The type of the file
		"time":            timeKeywordFunc,                                     // The last modification time of the file
		"link":            linkKeywordFunc,                                     // The target of the symbolic link when type=link
		"uid":             uidKeywordFunc,                                      // The file owner as a numeric value
		"gid":             gidKeywordFunc,                                      // The file group as a numeric value
		"nlink":           nlinkKeywordFunc,                                    // The number of hard links the file is expected to have
		"uname":           unameKeywordFunc,                                    // The file owner as a symbolic name
		"mode":            modeKeywordFunc,                                     // The current file's permissions as a numeric (octal) or symbolic value
		"cksum":           cksumKeywordFunc,                                    // The checksum of the file using the default algorithm specified by the cksum(1) utility
		"md5":             hasherKeywordFunc("md5", md5.New),                   // The MD5 message digest of the file
		"md5digest":       hasherKeywordFunc("md5digest", md5.New),             // A synonym for `md5`
		"rmd160":          hasherKeywordFunc("rmd160", ripemd160.New),          // The RIPEMD160 message digest of the file
		"rmd160digest":    hasherKeywordFunc("rmd160digest", ripemd160.New),    // A synonym for `rmd160`
		"ripemd160digest": hasherKeywordFunc("ripemd160digest", ripemd160.New), // A synonym for `rmd160`
		"sha1":            hasherKeywordFunc("sha1", sha1.New),                 // The SHA1 message digest of the file
		"sha1digest":      hasherKeywordFunc("sha1digest", sha1.New),           // A synonym for `sha1`
		"sha256":          hasherKeywordFunc("sha256", sha256.New),             // The SHA256 message digest of the file
		"sha256digest":    hasherKeywordFunc("sha256digest", sha256.New),       // A synonym for `sha256`
		"sha384":          hasherKeywordFunc("sha384", sha512.New384),          // The SHA384 message digest of the file
		"sha384digest":    hasherKeywordFunc("sha384digest", sha512.New384),    // A synonym for `sha384`
		"sha512":          hasherKeywordFunc("sha512", sha512.New),             // The SHA512 message digest of the file
		"sha512digest":    hasherKeywordFunc("sha512digest", sha512.New),       // A synonym for `sha512`

		// This is not an upstreamed keyword, but a needed attribute for file validation.
		// The pattern for this keyword key is prefixed by "xattr." followed by the extended attribute "namespace.key".
		// The keyword value is the SHA1 digest of the extended attribute's value.
		// In this way, the order of the keys does not matter, and the contents of the value is not revealed.
		"xattr": xattrKeywordFunc,
	}
)

var (
	modeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		permissions := info.Mode().Perm()
		if os.ModeSetuid&info.Mode() > 0 {
			permissions |= (1 << 11)
		}
		if os.ModeSetgid&info.Mode() > 0 {
			permissions |= (1 << 10)
		}
		if os.ModeSticky&info.Mode() > 0 {
			permissions |= (1 << 9)
		}
		return fmt.Sprintf("mode=%#o", permissions), nil
	}
	sizeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		return fmt.Sprintf("size=%d", info.Size()), nil
	}
	cksumKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		if !info.Mode().IsRegular() {
			return "", nil
		}

		fh, err := os.Open(path)
		if err != nil {
			return "", err
		}
		defer fh.Close()
		sum, _, err := cksum(fh)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("cksum=%d", sum), nil
	}
	hasherKeywordFunc = func(name string, newHash func() hash.Hash) KeywordFunc {
		return func(path string, info os.FileInfo) (string, error) {
			if !info.Mode().IsRegular() {
				return "", nil
			}

			fh, err := os.Open(path)
			if err != nil {
				return "", err
			}
			defer fh.Close()

			h := newHash()
			if _, err := io.Copy(h, fh); err != nil {
				return "", err
			}
			return fmt.Sprintf("%s=%x", name, h.Sum(nil)), nil
		}
	}
	timeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		t := info.ModTime().UnixNano()
		if t == 0 {
			return "time=0.000000000", nil
		}
		return fmt.Sprintf("time=%d.%9.9d", (t / 1e9), (t % (t / 1e9))), nil
	}
	linkKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		if info.Mode()&os.ModeSymlink != 0 {
			str, err := os.Readlink(path)
			if err != nil {
				return "", err
			}
			return fmt.Sprintf("link=%s", str), nil
		}
		return "", nil
	}
	typeKeywordFunc = func(path string, info os.FileInfo) (string, error) {
		if info.Mode().IsDir() {
			return "type=dir", nil
		}
		if info.Mode().IsRegular() {
			return "type=file", nil
		}
		if info.Mode()&os.ModeSocket != 0 {
			return "type=socket", nil
		}
		if info.Mode()&os.ModeSymlink != 0 {
			return "type=link", nil
		}
		if info.Mode()&os.ModeNamedPipe != 0 {
			return "type=fifo", nil
		}
		if info.Mode()&os.ModeDevice != 0 {
			if info.Mode()&os.ModeCharDevice != 0 {
				return "type=char", nil
			}
			return "type=device", nil
		}
		return "", nil
	}
)
<|endoftext|>


<commit_before>package s3api

import (
	"context"
	"encoding/xml"
	"errors"
	"fmt"
	"math"
	"net/http"
	"time"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
	"github.com/chrislusf/seaweedfs/weed/storage/needle"

	xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

type ListAllMyBucketsResult struct {
	XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult"`
	Owner   *s3.Owner
	Buckets []*s3.Bucket `xml:"Buckets>Bucket"`
}

func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {

	glog.V(3).Infof("ListBucketsHandler")

	var identity *Identity
	var s3Err s3err.ErrorCode
	if s3a.iam.isEnabled() {
		identity, s3Err = s3a.iam.authUser(r)
		if s3Err != s3err.ErrNone {
			s3err.WriteErrorResponse(w, r, s3Err)
			return
		}
	}

	var response ListAllMyBucketsResult

	entries, _, err := s3a.list(s3a.option.BucketsPath, "", "", false, math.MaxInt32)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	identityId := r.Header.Get(xhttp.AmzIdentityId)

	var buckets []*s3.Bucket
	for _, entry := range entries {
		if entry.IsDirectory {
			if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name, "") {
				continue
			}
			buckets = append(buckets, &s3.Bucket{
				Name:         aws.String(entry.Name),
				CreationDate: aws.Time(time.Unix(entry.Attributes.Crtime, 0).UTC()),
			})
		}
	}

	response = ListAllMyBucketsResult{
		Owner: &s3.Owner{
			ID:          aws.String(identityId),
			DisplayName: aws.String(identityId),
		},
		Buckets: buckets,
	}

	writeSuccessResponseXML(w, r, response)
}

func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("PutBucketHandler %s", bucket)

	// avoid duplicated buckets
	errCode := s3err.ErrNone
	if err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
		if resp, err := client.CollectionList(context.Background(), &filer_pb.CollectionListRequest{
			IncludeEcVolumes:     true,
			IncludeNormalVolumes: true,
		}); err != nil {
			glog.Errorf("list collection: %v", err)
			return fmt.Errorf("list collections: %v", err)
		} else {
			for _, c := range resp.Collections {
				if bucket == c.Name {
					errCode = s3err.ErrBucketAlreadyExists
					break
				}
			}
		}
		return nil
	}); err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}
	if exist, err := s3a.exists(s3a.option.BucketsPath, bucket, true); err == nil && exist {
		errCode = s3err.ErrBucketAlreadyExists
	}
	if errCode != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, errCode)
		return
	}

	if s3a.iam.isEnabled() {
		if _, errCode = s3a.iam.authRequest(r, s3_constants.ACTION_ADMIN); errCode != s3err.ErrNone {
			s3err.WriteErrorResponse(w, r, errCode)
			return
		}
	}

	fn := func(entry *filer_pb.Entry) {
		if identityId := r.Header.Get(xhttp.AmzIdentityId); identityId != "" {
			if entry.Extended == nil {
				entry.Extended = make(map[string][]byte)
			}
			entry.Extended[xhttp.AmzIdentityId] = []byte(identityId)
		}
	}

	// create the folder for bucket, but lazily create actual collection
	if err := s3a.mkdir(s3a.option.BucketsPath, bucket, fn); err != nil {
		glog.Errorf("PutBucketHandler mkdir: %v", err)
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}
	writeSuccessResponseEmpty(w, r)
}

func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("DeleteBucketHandler %s", bucket)

	if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, err)
		return
	}

	err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
		if !s3a.option.AllowDeleteBucketNotEmpty {
			entries, _, err := s3a.list(s3a.option.BucketsPath+"/"+bucket, "", "", false, 1)
			if err != nil {
				return fmt.Errorf("failed to list bucket %s: %v", bucket, err)
			}
			if len(entries) > 0 {
				return errors.New(s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code)
			}
		}

		// delete collection
		deleteCollectionRequest := &filer_pb.DeleteCollectionRequest{
			Collection: bucket,
		}

		glog.V(1).Infof("delete collection: %v", deleteCollectionRequest)
		if _, err := client.DeleteCollection(context.Background(), deleteCollectionRequest); err != nil {
			return fmt.Errorf("delete collection %s: %v", bucket, err)
		}

		return nil
	})

	if err != nil {
		s3ErrorCode := s3err.ErrInternalError
		if err.Error() == s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code {
			s3ErrorCode = s3err.ErrBucketNotEmpty
		}
		s3err.WriteErrorResponse(w, r, s3ErrorCode)
		return
	}

	err = s3a.rm(s3a.option.BucketsPath, bucket, false, true)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
}

func (s3a *S3ApiServer) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("HeadBucketHandler %s", bucket)

	if entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket); entry == nil || err == filer_pb.ErrNotFound {
		s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
		return
	}

	writeSuccessResponseEmpty(w, r)
}

func (s3a *S3ApiServer) checkBucket(r *http.Request, bucket string) s3err.ErrorCode {
	entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
	if entry == nil || err == filer_pb.ErrNotFound {
		return s3err.ErrNoSuchBucket
	}

	if !s3a.hasAccess(r, entry) {
		return s3err.ErrAccessDenied
	}
	return s3err.ErrNone
}

func (s3a *S3ApiServer) hasAccess(r *http.Request, entry *filer_pb.Entry) bool {
	isAdmin := r.Header.Get(xhttp.AmzIsAdmin) != ""
	if isAdmin {
		return true
	}
	if entry.Extended == nil {
		return true
	}

	identityId := r.Header.Get(xhttp.AmzIdentityId)
	if id, ok := entry.Extended[xhttp.AmzIdentityId]; ok {
		if identityId != string(id) {
			return false
		}
	}
	return true
}

// GetBucketAclHandler Get Bucket ACL
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAcl.html
func (s3a *S3ApiServer) GetBucketAclHandler(w http.ResponseWriter, r *http.Request) {
	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("GetBucketAclHandler %s", bucket)

	if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, err)
		return
	}

	response := AccessControlPolicy{}
	for _, ident := range s3a.iam.identities {
		if len(ident.Credentials) == 0 {
			continue
		}
		for _, action := range ident.Actions {
			if !action.overBucket(bucket) || action.getPermission() == "" {
				continue
			}
			id := ident.Credentials[0].AccessKey
			if response.Owner.DisplayName == "" && action.isOwner(bucket) && len(ident.Credentials) > 0 {
				response.Owner.DisplayName = ident.Name
				response.Owner.ID = id
			}
			response.AccessControlList.Grant = append(response.AccessControlList.Grant, Grant{
				Grantee: Grantee{
					ID:          id,
					DisplayName: ident.Name,
					Type:        "CanonicalUser",
					XMLXSI:      "CanonicalUser",
					XMLNS:       "http://www.w3.org/2001/XMLSchema-instance"},
				Permission: action.getPermission(),
			})
		}
	}
	writeSuccessResponseXML(w, r, response)
}

// GetBucketLifecycleConfigurationHandler Get Bucket Lifecycle configuration
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html
func (s3a *S3ApiServer) GetBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {
	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("GetBucketLifecycleConfigurationHandler %s", bucket)

	if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, err)
		return
	}
	fc, err := filer.ReadFilerConf(s3a.option.Filer, s3a.option.GrpcDialOption, nil)
	if err != nil {
		glog.Errorf("GetBucketLifecycleConfigurationHandler: %s", err)
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}
	ttls := fc.GetCollectionTtls(bucket)
	if len(ttls) == 0 {
		s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchLifecycleConfiguration)
		return
	}
	response := Lifecycle{}
	for prefix, internalTtl := range ttls {
		ttl, _ := needle.ReadTTL(internalTtl)
		days := int(ttl.Minutes() / 60 / 24)
		if days == 0 {
			continue
		}
		response.Rules = append(response.Rules, Rule{
			Status: Enabled, Filter: Filter{
				Prefix: Prefix{string: prefix, set: true},
				set:    true,
			},
			Expiration: Expiration{Days: days, set: true},
		})
	}
	writeSuccessResponseXML(w, r, response)
}

// PutBucketLifecycleConfigurationHandler Put Bucket Lifecycle configuration
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html
func (s3a *S3ApiServer) PutBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {

	s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)

}

// DeleteBucketMetricsConfiguration Delete Bucket Lifecycle
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html
func (s3a *S3ApiServer) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {

	s3err.WriteEmptyResponse(w, r, http.StatusNoContent)

}

// GetBucketLocationHandler Get bucket location
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html
func (s3a *S3ApiServer) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
	writeSuccessResponseXML(w, r, LocationConstraint{})
}

// GetBucketRequestPaymentHandler Get bucket location
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketRequestPayment.html
func (s3a *S3ApiServer) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
	writeSuccessResponseXML(w, r, RequestPaymentConfiguration{Payer: "BucketOwner"})
}
<commit_msg>Add "Location:" in response when creating bucket according to "https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html"<commit_after>package s3api

import (
	"context"
	"encoding/xml"
	"errors"
	"fmt"
	"math"
	"net/http"
	"time"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
	"github.com/chrislusf/seaweedfs/weed/storage/needle"

	xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

type ListAllMyBucketsResult struct {
	XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult"`
	Owner   *s3.Owner
	Buckets []*s3.Bucket `xml:"Buckets>Bucket"`
}

func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {

	glog.V(3).Infof("ListBucketsHandler")

	var identity *Identity
	var s3Err s3err.ErrorCode
	if s3a.iam.isEnabled() {
		identity, s3Err = s3a.iam.authUser(r)
		if s3Err != s3err.ErrNone {
			s3err.WriteErrorResponse(w, r, s3Err)
			return
		}
	}

	var response ListAllMyBucketsResult

	entries, _, err := s3a.list(s3a.option.BucketsPath, "", "", false, math.MaxInt32)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	identityId := r.Header.Get(xhttp.AmzIdentityId)

	var buckets []*s3.Bucket
	for _, entry := range entries {
		if entry.IsDirectory {
			if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name, "") {
				continue
			}
			buckets = append(buckets, &s3.Bucket{
				Name:         aws.String(entry.Name),
				CreationDate: aws.Time(time.Unix(entry.Attributes.Crtime, 0).UTC()),
			})
		}
	}

	response = ListAllMyBucketsResult{
		Owner: &s3.Owner{
			ID:          aws.String(identityId),
			DisplayName: aws.String(identityId),
		},
		Buckets: buckets,
	}

	writeSuccessResponseXML(w, r, response)
}

func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("PutBucketHandler %s", bucket)

	// avoid duplicated buckets
	errCode := s3err.ErrNone
	if err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
		if resp, err := client.CollectionList(context.Background(), &filer_pb.CollectionListRequest{
			IncludeEcVolumes:     true,
			IncludeNormalVolumes: true,
		}); err != nil {
			glog.Errorf("list collection: %v", err)
			return fmt.Errorf("list collections: %v", err)
		} else {
			for _, c := range resp.Collections {
				if bucket == c.Name {
					errCode = s3err.ErrBucketAlreadyExists
					break
				}
			}
		}
		return nil
	}); err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}
	if exist, err := s3a.exists(s3a.option.BucketsPath, bucket, true); err == nil && exist {
		errCode = s3err.ErrBucketAlreadyExists
	}
	if errCode != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, errCode)
		return
	}

	if s3a.iam.isEnabled() {
		if _, errCode = s3a.iam.authRequest(r, s3_constants.ACTION_ADMIN); errCode != s3err.ErrNone {
			s3err.WriteErrorResponse(w, r, errCode)
			return
		}
	}

	fn := func(entry *filer_pb.Entry) {
		if identityId := r.Header.Get(xhttp.AmzIdentityId); identityId != "" {
			if entry.Extended == nil {
				entry.Extended = make(map[string][]byte)
			}
			entry.Extended[xhttp.AmzIdentityId] = []byte(identityId)
		}
	}

	// create the folder for bucket, but lazily create actual collection
	if err := s3a.mkdir(s3a.option.BucketsPath, bucket, fn); err != nil {
		glog.Errorf("PutBucketHandler mkdir: %v", err)
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}
	w.Header().Set("Location", "/" + bucket)
	writeSuccessResponseEmpty(w, r)
}

func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("DeleteBucketHandler %s", bucket)

	if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, err)
		return
	}

	err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
		if !s3a.option.AllowDeleteBucketNotEmpty {
			entries, _, err := s3a.list(s3a.option.BucketsPath+"/"+bucket, "", "", false, 1)
			if err != nil {
				return fmt.Errorf("failed to list bucket %s: %v", bucket, err)
			}
			if len(entries) > 0 {
				return errors.New(s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code)
			}
		}

		// delete collection
		deleteCollectionRequest := &filer_pb.DeleteCollectionRequest{
			Collection: bucket,
		}

		glog.V(1).Infof("delete collection: %v", deleteCollectionRequest)
		if _, err := client.DeleteCollection(context.Background(), deleteCollectionRequest); err != nil {
			return fmt.Errorf("delete collection %s: %v", bucket, err)
		}

		return nil
	})

	if err != nil {
		s3ErrorCode := s3err.ErrInternalError
		if err.Error() == s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code {
			s3ErrorCode = s3err.ErrBucketNotEmpty
		}
		s3err.WriteErrorResponse(w, r, s3ErrorCode)
		return
	}

	err = s3a.rm(s3a.option.BucketsPath, bucket, false, true)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
}

func (s3a *S3ApiServer) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("HeadBucketHandler %s", bucket)

	if entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket); entry == nil || err == filer_pb.ErrNotFound {
		s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
		return
	}

	writeSuccessResponseEmpty(w, r)
}

func (s3a *S3ApiServer) checkBucket(r *http.Request, bucket string) s3err.ErrorCode {
	entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
	if entry == nil || err == filer_pb.ErrNotFound {
		return s3err.ErrNoSuchBucket
	}

	if !s3a.hasAccess(r, entry) {
		return s3err.ErrAccessDenied
	}
	return s3err.ErrNone
}

func (s3a *S3ApiServer) hasAccess(r *http.Request, entry *filer_pb.Entry) bool {
	isAdmin := r.Header.Get(xhttp.AmzIsAdmin) != ""
	if isAdmin {
		return true
	}
	if entry.Extended == nil {
		return true
	}

	identityId := r.Header.Get(xhttp.AmzIdentityId)
	if id, ok := entry.Extended[xhttp.AmzIdentityId]; ok {
		if identityId != string(id) {
			return false
		}
	}
	return true
}

// GetBucketAclHandler Get Bucket ACL
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAcl.html
func (s3a *S3ApiServer) GetBucketAclHandler(w http.ResponseWriter, r *http.Request) {
	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("GetBucketAclHandler %s", bucket)

	if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, err)
		return
	}

	response := AccessControlPolicy{}
	for _, ident := range s3a.iam.identities {
		if len(ident.Credentials) == 0 {
			continue
		}
		for _, action := range ident.Actions {
			if !action.overBucket(bucket) || action.getPermission() == "" {
				continue
			}
			id := ident.Credentials[0].AccessKey
			if response.Owner.DisplayName == "" && action.isOwner(bucket) && len(ident.Credentials) > 0 {
				response.Owner.DisplayName = ident.Name
				response.Owner.ID = id
			}
			response.AccessControlList.Grant = append(response.AccessControlList.Grant, Grant{
				Grantee: Grantee{
					ID:          id,
					DisplayName: ident.Name,
					Type:        "CanonicalUser",
					XMLXSI:      "CanonicalUser",
					XMLNS:       "http://www.w3.org/2001/XMLSchema-instance"},
				Permission: action.getPermission(),
			})
		}
	}
	writeSuccessResponseXML(w, r, response)
}

// GetBucketLifecycleConfigurationHandler Get Bucket Lifecycle configuration
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html
func (s3a *S3ApiServer) GetBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {
	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("GetBucketLifecycleConfigurationHandler %s", bucket)

	if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
		s3err.WriteErrorResponse(w, r, err)
		return
	}
	fc, err := filer.ReadFilerConf(s3a.option.Filer, s3a.option.GrpcDialOption, nil)
	if err != nil {
		glog.Errorf("GetBucketLifecycleConfigurationHandler: %s", err)
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}
	ttls := fc.GetCollectionTtls(bucket)
	if len(ttls) == 0 {
		s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchLifecycleConfiguration)
		return
	}
	response := Lifecycle{}
	for prefix, internalTtl := range ttls {
		ttl, _ := needle.ReadTTL(internalTtl)
		days := int(ttl.Minutes() / 60 / 24)
		if days == 0 {
			continue
		}
		response.Rules = append(response.Rules, Rule{
			Status: Enabled, Filter: Filter{
				Prefix: Prefix{string: prefix, set: true},
				set:    true,
			},
			Expiration: Expiration{Days: days, set: true},
		})
	}
	writeSuccessResponseXML(w, r, response)
}

// PutBucketLifecycleConfigurationHandler Put Bucket Lifecycle configuration
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html
func (s3a *S3ApiServer) PutBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {

	s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)

}

// DeleteBucketMetricsConfiguration Delete Bucket Lifecycle
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html
func (s3a *S3ApiServer) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {

	s3err.WriteEmptyResponse(w, r, http.StatusNoContent)

}

// GetBucketLocationHandler Get bucket location
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html
func (s3a *S3ApiServer) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
	writeSuccessResponseXML(w, r, LocationConstraint{})
}

// GetBucketRequestPaymentHandler Get bucket location
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketRequestPayment.html
func (s3a *S3ApiServer) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
	writeSuccessResponseXML(w, r, RequestPaymentConfiguration{Payer: "BucketOwner"})
}
<|endoftext|>


<commit_before>package infermedica

import (
	"encoding/json"
	"net/http"

	"github.com/pkg/errors"
)

type LabTestsRes struct {
	ID         string      `json:"id"`
	Name       string      `json:"name"`
	CommonName string      `json:"common_name"`
	Category   string      `json:"category"`
	Results    []LabResult `json:"results"`
}

type LabResult struct {
	ID   string `json:"id"`
	Type string `json:"type"`
}

func (a *App) LabTests() (*[]LabTestsRes, error) {
	req, err := a.prepareRequest("GET", "lab_tests", nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := []LabTestsRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

func (a *App) LabTestByID(id string) (*LabTestsRes, error) {
	req, err := a.prepareRequest("GET", "lab_tests/"+id, nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := LabTestsRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

type LabTestsRecommendRes struct {
	Recommended []LabTestsRecommendation `json:"recommended"`
	Obligatory  []LabTestsRecommendation `json:"obligatory"`
}
type LabTestsRecommendation struct {
	PanelID  string       `json:"panel_id"`
	Name     string       `json:"name"`
	Position int          `json:"position"`
	LabTests []LabTestsID `json:"lab_tests"`
}
type LabTestsID struct {
	ID string `json:"id"`
}

// Recommend is a func to request lab test recommendations for given data
func (a *App) LabTestsRecommend(dr DiagnosisReq) (*LabTestsRecommendRes, error) {
	if !dr.Sex.IsValid() {
		return nil, errors.New("Unexpected value for Sex")
	}
	req, err := a.prepareRequest("POST", "lab_tests/recommend", dr)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := LabTestsRecommendRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}
<commit_msg>added LabTestsIDMap<commit_after>package infermedica

import (
	"encoding/json"
	"net/http"

	"github.com/pkg/errors"
)

type LabTestsRes struct {
	ID         string      `json:"id"`
	Name       string      `json:"name"`
	CommonName string      `json:"common_name"`
	Category   string      `json:"category"`
	Results    []LabResult `json:"results"`
}

type LabResult struct {
	ID   string `json:"id"`
	Type string `json:"type"`
}

func (a *App) LabTests() (*[]LabTestsRes, error) {
	req, err := a.prepareRequest("GET", "lab_tests", nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := []LabTestsRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

func (a *App) LabTestsIDMap() (*map[string]LabTestsRes, error) {
	r, err := a.LabTests()
	if err != nil {
		return nil, err
	}
	rmap := make(map[string]LabTestsRes)
	for _, sr := range *r {
		rmap[sr.ID] = sr
	}
	return &rmap, nil
}

func (a *App) LabTestByID(id string) (*LabTestsRes, error) {
	req, err := a.prepareRequest("GET", "lab_tests/"+id, nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := LabTestsRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

type LabTestsRecommendRes struct {
	Recommended []LabTestsRecommendation `json:"recommended"`
	Obligatory  []LabTestsRecommendation `json:"obligatory"`
}
type LabTestsRecommendation struct {
	PanelID  string       `json:"panel_id"`
	Name     string       `json:"name"`
	Position int          `json:"position"`
	LabTests []LabTestsID `json:"lab_tests"`
}
type LabTestsID struct {
	ID string `json:"id"`
}

// Recommend is a func to request lab test recommendations for given data
func (a *App) LabTestsRecommend(dr DiagnosisReq) (*LabTestsRecommendRes, error) {
	if !dr.Sex.IsValid() {
		return nil, errors.New("Unexpected value for Sex")
	}
	req, err := a.prepareRequest("POST", "lab_tests/recommend", dr)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := LabTestsRecommendRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}
<|endoftext|>


<commit_before>package lfs

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/rubyist/tracerx"
	"io"
	"net/http"
	"os"
	"strings"
)

func DoHTTP(c *Configuration, req *http.Request) (*http.Response, error) {
	traceHttpRequest(c, req)
	res, err := c.HttpClient().Do(req)
	if res == nil {
		res = &http.Response{StatusCode: 0, Header: make(http.Header), Request: req}
	}
	traceHttpResponse(c, res)
	return res, err
}

func (c *Configuration) HttpClient() *http.Client {
	if c.httpClient == nil {
		tr := &http.Transport{}
		sslVerify, _ := c.GitConfig("http.sslverify")
		if sslVerify == "false" || len(os.Getenv("GIT_SSL_NO_VERIFY")) > 0 {
			tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		}
		c.httpClient = &http.Client{
			Transport:     tr,
			CheckRedirect: checkRedirect,
		}
	}
	return c.httpClient
}

func checkRedirect(req *http.Request, via []*http.Request) error {
	if len(via) >= 3 {
		return errors.New("stopped after 3 redirects")
	}

	oldest := via[0]
	for key, _ := range oldest.Header {
		if key == "Authorization" {
			if req.URL.Scheme != oldest.URL.Scheme || req.URL.Host != oldest.URL.Host {
				continue
			}
		}
		req.Header.Set(key, oldest.Header.Get(key))
	}

	tracerx.Printf("api: redirect %s %s to %s", oldest.Method, oldest.URL, req.URL)

	return nil
}

var tracedTypes = []string{"json", "text", "xml", "html"}

func traceHttpRequest(c *Configuration, req *http.Request) {
	tracerx.Printf("HTTP: %s %s", req.Method, req.URL.String())

	if c.isTracingHttp == false {
		return
	}

	if req.Body != nil {
		req.Body = newCountedRequest(req)
	}

	fmt.Fprintf(os.Stderr, "> %s %s %s\n", req.Method, req.URL.RequestURI(), req.Proto)
	for key, _ := range req.Header {
		fmt.Fprintf(os.Stderr, "> %s: %s\n", key, req.Header.Get(key))
	}
}

func traceHttpResponse(c *Configuration, res *http.Response) {
	if res == nil {
		return
	}

	tracerx.Printf("HTTP: %d", res.StatusCode)

	if c.isTracingHttp == false {
		return
	}

	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "< %s %s\n", res.Proto, res.Status)
	for key, _ := range res.Header {
		fmt.Fprintf(os.Stderr, "< %s: %s\n", key, res.Header.Get(key))
	}

	traceBody := false
	ctype := strings.ToLower(strings.SplitN(res.Header.Get("Content-Type"), ";", 2)[0])
	for _, tracedType := range tracedTypes {
		if strings.Contains(ctype, tracedType) {
			traceBody = true
		}
	}

	res.Body = newCountedResponse(res)
	if traceBody {
		res.Body = newTracedBody(res.Body)
	}

	fmt.Fprintf(os.Stderr, "\n")
}

const (
	countingUpload = iota
	countingDownload
)

type countingBody struct {
	Direction int
	Size      int64
	io.ReadCloser
}

func (r *countingBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	r.Size += int64(n)
	return n, err
}

func (r *countingBody) Close() error {
	if r.Direction == countingUpload {
		fmt.Fprintf(os.Stderr, "* uploaded %d bytes\n", r.Size)
	} else {
		fmt.Fprintf(os.Stderr, "* downloaded %d bytes\n", r.Size)
	}
	return r.ReadCloser.Close()
}

func newCountedResponse(res *http.Response) *countingBody {
	return &countingBody{countingDownload, 0, res.Body}
}

func newCountedRequest(req *http.Request) *countingBody {
	return &countingBody{countingUpload, 0, req.Body}
}

type tracedBody struct {
	io.ReadCloser
}

func (r *tracedBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	fmt.Fprintf(os.Stderr, "%s\n", string(p[0:n]))
	return n, err
}

func (r *tracedBody) Close() error {
	return r.ReadCloser.Close()
}

func newTracedBody(body io.ReadCloser) *tracedBody {
	return &tracedBody{body}
}
<commit_msg>add default timeout to http client<commit_after>package lfs

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/rubyist/tracerx"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"time"
)

func DoHTTP(c *Configuration, req *http.Request) (*http.Response, error) {
	traceHttpRequest(c, req)
	res, err := c.HttpClient().Do(req)
	if res == nil {
		res = &http.Response{StatusCode: 0, Header: make(http.Header), Request: req}
	}
	traceHttpResponse(c, res)
	return res, err
}

func (c *Configuration) HttpClient() *http.Client {
	if c.httpClient == nil {
		tr := &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: 30 * time.Second,
		}
		sslVerify, _ := c.GitConfig("http.sslverify")
		if sslVerify == "false" || len(os.Getenv("GIT_SSL_NO_VERIFY")) > 0 {
			tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		}
		c.httpClient = &http.Client{
			Transport:     tr,
			CheckRedirect: checkRedirect,
		}
	}
	return c.httpClient
}

func checkRedirect(req *http.Request, via []*http.Request) error {
	if len(via) >= 3 {
		return errors.New("stopped after 3 redirects")
	}

	oldest := via[0]
	for key, _ := range oldest.Header {
		if key == "Authorization" {
			if req.URL.Scheme != oldest.URL.Scheme || req.URL.Host != oldest.URL.Host {
				continue
			}
		}
		req.Header.Set(key, oldest.Header.Get(key))
	}

	tracerx.Printf("api: redirect %s %s to %s", oldest.Method, oldest.URL, req.URL)

	return nil
}

var tracedTypes = []string{"json", "text", "xml", "html"}

func traceHttpRequest(c *Configuration, req *http.Request) {
	tracerx.Printf("HTTP: %s %s", req.Method, req.URL.String())

	if c.isTracingHttp == false {
		return
	}

	if req.Body != nil {
		req.Body = newCountedRequest(req)
	}

	fmt.Fprintf(os.Stderr, "> %s %s %s\n", req.Method, req.URL.RequestURI(), req.Proto)
	for key, _ := range req.Header {
		fmt.Fprintf(os.Stderr, "> %s: %s\n", key, req.Header.Get(key))
	}
}

func traceHttpResponse(c *Configuration, res *http.Response) {
	if res == nil {
		return
	}

	tracerx.Printf("HTTP: %d", res.StatusCode)

	if c.isTracingHttp == false {
		return
	}

	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "< %s %s\n", res.Proto, res.Status)
	for key, _ := range res.Header {
		fmt.Fprintf(os.Stderr, "< %s: %s\n", key, res.Header.Get(key))
	}

	traceBody := false
	ctype := strings.ToLower(strings.SplitN(res.Header.Get("Content-Type"), ";", 2)[0])
	for _, tracedType := range tracedTypes {
		if strings.Contains(ctype, tracedType) {
			traceBody = true
		}
	}

	res.Body = newCountedResponse(res)
	if traceBody {
		res.Body = newTracedBody(res.Body)
	}

	fmt.Fprintf(os.Stderr, "\n")
}

const (
	countingUpload = iota
	countingDownload
)

type countingBody struct {
	Direction int
	Size      int64
	io.ReadCloser
}

func (r *countingBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	r.Size += int64(n)
	return n, err
}

func (r *countingBody) Close() error {
	if r.Direction == countingUpload {
		fmt.Fprintf(os.Stderr, "* uploaded %d bytes\n", r.Size)
	} else {
		fmt.Fprintf(os.Stderr, "* downloaded %d bytes\n", r.Size)
	}
	return r.ReadCloser.Close()
}

func newCountedResponse(res *http.Response) *countingBody {
	return &countingBody{countingDownload, 0, res.Body}
}

func newCountedRequest(req *http.Request) *countingBody {
	return &countingBody{countingUpload, 0, req.Body}
}

type tracedBody struct {
	io.ReadCloser
}

func (r *tracedBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	fmt.Fprintf(os.Stderr, "%s\n", string(p[0:n]))
	return n, err
}

func (r *tracedBody) Close() error {
	return r.ReadCloser.Close()
}

func newTracedBody(body io.ReadCloser) *tracedBody {
	return &tracedBody{body}
}
<|endoftext|>


<commit_before>// This file includes unit tests for the LGE data type.

package intern_test

import (
	"fmt"
	"math/rand"
	"testing"

	"github.com/spakin/intern"
)

// TestPreLGEDups tests if we can create a large number of symbols for which
// duplicates are certain to occur.
func TestPreLGEDups(t *testing.T) {
	intern.ForgetAllLGE()
	const sLen = 3                        // Symbol length in characters
	const nSymbols = 1000000              // Must be greater than len(charSet) choose sLen
	prng := rand.New(rand.NewSource(910)) // Constant for reproducibility
	for i := 0; i < nSymbols; i++ {
		intern.PreLGE(randomString(prng, sLen))
	}
	_, err := intern.NewLGE("Yet another string") // Force tree construction.
	if err != nil {
		t.Fatal(err)
	}
}

// TestPreLGENoDups tests if we can create a large number of symbols for which
// duplicates are extremely unlikely to occur.
func TestPreLGENoDups(t *testing.T) {
	intern.ForgetAllLGE()
	const sLen = 50                        // Symbol length in characters
	const nSymbols = 100000                // Number of symbols to generate
	prng := rand.New(rand.NewSource(1112)) // Constant for reproducibility
	for i := 0; i < nSymbols; i++ {
		intern.PreLGE(randomString(prng, sLen))
	}
	_, err := intern.NewLGE("Yet another string") // Force tree construction.
	if err != nil {
		t.Fatal(err)
	}
}

// TestNewLGEFull tests that the tree does fill up and return an error if we
// don't use PreLGE.
func TestNewLGEFull(t *testing.T) {
	// Creating 64 symbols in alphabetical order should work.
	intern.ForgetAllLGE()
	var i int
	for i = 0; i < 64; i++ {
		str := fmt.Sprintf("This is symbol #%03d.", i+1)
		_, err := intern.NewLGE(str)
		if err != nil {
			t.Fatal(err)
		}
	}

	// Creating 65 symbols in alphabetical order should fail.
	str := fmt.Sprintf("This is symbol #%03d.", i)
	_, err := intern.NewLGE(str)
	if err == nil {
		t.Fatal("NewLGE failed to return an error when its symbol table filled up")
	}
}

// TestLGEOrder ensures that LGE symbol comparisons match the corresponding
// string comparisons.
func TestLGEOrder(t *testing.T) {
	// Create a bunch of random strings.
	intern.ForgetAllLGE()
	const sLen = 10                        // Symbol length in characters
	const nSymbols = 100                   // Number of symbols to generate
	prng := rand.New(rand.NewSource(1314)) // Constant for reproducibility
	strList := make([]string, nSymbols)
	for i := range strList {
		strList[i] = randomString(prng, sLen)
	}

	// Convert all of the strings to LGE symbols.
	for _, str := range strList {
		intern.PreLGE(str)
	}
	symList := make([]intern.LGE, nSymbols)
	for i, str := range strList {
		var err error
		symList[i], err = intern.NewLGE(str)
		if err != nil {
			t.Fatal(err)
		}
	}

	// Compare all symbols.
	for i, sym1 := range symList {
		str1 := strList[i]
		for j, sym2 := range symList {
			str2 := strList[j]
			switch {
			case sym1 < sym2 && str1 < str2:
			case sym1 == sym2 && str1 == str2:
			case sym1 > sym2 && str1 > str2:
			default:
				t.Fatalf("Strings %q and %q mapped incorrectly to LGEs %d and %d", str1, str2, sym1, sym2)
			}
		}
	}
}

// TestLGEString tests if we can convert strings to LGEs and back to strings.
func TestLGEString(t *testing.T) {
	// Prepare the test.
	const ns = 10000                       // Number of strings to generate
	strs := make([]string, ns)             // Original strings
	syms := make([]intern.LGE, ns)         // Interned strings
	prng := rand.New(rand.NewSource(1516)) // Constant for reproducibility

	// Generate a bunch of strings.
	for i := range strs {
		nc := prng.Intn(20) + 1 // Number of characters
		strs[i] = randomString(prng, nc)
	}

	// Intern each string to an LGE.
	for _, s := range strs {
		intern.PreLGE(s)
	}
	var err error
	for i, s := range strs {
		syms[i], err = intern.NewLGE(s)
		if err != nil {
			t.Error(err)
		}
	}

	// Ensure that converting an LGE back to a string is a lossless
	// operation.  We use fmt.Sprintf as this represents a typical way an
	// LGE might be converted to a string.
	for i, str := range strs {
		sym := syms[i]
		sStr := fmt.Sprintf("%s", sym)
		if str != sStr {
			t.Errorf("Expected %q but saw %q", str, sStr)
		}
	}
}

// TestBadLGE ensures we panic when converting an invalid LGE to a
// string.
func TestBadLGE(t *testing.T) {
	defer func() { _ = recover() }()
	var bad intern.LGE
	_ = bad.String() // Should panic
	t.Errorf("Failed to catch invalid intern.LGE %d", bad)
}
<commit_msg>Added another LGE test (TestLGECase)<commit_after>// This file includes unit tests for the LGE data type.

package intern_test

import (
	"fmt"
	"math/rand"
	"testing"

	"github.com/spakin/intern"
)

// TestPreLGEDups tests if we can create a large number of symbols for which
// duplicates are certain to occur.
func TestPreLGEDups(t *testing.T) {
	intern.ForgetAllLGE()
	const sLen = 3                        // Symbol length in characters
	const nSymbols = 1000000              // Must be greater than len(charSet) choose sLen
	prng := rand.New(rand.NewSource(910)) // Constant for reproducibility
	for i := 0; i < nSymbols; i++ {
		intern.PreLGE(randomString(prng, sLen))
	}
	_, err := intern.NewLGE("Yet another string") // Force tree construction.
	if err != nil {
		t.Fatal(err)
	}
}

// TestPreLGENoDups tests if we can create a large number of symbols for which
// duplicates are extremely unlikely to occur.
func TestPreLGENoDups(t *testing.T) {
	intern.ForgetAllLGE()
	const sLen = 50                        // Symbol length in characters
	const nSymbols = 100000                // Number of symbols to generate
	prng := rand.New(rand.NewSource(1112)) // Constant for reproducibility
	for i := 0; i < nSymbols; i++ {
		intern.PreLGE(randomString(prng, sLen))
	}
	_, err := intern.NewLGE("Yet another string") // Force tree construction.
	if err != nil {
		t.Fatal(err)
	}
}

// TestNewLGEFull tests that the tree does fill up and return an error if we
// don't use PreLGE.
func TestNewLGEFull(t *testing.T) {
	// Creating 64 symbols in alphabetical order should work.
	intern.ForgetAllLGE()
	var i int
	for i = 0; i < 64; i++ {
		str := fmt.Sprintf("This is symbol #%03d.", i+1)
		_, err := intern.NewLGE(str)
		if err != nil {
			t.Fatal(err)
		}
	}

	// Creating 65 symbols in alphabetical order should fail.
	str := fmt.Sprintf("This is symbol #%03d.", i)
	_, err := intern.NewLGE(str)
	if err == nil {
		t.Fatal("NewLGE failed to return an error when its symbol table filled up")
	}
}

// TestLGEOrder ensures that LGE symbol comparisons match the corresponding
// string comparisons.
func TestLGEOrder(t *testing.T) {
	// Create a bunch of random strings.
	intern.ForgetAllLGE()
	const sLen = 10                        // Symbol length in characters
	const nSymbols = 100                   // Number of symbols to generate
	prng := rand.New(rand.NewSource(1314)) // Constant for reproducibility
	strList := make([]string, nSymbols)
	for i := range strList {
		strList[i] = randomString(prng, sLen)
	}

	// Convert all of the strings to LGE symbols.
	for _, str := range strList {
		intern.PreLGE(str)
	}
	symList := make([]intern.LGE, nSymbols)
	for i, str := range strList {
		var err error
		symList[i], err = intern.NewLGE(str)
		if err != nil {
			t.Fatal(err)
		}
	}

	// Compare all symbols.
	for i, sym1 := range symList {
		str1 := strList[i]
		for j, sym2 := range symList {
			str2 := strList[j]
			switch {
			case sym1 < sym2 && str1 < str2:
			case sym1 == sym2 && str1 == str2:
			case sym1 > sym2 && str1 > str2:
			default:
				t.Fatalf("Strings %q and %q mapped incorrectly to LGEs %d and %d", str1, str2, sym1, sym2)
			}
		}
	}
}

// TestLGEString tests if we can convert strings to LGEs and back to strings.
func TestLGEString(t *testing.T) {
	// Prepare the test.
	const ns = 10000                       // Number of strings to generate
	strs := make([]string, ns)             // Original strings
	syms := make([]intern.LGE, ns)         // Interned strings
	prng := rand.New(rand.NewSource(1516)) // Constant for reproducibility

	// Generate a bunch of strings.
	for i := range strs {
		nc := prng.Intn(20) + 1 // Number of characters
		strs[i] = randomString(prng, nc)
	}

	// Intern each string to an LGE.
	for _, s := range strs {
		intern.PreLGE(s)
	}
	var err error
	for i, s := range strs {
		syms[i], err = intern.NewLGE(s)
		if err != nil {
			t.Error(err)
		}
	}

	// Ensure that converting an LGE back to a string is a lossless
	// operation.  We use fmt.Sprintf as this represents a typical way an
	// LGE might be converted to a string.
	for i, str := range strs {
		sym := syms[i]
		sStr := fmt.Sprintf("%s", sym)
		if str != sStr {
			t.Errorf("Expected %q but saw %q", str, sStr)
		}
	}
}

// TestBadLGE ensures we panic when converting an invalid LGE to a
// string.
func TestBadLGE(t *testing.T) {
	defer func() { _ = recover() }()
	var bad intern.LGE
	_ = bad.String() // Should panic
	t.Errorf("Failed to catch invalid intern.LGE %d", bad)
}

// TestLGECase ensures that symbol comparisons are case-sensitive.
func TestLGECase(t *testing.T) {
	// Convert a set of strings to LGEs.
	strs := []string{
		"roadrunner",
		"Roadrunner",
		"roadRunner",
		"ROADRUNNER",
		"rOaDrUnNeR",
		"ROADrunner",
		"roadRUNNER",
	}
	syms := make([]intern.LGE, len(strs))
	var err error
	for i, s := range strs {
		syms[i], err = intern.NewLGE(s)
		if err != nil {
			t.Fatal(err)
		}
	}

	// Ensure that each symbol is equal only to itself.
	numLGE := 0
	for _, s1 := range syms {
		for _, s2 := range syms {
			if s1 == s2 {
				numLGE++
			}
		}
	}
	if numLGE != len(syms) {
		t.Errorf("Expected %d case-sensitive comparisons but saw %d",
			len(syms), numLGE)
	}
}
<|endoftext|>


<commit_before>package jog

import (
	"bytes"
	"testing"
	"time"
)

func FixedClock() time.Time {
	return time.Date(2012, 11, 24, 16, 32, 56, 123456789, time.UTC)
}

var FIXED_TIME = FixedClock().Format(time.RFC3339Nano)

func TestExplicit(t *testing.T) {
	// one test avoiding the formatting helpers, just to show they're
	// not buggy
	buf := new(bytes.Buffer)
	conf := Config{
		Out: buf,
	}
	log := New(&conf)
	log.clock = FixedClock
	type xyzzy struct {
		Quux string
		Thud int
	}
	log.Event(xyzzy{Quux: "foo", Thud: 42})
	got := buf.String()
	want := `{"Time":"` + FIXED_TIME + `","Type":"github.com/tv42/jog#xyzzy","Data":{"Quux":"foo","Thud":42}}` + "\n"
	if got != want {
		t.Errorf("wrong output: %q != %s", got, want)
	}
}

func testEvent(t *testing.T, data interface{}, type_ string, want string) {
	buf := new(bytes.Buffer)
	conf := Config{
		Out: buf,
	}
	log := New(&conf)
	log.clock = FixedClock
	log.Event(data)
	got := buf.String()
	want = `{"Time":"` + FIXED_TIME + `","Type":"` + type_ + `","Data":` + want + `}` + "\n"
	if got != want {
		t.Errorf("wrong output: %q != %s", got, want)
	}
}

func TestSimple(t *testing.T) {
	type frob struct {
		Quux string
		Thud int
	}
	testEvent(t, frob{Quux: "foo", Thud: 42},
		"github.com/tv42/jog#frob", `{"Quux":"foo","Thud":42}`)
}

func TestPointer(t *testing.T) {
	type frob struct {
		Quux string
		Thud int
	}
	testEvent(t, &frob{Quux: "foo", Thud: 42},
		"github.com/tv42/jog#frob", `{"Quux":"foo","Thud":42}`)
}

func TestEmpty(t *testing.T) {
	type justMyPresence struct {
	}
	testEvent(t, justMyPresence{},
		"github.com/tv42/jog#justMyPresence", `{}`)
}

func TestNilPointer(t *testing.T) {
	// still a typed nil, not an interface{} nil
	type justMyPresence struct {
	}
	testEvent(t, &justMyPresence{},
		"github.com/tv42/jog#justMyPresence", `{}`)
}
<commit_msg>Verify custom MarshalJSON output cannot contain newlines.<commit_after>package jog

import (
	"bytes"
	"testing"
	"time"
)

func FixedClock() time.Time {
	return time.Date(2012, 11, 24, 16, 32, 56, 123456789, time.UTC)
}

var FIXED_TIME = FixedClock().Format(time.RFC3339Nano)

func TestExplicit(t *testing.T) {
	// one test avoiding the formatting helpers, just to show they're
	// not buggy
	buf := new(bytes.Buffer)
	conf := Config{
		Out: buf,
	}
	log := New(&conf)
	log.clock = FixedClock
	type xyzzy struct {
		Quux string
		Thud int
	}
	log.Event(xyzzy{Quux: "foo", Thud: 42})
	got := buf.String()
	want := `{"Time":"` + FIXED_TIME + `","Type":"github.com/tv42/jog#xyzzy","Data":{"Quux":"foo","Thud":42}}` + "\n"
	if got != want {
		t.Errorf("wrong output: %q != %s", got, want)
	}
}

func testEvent(t *testing.T, data interface{}, type_ string, want string) {
	buf := new(bytes.Buffer)
	conf := Config{
		Out: buf,
	}
	log := New(&conf)
	log.clock = FixedClock
	log.Event(data)
	got := buf.String()
	want = `{"Time":"` + FIXED_TIME + `","Type":"` + type_ + `","Data":` + want + `}` + "\n"
	if got != want {
		t.Errorf("wrong output: %q != %s", got, want)
	}
}

func TestSimple(t *testing.T) {
	type frob struct {
		Quux string
		Thud int
	}
	testEvent(t, frob{Quux: "foo", Thud: 42},
		"github.com/tv42/jog#frob", `{"Quux":"foo","Thud":42}`)
}

func TestPointer(t *testing.T) {
	type frob struct {
		Quux string
		Thud int
	}
	testEvent(t, &frob{Quux: "foo", Thud: 42},
		"github.com/tv42/jog#frob", `{"Quux":"foo","Thud":42}`)
}

func TestEmpty(t *testing.T) {
	type justMyPresence struct {
	}
	testEvent(t, justMyPresence{},
		"github.com/tv42/jog#justMyPresence", `{}`)
}

func TestNilPointer(t *testing.T) {
	// still a typed nil, not an interface{} nil
	type justMyPresence struct {
	}
	testEvent(t, &justMyPresence{},
		"github.com/tv42/jog#justMyPresence", `{}`)
}

type extraNewlines struct {
}

func (extraNewlines) MarshalJSON() ([]byte, error) {
	return []byte{'{', '\n', '}'}, nil
}

// We rely on encoding/json compacting custom MarshalJSON output, and
// letting that guarantee there will be no extra newlines in the
// output. Verify that assumption.
func TestMarshalerNewline(t *testing.T) {
	testEvent(t, extraNewlines{},
		"github.com/tv42/jog#extraNewlines", `{}`)
}
<|endoftext|>


<commit_before>package jose

import (
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/json"
	"math/big"
)

// JWK

type rawJsonWebKey struct {
	// Only public key fields, since we only require verification
	// Keep lexicographic order here so MarshalJSON outputs in the
	// same lexicographic order!
	Crv string     `json:"crv,omitempty"` // XXX Use an enum
	E   JsonBuffer `json:"e,omitempty"`
	Kty string     `json:"kty,omitempty"` // XXX Use an enum
	N   JsonBuffer `json:"n,omitempty"`
	X   JsonBuffer `json:"x,omitempty"`
	Y   JsonBuffer `json:"y,omitempty"`
}

type JsonWebKey struct {
	KeyType    JoseKeyType
	Rsa        *rsa.PublicKey
	Ec         *ecdsa.PublicKey
	Thumbprint string
}

func (jwk *JsonWebKey) ComputeThumbprint() {
	var jsonThumbprint []byte
	var err error
	jsonThumbprint, err = jwk.MarshalJSON()
	if err != nil {
		return
	}
	tpHash := sha256.Sum256(jsonThumbprint)

	jwk.Thumbprint = B64enc(tpHash[:])
}

// Normal Go == operator compares pointers directly, so it doesn't
// match the semantic of two keys being equivalent
func (jwk1 JsonWebKey) Equals(jwk2 JsonWebKey) bool {
	jwk1.ComputeThumbprint()
	jwk2.ComputeThumbprint()
	return (jwk1.Thumbprint == jwk2.Thumbprint)
}

func (jwk JsonWebKey) MarshalJSON() ([]byte, error) {
	raw := rawJsonWebKey{Kty: string(jwk.KeyType)}
	if jwk.Rsa != nil {
		raw.N = jwk.Rsa.N.Bytes()
		raw.E = big.NewInt(int64(jwk.Rsa.E)).Bytes()
	}
	if jwk.Ec != nil {
		var err error
		raw.Crv, err = curve2name(jwk.Ec.Curve)
		if err != nil {
			return nil, err
		}

		raw.X = jwk.Ec.X.Bytes()
		raw.Y = jwk.Ec.Y.Bytes()
	}

	return json.Marshal(raw)
}

func (jwk *JsonWebKey) UnmarshalJSON(data []byte) error {
	var raw rawJsonWebKey
	err := json.Unmarshal(data, &raw)
	if err != nil {
		return err
	}

	jwk.KeyType = JoseKeyType(raw.Kty)
	switch jwk.KeyType {
	case "RSA":
		jwk.Rsa = &rsa.PublicKey{
			N: raw.N.ToBigInt(),
			E: raw.E.ToInt(),
		}
	case "EC":
		curve, err := name2curve(raw.Crv)
		if err != nil {
			return err
		}

		jwk.Ec = &ecdsa.PublicKey{
			Curve: curve,
			X:     raw.X.ToBigInt(),
			Y:     raw.Y.ToBigInt(),
		}
	}

	jwk.ComputeThumbprint()
	return nil
}
<commit_msg>get rid of useless var declarations<commit_after>package jose

import (
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/json"
	"math/big"
)

// JWK

type rawJsonWebKey struct {
	// Only public key fields, since we only require verification
	// Keep lexicographic order here so MarshalJSON outputs in the
	// same lexicographic order!
	Crv string     `json:"crv,omitempty"` // XXX Use an enum
	E   JsonBuffer `json:"e,omitempty"`
	Kty string     `json:"kty,omitempty"` // XXX Use an enum
	N   JsonBuffer `json:"n,omitempty"`
	X   JsonBuffer `json:"x,omitempty"`
	Y   JsonBuffer `json:"y,omitempty"`
}

type JsonWebKey struct {
	KeyType    JoseKeyType
	Rsa        *rsa.PublicKey
	Ec         *ecdsa.PublicKey
	Thumbprint string
}

func (jwk *JsonWebKey) ComputeThumbprint() {
	jsonThumbprint, err := jwk.MarshalJSON()
	if err != nil {
		return
	}
	tpHash := sha256.Sum256(jsonThumbprint)

	jwk.Thumbprint = B64enc(tpHash[:])
}

// Normal Go == operator compares pointers directly, so it doesn't
// match the semantic of two keys being equivalent
func (jwk1 JsonWebKey) Equals(jwk2 JsonWebKey) bool {
	jwk1.ComputeThumbprint()
	jwk2.ComputeThumbprint()
	return (jwk1.Thumbprint == jwk2.Thumbprint)
}

func (jwk JsonWebKey) MarshalJSON() ([]byte, error) {
	raw := rawJsonWebKey{Kty: string(jwk.KeyType)}
	if jwk.Rsa != nil {
		raw.N = jwk.Rsa.N.Bytes()
		raw.E = big.NewInt(int64(jwk.Rsa.E)).Bytes()
	}
	if jwk.Ec != nil {
		var err error
		raw.Crv, err = curve2name(jwk.Ec.Curve)
		if err != nil {
			return nil, err
		}

		raw.X = jwk.Ec.X.Bytes()
		raw.Y = jwk.Ec.Y.Bytes()
	}

	return json.Marshal(raw)
}

func (jwk *JsonWebKey) UnmarshalJSON(data []byte) error {
	var raw rawJsonWebKey
	err := json.Unmarshal(data, &raw)
	if err != nil {
		return err
	}

	jwk.KeyType = JoseKeyType(raw.Kty)
	switch jwk.KeyType {
	case "RSA":
		jwk.Rsa = &rsa.PublicKey{
			N: raw.N.ToBigInt(),
			E: raw.E.ToInt(),
		}
	case "EC":
		curve, err := name2curve(raw.Crv)
		if err != nil {
			return err
		}

		jwk.Ec = &ecdsa.PublicKey{
			Curve: curve,
			X:     raw.X.ToBigInt(),
			Y:     raw.Y.ToBigInt(),
		}
	}

	jwk.ComputeThumbprint()
	return nil
}
<|endoftext|>


<commit_before>package viber

// Keyboard struct
type Keyboard struct {
	DefaultHeight bool     `json:"DefaultHeight"`
	BgColor       string   `json:"BgColor"`
	Buttons       []Button `json:"Buttons"`
}

// AddButton to keyboard
func (k *Keyboard) AddButton(b *Button) {
	k.Buttons = append(k.Buttons, *b)
}

// NewKeyboard struct with attribs init
func NewKeyboard(bgcolor string, defaultHeight bool) *Keyboard {
	return &Keyboard{
		DefaultHeight: defaultHeight,
		BgColor:       bgcolor,
	}
}
<commit_msg>Bug-fix, add mandatory Type property<commit_after>package viber

// Keyboard struct
type Keyboard struct {
	Type          string   `json:"Type"`
	DefaultHeight bool     `json:"DefaultHeight,omitempty"`
	BgColor       string   `json:"BgColor,omitempty"`
	Buttons       []Button `json:"Buttons"`
}

// AddButton to keyboard
func (k *Keyboard) AddButton(b *Button) {
	k.Buttons = append(k.Buttons, *b)
}

// NewKeyboard struct with attribs init
func NewKeyboard(bgcolor string, defaultHeight bool) *Keyboard {
	return &Keyboard{
		Type:          "keyboard",
		DefaultHeight: defaultHeight,
		BgColor:       bgcolor,
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package s3 implements a key/value store in an Amazon S3 bucket.
package s3

import (
	"fmt"
	"github.com/jacobsa/aws/s3"
	"github.com/jacobsa/comeback/kv"
)

// Create a key/value store that stores data in the supplied S3 bucket. Keys
// supplied to its methods must be valid S3 keys. It is assumed that no keys in
// the bucket are ever removed.
//
// This function blocks while listing keys in the bucket.
func NewS3KvStore(bucket s3.Bucket) (kv.Store, error) {
	// List the keys in the bucket.
	keys, err := getAllKeys(bucket)
	if err != nil {
		return nil, err
	}

	// Create an appropriate map for efficient lookups.
	keyMap := make(map[string]bool)
	for _, key := range keys {
		keyMap[key] = true
	}

	return &kvStore{bucket, keyMap}, nil
}

func getAllKeys(bucket s3.Bucket) ([]string, error) {
	keys := []string{}
	for {
		var prevKey string
		if len(keys) > 0 {
			prevKey = keys[len(keys)-1]
		}

		partialKeys, err := bucket.ListKeys(prevKey)
		if err != nil {
			return nil, fmt.Errorf("ListKeys: %v", err)
		}

		if len(partialKeys) == 0 {
			break
		}

		keys = append(keys, partialKeys...)
	}

	return keys, nil
}

type kvStore struct {
	bucket s3.Bucket
	knownKeys map[string]bool
}

func (s *kvStore) Set(key []byte, val []byte) error {
	return fmt.Errorf("TODO")
}

func (s *kvStore) Get(key []byte) (val []byte, err error) {
	return nil, fmt.Errorf("TODO")
}

func (s *kvStore) Contains(key []byte) (res bool, err error) {
	return false, fmt.Errorf("TODO")
}
<commit_msg>Implemented Contains.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package s3 implements a key/value store in an Amazon S3 bucket.
package s3

import (
	"fmt"
	"github.com/jacobsa/aws/s3"
	"github.com/jacobsa/comeback/kv"
	"sync"
)

// Create a key/value store that stores data in the supplied S3 bucket. Keys
// supplied to its methods must be valid S3 keys. It is assumed that no keys in
// the bucket are ever removed.
//
// This function blocks while listing keys in the bucket.
func NewS3KvStore(bucket s3.Bucket) (kv.Store, error) {
	// List the keys in the bucket.
	keys, err := getAllKeys(bucket)
	if err != nil {
		return nil, err
	}

	// Create an appropriate map for efficient lookups.
	keyMap := make(map[string]bool)
	for _, key := range keys {
		keyMap[key] = true
	}

	store := &kvStore{
		bucket: bucket,
		knownKeys: keyMap,
	}

	return store, nil
}

func getAllKeys(bucket s3.Bucket) ([]string, error) {
	keys := []string{}
	for {
		var prevKey string
		if len(keys) > 0 {
			prevKey = keys[len(keys)-1]
		}

		partialKeys, err := bucket.ListKeys(prevKey)
		if err != nil {
			return nil, fmt.Errorf("ListKeys: %v", err)
		}

		if len(partialKeys) == 0 {
			break
		}

		keys = append(keys, partialKeys...)
	}

	return keys, nil
}

type kvStore struct {
	bucket s3.Bucket

	mutex sync.RWMutex
	knownKeys map[string]bool  // Protected by mutex
}

func (s *kvStore) Set(key []byte, val []byte) error {
	return fmt.Errorf("TODO")
}

func (s *kvStore) Get(key []byte) (val []byte, err error) {
	return nil, fmt.Errorf("TODO")
}

func (s *kvStore) Contains(key []byte) (res bool, err error) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	_, ok := s.knownKeys[string(key)]
	return ok, nil
}
<|endoftext|>