<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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 testserver

import (
	"fmt"
	"time"

	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/apiserver/pkg/storage/names"
	"k8s.io/client-go/dynamic"
)

//NewRandomNameCustomResourceDefinition generates a CRD with random name to avoid name conflict in e2e tests
func NewRandomNameCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	gName := names.SimpleNameGenerator.GenerateName("foo")
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: gName + "s.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1beta1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:   gName + "s",
				Singular: gName,
				Kind:     gName,
				ListKind: gName + "List",
			},
			Scope: scope,
		},
	}
}

func NewNoxuCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1beta1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:     "noxus",
				Singular:   "nonenglishnoxu",
				Kind:       "WishIHadChosenNoxu",
				ShortNames: []string{"foo", "bar", "abc", "def"},
				ListKind:   "NoxuItemList",
			},
			Scope: scope,
		},
	}
}

func NewNoxuInstance(namespace, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "mygroup.example.com/v1beta1",
			"kind":       "WishIHadChosenNoxu",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
			"content": map[string]interface{}{
				"key": "value",
			},
			"num": map[string]interface{}{
				"num1": 9223372036854775807,
				"num2": 1000000,
			},
		},
	}
}

func NewNoxu2CustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: "noxus2.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1alpha1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:     "noxus2",
				Singular:   "nonenglishnoxu2",
				Kind:       "WishIHadChosenNoxu2",
				ShortNames: []string{"foo", "bar", "abc", "def"},
				ListKind:   "Noxu2ItemList",
			},
			Scope: scope,
		},
	}
}

func NewCurletCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: "curlets.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1beta1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:   "curlets",
				Singular: "curlet",
				Kind:     "Curlet",
				ListKind: "CurletList",
			},
			Scope: scope,
		},
	}
}

func NewCurletInstance(namespace, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "mygroup.example.com/v1beta1",
			"kind":       "Curlet",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
			"content": map[string]interface{}{
				"key": "value",
			},
		},
	}
}

func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, clientPool dynamic.ClientPool) (dynamic.Interface, error) {
	_, err := apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Create(crd)
	if err != nil {
		return nil, err
	}

	// wait until the resource appears in discovery
	err = wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
		resourceList, err := apiExtensionsClient.Discovery().ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
		if err != nil {
			return false, nil
		}
		for _, resource := range resourceList.APIResources {
			if resource.Name == crd.Spec.Names.Plural {
				return true, nil
			}
		}
		return false, nil
	})
	if err != nil {
		return nil, err
	}

	dynamicClient, err := clientPool.ClientForGroupVersionResource(schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural})
	if err != nil {
		return nil, err
	}

	// This is only for a test.  We need the watch cache to have a resource version that works for the test.
	// When new REST storage is created, the storage cacher for the CR starts asynchronously.
	// REST API operations return like list use the RV of etcd, but the storage cacher's reflector's list
	// can get a different RV because etcd can be touched in between the initial list operation (if that's what you're doing first)
	// and the storage cache reflector starting.
	// Later, you can issue a watch with the REST apis list.RV and end up earlier than the storage cacher.
	// The general working model is that if you get a "resourceVersion too old" message, you re-list and rewatch.
	// For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error.
	// This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen
	// before like the created RV could be too old to watch.
	var primingErr error
	wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
		primingErr = checkForWatchCachePrimed(crd, dynamicClient)
		if primingErr == nil {
			return true, nil
		}
		return false, nil
	})
	if primingErr != nil {
		return nil, primingErr
	}

	return dynamicClient, nil
}

func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) error {
	ns := ""
	if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
		ns = "aval"
	}
	resourceClient := dynamicClient.Resource(&metav1.APIResource{
		Name:       crd.Spec.Names.Plural,
		Namespaced: crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped,
	}, ns)
	initialList, err := resourceClient.List(metav1.ListOptions{})
	if err != nil {
		return err
	}
	initialListListMeta, err := meta.ListAccessor(initialList)
	if err != nil {
		return err
	}

	instanceName := "foo"
	instance := &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": crd.Spec.Group + "/" + crd.Spec.Version,
			"kind":       crd.Spec.Names.Kind,
			"metadata": map[string]interface{}{
				"namespace": ns,
				"name":      instanceName,
			},
		},
	}
	if _, err := resourceClient.Create(instance); err != nil {
		return err
	}
	// we created something, clean it up
	defer func() {
		resourceClient.Delete(instanceName, nil)
	}()

	noxuWatch, err := resourceClient.Watch(metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
	if err != nil {
		return err
	}
	defer noxuWatch.Stop()

	select {
	case watchEvent := <-noxuWatch.ResultChan():
		if watch.Added == watchEvent.Type {
			return nil
		}
		return fmt.Errorf("expected add, but got %#v", watchEvent)

	case <-time.After(5 * time.Second):
		return fmt.Errorf("gave up waiting for watch event")
	}
}

func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) error {
	if err := apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Delete(crd.Name, nil); err != nil {
		return err
	}
	err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
		groupResource, err := apiExtensionsClient.Discovery().ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
		if err != nil {
			if errors.IsNotFound(err) {
				return true, nil

			}
			return false, err
		}
		for _, g := range groupResource.APIResources {
			if g.Name == crd.Spec.Names.Plural {
				return false, nil
			}
		}
		return true, nil
	})
	return err
}

func GetCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
	return apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
}
<commit_msg>Change test to work around restmapper pluralization bug<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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 testserver

import (
	"fmt"
	"time"

	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/apiserver/pkg/storage/names"
	"k8s.io/client-go/dynamic"
)

//NewRandomNameCustomResourceDefinition generates a CRD with random name to avoid name conflict in e2e tests
func NewRandomNameCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	// ensure the singular doesn't end in an s for now
	gName := names.SimpleNameGenerator.GenerateName("foo") + "a"
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: gName + "s.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1beta1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:   gName + "s",
				Singular: gName,
				Kind:     gName,
				ListKind: gName + "List",
			},
			Scope: scope,
		},
	}
}

func NewNoxuCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1beta1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:     "noxus",
				Singular:   "nonenglishnoxu",
				Kind:       "WishIHadChosenNoxu",
				ShortNames: []string{"foo", "bar", "abc", "def"},
				ListKind:   "NoxuItemList",
			},
			Scope: scope,
		},
	}
}

func NewNoxuInstance(namespace, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "mygroup.example.com/v1beta1",
			"kind":       "WishIHadChosenNoxu",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
			"content": map[string]interface{}{
				"key": "value",
			},
			"num": map[string]interface{}{
				"num1": 9223372036854775807,
				"num2": 1000000,
			},
		},
	}
}

func NewNoxu2CustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: "noxus2.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1alpha1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:     "noxus2",
				Singular:   "nonenglishnoxu2",
				Kind:       "WishIHadChosenNoxu2",
				ShortNames: []string{"foo", "bar", "abc", "def"},
				ListKind:   "Noxu2ItemList",
			},
			Scope: scope,
		},
	}
}

func NewCurletCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
	return &apiextensionsv1beta1.CustomResourceDefinition{
		ObjectMeta: metav1.ObjectMeta{Name: "curlets.mygroup.example.com"},
		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
			Group:   "mygroup.example.com",
			Version: "v1beta1",
			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
				Plural:   "curlets",
				Singular: "curlet",
				Kind:     "Curlet",
				ListKind: "CurletList",
			},
			Scope: scope,
		},
	}
}

func NewCurletInstance(namespace, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "mygroup.example.com/v1beta1",
			"kind":       "Curlet",
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
			"content": map[string]interface{}{
				"key": "value",
			},
		},
	}
}

func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, clientPool dynamic.ClientPool) (dynamic.Interface, error) {
	_, err := apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Create(crd)
	if err != nil {
		return nil, err
	}

	// wait until the resource appears in discovery
	err = wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
		resourceList, err := apiExtensionsClient.Discovery().ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
		if err != nil {
			return false, nil
		}
		for _, resource := range resourceList.APIResources {
			if resource.Name == crd.Spec.Names.Plural {
				return true, nil
			}
		}
		return false, nil
	})
	if err != nil {
		return nil, err
	}

	dynamicClient, err := clientPool.ClientForGroupVersionResource(schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural})
	if err != nil {
		return nil, err
	}

	// This is only for a test.  We need the watch cache to have a resource version that works for the test.
	// When new REST storage is created, the storage cacher for the CR starts asynchronously.
	// REST API operations return like list use the RV of etcd, but the storage cacher's reflector's list
	// can get a different RV because etcd can be touched in between the initial list operation (if that's what you're doing first)
	// and the storage cache reflector starting.
	// Later, you can issue a watch with the REST apis list.RV and end up earlier than the storage cacher.
	// The general working model is that if you get a "resourceVersion too old" message, you re-list and rewatch.
	// For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error.
	// This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen
	// before like the created RV could be too old to watch.
	var primingErr error
	wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
		primingErr = checkForWatchCachePrimed(crd, dynamicClient)
		if primingErr == nil {
			return true, nil
		}
		return false, nil
	})
	if primingErr != nil {
		return nil, primingErr
	}

	return dynamicClient, nil
}

func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) error {
	ns := ""
	if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
		ns = "aval"
	}
	resourceClient := dynamicClient.Resource(&metav1.APIResource{
		Name:       crd.Spec.Names.Plural,
		Namespaced: crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped,
	}, ns)
	initialList, err := resourceClient.List(metav1.ListOptions{})
	if err != nil {
		return err
	}
	initialListListMeta, err := meta.ListAccessor(initialList)
	if err != nil {
		return err
	}

	instanceName := "foo"
	instance := &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": crd.Spec.Group + "/" + crd.Spec.Version,
			"kind":       crd.Spec.Names.Kind,
			"metadata": map[string]interface{}{
				"namespace": ns,
				"name":      instanceName,
			},
		},
	}
	if _, err := resourceClient.Create(instance); err != nil {
		return err
	}
	// we created something, clean it up
	defer func() {
		resourceClient.Delete(instanceName, nil)
	}()

	noxuWatch, err := resourceClient.Watch(metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
	if err != nil {
		return err
	}
	defer noxuWatch.Stop()

	select {
	case watchEvent := <-noxuWatch.ResultChan():
		if watch.Added == watchEvent.Type {
			return nil
		}
		return fmt.Errorf("expected add, but got %#v", watchEvent)

	case <-time.After(5 * time.Second):
		return fmt.Errorf("gave up waiting for watch event")
	}
}

