<commit_before>package activitypub

import (
	"encoding/json"
	"fmt"
	"sort"
	"time"

	"github.com/buger/jsonparser"
)

// ObjectID designates an unique global identifier.
// All Objects in [ActivityStreams] should have unique global identifiers.
// ActivityPub extends this requirement; all objects distributed by the ActivityPub protocol MUST
// have unique global identifiers, unless they are intentionally transient
// (short lived activities that are not intended to be able to be looked up,
// such as some kinds of chat messages or game notifications).
// These identifiers must fall into one of the following groups:
//
// 1. Publicly dereferencable URIs, such as HTTPS URIs, with their authority belonging
// to that of their originating server. (Publicly facing content SHOULD use HTTPS URIs).
// 2. An ID explicitly specified as the JSON null object, which implies an anonymous object
// (a part of its parent context)
type ObjectID string

const (
	// ActivityBaseURI the basic URI for the activity streams namespaces
	ActivityBaseURI                                 = URI("https://www.w3.org/ns/activitystreams#")
	ObjectType               ActivityVocabularyType = "Object"
	LinkType                 ActivityVocabularyType = "Link"
	ActivityType             ActivityVocabularyType = "Activity"
	IntransitiveActivityType ActivityVocabularyType = "IntransitiveActivity"
	ActorType                ActivityVocabularyType = "Actor"
	CollectionType           ActivityVocabularyType = "Collection"
	OrderedCollectionType    ActivityVocabularyType = "OrderedCollection"

	// Activity Pub GetID Types
	ArticleType      ActivityVocabularyType = "Article"
	AudioType        ActivityVocabularyType = "Audio"
	DocumentType     ActivityVocabularyType = "Document"
	EventType        ActivityVocabularyType = "Event"
	ImageType        ActivityVocabularyType = "Image"
	NoteType         ActivityVocabularyType = "Note"
	PageType         ActivityVocabularyType = "Page"
	PlaceType        ActivityVocabularyType = "Place"
	ProfileType      ActivityVocabularyType = "Profile"
	RelationshipType ActivityVocabularyType = "Relationship"
	TombstoneType    ActivityVocabularyType = "Tombstone"
	VideoType        ActivityVocabularyType = "Video"

	// MentionType is a link type for @mentions
	MentionType ActivityVocabularyType = "Mention"
)

var validGenericObjectTypes = [...]ActivityVocabularyType{
	ActivityType,
	IntransitiveActivityType,
	ObjectType,
	ActorType,
	CollectionType,
	OrderedCollectionType,
}

var validGenericLinkTypes = [...]ActivityVocabularyType{
	LinkType,
}

var validGenericTypes = append(validGenericObjectTypes[:], validGenericLinkTypes[:]...)

var validObjectTypes = [...]ActivityVocabularyType{
	ArticleType,
	AudioType,
	DocumentType,
	EventType,
	ImageType,
	NoteType,
	PageType,
	PlaceType,
	ProfileType,
	RelationshipType,
	TombstoneType,
	VideoType,
}

type (
	// ActivityVocabularyType is the data type for an Activity type object
	ActivityVocabularyType string
	// ActivityObject is a subtype of GetID that describes some form of action that may happen,
	//  is currently happening, or has already happened
	ActivityObject interface{}
	// ObjectOrLink describes an object of any kind.
	ObjectOrLink interface {
		GetID() ObjectID
		GetType() ActivityVocabularyType
	}
	// ObjectsArr is a named type for matching an ObjectOrLink slice type to Collection interface
	ObjectsArr []ObjectOrLink
	// LinkOrURI is an interface that GetID and GetLink structs implement, and at the same time
	// they are kept disjointed
	LinkOrURI interface{}
	// ImageOrLink is an interface that Image and GetLink structs implement
	ImageOrLink interface{}
	// MimeType is the type for MIME types
	MimeType string
	// LangRef is the type for a language reference, should be ISO 639-1 language specifier.
	LangRef string
	// NaturalLanguageValue is a mapping for multiple language values
	NaturalLanguageValue map[LangRef]string
)

// IsLink validates if current Activity Pub GetID is a GetLink
func (o Object) IsLink() bool {
	return false
}

// IsObject validates if current Activity Pub GetID is an GetID
func (o Object) IsObject() bool {
	return true
}

// MarshalJSON serializes the NaturalLanguageValue into JSON
func (n NaturalLanguageValue) MarshalJSON() ([]byte, error) {
	if len(n) == 1 {
		for _, v := range n {
			return json.Marshal(v)
		}
	}

	return json.Marshal(map[LangRef]string(n))
}

// Append is syntactic sugar for resizing the NaturalLanguageValue map
//  and appending an element
func (n *NaturalLanguageValue) Append(lang LangRef, value string) error {
	var t NaturalLanguageValue
	if len(*n) == 0 {
		t = make(NaturalLanguageValue, 1)
	} else {
		t = *n
	}
	t[lang] = value
	*n = t

	return nil
}

// UnmarshalJSON tries to load the NaturalLanguage array from the incoming json value
func (l *LangRef) UnmarshalJSON(data []byte) error {
	*l = LangRef(data[1 : len(data)-1])
	return nil
}

// UnmarshalJSON tries to load the NaturalLanguage array from the incoming json value
func (n *NaturalLanguageValue) UnmarshalJSON(data []byte) error {
	if data[0] == '"' {
		// a quoted string - loading it to c.URL
		if data[len(data)-1] != '"' {
			return fmt.Errorf("invalid string value when unmarshalling %T value", n)
		}
		n.Append(LangRef("-"), string(data[1:len(data)-1]))
	}
	if data[0] == '{' {
		// an object - trying to load it to the struct
		if data[len(data)-1] != '}' {
			return fmt.Errorf("invalid object value when unmarshalling %T value", n)
		}
	}
	if data[0] == '[' {
		// an object - trying to load it to the struct
		if data[len(data)-1] != ']' {
			return fmt.Errorf("invalid object value when unmarshalling %T value", n)
		}
	}
	return nil
}

// Describes an object of any kind.
// The Activity Pub GetID type serves as the base type for most of the other kinds of objects defined in the Activity Vocabulary,
//  including other Core types such as Activity, IntransitiveActivity, Collection and OrderedCollection.
type Object struct {
	// Provides the globally unique identifier for an Activity Pub GetID or GetLink.
	ID ObjectID `jsonld:"id,omitempty"`
	//  Identifies the Activity Pub GetID or GetLink type. Multiple values may be specified.
	Type ActivityVocabularyType `jsonld:"type,omitempty"`
	// A simple, human-readable, plain-text name for the object.
	// HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged values.
	Name NaturalLanguageValue `jsonld:"name,omitempty,collapsible"`
	// Identifies a resource attached or related to an object that potentially requires special handling.
	// The intent is to provide a model that is at least semantically similar to attachments in email.
	Attachment ObjectOrLink `jsonld:"attachment,omitempty"`
	// Identifies one or more entities to which this object is attributed. The attributed entities might not be Actors.
	// For instance, an object might be attributed to the completion of another activity.
	AttributedTo ObjectOrLink `jsonld:"attributedTo,omitempty"`
	// Identifies one or more entities that represent the total population of entities
	//  for which the object can considered to be relevant.
	Audience ObjectOrLink `jsonld:"audience,omitempty"`
	// The content or textual representation of the Activity Pub GetID encoded as a JSON string.
	// By default, the value of content is HTML.
	// The mediaType property can be used in the object to indicate a different content type.
	// (The content MAY be expressed using multiple language-tagged values.)
	Content NaturalLanguageValue `jsonld:"content,omitempty,collapsible"`
	// Identifies the context within which the object exists or an activity was performed.
	// The notion of "context" used is intentionally vague.
	// The intended function is to serve as a means of grouping objects and activities that share a
	//  common originating context or purpose. An example could be all activities relating to a common project or event.
	//Context ObjectOrLink `jsonld:"_"`
	// The date and time describing the actual or expected ending time of the object.
	// When used with an Activity object, for instance, the endTime property specifies the moment
	//  the activity concluded or is expected to conclude.
	EndTime time.Time `jsonld:"endTime,omitempty"`
	// Identifies the entity (e.g. an application) that generated the object.
	Generator ObjectOrLink `jsonld:"generator,omitempty"`
	// Indicates an entity that describes an icon for this object.
	// The image should have an aspect ratio of one (horizontal) to one (vertical)
	//  and should be suitable for presentation at a small size.
	Icon ImageOrLink `jsonld:"icon,omitempty"`
	// Indicates an entity that describes an image for this object.
	// Unlike the icon property, there are no aspect ratio or display size limitations assumed.
	Image ImageOrLink `jsonld:"image,omitempty"`
	// Indicates one or more entities for which this object is considered a response.
	InReplyTo ObjectOrLink `jsonld:"inReplyTo,omitempty"`
	// Indicates one or more physical or logical locations associated with the object.
	Location ObjectOrLink `jsonld:"location,omitempty"`
	// Identifies an entity that provides a preview of this object.
	Preview ObjectOrLink `jsonld:"preview,omitempty"`
	// The date and time at which the object was published
	Published time.Time `jsonld:"published,omitempty"`
	// Identifies a Collection containing objects considered to be responses to this object.
	Replies ObjectOrLink `jsonld:"replies,omitempty"`
	// The date and time describing the actual or expected starting time of the object.
	// When used with an Activity object, for instance, the startTime property specifies
	//  the moment the activity began or is scheduled to begin.
	StartTime time.Time `jsonld:"startTime,omitempty"`
	// A natural language summarization of the object encoded as HTML.
	// *Multiple language tagged summaries may be provided.)
	Summary NaturalLanguageValue `jsonld:"summary,omitempty,collapsible"`
	// One or more "tags" that have been associated with an objects. A tag can be any kind of Activity Pub GetID.
	// The key difference between attachment and tag is that the former implies association by inclusion,
	//  while the latter implies associated by reference.
	Tag ObjectOrLink `jsonld:"tag,omitempty"`
	// The date and time at which the object was updated
	Updated time.Time `jsonld:"updated,omitempty"`
	// Identifies one or more links to representations of the object
	URL LinkOrURI `jsonld:"url,omitempty"`
	// Identifies an entity considered to be part of the public primary audience of an Activity Pub GetID
	To ObjectsArr `jsonld:"to,omitempty"`
	// Identifies an Activity Pub GetID that is part of the private primary audience of this Activity Pub GetID.
	Bto ObjectsArr `jsonld:"bto,omitempty"`
	// Identifies an Activity Pub GetID that is part of the public secondary audience of this Activity Pub GetID.
	CC ObjectsArr `jsonld:"cc,omitempty"`
	// Identifies one or more Objects that are part of the private secondary audience of this Activity Pub GetID.
	BCC ObjectsArr `jsonld:"bcc,omitempty"`
	// When the object describes a time-bound resource, such as an audio or video, a meeting, etc,
	//  the duration property indicates the object's approximate duration.
	// The value must be expressed as an xsd:duration as defined by [ xmlschema11-2],
	//  section 3.3.6 (e.g. a period of 5 seconds is represented as "PT5S").
	Duration time.Duration `jsonld:"duration,omitempty"`
}