func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) error {
	if err := apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Delete(crd.Name, nil); err != nil {
		return err
	}
	err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
		groupResource, err := apiExtensionsClient.Discovery().ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
		if err != nil {
			if errors.IsNotFound(err) {
				return true, nil

			}
			return false, err
		}
		for _, g := range groupResource.APIResources {
			if g.Name == crd.Spec.Names.Plural {
				return false, nil
			}
		}
		return true, nil
	})
	return err
}

func GetCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
	return apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. All rights reserved.

Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/

// Package gcp is the Google Cloud Print API client.
package gcp

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"strings"
)

type cloudJobTicket struct {
	Version string             `json:"version"`
	Print   printTicketSection `json:"print"`
}

type printTicketSection struct {
	VendorTicketItems []vendorTicketItem        `json:"vendor_ticket_item"`
	Color             colorTicketItem           `json:"color"`
	Duplex            duplexTicketItem          `json:"duplex"`
	PageOrientation   pageOrientationTicketItem `json:"page_orientation"`
	Copies            copiesTicketItem          `json:"copies"`
	Margins           marginsTicketItem         `json:"margins"`
	DPI               dpiTicketItem             `json:"dpi"`
	FitToPage         fitToPageTicketItem       `json:"fit_to_page"`
	PageRange         pageRangeTicketItem       `json:"page_range"`
	MediaSize         mediaSizeTicketItem       `json:"media_size"`
	Collate           collateTicketItem         `json:"collate"`
	ReverseOrder      reverseOrderTicketItem    `json:"reverse_order"`
}

type vendorTicketItem struct {
	ID    string `json:"id"`
	Value string `json:"value"`
}

type colorTicketItem struct {
	VendorID string `json:"vendor_id"`
	Type     string `json:"type"`
}

type duplexTicketItem struct {
	Type string `json:"type"`
}

type pageOrientationTicketItem struct {
	Type string `json:"type"`
}

type copiesTicketItem struct {
	Copies int32 `json:"copies"`
}

type marginsTicketItem struct {
	TopMicrons    int32 `json:"top_microns"`
	RightMicrons  int32 `json:"right_microns"`
	BottomMicrons int32 `json:"bottom_microns"`
	LeftMicrons   int32 `json:"left_microns"`
}

type dpiTicketItem struct {
	HorizontalDPI int32  `json:"horizontal_dpi"`
	VerticalDPI   int32  `json:"vertical_dpi"`
	VendorID      string `json:"vendor_id"`
}

type fitToPageTicketItem struct {
	Type string `json:"type"`
}

type pageRangeTicketItem struct {
	Intervals []pageRangeInterval `json:"interval"`
}

type pageRangeInterval struct {
	Start int32 `json:"start"`
	End   int32 `json:"end"`
}

type mediaSizeTicketItem struct {
	WidthMicrons     int32  `json:"width_microns"`
	HeightMicrons    int32  `json:"height_microns"`
	IsContinuousFeed bool   `json:"is_continuous_feed"`
	VendorID         string `json:"vendor_id"`
}

type collateTicketItem struct {
	Collate bool `json:"collate"`
}

type reverseOrderTicketItem struct {
	ReverseOrder bool `json:"reverse_order"`
}

// Ticket gets a ticket, aka print job options.
func (gcp *GoogleCloudPrint) Ticket(ticketURL string) (map[string]string, error) {
	response, err := getWithRetry(gcp.robotClient, ticketURL+"&use_cjt=true")
	if err != nil {
		return nil, err
	}

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	var ticket cloudJobTicket
	ticket.Print.Margins.TopMicrons = math.MinInt32
	ticket.Print.Margins.RightMicrons = math.MinInt32
	ticket.Print.Margins.BottomMicrons = math.MinInt32
	ticket.Print.Margins.LeftMicrons = math.MinInt32
	err = json.Unmarshal(responseBody, &ticket)
	if err != nil {
		return nil, err
	}

	return translateTicketToOptions(&ticket.Print)
}

// translateTicketToOptions converts a GCP Ticket object to a map of CUPS-compatible options.
func translateTicketToOptions(ticket *printTicketSection) (map[string]string, error) {
	m := make(map[string]string)

	for _, vti := range ticket.VendorTicketItems {
		m[vti.ID] = vti.Value
	}
	if ticket.Color.VendorID != "" {
		m["ColorModel"] = ticket.Color.VendorID
	}
	switch strings.ToUpper(ticket.Duplex.Type) {
	case "LONG_EDGE":
		m["Duplex"] = "DuplexNoTumble"
	case "SHORT_EDGE":
		m["Duplex"] = "DuplexTumble"
	}
	switch strings.ToUpper(ticket.PageOrientation.Type) {
	case "PORTRAIT":
		m["orientation-requested"] = "3"
	case "LANDSCAPE":
		m["orientation-requested"] = "4"
	}
	if ticket.Copies.Copies != 0 {
		m["copies"] = fmt.Sprintf("%d", ticket.Copies.Copies)
	}
	if ticket.Margins.TopMicrons != math.MinInt32 {
		m["page-top"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.TopMicrons))
	}
	if ticket.Margins.RightMicrons != math.MinInt32 {
		m["page-right"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.RightMicrons))
	}
	if ticket.Margins.BottomMicrons != math.MinInt32 {
		m["page-bottom"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.BottomMicrons))
	}
	if ticket.Margins.LeftMicrons != math.MinInt32 {
		m["page-left"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.LeftMicrons))
	}
	if ticket.DPI.VendorID != "" {
		m["Resolution"] = ticket.DPI.VendorID
	}
	switch ticket.FitToPage.Type {
	case "FIT_TO_PAGE":
		m["fit-to-page"] = "true"
	case "NO_FITTING":
		m["fit-to-page"] = "false"
	}
	if len(ticket.PageRange.Intervals) > 0 {
		pageRanges := make([]string, 0, len(ticket.PageRange.Intervals))
		for _, interval := range ticket.PageRange.Intervals {
			if interval.End == 0 {
				pageRanges = append(pageRanges, fmt.Sprintf("%d", interval.Start))
			} else {
				pageRanges = append(pageRanges, fmt.Sprintf("%d-%d", interval.Start, interval.End))
			}
		}
		m["page-ranges"] = strings.Join(pageRanges, ",")
	}
	if ticket.MediaSize.VendorID != "" {
		m["media"] = ticket.MediaSize.VendorID
	}
	if ticket.Collate.Collate {
		m["Collate"] = "true"
	} else {
		m["Collate"] = "false"
	}
	if ticket.ReverseOrder.ReverseOrder {
		m["outputorder"] = "reverse"
	} else {
		m["outputorder"] = "normal"
	}

	return m, nil
}