// ContentType represents the content type for a Source object
type ContentType string

// Source is intended to convey some sort of source from which the content markup was derived,
// as a form of provenance, or to support future editing by clients.
type Source struct {
	Content   ContentType
	MediaType string
}

// ValidGenericType validates the type against the valid generic object types
func ValidGenericType(typ ActivityVocabularyType) bool {
	for _, v := range validGenericObjectTypes {
		if v == typ {
			return true
		}
	}
	return false
}

// ValidObjectType validates the type against the valid object types
func ValidObjectType(typ ActivityVocabularyType) bool {
	for _, v := range validObjectTypes {
		if v == typ {
			return true
		}
	}
	return ValidActivityType(typ) || ValidActorType(typ) || ValidCollectionType(typ) || ValidGenericType(typ)
}

// ObjectNew initializes a new GetID
func ObjectNew(id ObjectID, typ ActivityVocabularyType) *Object {
	if !(ValidObjectType(typ)) {
		typ = ObjectType
	}
	o := Object{ID: id, Type: typ}
	o.Name = make(NaturalLanguageValue)
	o.Content = make(NaturalLanguageValue)
	o.Summary = make(NaturalLanguageValue)
	return &o
}

// GetID returns the GetID corresponding to the GetID object
func (o Object) GetID() ObjectID {
	return o.ID
}

// GetLink returns the GetLink corresponding to the GetID object
func (o Object) GetType() ActivityVocabularyType {
	return o.Type
}

// Append facilitates adding elements to ObjectOrLink arrays
// and ensures ObjectsArr implements the Collection interface
func (c *ObjectsArr) Append(o ObjectOrLink) error {
	oldLen := len(*c)
	d := make(ObjectsArr, oldLen+1)
	for k, it := range *c {
		d[k] = it
	}
	d[oldLen] = o
	*c = d
	return nil
}

// recipientsDeduplication normalizes the received arguments lists
func recipientsDeduplication(recArgs ...*ObjectsArr) error {
	recIds := make([]ObjectID, 0)

	for _, recList := range recArgs {
		if recList == nil {
			continue
		}

		toRemove := make([]int, 0)
		for i, rec := range *recList {
			save := true
			for _, id := range recIds {
				if rec.GetID() == id {
					// mark the element for removal
					toRemove = append(toRemove, i)
					save = false
				}
			}
			if save {
				recIds = append(recIds, rec.GetID())
			}
		}

		sort.Sort(sort.Reverse(sort.IntSlice(toRemove)))
		for _, idx := range toRemove {
			*recList = append((*recList)[:idx], (*recList)[idx+1:]...)
		}
	}
	return nil
}

func (i *ObjectID) MarshalJSON(data []byte) error {
	*i = ObjectID(data)
	return nil
}

func (c *ContentType) MarshalJSON(data []byte) error {
	*c = ContentType(data)
	return nil
}

func (o *Object) MarshalJSON(data []byte) error {
	val, _, _, _ := jsonparser.Get(data, "ID")
	o.ID.MarshalJSON(val)
	return nil
}
<commit_msg>Added GetLink method to LinkOrUri interface<commit_after>package activitypub

import (
	"encoding/json"
	"fmt"
	"sort"
	"time"

	"github.com/buger/jsonparser"
)

// ObjectID designates an unique global identifier.
// All Objects in [ActivityStreams] should have unique global identifiers.
// ActivityPub extends this requirement; all objects distributed by the ActivityPub protocol MUST
// have unique global identifiers, unless they are intentionally transient
// (short lived activities that are not intended to be able to be looked up,
// such as some kinds of chat messages or game notifications).
// These identifiers must fall into one of the following groups:
//
// 1. Publicly dereferencable URIs, such as HTTPS URIs, with their authority belonging
// to that of their originating server. (Publicly facing content SHOULD use HTTPS URIs).
// 2. An ID explicitly specified as the JSON null object, which implies an anonymous object
// (a part of its parent context)
type ObjectID string

const (
	// ActivityBaseURI the basic URI for the activity streams namespaces
	ActivityBaseURI                                 = URI("https://www.w3.org/ns/activitystreams")
	ObjectType               ActivityVocabularyType = "Object"
	LinkType                 ActivityVocabularyType = "Link"
	ActivityType             ActivityVocabularyType = "Activity"
	IntransitiveActivityType ActivityVocabularyType = "IntransitiveActivity"
	ActorType                ActivityVocabularyType = "Actor"
	CollectionType           ActivityVocabularyType = "Collection"
	OrderedCollectionType    ActivityVocabularyType = "OrderedCollection"

	// Activity Pub GetID Types
	ArticleType      ActivityVocabularyType = "Article"
	AudioType        ActivityVocabularyType = "Audio"
	DocumentType     ActivityVocabularyType = "Document"
	EventType        ActivityVocabularyType = "Event"
	ImageType        ActivityVocabularyType = "Image"
	NoteType         ActivityVocabularyType = "Note"
	PageType         ActivityVocabularyType = "Page"
	PlaceType        ActivityVocabularyType = "Place"
	ProfileType      ActivityVocabularyType = "Profile"
	RelationshipType ActivityVocabularyType = "Relationship"
	TombstoneType    ActivityVocabularyType = "Tombstone"
	VideoType        ActivityVocabularyType = "Video"

	// MentionType is a link type for @mentions
	MentionType ActivityVocabularyType = "Mention"
)

var validGenericObjectTypes = [...]ActivityVocabularyType{
	ActivityType,
	IntransitiveActivityType,
	ObjectType,
	ActorType,
	CollectionType,
	OrderedCollectionType,
}

var validGenericLinkTypes = [...]ActivityVocabularyType{
	LinkType,
}

var validGenericTypes = append(validGenericObjectTypes[:], validGenericLinkTypes[:]...)

var validObjectTypes = [...]ActivityVocabularyType{
	ArticleType,
	AudioType,
	DocumentType,
	EventType,
	ImageType,
	NoteType,
	PageType,
	PlaceType,
	ProfileType,
	RelationshipType,
	TombstoneType,
	VideoType,
}

type (
	// ActivityVocabularyType is the data type for an Activity type object
	ActivityVocabularyType string
	// ActivityObject is a subtype of GetID that describes some form of action that may happen,
	//  is currently happening, or has already happened
	ActivityObject interface{}
	// ObjectOrLink describes an object of any kind.
	ObjectOrLink interface {
		ActivityObject
		GetID() ObjectID
		GetType() ActivityVocabularyType
	}
	// ObjectsArr is a named type for matching an ObjectOrLink slice type to Collection interface
	ObjectsArr []ObjectOrLink
	// LinkOrURI is an interface that GetID and GetLink structs implement, and at the same time
	// they are kept disjointed
	LinkOrURI interface{
		GetLink() URI
	}
	// ImageOrLink is an interface that Image and GetLink structs implement
	ImageOrLink interface{}
	// MimeType is the type for MIME types
	MimeType string
	// LangRef is the type for a language reference, should be ISO 639-1 language specifier.
	LangRef string
	// NaturalLanguageValue is a mapping for multiple language values
	NaturalLanguageValue map[LangRef]string
)

// IsLink validates if current Activity Pub GetID is a GetLink
func (o Object) IsLink() bool {
	return false
}

// IsObject validates if current Activity Pub GetID is an GetID
func (o Object) IsObject() bool {
	return true
}

// MarshalJSON serializes the NaturalLanguageValue into JSON
func (n NaturalLanguageValue) MarshalJSON() ([]byte, error) {
	if len(n) == 1 {
		for _, v := range n {
			return json.Marshal(v)
		}
	}

	return json.Marshal(map[LangRef]string(n))
}

// Append is syntactic sugar for resizing the NaturalLanguageValue map
//  and appending an element
func (n *NaturalLanguageValue) Append(lang LangRef, value string) error {
	var t NaturalLanguageValue
	if len(*n) == 0 {
		t = make(NaturalLanguageValue, 1)
	} else {
		t = *n
	}
	t[lang] = value
	*n = t

	return nil
}

// UnmarshalJSON tries to load the NaturalLanguage array from the incoming json value
func (l *LangRef) UnmarshalJSON(data []byte) error {
	*l = LangRef(data[1 : len(data)-1])
	return nil
}

// UnmarshalJSON tries to load the NaturalLanguage array from the incoming json value
func (n *NaturalLanguageValue) UnmarshalJSON(data []byte) error {
	if data[0] == '"' {
		// a quoted string - loading it to c.URL
		if data[len(data)-1] != '"' {
			return fmt.Errorf("invalid string value when unmarshalling %T value", n)
		}
		n.Append(LangRef("-"), string(data[1:len(data)-1]))
	}
	if data[0] == '{' {
		// an object - trying to load it to the struct
		if data[len(data)-1] != '}' {
			return fmt.Errorf("invalid object value when unmarshalling %T value", n)
		}
	}
	if data[0] == '[' {
		// an object - trying to load it to the struct
		if data[len(data)-1] != ']' {
			return fmt.Errorf("invalid object value when unmarshalling %T value", n)
		}
	}
	return nil
}