func micronsToPoints(microns int32) int32 {
	return int32(float32(microns)*72/25400 + 0.5)
}
<commit_msg>Duplex ticket attribute defaults to "None".<commit_after>/*
Copyright 2015 Google Inc. All rights reserved.

Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/

// Package gcp is the Google Cloud Print API client.
package gcp

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"strings"
)

type cloudJobTicket struct {
	Version string             `json:"version"`
	Print   printTicketSection `json:"print"`
}

type printTicketSection struct {
	VendorTicketItems []vendorTicketItem        `json:"vendor_ticket_item"`
	Color             colorTicketItem           `json:"color"`
	Duplex            duplexTicketItem          `json:"duplex"`
	PageOrientation   pageOrientationTicketItem `json:"page_orientation"`
	Copies            copiesTicketItem          `json:"copies"`
	Margins           marginsTicketItem         `json:"margins"`
	DPI               dpiTicketItem             `json:"dpi"`
	FitToPage         fitToPageTicketItem       `json:"fit_to_page"`
	PageRange         pageRangeTicketItem       `json:"page_range"`
	MediaSize         mediaSizeTicketItem       `json:"media_size"`
	Collate           collateTicketItem         `json:"collate"`
	ReverseOrder      reverseOrderTicketItem    `json:"reverse_order"`
}

type vendorTicketItem struct {
	ID    string `json:"id"`
	Value string `json:"value"`
}

type colorTicketItem struct {
	VendorID string `json:"vendor_id"`
	Type     string `json:"type"`
}

type duplexTicketItem struct {
	Type string `json:"type"`
}

type pageOrientationTicketItem struct {
	Type string `json:"type"`
}

type copiesTicketItem struct {
	Copies int32 `json:"copies"`
}

type marginsTicketItem struct {
	TopMicrons    int32 `json:"top_microns"`
	RightMicrons  int32 `json:"right_microns"`
	BottomMicrons int32 `json:"bottom_microns"`
	LeftMicrons   int32 `json:"left_microns"`
}

type dpiTicketItem struct {
	HorizontalDPI int32  `json:"horizontal_dpi"`
	VerticalDPI   int32  `json:"vertical_dpi"`
	VendorID      string `json:"vendor_id"`
}

type fitToPageTicketItem struct {
	Type string `json:"type"`
}

type pageRangeTicketItem struct {
	Intervals []pageRangeInterval `json:"interval"`
}

type pageRangeInterval struct {
	Start int32 `json:"start"`
	End   int32 `json:"end"`
}

type mediaSizeTicketItem struct {
	WidthMicrons     int32  `json:"width_microns"`
	HeightMicrons    int32  `json:"height_microns"`
	IsContinuousFeed bool   `json:"is_continuous_feed"`
	VendorID         string `json:"vendor_id"`
}

type collateTicketItem struct {
	Collate bool `json:"collate"`
}

type reverseOrderTicketItem struct {
	ReverseOrder bool `json:"reverse_order"`
}

// Ticket gets a ticket, aka print job options.
func (gcp *GoogleCloudPrint) Ticket(ticketURL string) (map[string]string, error) {
	response, err := getWithRetry(gcp.robotClient, ticketURL+"&use_cjt=true")
	if err != nil {
		return nil, err
	}

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	var ticket cloudJobTicket
	ticket.Print.Margins.TopMicrons = math.MinInt32
	ticket.Print.Margins.RightMicrons = math.MinInt32
	ticket.Print.Margins.BottomMicrons = math.MinInt32
	ticket.Print.Margins.LeftMicrons = math.MinInt32
	err = json.Unmarshal(responseBody, &ticket)
	if err != nil {
		return nil, err
	}

	return translateTicketToOptions(&ticket.Print)
}

// translateTicketToOptions converts a GCP Ticket object to a map of CUPS-compatible options.
func translateTicketToOptions(ticket *printTicketSection) (map[string]string, error) {
	m := make(map[string]string)

	for _, vti := range ticket.VendorTicketItems {
		m[vti.ID] = vti.Value
	}
	if ticket.Color.VendorID != "" {
		m["ColorModel"] = ticket.Color.VendorID
	}
	switch strings.ToUpper(ticket.Duplex.Type) {
	case "LONG_EDGE":
		m["Duplex"] = "DuplexNoTumble"
	case "SHORT_EDGE":
		m["Duplex"] = "DuplexTumble"
	default:
		m["Duplex"] = "None"
	}
	switch strings.ToUpper(ticket.PageOrientation.Type) {
	case "PORTRAIT":
		m["orientation-requested"] = "3"
	case "LANDSCAPE":
		m["orientation-requested"] = "4"
	}
	if ticket.Copies.Copies != 0 {
		m["copies"] = fmt.Sprintf("%d", ticket.Copies.Copies)
	}
	if ticket.Margins.TopMicrons != math.MinInt32 {
		m["page-top"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.TopMicrons))
	}
	if ticket.Margins.RightMicrons != math.MinInt32 {
		m["page-right"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.RightMicrons))
	}
	if ticket.Margins.BottomMicrons != math.MinInt32 {
		m["page-bottom"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.BottomMicrons))
	}
	if ticket.Margins.LeftMicrons != math.MinInt32 {
		m["page-left"] = fmt.Sprintf("%d", micronsToPoints(ticket.Margins.LeftMicrons))
	}
	if ticket.DPI.VendorID != "" {
		m["Resolution"] = ticket.DPI.VendorID
	}
	switch ticket.FitToPage.Type {
	case "FIT_TO_PAGE":
		m["fit-to-page"] = "true"
	case "NO_FITTING":
		m["fit-to-page"] = "false"
	}
	if len(ticket.PageRange.Intervals) > 0 {
		pageRanges := make([]string, 0, len(ticket.PageRange.Intervals))
		for _, interval := range ticket.PageRange.Intervals {
			if interval.End == 0 {
				pageRanges = append(pageRanges, fmt.Sprintf("%d", interval.Start))
			} else {
				pageRanges = append(pageRanges, fmt.Sprintf("%d-%d", interval.Start, interval.End))
			}
		}
		m["page-ranges"] = strings.Join(pageRanges, ",")
	}
	if ticket.MediaSize.VendorID != "" {
		m["media"] = ticket.MediaSize.VendorID
	}
	if ticket.Collate.Collate {
		m["Collate"] = "true"
	} else {
		m["Collate"] = "false"
	}
	if ticket.ReverseOrder.ReverseOrder {
		m["outputorder"] = "reverse"
	} else {
		m["outputorder"] = "normal"
	}

	return m, nil
}

func micronsToPoints(microns int32) int32 {
	return int32(float32(microns)*72/25400 + 0.5)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcs

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/jacobsa/gcloud/httputil"
	"golang.org/x/net/context"
	"google.golang.org/api/googleapi"
	storagev1 "google.golang.org/api/storage/v1"
)

const userAgent = "github.com-jacobsa-gloud-gcs"

// Bucket represents a GCS bucket, pre-bound with a bucket name and necessary
// authorization information.
//
// Each method that may block accepts a context object that is used for
// deadlines and cancellation. Users need not package authorization information
// into the context object (using cloud.WithContext or similar).
type Bucket interface {
	Name() string

	// Create a reader for the contents of a particular generation of an object.
	// The caller must arrange for the reader to be closed when it is no longer
	// needed.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/get
	NewReader(
		ctx context.Context,
		req *ReadObjectRequest) (io.ReadCloser, error)

	// Create or overwrite an object according to the supplied request. The new
	// object is guaranteed to exist immediately for the purposes of reading (and
	// eventually for listing) after this method returns a nil error. It is
	// guaranteed not to exist before req.Contents returns io.EOF.
	//
	// If the request fails due to a precondition not being met, the error will
	// be of type *PreconditionError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/insert
	//     https://cloud.google.com/storage/docs/json_api/v1/how-tos/upload
	CreateObject(
		ctx context.Context,
		req *CreateObjectRequest) (*Object, error)

	// Return current information about the object with the given name.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/get
	StatObject(
		ctx context.Context,
		req *StatObjectRequest) (*Object, error)

	// List the objects in the bucket that meet the criteria defined by the
	// request, returning a result object that contains the results and
	// potentially a cursor for retrieving the next portion of the larger set of
	// results.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/list
	ListObjects(
		ctx context.Context,
		req *ListObjectsRequest) (*Listing, error)

	// Update the object specified by newAttrs.Name, patching using the non-zero
	// fields of newAttrs.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/patch
	UpdateObject(
		ctx context.Context,
		req *UpdateObjectRequest) (*Object, error)

	// Delete the object with the given name.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/delete
	DeleteObject(ctx context.Context, name string) error
}

type bucket struct {
	projID     string
	client     *http.Client
	rawService *storagev1.Service
	name       string
}

func (b *bucket) Name() string {
	return b.name
}

func (b *bucket) ListObjects(
	ctx context.Context,
	req *ListObjectsRequest) (listing *Listing, err error) {
	// Construct an appropriate URL (cf. http://goo.gl/aVSAhT).
	opaque := fmt.Sprintf(
		"//www.googleapis.com/storage/v1/b/%s/o",
		httputil.EncodePathSegment(b.Name()))

	query := make(url.Values)
	query.Set("projection", "full")

	if req.Prefix != "" {
		query.Set("prefix", req.Prefix)
	}

	if req.Delimiter != "" {
		query.Set("delimiter", req.Delimiter)
	}

	if req.ContinuationToken != "" {
		query.Set("pageToken", req.ContinuationToken)
	}

	if req.MaxResults != 0 {
		query.Set("maxResults", fmt.Sprintf("%v", req.MaxResults))
	}

	url := &url.URL{
		Scheme:   "https",
		Opaque:   opaque,
		RawQuery: query.Encode(),
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("GET", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Call the server.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		err = fmt.Errorf("HTTP client: %v", err)
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	// Parse the response.
	var rawListing *storagev1.Objects
	if err = json.NewDecoder(httpRes.Body).Decode(&rawListing); err != nil {
		return
	}

	// Convert the response.
	if listing, err = toListing(rawListing); err != nil {
		return
	}

	return
}

func (b *bucket) NewReader(
	ctx context.Context,
	req *ReadObjectRequest) (rc io.ReadCloser, err error) {
	// Construct an appropriate URL.
	//
	// The documentation (http://goo.gl/gZo4oj) is extremely vague about how this
	// is supposed to work. As of 2015-03-13, it says only that "for most
	// operations" you can use the following form to access an object:
	//
	//     storage.googleapis.com/<bucket>/<object>
	//
	// In Google-internal bug 19718068, it was clarified that the intent is that
	// each of the bucket and object names are encoded into a single path
	// segment, as defined by RFC 3986.
	bucketSegment := httputil.EncodePathSegment(b.name)
	objectSegment := httputil.EncodePathSegment(req.Name)
	opaque := fmt.Sprintf(
		"//storage.googleapis.com/%s/%s",
		bucketSegment,
		objectSegment)

	query := make(url.Values)
	if req.Generation != 0 {
		query.Set("generation", fmt.Sprintf("%v", req.Generation))
	}

	url := &url.URL{
		Scheme: "https",
		Opaque: opaque,
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("GET", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Call the server.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		err = fmt.Errorf("HTTP client: %v", err)
		return
	}

	// Check for HTTP error statuses.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		googleapi.CloseBody(httpRes)

		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	// The body contains the object data.
	rc = httpRes.Body

	return
}

func (b *bucket) CreateObject(
	ctx context.Context,
	req *CreateObjectRequest) (o *Object, err error) {
	o, err = createObject(b.client, b.Name(), ctx, req)
	return
}

func (b *bucket) StatObject(
	ctx context.Context,
	req *StatObjectRequest) (o *Object, err error) {
	// Construct an appropriate URL (cf. http://goo.gl/MoITmB).
	opaque := fmt.Sprintf(
		"//www.googleapis.com/storage/v1/b/%s/o/%s",
		httputil.EncodePathSegment(b.Name()),
		httputil.EncodePathSegment(req.Name))

	query := make(url.Values)
	query.Set("projection", "full")

	url := &url.URL{
		Scheme:   "https",
		Opaque:   opaque,
		RawQuery: query.Encode(),
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("GET", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Execute the HTTP request.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	// Parse the response.
	var rawObject *storagev1.Object
	if err = json.NewDecoder(httpRes.Body).Decode(&rawObject); err != nil {
		return
	}

	// Convert the response.
	if o, err = toObject(rawObject); err != nil {
		err = fmt.Errorf("toObject: %v", err)
		return
	}

	return
}

func (b *bucket) UpdateObject(
	ctx context.Context,
	req *UpdateObjectRequest) (o *Object, err error) {
	o, err = updateObject(b.client, b.Name(), ctx, req)
	return
}

func (b *bucket) DeleteObject(ctx context.Context, name string) (err error) {
	// Construct an appropriate URL (cf. http://goo.gl/TRQJjZ).
	opaque := fmt.Sprintf(
		"//www.googleapis.com/storage/v1/b/%s/o/%s",
		httputil.EncodePathSegment(b.Name()),
		httputil.EncodePathSegment(name))

	url := &url.URL{
		Scheme: "https",
		Opaque: opaque,
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("DELETE", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Execute the HTTP request.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	return
}

func newBucket(
	projID string,
	client *http.Client,
	rawService *storagev1.Service,
	name string) Bucket {
	return &bucket{
		projID:     projID,
		client:     client,
		rawService: rawService,
		name:       name,
	}
}
<commit_msg>Fixed a bug introduced in bf9535d1e86bace32558ae6ce182960f79eecd16.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcs

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/jacobsa/gcloud/httputil"
	"golang.org/x/net/context"
	"google.golang.org/api/googleapi"
	storagev1 "google.golang.org/api/storage/v1"
)

const userAgent = "github.com-jacobsa-gloud-gcs"

// Bucket represents a GCS bucket, pre-bound with a bucket name and necessary
// authorization information.
//
// Each method that may block accepts a context object that is used for
// deadlines and cancellation. Users need not package authorization information
// into the context object (using cloud.WithContext or similar).
type Bucket interface {
	Name() string

	// Create a reader for the contents of a particular generation of an object.
	// The caller must arrange for the reader to be closed when it is no longer
	// needed.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/get
	NewReader(
		ctx context.Context,
		req *ReadObjectRequest) (io.ReadCloser, error)

	// Create or overwrite an object according to the supplied request. The new
	// object is guaranteed to exist immediately for the purposes of reading (and
	// eventually for listing) after this method returns a nil error. It is
	// guaranteed not to exist before req.Contents returns io.EOF.
	//
	// If the request fails due to a precondition not being met, the error will
	// be of type *PreconditionError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/insert
	//     https://cloud.google.com/storage/docs/json_api/v1/how-tos/upload
	CreateObject(
		ctx context.Context,
		req *CreateObjectRequest) (*Object, error)

	// Return current information about the object with the given name.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/get
	StatObject(
		ctx context.Context,
		req *StatObjectRequest) (*Object, error)

	// List the objects in the bucket that meet the criteria defined by the
	// request, returning a result object that contains the results and
	// potentially a cursor for retrieving the next portion of the larger set of
	// results.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/list
	ListObjects(
		ctx context.Context,
		req *ListObjectsRequest) (*Listing, error)

	// Update the object specified by newAttrs.Name, patching using the non-zero
	// fields of newAttrs.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/patch
	UpdateObject(
		ctx context.Context,
		req *UpdateObjectRequest) (*Object, error)

	// Delete the object with the given name.
	//
	// If the object doesn't exist, err will be of type *NotFoundError.
	//
	// Official documentation:
	//     https://cloud.google.com/storage/docs/json_api/v1/objects/delete
	DeleteObject(ctx context.Context, name string) error
}

type bucket struct {
	projID     string
	client     *http.Client
	rawService *storagev1.Service
	name       string
}

func (b *bucket) Name() string {
	return b.name
}

func (b *bucket) ListObjects(
	ctx context.Context,
	req *ListObjectsRequest) (listing *Listing, err error) {
	// Construct an appropriate URL (cf. http://goo.gl/aVSAhT).
	opaque := fmt.Sprintf(
		"//www.googleapis.com/storage/v1/b/%s/o",
		httputil.EncodePathSegment(b.Name()))

	query := make(url.Values)
	query.Set("projection", "full")

	if req.Prefix != "" {
		query.Set("prefix", req.Prefix)
	}

	if req.Delimiter != "" {
		query.Set("delimiter", req.Delimiter)
	}

	if req.ContinuationToken != "" {
		query.Set("pageToken", req.ContinuationToken)
	}

	if req.MaxResults != 0 {
		query.Set("maxResults", fmt.Sprintf("%v", req.MaxResults))
	}

	url := &url.URL{
		Scheme:   "https",
		Opaque:   opaque,
		RawQuery: query.Encode(),
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("GET", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Call the server.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		err = fmt.Errorf("HTTP client: %v", err)
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	// Parse the response.
	var rawListing *storagev1.Objects
	if err = json.NewDecoder(httpRes.Body).Decode(&rawListing); err != nil {
		return
	}

	// Convert the response.
	if listing, err = toListing(rawListing); err != nil {
		return
	}

	return
}

func (b *bucket) NewReader(
	ctx context.Context,
	req *ReadObjectRequest) (rc io.ReadCloser, err error) {
	// Construct an appropriate URL.
	//
	// The documentation (http://goo.gl/gZo4oj) is extremely vague about how this
	// is supposed to work. As of 2015-03-13, it says only that "for most
	// operations" you can use the following form to access an object:
	//
	//     storage.googleapis.com/<bucket>/<object>
	//
	// In Google-internal bug 19718068, it was clarified that the intent is that
	// each of the bucket and object names are encoded into a single path
	// segment, as defined by RFC 3986.
	bucketSegment := httputil.EncodePathSegment(b.name)
	objectSegment := httputil.EncodePathSegment(req.Name)
	opaque := fmt.Sprintf(
		"//storage.googleapis.com/%s/%s",
		bucketSegment,
		objectSegment)

	query := make(url.Values)
	if req.Generation != 0 {
		query.Set("generation", fmt.Sprintf("%v", req.Generation))
	}

	url := &url.URL{
		Scheme:   "https",
		Opaque:   opaque,
		RawQuery: query.Encode(),
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("GET", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Call the server.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		err = fmt.Errorf("HTTP client: %v", err)
		return
	}

	// Check for HTTP error statuses.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		googleapi.CloseBody(httpRes)

		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	// The body contains the object data.
	rc = httpRes.Body

	return
}

func (b *bucket) CreateObject(
	ctx context.Context,
	req *CreateObjectRequest) (o *Object, err error) {
	o, err = createObject(b.client, b.Name(), ctx, req)
	return
}

func (b *bucket) StatObject(
	ctx context.Context,
	req *StatObjectRequest) (o *Object, err error) {
	// Construct an appropriate URL (cf. http://goo.gl/MoITmB).
	opaque := fmt.Sprintf(
		"//www.googleapis.com/storage/v1/b/%s/o/%s",
		httputil.EncodePathSegment(b.Name()),
		httputil.EncodePathSegment(req.Name))

	query := make(url.Values)
	query.Set("projection", "full")

	url := &url.URL{
		Scheme:   "https",
		Opaque:   opaque,
		RawQuery: query.Encode(),
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("GET", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Execute the HTTP request.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	// Parse the response.
	var rawObject *storagev1.Object
	if err = json.NewDecoder(httpRes.Body).Decode(&rawObject); err != nil {
		return
	}

	// Convert the response.
	if o, err = toObject(rawObject); err != nil {
		err = fmt.Errorf("toObject: %v", err)
		return
	}

	return
}

func (b *bucket) UpdateObject(
	ctx context.Context,
	req *UpdateObjectRequest) (o *Object, err error) {
	o, err = updateObject(b.client, b.Name(), ctx, req)
	return
}

func (b *bucket) DeleteObject(ctx context.Context, name string) (err error) {
	// Construct an appropriate URL (cf. http://goo.gl/TRQJjZ).
	opaque := fmt.Sprintf(
		"//www.googleapis.com/storage/v1/b/%s/o/%s",
		httputil.EncodePathSegment(b.Name()),
		httputil.EncodePathSegment(name))

	url := &url.URL{
		Scheme: "https",
		Opaque: opaque,
	}

	// Create an HTTP request.
	httpReq, err := httputil.NewRequest("DELETE", url, nil, userAgent)
	if err != nil {
		err = fmt.Errorf("httputil.NewRequest: %v", err)
		return
	}

	// Execute the HTTP request.
	httpRes, err := b.client.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle not found errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusNotFound {
				err = &NotFoundError{Err: typed}
			}
		}

		return
	}

	return
}

func newBucket(
	projID string,
	client *http.Client,
	rawService *storagev1.Service,
	name string) Bucket {
	return &bucket{
		projID:     projID,
		client:     client,
		rawService: rawService,
		name:       name,
	}
}
<|endoftext|>


<commit_before>// package main
//
// import (
//   "fmt"
//   "log"
//   "cloud.google.com/go/storage"
//   "golang.org/x/net/context"
// )
//
// func main() {
//   ctx := context.Background()
//
//   projectId := "fury-panda"
//
//   client, err := storage.NewClient(ctx)
//   if err != nil {
//     log.FatalF("Failed to create client: %v", err)
//   }
//
//   bucketName := "android-double-dev-apk"
//
//
// }

package main

import (
	"fmt"
	"log"
	"os"

	"google.golang.org/api/storage/v1"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
)

// ListBuckets returns a slice of all the buckets for a given project.
func ListBuckets(projectID string) ([]*storage.Bucket, error) {
	ctx := context.Background()

	// Create the client that uses Application Default Credentials
	// See https://developers.google.com/identity/protocols/application-default-credentials
	client, err := google.DefaultClient(ctx, storage.DevstorageReadOnlyScope)
	if err != nil {
		return nil, err
	}

	// Create the Google Cloud Storage service
	service, err := storage.New(client)
	if err != nil {
		return nil, err
	}

	buckets, err := service.Buckets.List(projectID).Do()
	if err != nil {
		return nil, err
	}

	return buckets.Items, nil
}

func main() {
	os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", os.Getenv("service_account_json_key_path"))

	if len(os.Args) < 2 {
		fmt.Println("usage: listbuckets <projectID>")
		os.Exit(1)
	}
	project := os.Args[1]

	buckets, err := ListBuckets(project)
	if err != nil {
		log.Fatal(err)
	}

	// Print out the results
	for _, bucket := range buckets {
		fmt.Println(bucket.Name)
	}
}
<commit_msg>download keyfile<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"net/http"
	"path/filepath"

	"google.golang.org/api/storage/v1"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"

	"github.com/bitrise-io/go-utils/pathutil"
)

func downloadFile(downloadURL, targetPath string) error {
	outFile, err := os.Create(targetPath)
	if err != nil {
		return fmt.Errorf("failed to create (%s), error: %s", targetPath, err)
	}
	defer func() {
		if err := outFile.Close(); err != nil {
			log.Warnf("Failed to close (%s)", targetPath)
		}
	}()

	resp, err := http.Get(downloadURL)
	if err != nil {
		return fmt.Errorf("failed to download from (%s), error: %s", downloadURL, err)
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Warnf("failed to close (%s) body", downloadURL)
		}
	}()

	_, err = io.Copy(outFile, resp.Body)
	if err != nil {
		return fmt.Errorf("failed to download from (%s), error: %s", downloadURL, err)
	}

	return nil
}

// ListBuckets returns a slice of all the buckets for a given project.
func ListBuckets(projectID string) ([]*storage.Bucket, error) {
	ctx := context.Background()

	// Create the client that uses Application Default Credentials
	// See https://developers.google.com/identity/protocols/application-default-credentials
	client, err := google.DefaultClient(ctx, storage.DevstorageReadOnlyScope)
	if err != nil {
		return nil, err
	}

	// Create the Google Cloud Storage service
	service, err := storage.New(client)
	if err != nil {
		return nil, err
	}

	buckets, err := service.Buckets.List(projectID).Do()
	if err != nil {
		return nil, err
	}

	return buckets.Items, nil
}

func main() {
	key_path := os.Getenv("service_account_json_key_path")

	if strings.HasPrefix(key_path, "http") {
		downloadUrl := key_path
		tmpDir, err := pathutil.NormalizedOSTempDirPath("__google-cloud-storage__")

		if err != nil {
		  failf("Failed to create tmp dir, error: %s", err)
		}

		targetPath := filepath.Join(tmpDir, "key.json")
		if err := downloadFile(key_path, targetPath); err != nil {
				failf("Failed to download json key file, error: %s", err)
		}

		key_path = targetPath
	}

	os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", key_path)

	if len(os.Args) < 2 {
		fmt.Println("usage: listbuckets <projectID>")
		os.Exit(1)
	}
	project := os.Args[1]

	buckets, err := ListBuckets(project)
	if err != nil {
		log.Fatal(err)
	}

	// Print out the results
	for _, bucket := range buckets {
		fmt.Println(bucket.Name)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package sim

import (
	"errors"
	"log"

	"code.google.com/p/gopacket"

	"my/itto/verify/packet"
	"my/itto/verify/packet/itto"
)

var _ = log.Ldate

type IttoDbStats struct {
	Orders     int
	PeakOrders int
	Sessions   int
}

type IttoDbMessage struct {
	Pam     packet.ApplicationMessage
	Session *Session
}

type IttoDb interface {
	Stats() IttoDbStats
	NewMessage(packet.ApplicationMessage) *IttoDbMessage
	MessageOperations(*IttoDbMessage) []IttoOperation
	ApplyOperation(operation IttoOperation)
}

func NewIttoDb() IttoDb {
	return &db{
		orders: make(map[orderIndex]order),
	}
}

type dbStatSupport struct {
	maxOrders int
}
type db struct {
	sessions []Session
	orders   map[orderIndex]order
	stat     dbStatSupport
}

type orderIndex uint64

func NewOrderIndex(d *db, flow gopacket.Flow, refNumD itto.RefNumDelta) orderIndex {
	s := d.getSession(flow)
	return orderIndex(uint64(s.index)<<32 + uint64(refNumD.Delta()))
}

type order struct {
	OId itto.OptionId
	itto.OrderSide
}

type Session struct {
	flow  gopacket.Flow
	index int
}

func (s *Session) Index() int {
	return s.index
}

var orderNotFoundError = errors.New("order not found")

func (d *db) findOrder(flow gopacket.Flow, refNumD itto.RefNumDelta) (order order, err error) {
	order, ok := d.orders[NewOrderIndex(d, flow, refNumD)]
	if !ok {
		err = orderNotFoundError
	}
	return
}

func (d *db) getSession(flow gopacket.Flow) Session {
	for _, s := range d.sessions {
		if s.flow == flow {
			return s
		}
	}
	s := Session{
		flow:  flow,
		index: len(d.sessions),
	}
	d.sessions = append(d.sessions, s)
	return s
}

func (d *db) Stats() IttoDbStats {
	s := IttoDbStats{
		Orders:     len(d.orders),
		PeakOrders: d.stat.maxOrders,
		Sessions:   len(d.sessions),
	}
	return s
}

func (d *db) NewMessage(pam packet.ApplicationMessage) *IttoDbMessage {
	s := d.getSession(pam.Flow())
	m := &IttoDbMessage{
		Pam:     pam,
		Session: &s,
	}
	return m
}

func (d *db) ApplyOperation(operation IttoOperation) {
	operation.getOperation().populate()
	oid := operation.GetOptionId()
	if oid.Invalid() {
		return
	}
	switch op := operation.(type) {
	case *OperationAdd:
		// intentionally allow adding zero price/size orders
		o := order{OId: op.optionId, OrderSide: op.OrderSide}
		if op.origOrder != nil {
			if op.optionId.Valid() {
				log.Fatalf("bad option id for add operation %#v origOrder=%#v\n", op, *op.origOrder)
			}
			if op.Side != itto.MarketSideUnknown && op.Side != op.origOrder.Side {
				log.Fatalf("bad side for add operation %#v origOrder=%#v\n", op, *op.origOrder)
			}
			o.OId = op.origOrder.OId
			o.Side = op.origOrder.Side
		}
		d.orders[op.orderIndex()] = o
		if l := len(d.orders); l > d.stat.maxOrders {
			d.stat.maxOrders = l
		}
	default:
		o := *operation.getOperation().origOrder
		oidx := operation.getOperation().origOrderIndex()
		o.Size += op.GetSizeDelta()
		switch {
		case o.Size > 0:
			d.orders[oidx] = o
		case o.Size == 0:
			// treat OperationUpdate which zeroes order size as order removal
			delete(d.orders, oidx)
		case o.Size < 0:
			log.Fatalf("negative size after operation %#v origOrder=%#v\n", operation, o)
		}
	}
}

type IttoOperation interface {
	GetMessage() *IttoDbMessage
	GetOptionId() itto.OptionId
	GetOrigRef() itto.RefNumDelta
	GetSide() itto.MarketSide
	GetSizeDelta() int
	GetNewSize() int
	GetPrice() int
	getOperation() *Operation
}

type Operation struct {
	m           *IttoDbMessage
	d           *db
	origRefNumD itto.RefNumDelta
	origOrder   *order
	sibling     IttoOperation
}

func (op *Operation) GetMessage() *IttoDbMessage {
	return op.m
}
func (op *Operation) GetOrigRef() itto.RefNumDelta {
	return op.origRefNumD
}
func (op *Operation) populate() {
	if op.origOrder != nil {
		return
	}
	if op.sibling != nil {
		op.sibling.getOperation().populate()
		op.origOrder = op.sibling.getOperation().origOrder
	} else if op.origRefNumD != (itto.RefNumDelta{}) {
		if ord, err := op.d.findOrder(op.m.Pam.Flow(), op.origRefNumD); err == nil {
			op.origOrder = &ord
		}
	}
}
func (op *Operation) origOrderIndex() orderIndex {
	return NewOrderIndex(op.d, op.m.Pam.Flow(), op.origRefNumD)
}
func (o *Operation) getOptionId() (oid itto.OptionId) {
	o.populate()
	if o.origOrder != nil {
		return o.origOrder.OId
	} else {
		return itto.OptionId(0)
	}
}
func (o *Operation) getSide() (side itto.MarketSide) {
	o.populate()
	if o.origOrder != nil {
		side = o.origOrder.Side
	}
	return
}

type OperationAdd struct {
	Operation
	optionId itto.OptionId
	itto.OrderSide
}

func (o *OperationAdd) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationAdd) Independent() bool {
	return o.optionId.Valid()
}
func (o *OperationAdd) GetOptionId() itto.OptionId {
	if o.optionId.Valid() {
		return o.optionId
	} else {
		return o.Operation.getOptionId()
	}
}
func (o *OperationAdd) GetSide() (side itto.MarketSide) {
	if o.Side != itto.MarketSideUnknown {
		return o.Side
	} else {
		return o.Operation.getSide()
	}
}
func (o *OperationAdd) GetPrice() int {
	return o.Price
}
func (o *OperationAdd) GetSizeDelta() int {
	return o.Size
}
func (o *OperationAdd) GetNewSize() int {
	return o.Size
}
func (op *OperationAdd) orderIndex() orderIndex {
	return NewOrderIndex(op.d, op.m.Pam.Flow(), op.RefNumD)
}

type OperationRemove struct {
	Operation
}

func (o *OperationRemove) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationRemove) GetOptionId() itto.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationRemove) GetSide() (side itto.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationRemove) GetSizeDelta() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return -o.origOrder.Size
}
func (o *OperationRemove) GetNewSize() int {
	return 0
}
func (o *OperationRemove) GetPrice() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return o.origOrder.Price
}

type OperationUpdate struct {
	Operation
	sizeChange int
}

func (o *OperationUpdate) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationUpdate) GetOptionId() itto.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationUpdate) GetSide() (side itto.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationUpdate) GetSizeDelta() int {
	return -o.sizeChange
}
func (o *OperationUpdate) GetNewSize() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return o.origOrder.Size - o.sizeChange
}
func (o *OperationUpdate) GetPrice() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return o.origOrder.Price
}

func (d *db) MessageOperations(m *IttoDbMessage) []IttoOperation {
	var ops []IttoOperation
	addOperation := func(origRefNumD itto.RefNumDelta, operation IttoOperation) {
		opop := operation.getOperation()
		opop.m = m
		opop.d = d
		opop.origRefNumD = origRefNumD
		ops = append(ops, operation)
	}
	addOperationReplace := func(origRefNumD itto.RefNumDelta, orderSide itto.OrderSide) {
		opRemove := &OperationRemove{}
		opAdd := &OperationAdd{
			// unknown: optionId; maybe unknown: OrderSide.Side
			OrderSide: orderSide,
			Operation: Operation{sibling: opRemove},
		}
		addOperation(origRefNumD, opRemove)
		addOperation(itto.RefNumDelta{}, opAdd)
	}
	switch im := m.Pam.Layer().(type) {
	case *itto.IttoMessageAddOrder:
		addOperation(itto.RefNumDelta{}, &OperationAdd{optionId: im.OId, OrderSide: im.OrderSide})
	case *itto.IttoMessageAddQuote:
		addOperation(itto.RefNumDelta{}, &OperationAdd{optionId: im.OId, OrderSide: im.Bid})
		addOperation(itto.RefNumDelta{}, &OperationAdd{optionId: im.OId, OrderSide: im.Ask})
	case *itto.IttoMessageSingleSideExecuted:
		addOperation(im.OrigRefNumD, &OperationUpdate{sizeChange: im.Size})
	case *itto.IttoMessageSingleSideExecutedWithPrice:
		addOperation(im.OrigRefNumD, &OperationUpdate{sizeChange: im.Size})
	case *itto.IttoMessageOrderCancel:
		addOperation(im.OrigRefNumD, &OperationUpdate{sizeChange: im.Size})
	case *itto.IttoMessageSingleSideReplace:
		addOperationReplace(im.OrigRefNumD, im.OrderSide)
	case *itto.IttoMessageSingleSideDelete:
		addOperation(im.OrigRefNumD, &OperationRemove{})
	case *itto.IttoMessageSingleSideUpdate:
		addOperationReplace(im.RefNumD, im.OrderSide)
	case *itto.IttoMessageQuoteReplace:
		addOperationReplace(im.Bid.OrigRefNumD, im.Bid.OrderSide)
		addOperationReplace(im.Ask.OrigRefNumD, im.Ask.OrderSide)
	case *itto.IttoMessageQuoteDelete:
		addOperation(im.BidOrigRefNumD, &OperationRemove{})
		addOperation(im.AskOrigRefNumD, &OperationRemove{})
	case *itto.IttoMessageBlockSingleSideDelete:
		for _, r := range im.RefNumDs {
			addOperation(r, &OperationRemove{})
		}
	}
	return ops
}
<commit_msg>sim: implement subscription<commit_after>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package sim

import (
	"errors"
	"log"

	"code.google.com/p/gopacket"

	"my/itto/verify/packet"
	"my/itto/verify/packet/itto"
)

var _ = log.Ldate

type IttoDbStats struct {
	Orders     int
	PeakOrders int
	Sessions   int
}

type IttoDbMessage struct {
	Pam     packet.ApplicationMessage
	Session *Session
	subscr  *Subscr
}

func (m *IttoDbMessage) IgnoredBySubscriber() bool {
	if m.subscr == nil {
		return false
	}
	var oid itto.OptionId
	switch im := m.Pam.Layer().(type) {
	case *itto.IttoMessageAddOrder:
		oid = im.OId
	case *itto.IttoMessageAddQuote:
		oid = im.OId
	case *itto.IttoMessageOptionsTrade:
		oid = im.OId
	case *itto.IttoMessageOptionsCrossTrade:
		oid = im.OId
	}
	return oid.Valid() && !m.subscr.Subscribed(oid)
}

type IttoDb interface {
	Stats() IttoDbStats
	NewMessage(packet.ApplicationMessage) *IttoDbMessage
	MessageOperations(*IttoDbMessage) []IttoOperation
	ApplyOperation(operation IttoOperation)
	SetSubscription(s *Subscr)
}

func NewIttoDb() IttoDb {
	return &db{
		orders: make(map[orderIndex]order),
	}
}

type dbStatSupport struct {
	maxOrders int
}
type db struct {
	sessions []Session
	orders   map[orderIndex]order
	stat     dbStatSupport
	subscr   *Subscr
}

type orderIndex uint64

func NewOrderIndex(d *db, flow gopacket.Flow, refNumD itto.RefNumDelta) orderIndex {
	s := d.getSession(flow)
	return orderIndex(uint64(s.index)<<32 + uint64(refNumD.Delta()))
}

type order struct {
	OId itto.OptionId
	itto.OrderSide
}

type Session struct {
	flow  gopacket.Flow
	index int
}

func (s *Session) Index() int {
	return s.index
}

var orderNotFoundError = errors.New("order not found")

func (d *db) findOrder(flow gopacket.Flow, refNumD itto.RefNumDelta) (order order, err error) {
	order, ok := d.orders[NewOrderIndex(d, flow, refNumD)]
	if !ok {
		err = orderNotFoundError
	}
	return
}

func (d *db) getSession(flow gopacket.Flow) Session {
	for _, s := range d.sessions {
		if s.flow == flow {
			return s
		}
	}
	s := Session{
		flow:  flow,
		index: len(d.sessions),
	}
	d.sessions = append(d.sessions, s)
	return s
}

func (d *db) Stats() IttoDbStats {
	s := IttoDbStats{
		Orders:     len(d.orders),
		PeakOrders: d.stat.maxOrders,
		Sessions:   len(d.sessions),
	}
	return s
}

func (d *db) NewMessage(pam packet.ApplicationMessage) *IttoDbMessage {
	s := d.getSession(pam.Flow())
	m := &IttoDbMessage{
		Pam:     pam,
		Session: &s,
		subscr:  d.subscr,
	}
	return m
}

func (d *db) ApplyOperation(operation IttoOperation) {
	operation.getOperation().populate()
	oid := operation.GetOptionId()
	if oid.Invalid() {
		return
	}
	switch op := operation.(type) {
	case *OperationAdd:
		// intentionally allow adding zero price/size orders
		o := order{OId: op.optionId, OrderSide: op.OrderSide}
		if op.origOrder != nil {
			if op.optionId.Valid() {
				log.Fatalf("bad option id for add operation %#v origOrder=%#v\n", op, *op.origOrder)
			}
			if op.Side != itto.MarketSideUnknown && op.Side != op.origOrder.Side {
				log.Fatalf("bad side for add operation %#v origOrder=%#v\n", op, *op.origOrder)
			}
			o.OId = op.origOrder.OId
			o.Side = op.origOrder.Side
		}
		d.orders[op.orderIndex()] = o
		if l := len(d.orders); l > d.stat.maxOrders {
			d.stat.maxOrders = l
		}
	default:
		o := *operation.getOperation().origOrder
		oidx := operation.getOperation().origOrderIndex()
		o.Size += op.GetSizeDelta()
		switch {
		case o.Size > 0:
			d.orders[oidx] = o
		case o.Size == 0:
			// treat OperationUpdate which zeroes order size as order removal
			delete(d.orders, oidx)
		case o.Size < 0:
			log.Fatalf("negative size after operation %#v origOrder=%#v\n", operation, o)
		}
	}
}

type IttoOperation interface {
	GetMessage() *IttoDbMessage
	GetOptionId() itto.OptionId
	GetOrigRef() itto.RefNumDelta
	GetSide() itto.MarketSide
	GetSizeDelta() int
	GetNewSize() int
	GetPrice() int
	getOperation() *Operation
}

type Operation struct {
	m           *IttoDbMessage
	d           *db
	origRefNumD itto.RefNumDelta
	origOrder   *order
	sibling     IttoOperation
}

func (op *Operation) GetMessage() *IttoDbMessage {
	return op.m
}
func (op *Operation) GetOrigRef() itto.RefNumDelta {
	return op.origRefNumD
}
func (op *Operation) populate() {
	if op.origOrder != nil {
		return
	}
	if op.sibling != nil {
		op.sibling.getOperation().populate()
		op.origOrder = op.sibling.getOperation().origOrder
	} else if op.origRefNumD != (itto.RefNumDelta{}) {
		if ord, err := op.d.findOrder(op.m.Pam.Flow(), op.origRefNumD); err == nil {
			op.origOrder = &ord
		}
	}
}
func (op *Operation) origOrderIndex() orderIndex {
	return NewOrderIndex(op.d, op.m.Pam.Flow(), op.origRefNumD)
}
func (o *Operation) getOptionId() (oid itto.OptionId) {
	o.populate()
	if o.origOrder != nil {
		return o.origOrder.OId
	} else {
		return itto.OptionId(0)
	}
}
func (o *Operation) getSide() (side itto.MarketSide) {
	o.populate()
	if o.origOrder != nil {
		side = o.origOrder.Side
	}
	return
}

type OperationAdd struct {
	Operation
	optionId itto.OptionId
	itto.OrderSide
}

func (o *OperationAdd) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationAdd) Independent() bool {
	return o.optionId.Valid()
}
func (o *OperationAdd) GetOptionId() itto.OptionId {
	if o.optionId.Valid() {
		return o.optionId
	} else {
		return o.Operation.getOptionId()
	}
}
func (o *OperationAdd) GetSide() (side itto.MarketSide) {
	if o.Side != itto.MarketSideUnknown {
		return o.Side
	} else {
		return o.Operation.getSide()
	}
}
func (o *OperationAdd) GetPrice() int {
	return o.Price
}
func (o *OperationAdd) GetSizeDelta() int {
	return o.Size
}
func (o *OperationAdd) GetNewSize() int {
	return o.Size
}
func (op *OperationAdd) orderIndex() orderIndex {
	return NewOrderIndex(op.d, op.m.Pam.Flow(), op.RefNumD)
}

type OperationRemove struct {
	Operation
}

func (o *OperationRemove) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationRemove) GetOptionId() itto.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationRemove) GetSide() (side itto.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationRemove) GetSizeDelta() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return -o.origOrder.Size
}
func (o *OperationRemove) GetNewSize() int {
	return 0
}
func (o *OperationRemove) GetPrice() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return o.origOrder.Price
}

type OperationUpdate struct {
	Operation
	sizeChange int
}

func (o *OperationUpdate) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationUpdate) GetOptionId() itto.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationUpdate) GetSide() (side itto.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationUpdate) GetSizeDelta() int {
	return -o.sizeChange
}
func (o *OperationUpdate) GetNewSize() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return o.origOrder.Size - o.sizeChange
}
func (o *OperationUpdate) GetPrice() int {
	o.Operation.populate()
	if o.origOrder == nil {
		log.Fatal("no origOrder")
	}
	return o.origOrder.Price
}

func (d *db) MessageOperations(m *IttoDbMessage) []IttoOperation {
	var ops []IttoOperation
	addOperation := func(origRefNumD itto.RefNumDelta, operation IttoOperation) {
		opop := operation.getOperation()
		opop.m = m
		opop.d = d
		opop.origRefNumD = origRefNumD
		ops = append(ops, operation)
	}
	addOperationReplace := func(origRefNumD itto.RefNumDelta, orderSide itto.OrderSide) {
		opRemove := &OperationRemove{}
		opAdd := &OperationAdd{
			// unknown: optionId; maybe unknown: OrderSide.Side
			OrderSide: orderSide,
			Operation: Operation{sibling: opRemove},
		}
		addOperation(origRefNumD, opRemove)
		addOperation(itto.RefNumDelta{}, opAdd)
	}
	switch im := m.Pam.Layer().(type) {
	case *itto.IttoMessageAddOrder:
		var oid itto.OptionId
		if !m.IgnoredBySubscriber() {
			oid = im.OId
		}
		addOperation(itto.RefNumDelta{}, &OperationAdd{optionId: oid, OrderSide: im.OrderSide})
	case *itto.IttoMessageAddQuote:
		var oid itto.OptionId
		if !m.IgnoredBySubscriber() {
			oid = im.OId
		}
		addOperation(itto.RefNumDelta{}, &OperationAdd{optionId: oid, OrderSide: im.Bid})
		addOperation(itto.RefNumDelta{}, &OperationAdd{optionId: oid, OrderSide: im.Ask})
	case *itto.IttoMessageSingleSideExecuted:
		addOperation(im.OrigRefNumD, &OperationUpdate{sizeChange: im.Size})
	case *itto.IttoMessageSingleSideExecutedWithPrice:
		addOperation(im.OrigRefNumD, &OperationUpdate{sizeChange: im.Size})
	case *itto.IttoMessageOrderCancel:
		addOperation(im.OrigRefNumD, &OperationUpdate{sizeChange: im.Size})
	case *itto.IttoMessageSingleSideReplace:
		addOperationReplace(im.OrigRefNumD, im.OrderSide)
	case *itto.IttoMessageSingleSideDelete:
		addOperation(im.OrigRefNumD, &OperationRemove{})
	case *itto.IttoMessageSingleSideUpdate:
		addOperationReplace(im.RefNumD, im.OrderSide)
	case *itto.IttoMessageQuoteReplace:
		addOperationReplace(im.Bid.OrigRefNumD, im.Bid.OrderSide)
		addOperationReplace(im.Ask.OrigRefNumD, im.Ask.OrderSide)
	case *itto.IttoMessageQuoteDelete:
		addOperation(im.BidOrigRefNumD, &OperationRemove{})
		addOperation(im.AskOrigRefNumD, &OperationRemove{})
	case *itto.IttoMessageBlockSingleSideDelete:
		for _, r := range im.RefNumDs {
			addOperation(r, &OperationRemove{})
		}
	}
	return ops
}

func (d *db) SetSubscription(s *Subscr) {
	d.subscr = s
}
<|endoftext|>


<commit_before>package gocli

import (
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"
)

type Router struct {
	Actions   map[string]*Action
	Separator string
}

func NewRouter(mapping map[string]*Action) *Router {
	router := &Router{}
	for path, action := range mapping {
		router.Register(path, action)
	}
	return router
}

func (cli *Router) Register(path string, action *Action) {
	if cli.Actions == nil {
		cli.Actions = make(map[string]*Action)
	}
	cli.Actions[path] = action
}

func (router *Router) Search(patterns []string) map[string]*Action {
	re := regexp.MustCompile(strings.Join(patterns, ".*/"))
	actions := make(map[string]*Action)
	for key, action := range router.Actions {
		if re.MatchString(key) {
			actions[key] = action
		}
	}
	return actions
}

func (cli *Router) Usage() string {
	keys := []string{}
	for key := range cli.Actions {
		keys = append(keys, key)
	}
	return cli.UsageForKeys(keys, "")
}

func (cli *Router) UsageForKeys(keys []string, pattern string) string {
	sort.Strings(keys)
	table := NewTable()
	if cli.Separator != "" {
		table.Separator = cli.Separator
	}
	maxParts := 0
	selected := []string{}
	for _, key := range keys {
		partsCount := len(strings.Split(key, "/"))
		if partsCount > maxParts {
			maxParts = partsCount
		}
		selected = append(selected, key)
	}
	for _, key := range selected {
		parts := strings.Split(key, "/")
		action := cli.Actions[key]

		// fill up parts
		for i := (maxParts - len(parts)); i > 0; i-- {
			parts = append(parts, "")
		}

		parts = append(parts, action.Usage, action.Description)
		table.Add(parts...)
		if action.Args != nil {
			usage := action.Args.Usage()
			if usage != "" {
				lines := strings.Split(usage, "\n")
				for _, line := range lines {
					usageParts := []string{}
					for j := 0; j < 3; j++ {
						usageParts = append(usageParts, "")
					}
					current := append(usageParts, line)
					table.Add(current...)
				}
			}
		}
	}
	out := []string{"USAGE"}
	out = append(out, table.String())
	return strings.Join(out, "\n")
}