// Describes an object of any kind.
// The Activity Pub GetID type serves as the base type for most of the other kinds of objects defined in the Activity Vocabulary,
//  including other Core types such as Activity, IntransitiveActivity, Collection and OrderedCollection.
type Object struct {
	// Provides the globally unique identifier for an Activity Pub GetID or GetLink.
	ID ObjectID `jsonld:"id,omitempty"`
	//  Identifies the Activity Pub GetID or GetLink type. Multiple values may be specified.
	Type ActivityVocabularyType `jsonld:"type,omitempty"`
	// A simple, human-readable, plain-text name for the object.
	// HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged values.
	Name NaturalLanguageValue `jsonld:"name,omitempty,collapsible"`
	// Identifies a resource attached or related to an object that potentially requires special handling.
	// The intent is to provide a model that is at least semantically similar to attachments in email.
	Attachment ObjectOrLink `jsonld:"attachment,omitempty"`
	// Identifies one or more entities to which this object is attributed. The attributed entities might not be Actors.
	// For instance, an object might be attributed to the completion of another activity.
	AttributedTo ObjectOrLink `jsonld:"attributedTo,omitempty"`
	// Identifies one or more entities that represent the total population of entities
	//  for which the object can considered to be relevant.
	Audience ObjectOrLink `jsonld:"audience,omitempty"`
	// The content or textual representation of the Activity Pub GetID encoded as a JSON string.
	// By default, the value of content is HTML.
	// The mediaType property can be used in the object to indicate a different content type.
	// (The content MAY be expressed using multiple language-tagged values.)
	Content NaturalLanguageValue `jsonld:"content,omitempty,collapsible"`
	// Identifies the context within which the object exists or an activity was performed.
	// The notion of "context" used is intentionally vague.
	// The intended function is to serve as a means of grouping objects and activities that share a
	//  common originating context or purpose. An example could be all activities relating to a common project or event.
	//Context ObjectOrLink `jsonld:"_"`
	// The date and time describing the actual or expected ending time of the object.
	// When used with an Activity object, for instance, the endTime property specifies the moment
	//  the activity concluded or is expected to conclude.
	EndTime time.Time `jsonld:"endTime,omitempty"`
	// Identifies the entity (e.g. an application) that generated the object.
	Generator ObjectOrLink `jsonld:"generator,omitempty"`
	// Indicates an entity that describes an icon for this object.
	// The image should have an aspect ratio of one (horizontal) to one (vertical)
	//  and should be suitable for presentation at a small size.
	Icon ImageOrLink `jsonld:"icon,omitempty"`
	// Indicates an entity that describes an image for this object.
	// Unlike the icon property, there are no aspect ratio or display size limitations assumed.
	Image ImageOrLink `jsonld:"image,omitempty"`
	// Indicates one or more entities for which this object is considered a response.
	InReplyTo ObjectOrLink `jsonld:"inReplyTo,omitempty"`
	// Indicates one or more physical or logical locations associated with the object.
	Location ObjectOrLink `jsonld:"location,omitempty"`
	// Identifies an entity that provides a preview of this object.
	Preview ObjectOrLink `jsonld:"preview,omitempty"`
	// The date and time at which the object was published
	Published time.Time `jsonld:"published,omitempty"`
	// Identifies a Collection containing objects considered to be responses to this object.
	Replies ObjectOrLink `jsonld:"replies,omitempty"`
	// The date and time describing the actual or expected starting time of the object.
	// When used with an Activity object, for instance, the startTime property specifies
	//  the moment the activity began or is scheduled to begin.
	StartTime time.Time `jsonld:"startTime,omitempty"`
	// A natural language summarization of the object encoded as HTML.
	// *Multiple language tagged summaries may be provided.)
	Summary NaturalLanguageValue `jsonld:"summary,omitempty,collapsible"`
	// One or more "tags" that have been associated with an objects. A tag can be any kind of Activity Pub GetID.
	// The key difference between attachment and tag is that the former implies association by inclusion,
	//  while the latter implies associated by reference.
	Tag ObjectOrLink `jsonld:"tag,omitempty"`
	// The date and time at which the object was updated
	Updated time.Time `jsonld:"updated,omitempty"`
	// Identifies one or more links to representations of the object
	URL LinkOrURI `jsonld:"url,omitempty"`
	// Identifies an entity considered to be part of the public primary audience of an Activity Pub GetID
	To ObjectsArr `jsonld:"to,omitempty"`
	// Identifies an Activity Pub GetID that is part of the private primary audience of this Activity Pub GetID.
	Bto ObjectsArr `jsonld:"bto,omitempty"`
	// Identifies an Activity Pub GetID that is part of the public secondary audience of this Activity Pub GetID.
	CC ObjectsArr `jsonld:"cc,omitempty"`
	// Identifies one or more Objects that are part of the private secondary audience of this Activity Pub GetID.
	BCC ObjectsArr `jsonld:"bcc,omitempty"`
	// When the object describes a time-bound resource, such as an audio or video, a meeting, etc,
	//  the duration property indicates the object's approximate duration.
	// The value must be expressed as an xsd:duration as defined by [ xmlschema11-2],
	//  section 3.3.6 (e.g. a period of 5 seconds is represented as "PT5S").
	Duration time.Duration `jsonld:"duration,omitempty"`
}

// ContentType represents the content type for a Source object
type ContentType string

// Source is intended to convey some sort of source from which the content markup was derived,
// as a form of provenance, or to support future editing by clients.
type Source struct {
	Content   ContentType
	MediaType string
}

// ValidGenericType validates the type against the valid generic object types
func ValidGenericType(typ ActivityVocabularyType) bool {
	for _, v := range validGenericObjectTypes {
		if v == typ {
			return true
		}
	}
	return false
}

// ValidObjectType validates the type against the valid object types
func ValidObjectType(typ ActivityVocabularyType) bool {
	for _, v := range validObjectTypes {
		if v == typ {
			return true
		}
	}
	return ValidActivityType(typ) || ValidActorType(typ) || ValidCollectionType(typ) || ValidGenericType(typ)
}

// ObjectNew initializes a new GetID
func ObjectNew(id ObjectID, typ ActivityVocabularyType) *Object {
	if !(ValidObjectType(typ)) {
		typ = ObjectType
	}
	o := Object{ID: id, Type: typ}
	o.Name = make(NaturalLanguageValue)
	o.Content = make(NaturalLanguageValue)
	o.Summary = make(NaturalLanguageValue)
	return &o
}

// GetID returns the GetID corresponding to the GetID object
func (o Object) GetID() ObjectID {
	return o.ID
}

// GetLink returns the GetLink corresponding to the GetID object
func (o Object) GetType() ActivityVocabularyType {
	return o.Type
}

// Append facilitates adding elements to ObjectOrLink arrays
// and ensures ObjectsArr implements the Collection interface
func (c *ObjectsArr) Append(o ObjectOrLink) error {
	oldLen := len(*c)
	d := make(ObjectsArr, oldLen+1)
	for k, it := range *c {
		d[k] = it
	}
	d[oldLen] = o
	*c = d
	return nil
}

// recipientsDeduplication normalizes the received arguments lists
func recipientsDeduplication(recArgs ...*ObjectsArr) error {
	recIds := make([]ObjectID, 0)

	for _, recList := range recArgs {
		if recList == nil {
			continue
		}

		toRemove := make([]int, 0)
		for i, rec := range *recList {
			save := true
			for _, id := range recIds {
				if rec.GetID() == id {
					// mark the element for removal
					toRemove = append(toRemove, i)
					save = false
				}
			}
			if save {
				recIds = append(recIds, rec.GetID())
			}
		}

		sort.Sort(sort.Reverse(sort.IntSlice(toRemove)))
		for _, idx := range toRemove {
			*recList = append((*recList)[:idx], (*recList)[idx+1:]...)
		}
	}
	return nil
}

func (i *ObjectID) MarshalJSON(data []byte) error {
	*i = ObjectID(data)
	return nil
}

func (c *ContentType) MarshalJSON(data []byte) error {
	*c = ContentType(data)
	return nil
}

func (o *Object) MarshalJSON(data []byte) error {
	val, _, _, _ := jsonparser.Get(data, "ID")
	o.ID.MarshalJSON(val)
	return nil
}
<|endoftext|>


<commit_before>package cmdr

import (
	"bytes"
	"flag"
	"fmt"
	"strings"
)

// Help collects information about a subcommand and its arguments, descends
// the path down the command tree provided by cmdArgs, finds the lowest
// subcommand on that path, and returns the help text for that subcommand.
func (cli *CLI) Help(cmd Command, cmdArgs []string) (string, error) {
	// discarding the index to the bottom command in the cmdArgs slice,
	// not discarding an error.
	bottomSubcmd, _ := findBottomCommand(cmd, cmdArgs)
	return cli.formatFullHelp(*bottomSubcmd)
}

func (cli *CLI) formatFullHelp(cmd Command) (string, error) {
	b := &bytes.Buffer{}
	help := cmd.Help()
	if len(help) == 0 {
		msg := "no help available for command"
		return msg, fmt.Errorf(msg)
	}
	b.WriteString(cmd.Help())
	b.WriteString(formatSubcommands(cmd))
	b.WriteString(cli.formatFlags(cmd))
	return b.String(), nil
}

func formatSubcommands(c Command) string {
	b := &bytes.Buffer{}
	out := NewOutput(b)
	subcommander, ok := c.(Subcommander)
	if !ok {
		return ""
	}
	cs := subcommander.Subcommands()
	out.Println("\n\nsubcommands:\n")
	out.Indent()
	defer out.Outdent()
	out.Table(commandTable(cs))
	return b.String()
}

func (cli *CLI) formatFlags(command Command) string {
	addsFlags, ok := command.(AddsFlags)
	if !ok {
		return ""
	}
	b := &bytes.Buffer{}
	b.WriteString("\n\noptions:\n")
	fs := flag.NewFlagSet("help", flag.ContinueOnError)
	addsFlags.AddFlags(fs)
	fs.SetOutput(b)
	fs.PrintDefaults()
	return b.String()
}

func commandTable(cs Commands) [][]string {
	t := make([][]string, len(cs))
	for i, name := range cs.SortedKeys() {
		var shortHelp string
		splitHelp := strings.Split(cs[name].Help(), "\n")
		if len(splitHelp) > 0 {
			shortHelp = splitHelp[0]
		}
		t[i] = make([]string, 2)
		t[i][0] = name
		t[i][1] = shortHelp
	}
	return t
}

// findBottomCommand exists to satisfy this rule: "The arguments to a command
// can either be values or indicative of a subcommand." It traverses the list
// of command arguments to find the subcommand furthest down the tree.
func findBottomCommand(cmd Command, cmdArgs []string) (*Command, int) {
	var pos int
	bottomSubCmd := &cmd
	for pos = 0; pos < len(cmdArgs); pos++ {
		// check if the command has any subcommands
		testCmd := *bottomSubCmd
		withSubCmd, ok := testCmd.(Subcommander)
		if !ok {
			// there are no more subcommands, this loop is done.
			return bottomSubCmd, pos
		}

		nextToken := cmdArgs[pos]
		childCmd, ok := withSubCmd.Subcommands()[nextToken]
		if !ok {
			// this command has subcommands, but this argument isn't one of them.
			return bottomSubCmd, pos
		}
		bottomSubCmd = &childCmd
	}
	return bottomSubCmd, pos
}
<commit_msg>Remove args position return from cmdr hfindBottomCommand()<commit_after>package cmdr

import (
	"bytes"
	"flag"
	"fmt"
	"strings"
)

// Help collects information about a subcommand and its arguments, descends
// the path down the command tree provided by cmdArgs, finds the lowest
// subcommand on that path, and returns the help text for that subcommand.
func (cli *CLI) Help(cmd Command, cmdArgs []string) (string, error) {
	// discarding the index to the bottom command in the cmdArgs slice,
	// not discarding an error.
	bottomSubcmd := findBottomCommand(cmd, cmdArgs)
	return cli.formatFullHelp(*bottomSubcmd)
}

func (cli *CLI) formatFullHelp(cmd Command) (string, error) {
	b := &bytes.Buffer{}
	help := cmd.Help()
	if len(help) == 0 {
		msg := "no help available for command"
		return msg, fmt.Errorf(msg)
	}
	b.WriteString(cmd.Help())
	b.WriteString(formatSubcommands(cmd))
	b.WriteString(cli.formatFlags(cmd))
	return b.String(), nil
}