func AddActionUsage(parts []string, table *Table, action *Action) {
	parts = append(parts, action.Usage, action.Description)
	table.Add(parts...)
	if action.Args != nil {
		usage := action.Args.Usage()
		if usage != "" {
			lines := strings.Split(usage, "\n")
			for _, line := range lines {
				usageParts := []string{}
				for j := 0; j < 3; j++ {
					usageParts = append(usageParts, "")
				}
				current := append(usageParts, line)
				table.Add(current...)
			}
		}
	}
}

func (cli *Router) Handle(raw []string) error {
	for i := len(raw); i > 0; i-- {
		parts := raw[1:i]
		actions := cli.Search(parts)
		switch len(actions) {
		case 0:
			continue
		case 1:
			var action *Action
			for k, a := range actions {
				parts = strings.Split(k, "/")
				action = a
			}
			args := action.Args
			if args == nil {
				args = &Args{}
			}
			e := args.Parse(raw[i:])
			if e == nil {
				e = action.Handler(args)
			}
			if e != nil {
				table := NewTable()
				AddActionUsage(parts, table, action)
				fmt.Println(table.String())
				os.Exit(0)
			}
			return nil
		default:
			keys := []string{}
			for key, _ := range actions {
				keys = append(keys, key)
			}
			fmt.Println(cli.UsageForKeys(keys, ""))
			return nil

		}
	}
	fmt.Println(cli.Usage())
	return nil
}
<commit_msg>print error message and do a exit 1 when handler returned error<commit_after>package gocli

import (
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"
)

type Router struct {
	Actions   map[string]*Action
	Separator string
}

func NewRouter(mapping map[string]*Action) *Router {
	router := &Router{}
	for path, action := range mapping {
		router.Register(path, action)
	}
	return router
}

func (cli *Router) Register(path string, action *Action) {
	if cli.Actions == nil {
		cli.Actions = make(map[string]*Action)
	}
	cli.Actions[path] = action
}

func (router *Router) Search(patterns []string) map[string]*Action {
	re := regexp.MustCompile(strings.Join(patterns, ".*/"))
	actions := make(map[string]*Action)
	for key, action := range router.Actions {
		if re.MatchString(key) {
			actions[key] = action
		}
	}
	return actions
}

func (cli *Router) Usage() string {
	keys := []string{}
	for key := range cli.Actions {
		keys = append(keys, key)
	}
	return cli.UsageForKeys(keys, "")
}

func (cli *Router) UsageForKeys(keys []string, pattern string) string {
	sort.Strings(keys)
	table := NewTable()
	if cli.Separator != "" {
		table.Separator = cli.Separator
	}
	maxParts := 0
	selected := []string{}
	for _, key := range keys {
		partsCount := len(strings.Split(key, "/"))
		if partsCount > maxParts {
			maxParts = partsCount
		}
		selected = append(selected, key)
	}
	for _, key := range selected {
		parts := strings.Split(key, "/")
		action := cli.Actions[key]

		// fill up parts
		for i := (maxParts - len(parts)); i > 0; i-- {
			parts = append(parts, "")
		}

		parts = append(parts, action.Usage, action.Description)
		table.Add(parts...)
		if action.Args != nil {
			usage := action.Args.Usage()
			if usage != "" {
				lines := strings.Split(usage, "\n")
				for _, line := range lines {
					usageParts := []string{}
					for j := 0; j < 3; j++ {
						usageParts = append(usageParts, "")
					}
					current := append(usageParts, line)
					table.Add(current...)
				}
			}
		}
	}
	out := []string{"USAGE"}
	out = append(out, table.String())
	return strings.Join(out, "\n")
}