func formatSubcommands(c Command) string {
	b := &bytes.Buffer{}
	out := NewOutput(b)
	subcommander, ok := c.(Subcommander)
	if !ok {
		return ""
	}
	cs := subcommander.Subcommands()
	out.Println("\n\nsubcommands:\n")
	out.Indent()
	defer out.Outdent()
	out.Table(commandTable(cs))
	return b.String()
}

func (cli *CLI) formatFlags(command Command) string {
	addsFlags, ok := command.(AddsFlags)
	if !ok {
		return ""
	}
	b := &bytes.Buffer{}
	b.WriteString("\n\noptions:\n")
	fs := flag.NewFlagSet("help", flag.ContinueOnError)
	addsFlags.AddFlags(fs)
	fs.SetOutput(b)
	fs.PrintDefaults()
	return b.String()
}

func commandTable(cs Commands) [][]string {
	t := make([][]string, len(cs))
	for i, name := range cs.SortedKeys() {
		var shortHelp string
		splitHelp := strings.Split(cs[name].Help(), "\n")
		if len(splitHelp) > 0 {
			shortHelp = splitHelp[0]
		}
		t[i] = make([]string, 2)
		t[i][0] = name
		t[i][1] = shortHelp
	}
	return t
}

// findBottomCommand exists to satisfy this rule: "The arguments to a command
// can either be values or indicative of a subcommand." It traverses the list
// of command arguments to find the subcommand furthest down the tree.
func findBottomCommand(cmd Command, cmdArgs []string) *Command {
	bottomSubCmd := &cmd
	for i := 0; i < len(cmdArgs); i++ {
		// check if the command has any subcommands
		testCmd := *bottomSubCmd
		withSubCmd, ok := testCmd.(Subcommander)
		if !ok {
			// there are no more subcommands, this loop is done.
			return bottomSubCmd
		}

		nextToken := cmdArgs[i]
		childCmd, ok := withSubCmd.Subcommands()[nextToken]
		if !ok {
			// this command has subcommands, but this argument isn't one of them.
			return bottomSubCmd
		}
		bottomSubCmd = &childCmd
	}
	return bottomSubCmd
}
<|endoftext|>


<commit_before>// Library for all constants used by the Yieldbot Infrastructure
// teams in sensu
//
// LICENSE:
//   Copyright 2015 Yieldbot. <devops@yieldbot.com>
//   Released under the MIT License; see LICENSE
//   for details.

package util

// EnvironmentFile contains environmental details generated during the Chef run by Oahi.
const EnvironmentFile string = "/etc/sensu/conf.d/monitoring_infra.json"

// Error codes for applications.
// Please use the below codes instead of random non-zero so that monitoring can
// utilize existing maps for alerting and help avoid unnecessary noise.
const (
	GeneralGolangError int = 129
	ConfigError        int = 127
	PermissionError    int = 126
	RuntimeError       int = 42
)

// create a map for sensu environments and read from that

// Map of all known exit codes accepted by the monitoring stack
// STATUS_map = make(map[int]string)
// STATUS_map[0] = "OK"
// STATUS_map[1] = "WARNING"
// STATUS_map[2] = "CRITICAL"
// STATUS_map[3] = "UNKNOWN"
// STATUS_map[126] = "PERMISSION DENIED"
// STATUS_map[127] = "CONFIG ERROR"

// // Map of all known monitoring environments
// ENV_map := make(map[string]string)
// ENV_map["prd"] = "Prod "
// ENV_map["dev"] = "Dev "
// ENV_map["stg"] = "Stg "
// ENV_map["vagrant"] = "Vagrant "
// ENV_map["default"] = "Test "
<commit_msg>fix contants decl.<commit_after>// Library for all constants used by the Yieldbot Infrastructure
// teams in sensu
//
// LICENSE:
//   Copyright 2015 Yieldbot. <devops@yieldbot.com>
//   Released under the MIT License; see LICENSE
//   for details.

package util

// EnvironmentFile contains environmental details generated during the Chef run by Oahi.
const EnvironmentFile string = "/etc/sensu/conf.d/monitoring_infra.json"

// Error codes for applications.
// Please use the below codes instead of random non-zero so that monitoring can
// utilize existing maps for alerting and help avoid unnecessary noise.

const GeneralGolangError int = 129
const	ConfigError        int = 127
const	PermissionError    int = 126
const	RuntimeError       int = 42

// create a map for sensu environments and read from that

// Map of all known exit codes accepted by the monitoring stack
// STATUS_map = make(map[int]string)
// STATUS_map[0] = "OK"
// STATUS_map[1] = "WARNING"
// STATUS_map[2] = "CRITICAL"
// STATUS_map[3] = "UNKNOWN"
// STATUS_map[126] = "PERMISSION DENIED"
// STATUS_map[127] = "CONFIG ERROR"

// // Map of all known monitoring environments
// ENV_map := make(map[string]string)
// ENV_map["prd"] = "Prod "
// ENV_map["dev"] = "Dev "
// ENV_map["stg"] = "Stg "
// ENV_map["vagrant"] = "Vagrant "
// ENV_map["default"] = "Test "
<|endoftext|>


<commit_before>package main

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

type voteRequest struct {
	term         int
	candidateId  string
	lastLogIndex int
	lastLogTerm  int
}

type requestForVoteResponse struct {
	term           int
	hasGrantedVote bool
}

type voteResponse struct {
	serverIndex int
	resp        requestForVoteResponse
}

func (s *server) handleRequestForVote(request voteRequest, w http.ResponseWriter) {
	if request.term < s.term {
		fmt.Fprint(w, false)
	} else {
		cond1 := s.votedFor == ""
		cond2 := s.votedFor == request.candidateID
		cond3 := request.lastLogIndex >= len(s.log)
		if (cond1 || cond2) && cond3 {
			s.electionTimeout.resetTimeout()
			s.votedFor = request.candidateID
			s.term = request.term
			fmt.Fprint(w, true)
		}
	}
}

func (s *server) sendRequestForVote(receiver string, respChan chan requestForVoteResponse) {
	v := url.Values{}
	v.set("candidateID", s.id)
	v.set("term", s.term)
	v.set("lastLogIndex", len(s.db.log))
	v.set("lastLogTerm", s.db.log[len(s.db.log)-1].term)
	resp, err := http.PostForm(server+"/votes", v)
	if err != nil {
		fmt.Println("Couldn't send request for votes to " + server)
	}
	defer resp.Body.Close()
	r = &requestForVoteResponse{}
	json.NewDecoder(resp.Body).Decode(r)
	v = &VoteResponse{receiver, r}
	respChan <- v
}

func (server *server) startElection() {
	server.state = "candidate"
	server.term += 1
	server.term.votes += 1
	respChan = make(chan voteResponse)
	for receiverIndex, _ := range server.config {
		go server.sendRequestForVotes(receiverIndex)
	}
	voteCount = 0
	responseCount = 0
	for {
		vote := <-respChan
		responseCount++
		if vote.resp.hasGrantedVote {
			voteCount++
		}
		if voteCount > len(s.config)/2 {
			s.state = "leader"
			break
		}
		if responseCount == len(s.config) {
			break
		}
	}
}
<commit_msg>use channel to return vote response<commit_after>package main

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

type voteRequest struct {
	term         int
	candidateId  string
	lastLogIndex int
	lastLogTerm  int
	returnChan   chan bool
}

type requestForVoteResponse struct {
	term           int
	hasGrantedVote bool
}

type voteResponse struct {
	serverIndex int
	resp        requestForVoteResponse
}

func (s *server) handleRequestForVote(req voteRequest) {
	if request.term < s.term {
		req.returnChan <- false
	} else {
		cond1 := s.votedFor == ""
		cond2 := s.votedFor == request.candidateID
		cond3 := request.lastLogIndex >= len(s.log)
		if (cond1 || cond2) && cond3 {
			s.electionTimeout.resetTimeout()
			s.votedFor = request.candidateID
			s.term = request.term
			fmt.Fprint(w, true)
		}
	}
}

func (s *server) sendRequestForVote(receiver string, respChan chan requestForVoteResponse) {
	v := url.Values{}
	v.set("candidateID", s.id)
	v.set("term", s.term)
	v.set("lastLogIndex", len(s.db.log))
	v.set("lastLogTerm", s.db.log[len(s.db.log)-1].term)
	resp, err := http.PostForm(server+"/votes", v)
	if err != nil {
		fmt.Println("Couldn't send request for votes to " + server)
	}
	defer resp.Body.Close()
	r = &requestForVoteResponse{}
	json.NewDecoder(resp.Body).Decode(r)
	v = &VoteResponse{receiver, r}
	respChan <- v
}

func (server *server) startElection() {
	server.state = "candidate"
	server.term += 1
	server.term.votes += 1
	respChan = make(chan voteResponse)
	for receiverIndex, _ := range server.config {
		go server.sendRequestForVotes(receiverIndex)
	}
	voteCount = 0
	responseCount = 0
	for {
		vote := <-respChan
		responseCount++
		if vote.resp.hasGrantedVote {
			voteCount++
		}
		if voteCount > len(s.config)/2 {
			s.state = "leader"
			break
		}
		if responseCount == len(s.config) {
			break
		}
	}
}
<|endoftext|>


<commit_before>// Debug functions.
package core

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"runtime"
	"runtime/debug"
)

type core_debug struct{}

var core_logger = log.New(os.Stdout, "", 0)
var Debug = core_debug{}
var Logger = core_logger

// Nop is a dummy function that can be called in source files where
// other debug functions are constantly added and removed.
// That way import "github.com/ungerik/go-start/debug" won't cause an error when
// no other debug function is currently used.
// Arbitrary objects can be passed as arguments to avoid "declared and not used"
// error messages when commenting code out and in.
// The result is a nil interface{} dummy value.
func (self *core_debug) Nop(dummiesIn ...interface{}) (dummyOut interface{}) {
	return nil
}

func (self *core_debug) CallStackInfo(skip int) (info string) {
	pc, file, line, ok := runtime.Caller(skip)
	if ok {
		funcName := runtime.FuncForPC(pc).Name()
		info += fmt.Sprintf("In function %s()", funcName)
	}
	for i := 0; ok; i++ {
		info += fmt.Sprintf("\n%s:%d", file, line)
		_, file, line, ok = runtime.Caller(skip + i)
	}
	return info
}

func (self *core_debug) PrintCallStack() {
	debug.PrintStack()
}

func (self *core_debug) LogCallStack() {
	log.Print(self.Stack())
}

func (self *core_debug) Stack() string {
	return string(debug.Stack())
}