func AddActionUsage(parts []string, table *Table, action *Action) {
	parts = append(parts, action.Usage, action.Description)
	table.Add(parts...)
	if action.Args != nil {
		usage := action.Args.Usage()
		if usage != "" {
			lines := strings.Split(usage, "\n")
			for _, line := range lines {
				usageParts := []string{}
				for j := 0; j < 3; j++ {
					usageParts = append(usageParts, "")
				}
				current := append(usageParts, line)
				table.Add(current...)
			}
		}
	}
}

func (cli *Router) Handle(raw []string) error {
	for i := len(raw); i > 0; i-- {
		parts := raw[1:i]
		actions := cli.Search(parts)
		switch len(actions) {
		case 0:
			continue
		case 1:
			var action *Action
			for k, a := range actions {
				parts = strings.Split(k, "/")
				action = a
			}
			args := action.Args
			if args == nil {
				args = &Args{}
			}
			e := args.Parse(raw[i:])
			if e == nil {
				e = action.Handler(args)
			}
			if e != nil {
				table := NewTable()
				fmt.Println("ERROR: " + e.Error())
				AddActionUsage(parts, table, action)
				fmt.Println(table.String())
				os.Exit(1)
			}
			return nil
		default:
			keys := []string{}
			for key, _ := range actions {
				keys = append(keys, key)
			}
			fmt.Println(cli.UsageForKeys(keys, ""))
			return nil

		}
	}
	fmt.Println(cli.Usage())
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Julien Schmidt. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

// Package httprouter is a trie based high performance HTTP request router.
//
// A trivial example is:
//
//  package main
//
//  import (
//      "fmt"
//      "github.com/julienschmidt/httprouter"
//      "net/http"
//      "log"
//  )
//
//  func Index(w http.ResponseWriter, r *http.Request, _ map[string]string) {
//      fmt.Fprint(w, "Welcome!\n")
//  }
//
//  func Hello(w http.ResponseWriter, r *http.Request, vars map[string]string) {
//      fmt.Fprintf(w, "hello, %s!\n", vars["name"])
//  }
//
//  func main() {
//      router := httprouter.New()
//      router.GET("/", Index)
//      router.GET("/hello/:name", Hello)
//
//      log.Fatal(http.ListenAndServe(":12345", router))
//  }
//
// The router matches incoming requests by the request method and the path.
// If a handle is registered for this path and method, the router delegates the
// request to that function.
// For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to
// register handles, for all other methods router.Handle can be used.
//
// The registered path, against which the router matches incoming requests, can
// contain two types of wildcards:
//  Syntax    Type
//  :name     Parameter
//  *name     CatchAll
// The value of wildcards is saved in a map as vars["name"] = value. The map is
// passed to the Handle func as a parameter.
//
// Parameters are variable path segments. They match anything until the next '/'
// or the path end:
//  Path: /blog/:category/:post
//
//  Requests:
//   /blog/go/request-routers            match: category="go", post="request-routers"
//   /blog/go/request-routers/           no match, but the router would redirect
//   /blog/go/                           no match
//   /blog/go/request-routers/comments   no match
//
// CatchAll wildcards match anything until the path end, including the directory
// index (the '/'' before the CatchAll). Since they match anything until the end,
// CatchAll wildcards must always be the final path element.
//  Path: /files/*filepath
//
//  Requests:
//   /files/                             match: filepath="/"
//   /files/LICENSE                      match: filepath="/LICENSE"
//   /files/templates/article.html       match: filepath="/templates/article.html"
//   /files                              no match, but the router would redirect
//
package httprouter

import (
	"net/http"
)

// Handle is a function that can be registered to a route to handle HTTP
// requests. Like http.HandlerFunc, but has a third parameter for the values of
// wildcards (variables).
type Handle func(http.ResponseWriter, *http.Request, map[string]string)

// NotFound is the default HTTP handler func for routes that can't be matched
// with an existing route.
// NotFound tries to redirect to a canonical URL generated with CleanPath.
// Otherwise the request is delegated to http.NotFound.
func NotFound(w http.ResponseWriter, req *http.Request) {
	if req.Method != "CONNECT" {
		path := req.URL.Path
		if cp := CleanPath(path); cp != path && cp != req.Referer() {
			http.Redirect(w, req, cp, http.StatusMovedPermanently)
			return
		}
	}

	http.NotFound(w, req)
}

// Router is a http.Handler which can be used to dispatch requests to different
// handler functions via configurable routes
type Router struct {
	node

	// Enables automatic redirection if the current route can't be matched but
	// handler for the path with (without) the trailing slash exists.
	// For example if /foo/ is requested but a route only exists for /foo, the
	// client is redirected to /foo with http status code 301.
	RedirectTrailingSlash bool

	// Configurable handler func which is used when no matching route is found.
	// Default is the NotFound func of this package.
	NotFound http.HandlerFunc

	// Handler func to handle panics recovered from http handlers.
	// It should be used to generate a error page and return the http error code
	// "500 - Internal Server Error".
	// The handler can be used to keep your server from crashing because of
	// unrecovered panics.
	PanicHandler func(http.ResponseWriter, *http.Request, interface{})
}

// Make sure the Router conforms with the http.Handler interface
var _ http.Handler = New()

// New returnes a new initialized Router.
// The router can be configured to also match the requested HTTP method or the
// requested Host.
func New() *Router {
	return &Router{
		RedirectTrailingSlash: true,
		NotFound:              NotFound,
	}
}

// GET is a shortcut for router.Handle("GET", path, handle)
func (r *Router) GET(path string, handle Handle) {
	r.Handle("GET", path, handle)
}

// POST is a shortcut for router.Handle("POST", path, handle)
func (r *Router) POST(path string, handle Handle) {
	r.Handle("POST", path, handle)
}

// PUT is a shortcut for router.Handle("PUT", path, handle)
func (r *Router) PUT(path string, handle Handle) {
	r.Handle("PUT", path, handle)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func (r *Router) PATCH(path string, handle Handle) {
	r.Handle("PATCH", path, handle)
}

// DELETE is a shortcut for router.Handle("DELETE", path, handle)
func (r *Router) DELETE(path string, handle Handle) {
	r.Handle("DELETE", path, handle)
}

// Handle registers a new request handle with the given path and method.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (r *Router) Handle(method, path string, handle Handle) {
	if path[0] != '/' {
		panic("path must begin with '/'")
	}
	r.addRoute(method, path, handle)
}

// HandlerFunc is an adapter which allows the usage of a http.HandlerFunc as a
// request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(w http.ResponseWriter, req *http.Request, _ map[string]string) {
			handler(w, req)
		},
	)
}