func (self *core_debug) formatValue(value interface{}) string {
	return fmt.Sprintf("\n     Type: %T\n    Value: %v\nGo Syntax: %#v", value, value, value)
}

func (self *core_debug) formatCallstack(skip int) string {
	return fmt.Sprintf("\nCallstack: %s", self.CallStackInfo(skip+1))
}

func (self *core_debug) FormatSkip(skip int, value interface{}) string {
	return self.formatValue(value) + self.formatCallstack(skip+1)
}

func (self *core_debug) Format(value interface{}) string {
	return self.FormatSkip(2, value)
}

func (self *core_debug) Dump(values ...interface{}) {
	Logger.Println("!!!!!!!!!!!!!DEBUG!!!!!!!!!!!!!")
	Logger.Println("")
	Logger.Println("")
	Logger.Println("")
	if Logger != nil {
		for _, value := range values {
			Logger.Println(fmt.Printf("%+v\n", value))
		}
	}
	Logger.Println("")
	Logger.Println("")
	Logger.Println("")
	Logger.Println("!!!!!!!!!!!!!ENDDEBUG!!!!!!!!!!!!!")
}

func (self *core_debug) GetDump(values ...interface{}) string {
	var buffer bytes.Buffer
	for _, value := range values {
		buffer.WriteString(fmt.Sprintf("%+v\n", value))
	}
	return buffer.String()
}

func (self *core_debug) Print(values ...interface{}) {
	if Logger != nil {
		Logger.Print(values...)
	}
}

func (self *core_debug) Printf(format string, values ...interface{}) {
	if Logger != nil {
		Logger.Printf(format, values...)
	}
}
<commit_msg>Added instance type to Dump<commit_after>// Debug functions.
package core

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
)

type core_debug struct{}

var core_logger = log.New(os.Stdout, "", 0)
var Debug = core_debug{}
var Logger = core_logger

// Nop is a dummy function that can be called in source files where
// other debug functions are constantly added and removed.
// That way import "github.com/ungerik/go-start/debug" won't cause an error when
// no other debug function is currently used.
// Arbitrary objects can be passed as arguments to avoid "declared and not used"
// error messages when commenting code out and in.
// The result is a nil interface{} dummy value.
func (self *core_debug) Nop(dummiesIn ...interface{}) (dummyOut interface{}) {
	return nil
}

func (self *core_debug) CallStackInfo(skip int) (info string) {
	pc, file, line, ok := runtime.Caller(skip)
	if ok {
		funcName := runtime.FuncForPC(pc).Name()
		info += fmt.Sprintf("In function %s()", funcName)
	}
	for i := 0; ok; i++ {
		info += fmt.Sprintf("\n%s:%d", file, line)
		_, file, line, ok = runtime.Caller(skip + i)
	}
	return info
}

func (self *core_debug) PrintCallStack() {
	debug.PrintStack()
}

func (self *core_debug) LogCallStack() {
	log.Print(self.Stack())
}

func (self *core_debug) Stack() string {
	return string(debug.Stack())
}

func (self *core_debug) formatValue(value interface{}) string {
	return fmt.Sprintf("\n     Type: %T\n    Value: %v\nGo Syntax: %#v", value, value, value)
}

func (self *core_debug) formatCallstack(skip int) string {
	return fmt.Sprintf("\nCallstack: %s", self.CallStackInfo(skip+1))
}

func (self *core_debug) FormatSkip(skip int, value interface{}) string {
	return self.formatValue(value) + self.formatCallstack(skip+1)
}

func (self *core_debug) Format(value interface{}) string {
	return self.FormatSkip(2, value)
}

func (self *core_debug) Dump(values ...interface{}) {
	Logger.Println("!!!!!!!!!!!!!DEBUG!!!!!!!!!!!!!")
	Logger.Println("")
	Logger.Println("")
	Logger.Println("")
	if Logger != nil {
		for _, value := range values {
			Logger.Println("Instance Type:" + reflect.TypeOf(value).Name())
			Logger.Println(fmt.Printf("%+v\n", value))
		}
	}
	Logger.Println("")
	Logger.Println("")
	Logger.Println("")
	Logger.Println("!!!!!!!!!!!!!ENDDEBUG!!!!!!!!!!!!!")
}

func (self *core_debug) GetDump(values ...interface{}) string {
	var buffer bytes.Buffer
	for _, value := range values {
		buffer.WriteString("(" + reflect.TypeOf(value).Name() + ")" + fmt.Sprintf("%+v\n", value))
	}
	return buffer.String()
}

func (self *core_debug) Print(values ...interface{}) {
	if Logger != nil {
		Logger.Print(values...)
	}
}

func (self *core_debug) Printf(format string, values ...interface{}) {
	if Logger != nil {
		Logger.Printf(format, values...)
	}
}
<|endoftext|>


<commit_before>package web

import (
	"log"
	"net/http"
	"regexp"
	"sort"
	"strings"
	"sync"
)

type method int

const (
	mCONNECT method = 1 << iota
	mDELETE
	mGET
	mHEAD
	mOPTIONS
	mPATCH
	mPOST
	mPUT
	mTRACE
	// We only natively support the methods above, but we pass through other
	// methods. This constant pretty much only exists for the sake of mALL.
	mIDK

	mALL method = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH |
		mPOST | mPUT | mTRACE | mIDK
)

// The key used to communicate to the NotFound handler what methods would have
// been allowed if they'd been provided.
const ValidMethodsKey = "goji.web.validMethods"

var validMethodsMap = map[string]method{
	"CONNECT": mCONNECT,
	"DELETE":  mDELETE,
	"GET":     mGET,
	"HEAD":    mHEAD,
	"OPTIONS": mOPTIONS,
	"PATCH":   mPATCH,
	"POST":    mPOST,
	"PUT":     mPUT,
	"TRACE":   mTRACE,
}

type route struct {
	// Theory: most real world routes have a string prefix which is both
	// cheap(-ish) to test against and pretty selective. And, conveniently,
	// both regexes and string patterns give us this out-of-box.
	prefix  string
	method  method
	pattern Pattern
	handler Handler
}

type router struct {
	lock     sync.Mutex
	routes   []route
	notFound Handler
	machine  *routeMachine
}

// A Pattern determines whether or not a given request matches some criteria.
// They are often used in routes, which are essentially (pattern, methodSet,
// handler) tuples. If the method and pattern match, the given handler is used.
//
// Built-in implementations of this interface are used to implement regular
// expression and string matching.
type Pattern interface {
	// In practice, most real-world routes have a string prefix that can be
	// used to quickly determine if a pattern is an eligible match. The
	// router uses the result of this function to optimize away calls to the
	// full Match function, which is likely much more expensive to compute.
	// If your Pattern does not support prefixes, this function should
	// return the empty string.
	Prefix() string
	// Returns true if the request satisfies the pattern. This function is
	// free to examine both the request and the context to make this
	// decision. Match should not modify either argument, and since it will
	// potentially be called several times over the course of matching a
	// request, it should be reasonably efficient.
	Match(r *http.Request, c *C) bool
	// Run the pattern on the request and context, modifying the context as
	// necessary to bind URL parameters or other parsed state.
	Run(r *http.Request, c *C)
}

func parsePattern(p interface{}) Pattern {
	switch p.(type) {
	case Pattern:
		return p.(Pattern)
	case *regexp.Regexp:
		return parseRegexpPattern(p.(*regexp.Regexp))
	case string:
		return parseStringPattern(p.(string))
	default:
		log.Fatalf("Unknown pattern type %v. Expected a web.Pattern, "+
			"regexp.Regexp, or a string.", p)
	}
	panic("log.Fatalf does not return")
}

type netHTTPWrap struct {
	http.Handler
}

func (h netHTTPWrap) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Handler.ServeHTTP(w, r)
}
func (h netHTTPWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
	h.Handler.ServeHTTP(w, r)
}

func parseHandler(h interface{}) Handler {
	switch h.(type) {
	case Handler:
		return h.(Handler)
	case http.Handler:
		return netHTTPWrap{h.(http.Handler)}
	case func(c C, w http.ResponseWriter, r *http.Request):
		f := h.(func(c C, w http.ResponseWriter, r *http.Request))
		return HandlerFunc(f)
	case func(w http.ResponseWriter, r *http.Request):
		f := h.(func(w http.ResponseWriter, r *http.Request))
		return netHTTPWrap{http.HandlerFunc(f)}
	default:
		log.Fatalf("Unknown handler type %v. Expected a web.Handler, "+
			"a http.Handler, or a function with signature func(C, "+
			"http.ResponseWriter, *http.Request) or "+
			"func(http.ResponseWriter, *http.Request)", h)
	}
	panic("log.Fatalf does not return")
}

func httpMethod(mname string) method {
	if method, ok := validMethodsMap[mname]; ok {
		return method
	}
	return mIDK
}

type routeMachine struct {
	sm     stateMachine
	routes []route
}

func matchRoute(route route, m method, ms *method, r *http.Request, c *C) bool {
	if !route.pattern.Match(r, c) {
		return false
	}
	*ms |= route.method

	if route.method&m != 0 {
		route.pattern.Run(r, c)
		return true
	}
	return false
}

func (rm routeMachine) route(c *C, w http.ResponseWriter, r *http.Request) (method, bool) {
	m := httpMethod(r.Method)
	var methods method
	p := r.URL.Path

	if len(rm.sm) == 0 {
		return methods, false
	}

	var i int
	for {
		sm := rm.sm[i].mode
		if sm&smSetCursor != 0 {
			si := rm.sm[i].i
			p = r.URL.Path[si:]
			i++
			continue
		}

		length := int(sm & smLengthMask)
		match := false
		if length <= len(p) {
			bs := rm.sm[i].bs
			switch length {
			case 3:
				if p[2] != bs[2] {
					break
				}
				fallthrough
			case 2:
				if p[1] != bs[1] {
					break
				}
				fallthrough
			case 1:
				if p[0] != bs[0] {
					break
				}
				fallthrough
			case 0:
				p = p[length:]
				match = true
			}
		}

		if match && sm&smRoute != 0 {
			si := rm.sm[i].i
			if matchRoute(rm.routes[si], m, &methods, r, c) {
				rm.routes[si].handler.ServeHTTPC(*c, w, r)
				return 0, true
			}
			i++
		} else if (match && sm&smJumpOnMatch != 0) ||
			(!match && sm&smJumpOnMatch == 0) {

			if sm&smFail != 0 {
				return methods, false
			}
			i = int(rm.sm[i].i)
		} else {
			i++
		}
	}

	return methods, false
}

// Compile the list of routes into bytecode. This only needs to be done once
// after all the routes have been added, and will be called automatically for
// you (at some performance cost on the first request) if you do not call it
// explicitly.
func (rt *router) Compile() *routeMachine {
	rt.lock.Lock()
	defer rt.lock.Unlock()
	sm := routeMachine{
		sm:     compile(rt.routes),
		routes: rt.routes,
	}
	rt.setMachine(&sm)
	return &sm
}

func (rt *router) route(c *C, w http.ResponseWriter, r *http.Request) {
	rm := rt.getMachine()
	if rm == nil {
		rm = rt.Compile()
	}

	methods, ok := rm.route(c, w, r)
	if ok {
		return
	}

	if methods == 0 {
		rt.notFound.ServeHTTPC(*c, w, r)
		return
	}

	var methodsList = make([]string, 0)
	for mname, meth := range validMethodsMap {
		if methods&meth != 0 {
			methodsList = append(methodsList, mname)
		}
	}
	sort.Strings(methodsList)

	if c.Env == nil {
		c.Env = map[string]interface{}{
			ValidMethodsKey: methodsList,
		}
	} else {
		c.Env[ValidMethodsKey] = methodsList
	}
	rt.notFound.ServeHTTPC(*c, w, r)
}

func (rt *router) handleUntyped(p interface{}, m method, h interface{}) {
	pat := parsePattern(p)
	rt.handle(pat, m, parseHandler(h))
}

func (rt *router) handle(p Pattern, m method, h Handler) {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	// Calculate the sorted insertion point, because there's no reason to do
	// swapping hijinks if we're already making a copy. We need to use
	// bubble sort because we can only compare adjacent elements.
	pp := p.Prefix()
	var i int
	for i = len(rt.routes); i > 0; i-- {
		rip := rt.routes[i-1].prefix
		if rip <= pp || strings.HasPrefix(rip, pp) {
			break
		}
	}

	newRoutes := make([]route, len(rt.routes)+1)
	copy(newRoutes, rt.routes[:i])
	newRoutes[i] = route{
		prefix:  pp,
		method:  m,
		pattern: p,
		handler: h,
	}
	copy(newRoutes[i+1:], rt.routes[i:])

	rt.setMachine(nil)
	rt.routes = newRoutes
}

// This is a bit silly, but I've renamed the method receivers in the public
// functions here "m" instead of the standard "rt", since they will eventually
// be shown on the documentation for the Mux that they are included in.

/*
Dispatch to the given handler when the pattern matches, regardless of HTTP
method. See the documentation for type Mux for a description of what types are
accepted for pattern and handler.

This method is commonly used to implement sub-routing: an admin application, for
instance, can expose a single handler that is attached to the main Mux by
calling Handle("/admin*", adminHandler) or similar. Note that this function
doesn't strip this prefix from the path before forwarding it on (e.g., the
handler will see the full path, including the "/admin" part), but this
functionality can easily be performed by an extra middleware layer.
*/
func (rt *router) Handle(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mALL, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// CONNECT. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Connect(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mCONNECT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// DELETE. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Delete(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mDELETE, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// GET. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
//
// All GET handlers also transparently serve HEAD requests, since net/http will
// take care of all the fiddly bits for you. If you wish to provide an alternate
// implementation of HEAD, you should add a handler explicitly and place it
// above your GET handler.
func (rt *router) Get(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mGET|mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// HEAD. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Head(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// OPTIONS. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Options(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mOPTIONS, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PATCH. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Patch(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPATCH, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// POST. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Post(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPOST, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PUT. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Put(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPUT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// TRACE. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Trace(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mTRACE, handler)
}

// Set the fallback (i.e., 404) handler for this mux. See the documentation for
// type Mux for a description of what types are accepted for handler.
//
// As a convenience, the context environment variable "goji.web.validMethods"
// (also available as the constant ValidMethodsKey) will be set to the list of
// HTTP methods that could have been routed had they been provided on an
// otherwise identical request.
func (rt *router) NotFound(handler interface{}) {
	rt.notFound = parseHandler(handler)
}
<commit_msg>Don't expose the internal routeMachine<commit_after>package web

import (
	"log"
	"net/http"
	"regexp"
	"sort"
	"strings"
	"sync"
)

type method int

const (
	mCONNECT method = 1 << iota
	mDELETE
	mGET
	mHEAD
	mOPTIONS
	mPATCH
	mPOST
	mPUT
	mTRACE
	// We only natively support the methods above, but we pass through other
	// methods. This constant pretty much only exists for the sake of mALL.
	mIDK

	mALL method = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH |
		mPOST | mPUT | mTRACE | mIDK
)

// The key used to communicate to the NotFound handler what methods would have
// been allowed if they'd been provided.
const ValidMethodsKey = "goji.web.validMethods"

var validMethodsMap = map[string]method{
	"CONNECT": mCONNECT,
	"DELETE":  mDELETE,
	"GET":     mGET,
	"HEAD":    mHEAD,
	"OPTIONS": mOPTIONS,
	"PATCH":   mPATCH,
	"POST":    mPOST,
	"PUT":     mPUT,
	"TRACE":   mTRACE,
}

type route struct {
	// Theory: most real world routes have a string prefix which is both
	// cheap(-ish) to test against and pretty selective. And, conveniently,
	// both regexes and string patterns give us this out-of-box.
	prefix  string
	method  method
	pattern Pattern
	handler Handler
}

type router struct {
	lock     sync.Mutex
	routes   []route
	notFound Handler
	machine  *routeMachine
}

// A Pattern determines whether or not a given request matches some criteria.
// They are often used in routes, which are essentially (pattern, methodSet,
// handler) tuples. If the method and pattern match, the given handler is used.
//
// Built-in implementations of this interface are used to implement regular
// expression and string matching.
type Pattern interface {
	// In practice, most real-world routes have a string prefix that can be
	// used to quickly determine if a pattern is an eligible match. The
	// router uses the result of this function to optimize away calls to the
	// full Match function, which is likely much more expensive to compute.
	// If your Pattern does not support prefixes, this function should
	// return the empty string.
	Prefix() string
	// Returns true if the request satisfies the pattern. This function is
	// free to examine both the request and the context to make this
	// decision. Match should not modify either argument, and since it will
	// potentially be called several times over the course of matching a
	// request, it should be reasonably efficient.
	Match(r *http.Request, c *C) bool
	// Run the pattern on the request and context, modifying the context as
	// necessary to bind URL parameters or other parsed state.
	Run(r *http.Request, c *C)
}

func parsePattern(p interface{}) Pattern {
	switch p.(type) {
	case Pattern:
		return p.(Pattern)
	case *regexp.Regexp:
		return parseRegexpPattern(p.(*regexp.Regexp))
	case string:
		return parseStringPattern(p.(string))
	default:
		log.Fatalf("Unknown pattern type %v. Expected a web.Pattern, "+
			"regexp.Regexp, or a string.", p)
	}
	panic("log.Fatalf does not return")
}

type netHTTPWrap struct {
	http.Handler
}

func (h netHTTPWrap) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Handler.ServeHTTP(w, r)
}
func (h netHTTPWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
	h.Handler.ServeHTTP(w, r)
}

func parseHandler(h interface{}) Handler {
	switch h.(type) {
	case Handler:
		return h.(Handler)
	case http.Handler:
		return netHTTPWrap{h.(http.Handler)}
	case func(c C, w http.ResponseWriter, r *http.Request):
		f := h.(func(c C, w http.ResponseWriter, r *http.Request))
		return HandlerFunc(f)
	case func(w http.ResponseWriter, r *http.Request):
		f := h.(func(w http.ResponseWriter, r *http.Request))
		return netHTTPWrap{http.HandlerFunc(f)}
	default:
		log.Fatalf("Unknown handler type %v. Expected a web.Handler, "+
			"a http.Handler, or a function with signature func(C, "+
			"http.ResponseWriter, *http.Request) or "+
			"func(http.ResponseWriter, *http.Request)", h)
	}
	panic("log.Fatalf does not return")
}

func httpMethod(mname string) method {
	if method, ok := validMethodsMap[mname]; ok {
		return method
	}
	return mIDK
}

type routeMachine struct {
	sm     stateMachine
	routes []route
}

func matchRoute(route route, m method, ms *method, r *http.Request, c *C) bool {
	if !route.pattern.Match(r, c) {
		return false
	}
	*ms |= route.method

	if route.method&m != 0 {
		route.pattern.Run(r, c)
		return true
	}
	return false
}

func (rm routeMachine) route(c *C, w http.ResponseWriter, r *http.Request) (method, bool) {
	m := httpMethod(r.Method)
	var methods method
	p := r.URL.Path

	if len(rm.sm) == 0 {
		return methods, false
	}

	var i int
	for {
		sm := rm.sm[i].mode
		if sm&smSetCursor != 0 {
			si := rm.sm[i].i
			p = r.URL.Path[si:]
			i++
			continue
		}

		length := int(sm & smLengthMask)
		match := false
		if length <= len(p) {
			bs := rm.sm[i].bs
			switch length {
			case 3:
				if p[2] != bs[2] {
					break
				}
				fallthrough
			case 2:
				if p[1] != bs[1] {
					break
				}
				fallthrough
			case 1:
				if p[0] != bs[0] {
					break
				}
				fallthrough
			case 0:
				p = p[length:]
				match = true
			}
		}

		if match && sm&smRoute != 0 {
			si := rm.sm[i].i
			if matchRoute(rm.routes[si], m, &methods, r, c) {
				rm.routes[si].handler.ServeHTTPC(*c, w, r)
				return 0, true
			}
			i++
		} else if (match && sm&smJumpOnMatch != 0) ||
			(!match && sm&smJumpOnMatch == 0) {

			if sm&smFail != 0 {
				return methods, false
			}
			i = int(rm.sm[i].i)
		} else {
			i++
		}
	}

	return methods, false
}

// Compile the list of routes into bytecode. This only needs to be done once
// after all the routes have been added, and will be called automatically for
// you (at some performance cost on the first request) if you do not call it
// explicitly.
func (rt *router) Compile() {
	rt.compile()
}

func (rt *router) compile() *routeMachine {
	rt.lock.Lock()
	defer rt.lock.Unlock()
	sm := routeMachine{
		sm:     compile(rt.routes),
		routes: rt.routes,
	}
	rt.setMachine(&sm)
	return &sm
}

func (rt *router) route(c *C, w http.ResponseWriter, r *http.Request) {
	rm := rt.getMachine()
	if rm == nil {
		rm = rt.compile()
	}

	methods, ok := rm.route(c, w, r)
	if ok {
		return
	}

	if methods == 0 {
		rt.notFound.ServeHTTPC(*c, w, r)
		return
	}

	var methodsList = make([]string, 0)
	for mname, meth := range validMethodsMap {
		if methods&meth != 0 {
			methodsList = append(methodsList, mname)
		}
	}
	sort.Strings(methodsList)

	if c.Env == nil {
		c.Env = map[string]interface{}{
			ValidMethodsKey: methodsList,
		}
	} else {
		c.Env[ValidMethodsKey] = methodsList
	}
	rt.notFound.ServeHTTPC(*c, w, r)
}