// ServeFiles serves files from the given file system root.
// The path must end with "/*filepath", files are then served from the local
// path /defined/root/dir/*filepath.
// For example if root is "/etc" and *filepath is "passwd", the local file
// "/etc/passwd" would be served.
// Internally a http.FileServer is used, therefore http.NotFound is used instead
// of the Router's NotFound handler.
// To use the operating system's file system implementation,
// use http.Dir:
//
//     router.ServeFiles("/*filepath", http.Dir("/var/www"))
func (r *Router) ServeFiles(path string, root http.FileSystem) {
	if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
		panic("path must end with /*filepath")
	}

	fileServer := http.FileServer(root)

	r.GET(path, func(w http.ResponseWriter, req *http.Request, vars map[string]string) {
		req.URL.Path = vars["filepath"]
		fileServer.ServeHTTP(w, req)
	})
}

func (r *Router) recv(w http.ResponseWriter, req *http.Request) {
	if rcv := recover(); rcv != nil {
		r.PanicHandler(w, req, rcv)
	}
}

// Make the router implement the http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if r.PanicHandler != nil {
		defer r.recv(w, req)
	}

	path := req.URL.Path

	if handle, vars, tsr := r.getValue(req.Method, path); handle != nil {
		handle(w, req, vars)
	} else if tsr && r.RedirectTrailingSlash && path != "/" {
		if path[len(path)-1] == '/' {
			path = path[:len(path)-1]
		} else {
			path = path + "/"
		}
		http.Redirect(w, req, path, http.StatusMovedPermanently)
		return
	} else { // Handle 404
		if r.NotFound != nil {
			r.NotFound(w, req)
		} else {
			http.NotFound(w, req)
		}
	}
}
<commit_msg>Update godoc<commit_after>// Copyright 2013 Julien Schmidt. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

// Package httprouter is a trie based high performance HTTP request router.
//
// A trivial example is:
//
//  package main
//
//  import (
//      "fmt"
//      "github.com/julienschmidt/httprouter"
//      "net/http"
//      "log"
//  )
//
//  func Index(w http.ResponseWriter, r *http.Request, _ map[string]string) {
//      fmt.Fprint(w, "Welcome!\n")
//  }
//
//  func Hello(w http.ResponseWriter, r *http.Request, vars map[string]string) {
//      fmt.Fprintf(w, "hello, %s!\n", vars["name"])
//  }
//
//  func main() {
//      router := httprouter.New()
//      router.GET("/", Index)
//      router.GET("/hello/:name", Hello)
//
//      log.Fatal(http.ListenAndServe(":12345", router))
//  }
//
// The router matches incoming requests by the request method and the path.
// If a handle is registered for this path and method, the router delegates the
// request to that function.
// For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to
// register handles, for all other methods router.Handle can be used.
//
// The registered path, against which the router matches incoming requests, can
// contain two types of wildcards:
//  Syntax    Type
//  :name     Parameter
//  *name     CatchAll
// The value of wildcards is saved in a map as vars["name"] = value. The map is
// passed to the Handle func as a parameter.
//
// Parameters are variable path segments. They match anything until the next '/'
// or the path end:
//  Path: /blog/:category/:post
//
//  Requests:
//   /blog/go/request-routers            match: category="go", post="request-routers"
//   /blog/go/request-routers/           no match, but the router would redirect
//   /blog/go/                           no match
//   /blog/go/request-routers/comments   no match
//
// CatchAll wildcards match anything until the path end, including the directory
// index (the '/'' before the CatchAll). Since they match anything until the end,
// CatchAll wildcards must always be the final path element.
//  Path: /files/*filepath
//
//  Requests:
//   /files/                             match: filepath="/"
//   /files/LICENSE                      match: filepath="/LICENSE"
//   /files/templates/article.html       match: filepath="/templates/article.html"
//   /files                              no match, but the router would redirect
//
package httprouter

import (
	"net/http"
)

// Handle is a function that can be registered to a route to handle HTTP
// requests. Like http.HandlerFunc, but has a third parameter for the values of
// wildcards (variables).
type Handle func(http.ResponseWriter, *http.Request, map[string]string)

// NotFound is the default HTTP handler func for routes that can't be matched
// with an existing route.
// NotFound tries to redirect to a canonical URL generated with CleanPath.
// Otherwise the request is delegated to http.NotFound.
func NotFound(w http.ResponseWriter, req *http.Request) {
	if req.Method != "CONNECT" {
		path := req.URL.Path
		if cp := CleanPath(path); cp != path && cp != req.Referer() {
			http.Redirect(w, req, cp, http.StatusMovedPermanently)
			return
		}
	}

	http.NotFound(w, req)
}

// Router is a http.Handler which can be used to dispatch requests to different
// handler functions via configurable routes
type Router struct {
	node

	// Enables automatic redirection if the current route can't be matched but
	// handler for the path with (without) the trailing slash exists.
	// For example if /foo/ is requested but a route only exists for /foo, the
	// client is redirected to /foo with http status code 301.
	RedirectTrailingSlash bool

	// Configurable handler func which is used when no matching route is found.
	// Default is the NotFound func of this package.
	NotFound http.HandlerFunc

	// Handler func to handle panics recovered from http handlers.
	// It should be used to generate a error page and return the http error code
	// "500 - Internal Server Error".
	// The handler can be used to keep your server from crashing because of
	// unrecovered panics.
	PanicHandler func(http.ResponseWriter, *http.Request, interface{})
}

// Make sure the Router conforms with the http.Handler interface
var _ http.Handler = New()

// New returnes a new initialized Router.
// The router can be configured to also match the requested HTTP method or the
// requested Host.
func New() *Router {
	return &Router{
		RedirectTrailingSlash: true,
		NotFound:              NotFound,
	}
}

// GET is a shortcut for router.Handle("GET", path, handle)
func (r *Router) GET(path string, handle Handle) {
	r.Handle("GET", path, handle)
}

// POST is a shortcut for router.Handle("POST", path, handle)
func (r *Router) POST(path string, handle Handle) {
	r.Handle("POST", path, handle)
}

// PUT is a shortcut for router.Handle("PUT", path, handle)
func (r *Router) PUT(path string, handle Handle) {
	r.Handle("PUT", path, handle)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func (r *Router) PATCH(path string, handle Handle) {
	r.Handle("PATCH", path, handle)
}

// DELETE is a shortcut for router.Handle("DELETE", path, handle)
func (r *Router) DELETE(path string, handle Handle) {
	r.Handle("DELETE", path, handle)
}

// Handle registers a new request handle with the given path and method.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (r *Router) Handle(method, path string, handle Handle) {
	if path[0] != '/' {
		panic("path must begin with '/'")
	}
	r.addRoute(method, path, handle)
}

// HandlerFunc is an adapter which allows the usage of a http.HandlerFunc as a
// request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(w http.ResponseWriter, req *http.Request, _ map[string]string) {
			handler(w, req)
		},
	)
}

// ServeFiles serves files from the given file system root.
// The path must end with "/*filepath", files are then served from the local
// path /defined/root/dir/*filepath.
// For example if root is "/etc" and *filepath is "passwd", the local file
// "/etc/passwd" would be served.
// Internally a http.FileServer is used, therefore http.NotFound is used instead
// of the Router's NotFound handler.
// To use the operating system's file system implementation,
// use http.Dir:
//     router.ServeFiles("/src/*filepath", http.Dir("/var/www"))
func (r *Router) ServeFiles(path string, root http.FileSystem) {
	if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
		panic("path must end with /*filepath")
	}

	fileServer := http.FileServer(root)

	r.GET(path, func(w http.ResponseWriter, req *http.Request, vars map[string]string) {
		req.URL.Path = vars["filepath"]
		fileServer.ServeHTTP(w, req)
	})
}

func (r *Router) recv(w http.ResponseWriter, req *http.Request) {
	if rcv := recover(); rcv != nil {
		r.PanicHandler(w, req, rcv)
	}
}

// Make the router implement the http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if r.PanicHandler != nil {
		defer r.recv(w, req)
	}

	path := req.URL.Path

	if handle, vars, tsr := r.getValue(req.Method, path); handle != nil {
		handle(w, req, vars)
	} else if tsr && r.RedirectTrailingSlash && path != "/" {
		if path[len(path)-1] == '/' {
			path = path[:len(path)-1]
		} else {
			path = path + "/"
		}
		http.Redirect(w, req, path, http.StatusMovedPermanently)
		return
	} else { // Handle 404
		if r.NotFound != nil {
			r.NotFound(w, req)
		} else {
			http.NotFound(w, req)
		}
	}
}
<|endoftext|>