func (rt *router) handleUntyped(p interface{}, m method, h interface{}) {
	pat := parsePattern(p)
	rt.handle(pat, m, parseHandler(h))
}

func (rt *router) handle(p Pattern, m method, h Handler) {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	// Calculate the sorted insertion point, because there's no reason to do
	// swapping hijinks if we're already making a copy. We need to use
	// bubble sort because we can only compare adjacent elements.
	pp := p.Prefix()
	var i int
	for i = len(rt.routes); i > 0; i-- {
		rip := rt.routes[i-1].prefix
		if rip <= pp || strings.HasPrefix(rip, pp) {
			break
		}
	}

	newRoutes := make([]route, len(rt.routes)+1)
	copy(newRoutes, rt.routes[:i])
	newRoutes[i] = route{
		prefix:  pp,
		method:  m,
		pattern: p,
		handler: h,
	}
	copy(newRoutes[i+1:], rt.routes[i:])

	rt.setMachine(nil)
	rt.routes = newRoutes
}

// This is a bit silly, but I've renamed the method receivers in the public
// functions here "m" instead of the standard "rt", since they will eventually
// be shown on the documentation for the Mux that they are included in.

/*
Dispatch to the given handler when the pattern matches, regardless of HTTP
method. See the documentation for type Mux for a description of what types are
accepted for pattern and handler.

This method is commonly used to implement sub-routing: an admin application, for
instance, can expose a single handler that is attached to the main Mux by
calling Handle("/admin*", adminHandler) or similar. Note that this function
doesn't strip this prefix from the path before forwarding it on (e.g., the
handler will see the full path, including the "/admin" part), but this
functionality can easily be performed by an extra middleware layer.
*/
func (rt *router) Handle(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mALL, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// CONNECT. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Connect(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mCONNECT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// DELETE. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Delete(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mDELETE, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// GET. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
//
// All GET handlers also transparently serve HEAD requests, since net/http will
// take care of all the fiddly bits for you. If you wish to provide an alternate
// implementation of HEAD, you should add a handler explicitly and place it
// above your GET handler.
func (rt *router) Get(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mGET|mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// HEAD. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Head(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// OPTIONS. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Options(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mOPTIONS, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PATCH. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Patch(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPATCH, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// POST. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Post(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPOST, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PUT. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Put(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPUT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// TRACE. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Trace(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mTRACE, handler)
}

// Set the fallback (i.e., 404) handler for this mux. See the documentation for
// type Mux for a description of what types are accepted for handler.
//
// As a convenience, the context environment variable "goji.web.validMethods"
// (also available as the constant ValidMethodsKey) will be set to the list of
// HTTP methods that could have been routed had they been provided on an
// otherwise identical request.
func (rt *router) NotFound(handler interface{}) {
	rt.notFound = parseHandler(handler)
}
<|endoftext|>


<commit_before>package web

import (
	"github.com/Masterminds/cookoo"
	"log"
	"net/http"
	"runtime"
)

// Create a new Cookoo web server.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
// - The handler includes logic to redirect "not found" errors to a path named "@404" if present.
//
// Context Params:
//
// - server.Address: If this key exists in the context, it will be used to determine the host/port the
//   server runes on. EXPERIMENTAL. Default is ":8080".
//
// Example:
//
//    package main
//
//    import (
//      //This is the path to Cookoo
//      "github.com/Masterminds/cookoo/src/cookoo"
//      "github.com/Masterminds/cookoo/src/cookoo/web"
//      "fmt"
//    )
//
//    func main() {
//      // Build a new Cookoo app.
//      handler, registry, router, context := cookoo.Cookoo()
//
//      // Fill the registry.
//      registry.Route("GET /", "The index").Does(web.Flush, "example").
//      	Using("content").WithDefault("Hello World")
//      	Using("writer").From(
//
//    	// Create a server
//    	web.Serve(reg, router, cookoo.SyncContext(cxt))
//    }
//
// Note that we synchronize the context before passing it into Serve(). This
// is optional because each handler gets its own copy of the context already.
// However, if commands pass the context to goroutines, the context ought to be
// synchronized to avoid race conditions.
//
// Note that copies of the context are not synchronized with each other.
// So by declaring the context synchronized here, you
// are not therefore synchronizing across handlers.
func Serve(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) {
	defer shutdown(router, cxt)

	addr := cxt.Get("server.Address", ":8080").(string)

	handler := NewCookooHandler(reg, router, cxt)
	http.Handle("/", handler)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		cxt.Logf("error", "Caught error while serving: %s", err)
		if router.HasRoute("@crash") {
			router.HandleRequest("@crash", cxt, false)
		}
	}
	// TODO: Need to figure out how to trap signals here, instead of outside.
}
func shutdown(router *cookoo.Router, cxt cookoo.Context) {
	log.Print("Shutdown")
	if router.HasRoute("@shutdown") {
		router.HandleRequest("@shutdown", cxt, false)
	}
}

// The handler for Cookoo.
// You way use this handler in your own web apps, or you can use
// the Serve() function to create and manage a handler for you.
type CookooHandler struct {
	Registry    *cookoo.Registry
	Router      *cookoo.Router
	BaseContext cookoo.Context
}

// Create a new Cookoo HTTP handler.
//
// This will create an HTTP hanlder, but will not automatically attach it to a server. Implementors
// can take the handler and attach it to an existing HTTP server wiht http.HandleFunc() or
// http.ListenAndServe().
//
// For simple web servers, using this package's Serve() function may be the easier route.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
func NewCookooHandler(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) *CookooHandler {
	handler := new(CookooHandler)
	handler.Registry = reg
	handler.Router = router
	handler.BaseContext = cxt

	// Use the URI oriented request resolver in this package.
	resolver := new(URIPathResolver)
	resolver.Init(reg)
	router.SetRequestResolver(resolver)

	return handler
}

// Adds the built-in HTTP-specific datasources.
func (h *CookooHandler) addDatasources(cxt cookoo.Context, req *http.Request) {
	parsedURL := req.URL
	urlDS := new(URLDatasource).Init(parsedURL)
	queryDS := new(QueryParameterDatasource).Init(parsedURL.Query())
	formDS := new(FormValuesDatasource).Init(req)
	pathDS := new(PathDatasource).Init(parsedURL.Path)

	cxt.AddDatasource("url", urlDS)
	cxt.AddDatasource("query", queryDS)
	// cxt.AddDatasource("q", queryDS)
	cxt.AddDatasource("post", formDS)
	cxt.AddDatasource("path", pathDS)
}

// ServeHTTP is the Cookoo request handling function.
//
// This is capable of handling HTTP and HTTPS requests.
func (h *CookooHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	// First we need to clone the context so we have a mutable copy.
	cxt := h.BaseContext.Copy()
	// Trap panics and make them 500 errors:
	defer func() {
		// fmt.Printf("Deferred function executed for path %s\n", req.URL.Path)
		if err := recover(); err != nil {
			//log.Printf("FOUND ERROR: %v", err)
			where := cxt.Get("command.Name", "<unknown>").(string)
			rname := cxt.Get("route.Name", "<unknown>").(string)
			h.BaseContext.Logf("error", "CookooHandler trapped a panic on route '%s' in command '%s': %v", rname, where, err)

			// Buffer for a stack trace.
			stack := make([]byte, 8192)
			size := runtime.Stack(stack, false)
			h.BaseContext.Logf("error", "Stack: %s", stack)

			if size == 8192 {
				h.BaseContext.Logf("error", "<truncated stack trace at 8192 bytes>")
			}

			http.Error(res, "An internal error occurred.", http.StatusInternalServerError)
		}
	}()

	cxt.Put("http.Request", req)
	cxt.Put("http.ResponseWriter", res)

	// Next, we add the datasources for URL and Query params.
	h.addDatasources(cxt, req)

	// Find the route
	path := req.Method + " " + req.URL.Path

	cxt.Logf("info", "Handling request for %s\n", path)

	// If a route matches, run it.
	err := h.Router.HandleRequest(path, cxt, true)
	if err != nil {
		switch err.(type) {

		// For a 404, we bail.
		case *cookoo.RouteError:
			if h.Router.HasRoute("@404") {
				h.Router.HandleRequest("@404", cxt, false)
			} else {
				http.NotFound(res, req)
			}
			return
		// For any other, we go to a 500.
		case *cookoo.FatalError:
			cxt.Logf("error", "Fatal Error on route '%s': %s", path, err)
		default:
			cxt.Logf("error", "Untagged error on route '%s': %v (%T)", path, err, err)
		}

		if h.Router.HasRoute("@500") {
			cxt.Put("error", err)
			h.Router.HandleRequest("@500", cxt, false)
		} else {
			// Passing the error back to the client is a bad default.
			//http.Error(res, err.Error(), http.StatusInternalServerError)
			http.Error(res, "Internal error processing the request.", http.StatusInternalServerError)
		}
	}
}
<commit_msg>Updated comments.<commit_after>package web

import (
	"github.com/Masterminds/cookoo"
	"log"
	"net/http"
	"runtime"
)

// Create a new Cookoo web server.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
// - The handler includes logic to redirect "not found" errors to a path named "@404" if present.
//
// Context Params:
//
// - server.Address: If this key exists in the context, it will be used to determine the host/port the
//   server runes on. EXPERIMENTAL. Default is ":8080".
//
// Example:
//
//    package main
//
//    import (
//      //This is the path to Cookoo
//      "github.com/Masterminds/cookoo/src/cookoo"
//      "github.com/Masterminds/cookoo/src/cookoo/web"
//      "fmt"
//    )
//
//    func main() {
//      // Build a new Cookoo app.
//      handler, registry, router, context := cookoo.Cookoo()
//
//      // Fill the registry.
//      registry.Route("GET /", "The index").Does(web.Flush, "example").
//      	Using("content").WithDefault("Hello World")
//      	Using("writer").From(
//
//    	// Create a server
//    	web.Serve(reg, router, cookoo.SyncContext(cxt))
//    }
//
// Note that we synchronize the context before passing it into Serve(). This
// is optional because each handler gets its own copy of the context already.
// However, if commands pass the context to goroutines, the context ought to be
// synchronized to avoid race conditions.
//
// Note that copies of the context are not synchronized with each other.
// So by declaring the context synchronized here, you
// are not therefore synchronizing across handlers.
func Serve(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) {
	defer shutdown(router, cxt)

	addr := cxt.Get("server.Address", ":8080").(string)

	handler := NewCookooHandler(reg, router, cxt)
	http.Handle("/", handler)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		cxt.Logf("error", "Caught error while serving: %s", err)
		if router.HasRoute("@crash") {
			router.HandleRequest("@crash", cxt, false)
		}
	}
	// TODO: Need to figure out how to trap signals here, instead of outside.
}
func shutdown(router *cookoo.Router, cxt cookoo.Context) {
	log.Print("Shutdown")
	if router.HasRoute("@shutdown") {
		router.HandleRequest("@shutdown", cxt, false)
	}
}

// The handler for Cookoo.
// You way use this handler in your own web apps, or you can use
// the Serve() function to create and manage a handler for you.
type CookooHandler struct {
	Registry    *cookoo.Registry
	Router      *cookoo.Router
	BaseContext cookoo.Context
}

// Create a new Cookoo HTTP handler.
//
// This will create an HTTP hanlder, but will not automatically attach it to a server. Implementors
// can take the handler and attach it to an existing HTTP server wiht http.HandleFunc() or
// http.ListenAndServe().
//
// For simple web servers, using this package's Serve() function may be the easier route.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
func NewCookooHandler(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) *CookooHandler {
	handler := new(CookooHandler)
	handler.Registry = reg
	handler.Router = router
	handler.BaseContext = cxt

	// Use the URI oriented request resolver in this package.
	resolver := new(URIPathResolver)
	resolver.Init(reg)
	router.SetRequestResolver(resolver)

	return handler
}

// Adds the built-in HTTP-specific datasources.
func (h *CookooHandler) addDatasources(cxt cookoo.Context, req *http.Request) {
	parsedURL := req.URL
	urlDS := new(URLDatasource).Init(parsedURL)
	queryDS := new(QueryParameterDatasource).Init(parsedURL.Query())
	formDS := new(FormValuesDatasource).Init(req)
	pathDS := new(PathDatasource).Init(parsedURL.Path)

	cxt.AddDatasource("url", urlDS)
	cxt.AddDatasource("query", queryDS)
	// cxt.AddDatasource("q", queryDS)
	cxt.AddDatasource("post", formDS)
	cxt.AddDatasource("path", pathDS)
}

// ServeHTTP is the Cookoo request handling function.
//
// This is capable of handling HTTP and HTTPS requests.
func (h *CookooHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	// First we need to clone the context so we have a mutable copy.
	cxt := h.BaseContext.Copy()
	// Trap panics and make them 500 errors:
	defer func() {
		// fmt.Printf("Deferred function executed for path %s\n", req.URL.Path)
		if err := recover(); err != nil {
			//log.Printf("FOUND ERROR: %v", err)
			where := cxt.Get("command.Name", "<unknown>").(string)
			rname := cxt.Get("route.Name", "<unknown>").(string)
			h.BaseContext.Logf("error", "CookooHandler trapped a panic on route '%s' in command '%s': %v", rname, where, err)

			// Buffer for a stack trace.
			// This is pretty much always worthless, as the stack has been
			// unwound up to here.
			stack := make([]byte, 8192)
			size := runtime.Stack(stack, false)
			h.BaseContext.Logf("error", "Stack: %s", stack)

			if size == 8192 {
				h.BaseContext.Logf("error", "<truncated stack trace at 8192 bytes>")
			}

			http.Error(res, "An internal error occurred.", http.StatusInternalServerError)
		}
	}()

	cxt.Put("http.Request", req)
	cxt.Put("http.ResponseWriter", res)

	// Next, we add the datasources for URL and Query params.
	h.addDatasources(cxt, req)

	// Find the route
	path := req.Method + " " + req.URL.Path

	cxt.Logf("info", "Handling request for %s\n", path)

	// If a route matches, run it.
	err := h.Router.HandleRequest(path, cxt, true)
	if err != nil {
		switch err.(type) {

		// For a 404, we bail.
		case *cookoo.RouteError:
			if h.Router.HasRoute("@404") {
				h.Router.HandleRequest("@404", cxt, false)
			} else {
				http.NotFound(res, req)
			}
			return
		// For any other, we go to a 500.
		case *cookoo.FatalError:
			cxt.Logf("error", "Fatal Error on route '%s': %s", path, err)
		default:
			cxt.Logf("error", "Untagged error on route '%s': %v (%T)", path, err, err)
		}

		if h.Router.HasRoute("@500") {
			cxt.Put("error", err)
			h.Router.HandleRequest("@500", cxt, false)
		} else {
			// Passing the error back to the client is a bad default.
			//http.Error(res, err.Error(), http.StatusInternalServerError)
			http.Error(res, "Internal error processing the request.", http.StatusInternalServerError)
		}
	}
}
<|endoftext|>


<commit_before>package agents

import (
	"fmt"
	"os/exec"
	"syscall"

	log "github.com/Sirupsen/logrus"
	"github.com/shirou/gopsutil/process"
)

// JobControl provides a command interface for control
type JobControl interface {
	Start() error
	Stop() error
	Suspend() error
	Resume() error
	Kill(int64) error
	Done() chan error
	Process() *process.Process
}

// Job contains a handle to the actual process struct.
type Job struct {
	JobControl
	Cmd  *exec.Cmd
	Proc *process.Process
	done chan error
}

// NewControlledProcess creates the child proc.
// TODO: Add log limiting support by byte counting stdout
func NewControlledProcess(cmd string, arguments []string, doneChan chan error) (JobControl, error) {
	j := &Job{
		nil,
		nil,
		nil,
		doneChan,
	}

	j.Cmd = exec.Command(cmd)
	// Map all child processes under this tree so Kill really ends everything.
	j.Cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	j.Cmd.Args = arguments
	log.Debugf("%#v\n", j.Cmd)

	// Start the sub-process but don't wait for completion to pickup the Pid
	// for resource monitoring.
	err := j.Cmd.Start()
	if err != nil {
		return nil, fmt.Errorf("Failed to execute sub-process: %s\n", err)
	}

	pid := int32(j.Cmd.Process.Pid)

	j.Proc, err = process.NewProcess(pid)
	if err != nil {
		return nil, fmt.Errorf("Unable to create process.NewProcess: %s\n", err)
	}

	// Background waiting for the job to finish and emit a done channel message
	// when complete.
	go func(j *Job) {
		err := j.Cmd.Wait()
		log.Debugf("Job finished: %q\n", err)
		j.done <- err
	}(j)

	return j, nil
}

// Stop gracefully ends the process
func (j *Job) Stop() error {
	if err := j.Proc.Terminate(); err != nil {
		log.Warnf("Error received calling terminate on sub-process: %s\n", err)
		return err
	}
	return nil
}

// Resume continues a suspended process
func (j *Job) Resume() error {
	if err := j.Proc.Resume(); err != nil {
		log.Warnf("Error received calling resume on sub-process: %s", err)
		return err
	}
	return nil
}

// Suspend pauses a running process
func (j *Job) Suspend() error {
	if err := j.Proc.Suspend(); err != nil {
		log.Warnf("Error received calling suspend on sub-process: %s", err)
		return err
	}
	return nil
}

// Kill forcefully stops a process
func (j *Job) Kill(sig int64) error {
	var err error

	switch sig {
	case -9:
		err = j.Proc.Kill()
	default:
		signal := syscall.Signal(sig)
		err = j.Proc.SendSignal(signal)
	}

	if err != nil {
		log.Warnf("Error received calling kill on sub-process: %s", err)
		return err
	}
	return nil
}

// Done returns the channel used when the process is finished
func (j *Job) Done() chan error {
	return j.done
}

// Process returns a handle to the underlying process through gopsutil.
func (j *Job) Process() *process.Process {
	return j.Proc
}
<commit_msg>Fix #1 - missing stderr/out redirect.<commit_after>package agents

import (
	"bufio"
	"fmt"
	"io"
	"os/exec"
	"syscall"

	log "github.com/Sirupsen/logrus"
	"github.com/shirou/gopsutil/process"
)

// JobControl provides a command interface for control
type JobControl interface {
	Start() error
	Stop() error
	Suspend() error
	Resume() error
	Kill(int64) error
	Done() chan error
	Process() *process.Process
}

// Job contains a handle to the actual process struct.
type Job struct {
	JobControl
	Cmd  *exec.Cmd
	Proc *process.Process
	done chan error
}

// NewControlledProcess creates the child proc.
// TODO: Add log limiting support by byte counting stdout
func NewControlledProcess(cmd string, arguments []string, doneChan chan error) (JobControl, error) {
	j := &Job{
		nil,
		nil,
		nil,
		doneChan,
	}

	j.Cmd = exec.Command(cmd)

	// Collect stdout from the process to redirect o real stdout
	stdout, err := j.Cmd.StdoutPipe()
	if err != nil {
		return nil, fmt.Errorf("Failed to acquire stdout: %s", err)
	}
	stderr, err := j.Cmd.StderrPipe()
	if err != nil {
		return nil, fmt.Errorf("Failed to acquire stderr: %s", err)
	}

	// Map all child processes under this tree so Kill really ends everything.
	j.Cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	j.Cmd.Args = arguments
	log.Debugf("%#v\n", j.Cmd)

	// Start the sub-process but don't wait for completion to pickup the Pid
	// for resource monitoring.
	err = j.Cmd.Start()
	if err != nil {
		return nil, fmt.Errorf("Failed to execute sub-process: %s\n", err)
	}

	pid := int32(j.Cmd.Process.Pid)

	j.Proc, err = process.NewProcess(pid)
	if err != nil {
		return nil, fmt.Errorf("Unable to create process.NewProcess: %s\n", err)
	}

	go stdRedirect(stdout)
	go stdRedirect(stderr)

	// Background waiting for the job to finish and emit a done channel message
	// when complete.
	go func(j *Job) {
		err := j.Cmd.Wait()
		log.Debugf("Job finished: %q\n", err)
		j.done <- err
	}(j)

	return j, nil
}

func stdRedirect(r io.Reader) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		fmt.Printf("%s\n", scanner.Text())
	}
}

// Stop gracefully ends the process
func (j *Job) Stop() error {
	if err := j.Proc.Terminate(); err != nil {
		log.Warnf("Error received calling terminate on sub-process: %s\n", err)
		return err
	}
	return nil
}

// Resume continues a suspended process
func (j *Job) Resume() error {
	if err := j.Proc.Resume(); err != nil {
		log.Warnf("Error received calling resume on sub-process: %s", err)
		return err
	}
	return nil
}

// Suspend pauses a running process
func (j *Job) Suspend() error {
	if err := j.Proc.Suspend(); err != nil {
		log.Warnf("Error received calling suspend on sub-process: %s", err)
		return err
	}
	return nil
}

// Kill forcefully stops a process
func (j *Job) Kill(sig int64) error {
	var err error

	switch sig {
	case -9:
		err = j.Proc.Kill()
	default:
		signal := syscall.Signal(sig)
		err = j.Proc.SendSignal(signal)
	}

	if err != nil {
		log.Warnf("Error received calling kill on sub-process: %s", err)
		return err
	}
	return nil
}

// Done returns the channel used when the process is finished
func (j *Job) Done() chan error {
	return j.done
}

// Process returns a handle to the underlying process through gopsutil.
func (j *Job) Process() *process.Process {
	return j.Proc
}
<|endoftext|>