<commit_before>package opentracing

import (
	"time"

	"github.com/opentracing/opentracing-go/log"
)

// SpanContext represents Span state that must propagate to descendant Spans and across process
// boundaries (e.g., a <trace_id, span_id, sampled> tuple).
type SpanContext interface {
	// ForeachBaggageItem grants access to all baggage items stored in the
	// SpanContext.
	// The handler function will be called for each baggage key/value pair.
	// The ordering of items is not guaranteed.
	//
	// The bool return value indicates if the handler wants to continue iterating
	// through the rest of the baggage items; for example if the handler is trying to
	// find some baggage item by pattern matching the name, it can return false
	// as soon as the item is found to stop further iterations.
	ForeachBaggageItem(handler func(k, v string) bool)
}

// Span represents an active, un-finished span in the OpenTracing system.
//
// Spans are created by the Tracer interface.
type Span interface {
	// Sets the end timestamp and finalizes Span state.
	//
	// With the exception of calls to Context() (which are always allowed),
	// Finish() must be the last call made to any span instance, and to do
	// otherwise leads to undefined behavior.
	Finish()
	// FinishWithOptions is like Finish() but with explicit control over
	// timestamps and log data.
	FinishWithOptions(opts FinishOptions)

	// Context() yields the SpanContext for this Span. Note that the return
	// value of Context() is still valid after a call to Span.Finish(), as is
	// a call to Span.Context() after a call to Span.Finish().
	Context() SpanContext

	// Sets or changes the operation name.
	SetOperationName(operationName string) Span

	// Adds a tag to the span.
	//
	// If there is a pre-existing tag set for `key`, it is overwritten.
	//
	// Tag values can be numeric types, strings, or bools. The behavior of
	// other tag value types is undefined at the OpenTracing level. If a
	// tracing system does not know how to handle a particular value type, it
	// may ignore the tag, but shall not panic.
	SetTag(key string, value interface{}) Span

	// LogFields is an efficient and type-checked way to record key:value
	// logging data about a Span, though the programming interface is a little
	// more verbose than LogKV(). Here's an example:
	//
	//    span.LogFields(
	//        log.String("event", "soft error"),
	//        log.String("type", "cache timeout"),
	//        log.Int("waited.millis", 1500))
	//
	// Also see Span.FinishWithOptions() and FinishOptions.BulkLogData.
	LogFields(fields ...log.Field)

	// LogKV is a concise, readable way to record key:value logging data about
	// a Span, though unfortunately this also makes it less efficient and less
	// type-safe than LogFields(). Here's an example:
	//
	//    span.LogKV(
	//        "event", "soft error",
	//        "type", "cache timeout",
	//        "waited.millis", 1500)
	//
	// For LogKV (as opposed to LogFields()), the parameters must appear as
	// key-value pairs, like
	//
	//    span.LogKV(key1, val1, key2, val2, key3, val3, ...)
	//
	// The keys must all be strings. The values may be strings, numeric types,
	// bools, Go error instances, or arbitrary structs.
	//
	// (Note to implementors: consider the log.InterleavedKVToFields() helper)
	LogKV(alternatingKeyValues ...interface{})

	// SetBaggageItem sets a key:value pair on this Span and its SpanContext
	// that also propagates to descendants of this Span.
	//
	// SetBaggageItem() enables powerful functionality given a full-stack
	// opentracing integration (e.g., arbitrary application data from a mobile
	// app can make it, transparently, all the way into the depths of a storage
	// system), and with it some powerful costs: use this feature with care.
	//
	// IMPORTANT NOTE #1: SetBaggageItem() will only propagate baggage items to
	// *future* causal descendants of the associated Span.
	//
	// IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and
	// value is copied into every local *and remote* child of the associated
	// Span, and that can add up to a lot of network and cpu overhead.
	//
	// Returns a reference to this Span for chaining.
	SetBaggageItem(restrictedKey, value string) Span

	// Gets the value for a baggage item given its key. Returns the empty string
	// if the value isn't found in this Span.
	BaggageItem(restrictedKey string) string

	// Provides access to the Tracer that created this Span.
	Tracer() Tracer

	// Deprecated: use LogFields or LogKV
	LogEvent(event string)
	// Deprecated: use LogFields or LogKV
	LogEventWithPayload(event string, payload interface{})
	// Deprecated: use LogFields or LogKV
	Log(data LogData)
}

// LogRecord is data associated with a single Span log. Every LogRecord
// instance must specify at least one Field.
type LogRecord struct {
	Timestamp time.Time
	Fields    []log.Field
}

// FinishOptions allows Span.FinishWithOptions callers to override the finish
// timestamp and provide log data via a bulk interface.
type FinishOptions struct {
	// FinishTime overrides the Span's finish time, or implicitly becomes
	// time.Now() if FinishTime.IsZero().
	//
	// FinishTime must resolve to a timestamp that's >= the Span's StartTime
	// (per StartSpanOptions).
	FinishTime time.Time

	// LogRecords allows the caller to specify the contents of many LogFields()
	// calls with a single slice. May be nil.
	//
	// None of the LogRecord.Timestamp values may be .IsZero() (i.e., they must
	// be set explicitly). Also, they must be >= the Span's start timestamp and
	// <= the FinishTime (or time.Now() if FinishTime.IsZero()). Otherwise the
	// behavior of FinishWithOptions() is undefined.
	//
	// If specified, the caller hands off ownership of LogRecords at
	// FinishWithOptions() invocation time.
	//
	// If specified, the (deprecated) BulkLogData must be nil or empty.
	LogRecords []LogRecord

	// BulkLogData is DEPRECATED.
	BulkLogData []LogData
}

// LogData is DEPRECATED
type LogData struct {
	Timestamp time.Time
	Event     string
	Payload   interface{}
}

// ToLogRecord converts a deprecated LogData to a non-deprecated LogRecord
func (ld *LogData) ToLogRecord() LogRecord {
	rval := LogRecord{
		Timestamp: ld.Timestamp,
	}
	if ld.Payload == nil {
		rval.Fields = []log.Field{
			log.String("event", ld.Event),
		}
	} else {
		rval.Fields = []log.Field{
			log.String("event", ld.Event),
			log.Object("payload", ld.Payload),
		}
	}
	return rval
}
<commit_msg>Handle the IsZero timestamp case<commit_after>package opentracing

import (
	"time"

	"github.com/opentracing/opentracing-go/log"
)

// SpanContext represents Span state that must propagate to descendant Spans and across process
// boundaries (e.g., a <trace_id, span_id, sampled> tuple).
type SpanContext interface {
	// ForeachBaggageItem grants access to all baggage items stored in the
	// SpanContext.
	// The handler function will be called for each baggage key/value pair.
	// The ordering of items is not guaranteed.
	//
	// The bool return value indicates if the handler wants to continue iterating
	// through the rest of the baggage items; for example if the handler is trying to
	// find some baggage item by pattern matching the name, it can return false
	// as soon as the item is found to stop further iterations.
	ForeachBaggageItem(handler func(k, v string) bool)
}

// Span represents an active, un-finished span in the OpenTracing system.
//
// Spans are created by the Tracer interface.
type Span interface {
	// Sets the end timestamp and finalizes Span state.
	//
	// With the exception of calls to Context() (which are always allowed),
	// Finish() must be the last call made to any span instance, and to do
	// otherwise leads to undefined behavior.
	Finish()
	// FinishWithOptions is like Finish() but with explicit control over
	// timestamps and log data.
	FinishWithOptions(opts FinishOptions)

	// Context() yields the SpanContext for this Span. Note that the return
	// value of Context() is still valid after a call to Span.Finish(), as is
	// a call to Span.Context() after a call to Span.Finish().
	Context() SpanContext

	// Sets or changes the operation name.
	SetOperationName(operationName string) Span

	// Adds a tag to the span.
	//
	// If there is a pre-existing tag set for `key`, it is overwritten.
	//
	// Tag values can be numeric types, strings, or bools. The behavior of
	// other tag value types is undefined at the OpenTracing level. If a
	// tracing system does not know how to handle a particular value type, it
	// may ignore the tag, but shall not panic.
	SetTag(key string, value interface{}) Span

	// LogFields is an efficient and type-checked way to record key:value
	// logging data about a Span, though the programming interface is a little
	// more verbose than LogKV(). Here's an example:
	//
	//    span.LogFields(
	//        log.String("event", "soft error"),
	//        log.String("type", "cache timeout"),
	//        log.Int("waited.millis", 1500))
	//
	// Also see Span.FinishWithOptions() and FinishOptions.BulkLogData.
	LogFields(fields ...log.Field)

	// LogKV is a concise, readable way to record key:value logging data about
	// a Span, though unfortunately this also makes it less efficient and less
	// type-safe than LogFields(). Here's an example:
	//
	//    span.LogKV(
	//        "event", "soft error",
	//        "type", "cache timeout",
	//        "waited.millis", 1500)
	//
	// For LogKV (as opposed to LogFields()), the parameters must appear as
	// key-value pairs, like
	//
	//    span.LogKV(key1, val1, key2, val2, key3, val3, ...)
	//
	// The keys must all be strings. The values may be strings, numeric types,
	// bools, Go error instances, or arbitrary structs.
	//
	// (Note to implementors: consider the log.InterleavedKVToFields() helper)
	LogKV(alternatingKeyValues ...interface{})

	// SetBaggageItem sets a key:value pair on this Span and its SpanContext
	// that also propagates to descendants of this Span.
	//
	// SetBaggageItem() enables powerful functionality given a full-stack
	// opentracing integration (e.g., arbitrary application data from a mobile
	// app can make it, transparently, all the way into the depths of a storage
	// system), and with it some powerful costs: use this feature with care.
	//
	// IMPORTANT NOTE #1: SetBaggageItem() will only propagate baggage items to
	// *future* causal descendants of the associated Span.
	//
	// IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and
	// value is copied into every local *and remote* child of the associated
	// Span, and that can add up to a lot of network and cpu overhead.
	//
	// Returns a reference to this Span for chaining.
	SetBaggageItem(restrictedKey, value string) Span

	// Gets the value for a baggage item given its key. Returns the empty string
	// if the value isn't found in this Span.
	BaggageItem(restrictedKey string) string

	// Provides access to the Tracer that created this Span.
	Tracer() Tracer

	// Deprecated: use LogFields or LogKV
	LogEvent(event string)
	// Deprecated: use LogFields or LogKV
	LogEventWithPayload(event string, payload interface{})
	// Deprecated: use LogFields or LogKV
	Log(data LogData)
}

// LogRecord is data associated with a single Span log. Every LogRecord
// instance must specify at least one Field.
type LogRecord struct {
	Timestamp time.Time
	Fields    []log.Field
}

// FinishOptions allows Span.FinishWithOptions callers to override the finish
// timestamp and provide log data via a bulk interface.
type FinishOptions struct {
	// FinishTime overrides the Span's finish time, or implicitly becomes
	// time.Now() if FinishTime.IsZero().
	//
	// FinishTime must resolve to a timestamp that's >= the Span's StartTime
	// (per StartSpanOptions).
	FinishTime time.Time

	// LogRecords allows the caller to specify the contents of many LogFields()
	// calls with a single slice. May be nil.
	//
	// None of the LogRecord.Timestamp values may be .IsZero() (i.e., they must
	// be set explicitly). Also, they must be >= the Span's start timestamp and
	// <= the FinishTime (or time.Now() if FinishTime.IsZero()). Otherwise the
	// behavior of FinishWithOptions() is undefined.
	//
	// If specified, the caller hands off ownership of LogRecords at
	// FinishWithOptions() invocation time.
	//
	// If specified, the (deprecated) BulkLogData must be nil or empty.
	LogRecords []LogRecord

	// BulkLogData is DEPRECATED.
	BulkLogData []LogData
}

// LogData is DEPRECATED
type LogData struct {
	Timestamp time.Time
	Event     string
	Payload   interface{}
}

// ToLogRecord converts a deprecated LogData to a non-deprecated LogRecord
func (ld *LogData) ToLogRecord() LogRecord {
	var literalTimestamp time.Time
	if ld.Timestamp.IsZero() {
		literalTimestamp = time.Now()
	} else {
		literalTimestamp = ld.Timestamp
	}
	rval := LogRecord{
		Timestamp: literalTimestamp,
	}
	if ld.Payload == nil {
		rval.Fields = []log.Field{
			log.String("event", ld.Event),
		}
	} else {
		rval.Fields = []log.Field{
			log.String("event", ld.Event),
			log.Object("payload", ld.Payload),
		}
	}
	return rval
}
<|endoftext|>


<commit_before>// Copyright 2015 go-swagger maintainers
//
// 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 loads

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

	"github.com/go-openapi/analysis"
	"github.com/go-openapi/spec"
	"github.com/go-openapi/swag"
)

// JSONDoc loads a json document from either a file or a remote url
func JSONDoc(path string) (json.RawMessage, error) {
	data, err := swag.LoadFromFileOrHTTP(path)
	if err != nil {
		return nil, err
	}
	return json.RawMessage(data), nil
}

// DocLoader represents a doc loader type
type DocLoader func(string) (json.RawMessage, error)

// DocMatcher represents a predicate to check if a loader matches
type DocMatcher func(string) bool

var loaders *loader

func init() {
	loaders = &loader{Match: func(_ string) bool { return true }, Fn: JSONDoc}
}

// AddLoader for a document
func AddLoader(predicate DocMatcher, load DocLoader) {
	prev := loaders
	loaders = &loader{
		Match: predicate,
		Fn:    load,
		Next:  prev,
	}

}

type loader struct {
	Fn    DocLoader
	Match DocMatcher
	Next  *loader
}

// JSONSpec loads a spec from a json document
func JSONSpec(path string) (*Document, error) {
	data, err := JSONDoc(path)
	if err != nil {
		return nil, err
	}
	// convert to json
	return Analyzed(json.RawMessage(data), "")
}

// Document represents a swagger spec document
type Document struct {
	// specAnalyzer
	Analyzer *analysis.Spec
	spec     *spec.Swagger
	origSpec *spec.Swagger
	schema   *spec.Schema
	raw      json.RawMessage
}

// Spec loads a new spec document
func Spec(path string) (*Document, error) {
	specURL, err := url.Parse(path)
	if err != nil {
		return nil, err
	}
	for l := loaders.Next; l != nil; l = l.Next {
		if loaders.Match(specURL.Path) {
			b, err2 := loaders.Fn(path)
			if err2 != nil {
				return nil, err2
			}
			return Analyzed(b, "")
		}
	}
	b, err := loaders.Fn(path)
	if err != nil {
		return nil, err
	}
	return Analyzed(b, "")
}

// Analyzed creates a new analyzed spec document
func Analyzed(data json.RawMessage, version string) (*Document, error) {
	if version == "" {
		version = "2.0"
	}
	if version != "2.0" {
		return nil, fmt.Errorf("spec version %q is not supported", version)
	}

	swspec := new(spec.Swagger)
	if err := json.Unmarshal(data, swspec); err != nil {
		return nil, err
	}

	origsqspec := new(spec.Swagger)
	if err := json.Unmarshal(data, origsqspec); err != nil {
		return nil, err
	}

	d := &Document{
		Analyzer: analysis.New(swspec),
		schema:   spec.MustLoadSwagger20Schema(),
		spec:     swspec,
		raw:      data,
		origSpec: origsqspec,
	}
	return d, nil
}

// Expanded expands the ref fields in the spec document and returns a new spec document
func (d *Document) Expanded() (*Document, error) {
	swspec := new(spec.Swagger)
	if err := json.Unmarshal(d.raw, swspec); err != nil {
		return nil, err
	}
	if err := spec.ExpandSpec(swspec); err != nil {
		return nil, err
	}

	dd := &Document{
		Analyzer: analysis.New(swspec),
		spec:     swspec,
		schema:   spec.MustLoadSwagger20Schema(),
		raw:      d.raw,
		origSpec: d.origSpec,
	}
	return dd, nil
}

// BasePath the base path for this spec
func (d *Document) BasePath() string {
	return d.spec.BasePath
}

// Version returns the version of this spec
func (d *Document) Version() string {
	return d.spec.Swagger
}

// Schema returns the swagger 2.0 schema
func (d *Document) Schema() *spec.Schema {
	return d.schema
}

// Spec returns the swagger spec object model
func (d *Document) Spec() *spec.Swagger {
	return d.spec
}

// Host returns the host for the API
func (d *Document) Host() string {
	return d.spec.Host
}

// Raw returns the raw swagger spec as json bytes
func (d *Document) Raw() json.RawMessage {
	return d.raw
}

func (d *Document) OrigSpec() *spec.Swagger {
	return d.origSpec
}

// ResetDefinitions gives a shallow copy with the models reset
func (d *Document) ResetDefinitions() *Document {
	defs := make(map[string]spec.Schema, len(d.origSpec.Definitions))
	for k, v := range d.origSpec.Definitions {
		defs[k] = v
	}

	d.spec.Definitions = defs
	return d
}

// Pristine creates a new pristine document instance based on the input data
func (d *Document) Pristine() *Document {
	dd, _ := Analyzed(d.Raw(), d.Version())
	return dd
}
<commit_msg>Add "*spec.ExpandOptions" parameter to Expanded().<commit_after>// Copyright 2015 go-swagger maintainers
//
// 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 loads

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

	"github.com/go-openapi/analysis"
	"github.com/go-openapi/spec"
	"github.com/go-openapi/swag"
)

// JSONDoc loads a json document from either a file or a remote url
func JSONDoc(path string) (json.RawMessage, error) {
	data, err := swag.LoadFromFileOrHTTP(path)
	if err != nil {
		return nil, err
	}
	return json.RawMessage(data), nil
}

// DocLoader represents a doc loader type
type DocLoader func(string) (json.RawMessage, error)

// DocMatcher represents a predicate to check if a loader matches
type DocMatcher func(string) bool

var loaders *loader

func init() {
	loaders = &loader{Match: func(_ string) bool { return true }, Fn: JSONDoc}
}

// AddLoader for a document
func AddLoader(predicate DocMatcher, load DocLoader) {
	prev := loaders
	loaders = &loader{
		Match: predicate,
		Fn:    load,
		Next:  prev,
	}

}

type loader struct {
	Fn    DocLoader
	Match DocMatcher
	Next  *loader
}

// JSONSpec loads a spec from a json document
func JSONSpec(path string) (*Document, error) {
	data, err := JSONDoc(path)
	if err != nil {
		return nil, err
	}
	// convert to json
	return Analyzed(json.RawMessage(data), "")
}

// Document represents a swagger spec document
type Document struct {
	// specAnalyzer
	Analyzer *analysis.Spec
	spec     *spec.Swagger
	origSpec *spec.Swagger
	schema   *spec.Schema
	raw      json.RawMessage
}

// Spec loads a new spec document
func Spec(path string) (*Document, error) {
	specURL, err := url.Parse(path)
	if err != nil {
		return nil, err
	}
	for l := loaders.Next; l != nil; l = l.Next {
		if loaders.Match(specURL.Path) {
			b, err2 := loaders.Fn(path)
			if err2 != nil {
				return nil, err2
			}
			return Analyzed(b, "")
		}
	}
	b, err := loaders.Fn(path)
	if err != nil {
		return nil, err
	}
	return Analyzed(b, "")
}

// Analyzed creates a new analyzed spec document
func Analyzed(data json.RawMessage, version string) (*Document, error) {
	if version == "" {
		version = "2.0"
	}
	if version != "2.0" {
		return nil, fmt.Errorf("spec version %q is not supported", version)
	}

	swspec := new(spec.Swagger)
	if err := json.Unmarshal(data, swspec); err != nil {
		return nil, err
	}

	origsqspec := new(spec.Swagger)
	if err := json.Unmarshal(data, origsqspec); err != nil {
		return nil, err
	}

	d := &Document{
		Analyzer: analysis.New(swspec),
		schema:   spec.MustLoadSwagger20Schema(),
		spec:     swspec,
		raw:      data,
		origSpec: origsqspec,
	}
	return d, nil
}

// Expanded expands the ref fields in the spec document and returns a new spec document
func (d *Document) Expanded(options *spec.ExpandOptions) (*Document, error) {
	swspec := new(spec.Swagger)
	if err := json.Unmarshal(d.raw, swspec); err != nil {
		return nil, err
	}
	if err := spec.ExpandSpec(swspec, options); err != nil {
		return nil, err
	}

	dd := &Document{
		Analyzer: analysis.New(swspec),
		spec:     swspec,
		schema:   spec.MustLoadSwagger20Schema(),
		raw:      d.raw,
		origSpec: d.origSpec,
	}
	return dd, nil
}

// BasePath the base path for this spec
func (d *Document) BasePath() string {
	return d.spec.BasePath
}

// Version returns the version of this spec
func (d *Document) Version() string {
	return d.spec.Swagger
}

// Schema returns the swagger 2.0 schema
func (d *Document) Schema() *spec.Schema {
	return d.schema
}

// Spec returns the swagger spec object model
func (d *Document) Spec() *spec.Swagger {
	return d.spec
}

// Host returns the host for the API
func (d *Document) Host() string {
	return d.spec.Host
}

// Raw returns the raw swagger spec as json bytes
func (d *Document) Raw() json.RawMessage {
	return d.raw
}

func (d *Document) OrigSpec() *spec.Swagger {
	return d.origSpec
}

// ResetDefinitions gives a shallow copy with the models reset
func (d *Document) ResetDefinitions() *Document {
	defs := make(map[string]spec.Schema, len(d.origSpec.Definitions))
	for k, v := range d.origSpec.Definitions {
		defs[k] = v
	}

	d.spec.Definitions = defs
	return d
}

// Pristine creates a new pristine document instance based on the input data
func (d *Document) Pristine() *Document {
	dd, _ := Analyzed(d.Raw(), d.Version())
	return dd
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/web"
	"github.com/spf13/cobra"
)

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts the stack and listens for HTTP calls",
	Long: `Starts the stack and listens for HTTP calls
It will accept HTTP requests on localhost:8080 by default.
Use the --port and --host flags to change the listening option.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		if err := instance.StartJobs(); err != nil {
			return err
		}
		return web.ListenAndServe()
	},
}

var serveAppDir = &cobra.Command{
	Use:   "serve-appdir [directory]",
	Short: "Starts the stack along with a ",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) != 1 {
			return cmd.Help()
		}
		if err := instance.StartJobs(); err != nil {
			return err
		}
		return web.ListenAndServeWithAppDir(args[0])
	},
}

func init() {
	RootCmd.AddCommand(serveCmd)
	RootCmd.AddCommand(serveAppDir)
}
<commit_msg>Prevent running cozy-stack serve as root accidentally<commit_after>package cmd

import (
	"errors"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/web"
	"github.com/spf13/cobra"
)

var flagAllowRoot bool

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts the stack and listens for HTTP calls",
	Long: `Starts the stack and listens for HTTP calls
It will accept HTTP requests on localhost:8080 by default.
Use the --port and --host flags to change the listening option.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		if !flagAllowRoot && os.Getuid() == 0 {
			log.Errorf("Use --allow-root if you really want to start with the root user")
			return errors.New("Starting cozy-stack serve as root not allowed")
		}
		if err := instance.StartJobs(); err != nil {
			return err
		}
		return web.ListenAndServe()
	},
}

var serveAppDir = &cobra.Command{
	Use:   "serve-appdir [directory]",
	Short: "Starts the stack along with a ",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) != 1 {
			return cmd.Help()
		}
		if err := instance.StartJobs(); err != nil {
			return err
		}
		return web.ListenAndServeWithAppDir(args[0])
	},
}

func init() {
	RootCmd.AddCommand(serveCmd)
	RootCmd.AddCommand(serveAppDir)
	serveCmd.Flags().BoolVar(&flagAllowRoot, "allow-root", false, "Allow to start as root (disabled by default)")
}
<|endoftext|>


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

package main

import (
	"log"
	"regexp"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/prometheus/client_golang/prometheus"
)

var additionalLabels = []string{"state", "protocol", "destination"}

type ConntrackCollector struct {
	containerLister func() ([]*docker.Container, error)
	conntrack       func() ([]conn, error)
	sync.Mutex
	connCount  map[string]map[string]int
	containers map[string]*docker.Container
}

func (c *ConntrackCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- prometheus.NewDesc("container_connections", "Number of outbound connections by destionation and state", []string{"id", "name"}, nil)
}

func (c *ConntrackCollector) Collect(ch chan<- prometheus.Metric) {
	counts, currContainers := c.getState()
	containers, err := c.containerLister()
	if err != nil {
		log.Print(err)
		return
	}
	conns, err := c.conntrack()
	if err != nil {
		log.Print(err)
		return
	}
	for _, container := range containers {
		for _, conn := range conns {
			value := ""
			switch container.NetworkSettings.IPAddress {
			case conn.SourceIP:
				value = conn.DestinationIP + ":" + conn.DestinationPort
			case conn.DestinationIP:
				value = conn.SourceIP + ":" + conn.SourcePort
			}
			if value != "" {
				key := conn.State + "-" + conn.Protocol + "-" + value
				if counts[container.ID] == nil {
					counts[container.ID] = make(map[string]int)
				}
				counts[container.ID][key] = counts[container.ID][key] + 1
			}
			currContainers[container.ID] = *container
		}
	}
	c.setState(counts, currContainers)
	c.sendMetrics(counts, ch)
}

func (c *ConntrackCollector) getState() (map[string]map[string]int, map[string]docker.Container) {
	c.Lock()
	defer c.Unlock()
	copyCont := make(map[string]docker.Container)
	for _, cont := range c.containers {
		copyCont[cont.ID] = *cont
	}
	copy := make(map[string]map[string]int)
	for k, v := range c.connCount {
		innerCopy := make(map[string]int)
		for ik, iv := range v {
			if iv == 0 {
				continue
			}
			innerCopy[ik] = 0
		}
		if len(innerCopy) == 0 {
			delete(copyCont, k)
			continue
		}
		copy[k] = innerCopy
	}
	return copy, copyCont
}

func (c *ConntrackCollector) setState(count map[string]map[string]int, containers map[string]docker.Container) {
	c.Lock()
	defer c.Unlock()
	c.connCount = count
	for k, v := range containers {
		c.containers[k] = &v
	}
}

func (c *ConntrackCollector) sendMetrics(metrics map[string]map[string]int, ch chan<- prometheus.Metric) {
	for contID, count := range metrics {
		labelsMap := containerLabels(c.containers[contID])
		labels := make([]string, len(labelsMap)+len(additionalLabels))
		values := make([]string, len(labelsMap)+len(additionalLabels))
		i := 0
		for k, v := range labelsMap {
			labels[i] = k
			values[i] = v
			i++
		}
		for _, l := range additionalLabels {
			labels[i] = l
			i++
		}
		i = i - len(additionalLabels)
		desc := prometheus.NewDesc("container_connections", "Number of outbound connections by destionation and state", labels, nil)
		for k, v := range count {
			keys := strings.SplitN(k, "-", 3)
			values[i] = keys[0]
			values[i+1] = keys[1]
			values[i+2] = keys[2]
			ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(v), values...)
		}
	}
}

func containerLabels(container *docker.Container) map[string]string {
	labels := map[string]string{
		"id":    container.ID,
		"name":  container.Name,
		"image": container.Config.Image,
	}
	for k, v := range container.Config.Labels {
		labels["container_label_"+k] = v
	}
	return labels
}

var invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)

func sanitizeLabelName(name string) string {
	return invalidLabelCharRE.ReplaceAllString(name, "_")
}
<commit_msg>remove global state acess<commit_after>// Copyright 2016 conntrack-prometheus authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"log"
	"regexp"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/prometheus/client_golang/prometheus"
)

var additionalLabels = []string{"state", "protocol", "destination"}

type ConntrackCollector struct {
	containerLister func() ([]*docker.Container, error)
	conntrack       func() ([]conn, error)
	sync.Mutex
	connCount  map[string]map[string]int
	containers map[string]*docker.Container
}

func (c *ConntrackCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- prometheus.NewDesc("container_connections", "Number of outbound connections by destionation and state", []string{"id", "name"}, nil)
}

func (c *ConntrackCollector) Collect(ch chan<- prometheus.Metric) {
	counts, currContainers := c.getState()
	containers, err := c.containerLister()
	if err != nil {
		log.Print(err)
		return
	}
	conns, err := c.conntrack()
	if err != nil {
		log.Print(err)
		return
	}
	for _, container := range containers {
		for _, conn := range conns {
			value := ""
			switch container.NetworkSettings.IPAddress {
			case conn.SourceIP:
				value = conn.DestinationIP + ":" + conn.DestinationPort
			case conn.DestinationIP:
				value = conn.SourceIP + ":" + conn.SourcePort
			}
			if value != "" {
				key := conn.State + "-" + conn.Protocol + "-" + value
				if counts[container.ID] == nil {
					counts[container.ID] = make(map[string]int)
				}
				counts[container.ID][key] = counts[container.ID][key] + 1
			}
			currContainers[container.ID] = *container
		}
	}
	log.Print(counts)
	c.setState(counts, currContainers)
	sendMetrics(counts, currContainers, ch)
}

func (c *ConntrackCollector) getState() (map[string]map[string]int, map[string]docker.Container) {
	c.Lock()
	defer c.Unlock()
	copyCont := make(map[string]docker.Container)
	for _, cont := range c.containers {
		copyCont[cont.ID] = *cont
	}
	copy := make(map[string]map[string]int)
	for k, v := range c.connCount {
		innerCopy := make(map[string]int)
		for ik, iv := range v {
			if iv == 0 {
				continue
			}
			innerCopy[ik] = 0
		}
		if len(innerCopy) == 0 {
			delete(copyCont, k)
			continue
		}
		copy[k] = innerCopy
	}
	return copy, copyCont
}

func (c *ConntrackCollector) setState(count map[string]map[string]int, containers map[string]docker.Container) {
	c.Lock()
	defer c.Unlock()
	c.connCount = count
	for k, v := range containers {
		c.containers[k] = &v
	}
}

func sendMetrics(metrics map[string]map[string]int, containers map[string]docker.Container, ch chan<- prometheus.Metric) {
	for contID, count := range metrics {
		labelsMap := containerLabels(containers[contID])
		labels := make([]string, len(labelsMap)+len(additionalLabels))
		values := make([]string, len(labelsMap)+len(additionalLabels))
		i := 0
		for k, v := range labelsMap {
			labels[i] = sanitizeLabelName(k)
			values[i] = v
			i++
		}
		for _, l := range additionalLabels {
			labels[i] = l
			i++
		}
		i = i - len(additionalLabels)
		desc := prometheus.NewDesc("container_connections", "Number of outbound connections by destionation and state", labels, nil)
		for k, v := range count {
			keys := strings.SplitN(k, "-", 3)
			values[i] = keys[0]
			values[i+1] = keys[1]
			values[i+2] = keys[2]
			ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(v), values...)
		}
	}
}

func containerLabels(container docker.Container) map[string]string {
	labels := map[string]string{
		"id":    container.ID,
		"name":  container.Name,
		"image": container.Config.Image,
	}
	for k, v := range container.Config.Labels {
		labels["container_label_"+k] = v
	}
	return labels
}

var invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)

func sanitizeLabelName(name string) string {
	return invalidLabelCharRE.ReplaceAllString(name, "_")
}
<|endoftext|>


<commit_before>// Package mmvdump implements a go port of the C mmvdump utility included in PCP Core
//
// https://github.com/performancecopilot/pcp/blob/master/src/pmdas/mmv/mmvdump.c
//
// It has been written for maximum portability with the C equivalent, without having to use cgo or any other ninja stuff
//
// the main difference is that the reader is separate from the cli with the reading primarily implemented in mmvdump.go while the cli is implemented in cmd/mmvdump
//
// the cli application is completely go gettable and outputs the same things, in mostly the same way as the C cli app, to try it out,
//
// ```
// go get github.com/performancecopilot/speed/mmvdump/cmd/mmvdump
// ```
package mmvdump

import (
	"errors"
	"fmt"
	"math"
	"sync"
	"unsafe"
)

func readHeader(data []byte) (*Header, error) {
	if uint64(len(data)) < HeaderLength {
		return nil, errors.New("file too small to contain a valid Header")
	}

	header := (*Header)(unsafe.Pointer(&data[0]))

	if m := header.Magic[:3]; string(m) != "MMV" {
		return nil, fmt.Errorf("Bad Magic: %v", string(m))
	}

	if header.G1 != header.G2 {
		return nil, fmt.Errorf("Mismatched version numbers, %v and %v", header.G1, header.G2)
	}

	return header, nil
}

func readToc(data []byte, offset uint64) (*Toc, error) {
	if uint64(len(data)) < offset+TocLength {
		return nil, errors.New("Incomplete/Partially Written TOC")
	}

	return (*Toc)(unsafe.Pointer(&data[offset])), nil
}

func readInstance(data []byte, offset uint64) (*Instance, error) {
	if uint64(len(data)) < offset+InstanceLength {
		return nil, errors.New("Incomplete/Partially Written Instance")
	}

	return (*Instance)(unsafe.Pointer(&data[offset])), nil
}

func readInstanceDomain(data []byte, offset uint64) (*InstanceDomain, error) {
	if uint64(len(data)) < offset+InstanceDomainLength {
		return nil, errors.New("Incomplete/Partially Written InstanceDomain")
	}

	return (*InstanceDomain)(unsafe.Pointer(&data[offset])), nil
}

func readMetric(data []byte, offset uint64) (*Metric, error) {
	if uint64(len(data)) < offset+MetricLength {
		return nil, errors.New("Incomplete/Partially Written Metric")
	}

	return (*Metric)(unsafe.Pointer(&data[offset])), nil
}

func readValue(data []byte, offset uint64) (*Value, error) {
	if uint64(len(data)) < offset+ValueLength {
		return nil, errors.New("Incomplete/Partially Written Value")
	}

	return (*Value)(unsafe.Pointer(&data[offset])), nil
}

func readString(data []byte, offset uint64) (*String, error) {
	if uint64(len(data)) < offset+StringLength {
		return nil, errors.New("Incomplete/Partially Written String")
	}

	return (*String)(unsafe.Pointer(&data[offset])), nil
}

func readTocs(data []byte, count int32) ([]*Toc, error) {
	tocs := make([]*Toc, count)

	for i := int32(0); i < count; i++ {
		t, err := readToc(data, HeaderLength+uint64(i)*TocLength)
		if err != nil {
			return nil, err
		}
		tocs[i] = t
	}

	return tocs, nil
}

func readInstances(data []byte, offset uint64, count int32) (map[uint64]*Instance, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	instances := make(map[uint64]*Instance)

	var (
		instance *Instance
		err      error
		m        sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+InstanceLength {
		go func(offset uint64) {
			instance, err = readInstance(data, offset)
			if err == nil {
				m.Lock()
				instances[offset] = instance
				m.Unlock()
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return instances, nil
}

func readInstanceDomains(data []byte, offset uint64, count int32) (map[uint64]*InstanceDomain, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	indoms := make(map[uint64]*InstanceDomain)

	var (
		indom *InstanceDomain
		err   error
		m     sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+InstanceDomainLength {
		go func(offset uint64) {
			indom, err = readInstanceDomain(data, offset)
			if err == nil {
				m.Lock()
				indoms[offset] = indom
				m.Unlock()
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return indoms, nil
}

func readMetrics(data []byte, offset uint64, count int32) (map[uint64]*Metric, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	metrics := make(map[uint64]*Metric)

	var (
		metric *Metric
		err    error
		m      sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+MetricLength {
		go func(offset uint64) {
			metric, err = readMetric(data, offset)
			if err == nil {
				m.Lock()
				metrics[offset] = metric
				m.Unlock()
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return metrics, nil
}

func readValues(data []byte, offset uint64, count int32) (map[uint64]*Value, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	values := make(map[uint64]*Value)

	var (
		value *Value
		err   error
		m     sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+ValueLength {
		go func(offset uint64) {
			value, err = readValue(data, offset)
			if err == nil {
				m.Lock()
				values[offset] = value
				m.Unlock()
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return values, nil
}

func readStrings(data []byte, offset uint64, count int32) (map[uint64]*String, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	strings := make(map[uint64]*String)

	var (
		str *String
		err error
		m   sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+StringLength {
		go func(offset uint64) {
			str, err = readString(data, offset)
			if err == nil {
				m.Lock()
				strings[offset] = str
				m.Unlock()
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return strings, nil
}

// Dump creates a data dump from the passed data
func Dump(data []byte) (
	h *Header,
	tocs []*Toc,
	metrics map[uint64]*Metric,
	values map[uint64]*Value,
	instances map[uint64]*Instance,
	indoms map[uint64]*InstanceDomain,
	strings map[uint64]*String,
	err error,
) {
	h, err = readHeader(data)
	if err != nil {
		return nil, nil, nil, nil, nil, nil, nil, err
	}

	tocs, err = readTocs(data, h.Toc)
	if err != nil {
		return nil, nil, nil, nil, nil, nil, nil, err
	}

	var wg sync.WaitGroup
	wg.Add(len(tocs))

	for _, toc := range tocs {
		switch toc.Type {
		case TocInstances:
			go func(offset uint64, count int32) {
				instances, err = readInstances(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocIndoms:
			go func(offset uint64, count int32) {
				indoms, err = readInstanceDomains(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocMetrics:
			go func(offset uint64, count int32) {
				metrics, err = readMetrics(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocValues:
			go func(offset uint64, count int32) {
				values, err = readValues(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocStrings:
			go func(offset uint64, count int32) {
				strings, err = readStrings(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		}
	}

	wg.Wait()

	if err != nil {
		return nil, nil, nil, nil, nil, nil, nil, err
	}

	return
}

// FixedVal will infer a fixed size value from the passed data
func FixedVal(data uint64, t Type) (interface{}, error) {
	switch t {
	case Int32Type:
		return int32(data), nil
	case Uint32Type:
		return uint32(data), nil
	case Int64Type:
		return int64(data), nil
	case Uint64Type:
		return data, nil
	case FloatType:
		return math.Float32frombits(uint32(data)), nil
	case DoubleType:
		return math.Float64frombits(data), nil
	}

	return nil, errors.New("invalid type")
}
<commit_msg>mmvdump: fix race condition<commit_after>// Package mmvdump implements a go port of the C mmvdump utility included in PCP Core
//
// https://github.com/performancecopilot/pcp/blob/master/src/pmdas/mmv/mmvdump.c
//
// It has been written for maximum portability with the C equivalent, without having to use cgo or any other ninja stuff
//
// the main difference is that the reader is separate from the cli with the reading primarily implemented in mmvdump.go while the cli is implemented in cmd/mmvdump
//
// the cli application is completely go gettable and outputs the same things, in mostly the same way as the C cli app, to try it out,
//
// ```
// go get github.com/performancecopilot/speed/mmvdump/cmd/mmvdump
// ```
package mmvdump

import (
	"errors"
	"fmt"
	"math"
	"sync"
	"unsafe"
)

func readHeader(data []byte) (*Header, error) {
	if uint64(len(data)) < HeaderLength {
		return nil, errors.New("file too small to contain a valid Header")
	}

	header := (*Header)(unsafe.Pointer(&data[0]))

	if m := header.Magic[:3]; string(m) != "MMV" {
		return nil, fmt.Errorf("Bad Magic: %v", string(m))
	}

	if header.G1 != header.G2 {
		return nil, fmt.Errorf("Mismatched version numbers, %v and %v", header.G1, header.G2)
	}

	return header, nil
}

func readToc(data []byte, offset uint64) (*Toc, error) {
	if uint64(len(data)) < offset+TocLength {
		return nil, errors.New("Incomplete/Partially Written TOC")
	}

	return (*Toc)(unsafe.Pointer(&data[offset])), nil
}

func readInstance(data []byte, offset uint64) (*Instance, error) {
	if uint64(len(data)) < offset+InstanceLength {
		return nil, errors.New("Incomplete/Partially Written Instance")
	}

	return (*Instance)(unsafe.Pointer(&data[offset])), nil
}

func readInstanceDomain(data []byte, offset uint64) (*InstanceDomain, error) {
	if uint64(len(data)) < offset+InstanceDomainLength {
		return nil, errors.New("Incomplete/Partially Written InstanceDomain")
	}

	return (*InstanceDomain)(unsafe.Pointer(&data[offset])), nil
}

func readMetric(data []byte, offset uint64) (*Metric, error) {
	if uint64(len(data)) < offset+MetricLength {
		return nil, errors.New("Incomplete/Partially Written Metric")
	}

	return (*Metric)(unsafe.Pointer(&data[offset])), nil
}

func readValue(data []byte, offset uint64) (*Value, error) {
	if uint64(len(data)) < offset+ValueLength {
		return nil, errors.New("Incomplete/Partially Written Value")
	}

	return (*Value)(unsafe.Pointer(&data[offset])), nil
}

func readString(data []byte, offset uint64) (*String, error) {
	if uint64(len(data)) < offset+StringLength {
		return nil, errors.New("Incomplete/Partially Written String")
	}

	return (*String)(unsafe.Pointer(&data[offset])), nil
}

func readTocs(data []byte, count int32) ([]*Toc, error) {
	tocs := make([]*Toc, count)

	for i := int32(0); i < count; i++ {
		t, err := readToc(data, HeaderLength+uint64(i)*TocLength)
		if err != nil {
			return nil, err
		}
		tocs[i] = t
	}

	return tocs, nil
}

func readInstances(data []byte, offset uint64, count int32) (map[uint64]*Instance, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	instances := make(map[uint64]*Instance)

	var (
		err error
		m   sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+InstanceLength {
		go func(offset uint64) {
			if err == nil {
				instance, ierr := readInstance(data, offset)
				if ierr == nil {
					m.Lock()
					instances[offset] = instance
					m.Unlock()
				} else {
					err = ierr
				}
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return instances, nil
}

func readInstanceDomains(data []byte, offset uint64, count int32) (map[uint64]*InstanceDomain, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	indoms := make(map[uint64]*InstanceDomain)

	var (
		err error
		m   sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+InstanceDomainLength {
		go func(offset uint64) {
			if err == nil {
				indom, ierr := readInstanceDomain(data, offset)
				if ierr == nil {
					m.Lock()
					indoms[offset] = indom
					m.Unlock()
				} else {
					err = ierr
				}
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return indoms, nil
}

func readMetrics(data []byte, offset uint64, count int32) (map[uint64]*Metric, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	metrics := make(map[uint64]*Metric)

	var (
		err error
		m   sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+MetricLength {
		go func(offset uint64) {
			if err == nil {
				metric, merr := readMetric(data, offset)
				if merr == nil {
					m.Lock()
					metrics[offset] = metric
					m.Unlock()
				} else {
					err = merr
				}
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return metrics, nil
}

func readValues(data []byte, offset uint64, count int32) (map[uint64]*Value, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	values := make(map[uint64]*Value)

	var (
		err error
		m   sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+ValueLength {
		go func(offset uint64) {
			if err == nil {
				value, verr := readValue(data, offset)
				if verr == nil {
					m.Lock()
					values[offset] = value
					m.Unlock()
				} else {
					err = verr
				}
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return values, nil
}

func readStrings(data []byte, offset uint64, count int32) (map[uint64]*String, error) {
	var wg sync.WaitGroup
	wg.Add(int(count))

	strings := make(map[uint64]*String)

	var (
		err error
		m   sync.Mutex
	)

	for i := int32(0); i < count; i, offset = i+1, offset+StringLength {
		go func(offset uint64) {
			if err == nil {
				str, serr := readString(data, offset)
				if serr == nil {
					m.Lock()
					strings[offset] = str
					m.Unlock()
				} else {
					err = serr
				}
			}
			wg.Done()
		}(offset)
	}

	wg.Wait()

	if err != nil {
		return nil, err
	}

	return strings, nil
}

// Dump creates a data dump from the passed data
func Dump(data []byte) (
	h *Header,
	tocs []*Toc,
	metrics map[uint64]*Metric,
	values map[uint64]*Value,
	instances map[uint64]*Instance,
	indoms map[uint64]*InstanceDomain,
	strings map[uint64]*String,
	err error,
) {
	h, err = readHeader(data)
	if err != nil {
		return nil, nil, nil, nil, nil, nil, nil, err
	}

	tocs, err = readTocs(data, h.Toc)
	if err != nil {
		return nil, nil, nil, nil, nil, nil, nil, err
	}

	var wg sync.WaitGroup
	wg.Add(len(tocs))

	var ierr, inerr, merr, verr, serr error

	for _, toc := range tocs {
		switch toc.Type {
		case TocInstances:
			go func(offset uint64, count int32) {
				instances, ierr = readInstances(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocIndoms:
			go func(offset uint64, count int32) {
				indoms, inerr = readInstanceDomains(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocMetrics:
			go func(offset uint64, count int32) {
				metrics, merr = readMetrics(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocValues:
			go func(offset uint64, count int32) {
				values, verr = readValues(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		case TocStrings:
			go func(offset uint64, count int32) {
				strings, serr = readStrings(data, offset, count)
				wg.Done()
			}(toc.Offset, toc.Count)
		}
	}

	wg.Wait()

	if ierr != nil {
		return nil, nil, nil, nil, nil, nil, nil, ierr
	}

	return
}

// FixedVal will infer a fixed size value from the passed data
func FixedVal(data uint64, t Type) (interface{}, error) {
	switch t {
	case Int32Type:
		return int32(data), nil
	case Uint32Type:
		return uint32(data), nil
	case Int64Type:
		return int64(data), nil
	case Uint64Type:
		return data, nil
	case FloatType:
		return math.Float32frombits(uint32(data)), nil
	case DoubleType:
		return math.Float64frombits(data), nil
	}

	return nil, errors.New("invalid type")
}
<|endoftext|>


<commit_before>// Copyright 2012 Google Inc.
//
// 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 main

// This file contains the model construction by reflection.

import (
	"bytes"
	"encoding/gob"
	"flag"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"

	"github.com/golang/mock/mockgen/model"
)

var (
	progOnly   = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout and exit.")
	execOnly   = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.")
	buildFlags = flag.String("build_flags", "", "(reflect mode) Additional flags for go build.")
)

func writeProgram(importPath string, symbols []string) ([]byte, error) {
	var program bytes.Buffer
	data := reflectData{
		ImportPath: importPath,
		Symbols:    symbols,
	}
	if err := reflectProgram.Execute(&program, &data); err != nil {
		return nil, err
	}
	return program.Bytes(), nil
}

// run the given program and parse the output as a model.Package.
func run(program string) (*model.Package, error) {
	f, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, err
	}

	filename := f.Name()
	defer os.Remove(filename)
	if err := f.Close(); err != nil {
		return nil, err
	}

	// Run the program.
	cmd := exec.Command(program, "-output", filename)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return nil, err
	}

	f, err = os.Open(filename)
	if err != nil {
		return nil, err
	}

	// Process output.
	var pkg model.Package
	if err := gob.NewDecoder(f).Decode(&pkg); err != nil {
		return nil, err
	}

	if err := f.Close(); err != nil {
		return nil, err
	}

	return &pkg, nil
}

// runInDir writes the given program into the given dir, runs it there, and
// parses the output as a model.Package.
func runInDir(program []byte, dir string) (*model.Package, error) {
	// We use TempDir instead of TempFile so we can control the filename.
	tmpDir, err := ioutil.TempDir(dir, "gomock_reflect_")
	if err != nil {
		return nil, err
	}
	defer func() {
		if err := os.RemoveAll(tmpDir); err != nil {
			log.Printf("failed to remove temp directory: %s", err)
		}
	}()
	const progSource = "prog.go"
	var progBinary = "prog.bin"
	if runtime.GOOS == "windows" {
		// Windows won't execute a program unless it has a ".exe" suffix.
		progBinary += ".exe"
	}

	if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil {
		return nil, err
	}

	cmdArgs := []string{}
	cmdArgs = append(cmdArgs, "build")
	if *buildFlags != "" {
		cmdArgs = append(cmdArgs, *buildFlags)
	}
	cmdArgs = append(cmdArgs, "-o", progBinary, progSource)

	// Build the program.
	cmd := exec.Command("go", cmdArgs...)
	cmd.Dir = tmpDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return nil, err
	}
	return run(filepath.Join(tmpDir, progBinary))
}

func reflect(importPath string, symbols []string) (*model.Package, error) {
	// TODO: sanity check arguments

	if *execOnly != "" {
		return run(*execOnly)
	}

	program, err := writeProgram(importPath, symbols)
	if err != nil {
		return nil, err
	}

	if *progOnly {
		os.Stdout.Write(program)
		os.Exit(0)
	}

	wd, _ := os.Getwd()

	// Try to run the program in the same directory as the input package.
	if p, err := build.Import(importPath, wd, build.FindOnly); err == nil {
		dir := p.Dir
		if p, err := runInDir(program, dir); err == nil {
			return p, nil
		}
	}

	// Since that didn't work, try to run it in the current working directory.
	if p, err := runInDir(program, wd); err == nil {
		return p, nil
	}
	// Since that didn't work, try to run it in a standard temp directory.
	return runInDir(program, "")
}

type reflectData struct {
	ImportPath string
	Symbols    []string
}

// This program reflects on an interface value, and prints the
// gob encoding of a model.Package to standard output.
// JSON doesn't work because of the model.Type interface.
var reflectProgram = template.Must(template.New("program").Parse(`
package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"os"
	"path"
	"reflect"

	"github.com/golang/mock/mockgen/model"

	pkg_ {{printf "%q" .ImportPath}}
)

var output = flag.String("output", "", "The output file name, or empty to use stdout.")

func main() {
	flag.Parse()

	its := []struct{
		sym string
		typ reflect.Type
	}{
		{{range .Symbols}}
		{ {{printf "%q" .}}, reflect.TypeOf((*pkg_.{{.}})(nil)).Elem()},
		{{end}}
	}
	pkg := &model.Package{
		// NOTE: This behaves contrary to documented behaviour if the
		// package name is not the final component of the import path.
		// The reflect package doesn't expose the package name, though.
		Name: path.Base({{printf "%q" .ImportPath}}),
	}

	for _, it := range its {
		intf, err := model.InterfaceFromInterfaceType(it.typ)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
			os.Exit(1)
		}
		intf.Name = it.sym
		pkg.Interfaces = append(pkg.Interfaces, intf)
	}

	outfile := os.Stdout
	if len(*output) != 0 {
		var err error
		outfile, err = os.Create(*output)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to open output file %q", *output)
		}
		defer func() {
			if err := outfile.Close(); err != nil {
				fmt.Fprintf(os.Stderr, "failed to close output file %q", *output)
				os.Exit(1)
			}
		}()
	}

	if err := gob.NewEncoder(outfile).Encode(pkg); err != nil {
		fmt.Fprintf(os.Stderr, "gob encode: %v\n", err)
		os.Exit(1)
	}
}
`))
<commit_msg>Pass -build_flags as Multiple Args (#357)<commit_after>// Copyright 2012 Google Inc.
//
// 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 main

// This file contains the model construction by reflection.

import (
	"bytes"
	"encoding/gob"
	"flag"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"

	"github.com/golang/mock/mockgen/model"
)

var (
	progOnly   = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout and exit.")
	execOnly   = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.")
	buildFlags = flag.String("build_flags", "", "(reflect mode) Additional flags for go build.")
)

func writeProgram(importPath string, symbols []string) ([]byte, error) {
	var program bytes.Buffer
	data := reflectData{
		ImportPath: importPath,
		Symbols:    symbols,
	}
	if err := reflectProgram.Execute(&program, &data); err != nil {
		return nil, err
	}
	return program.Bytes(), nil
}

// run the given program and parse the output as a model.Package.
func run(program string) (*model.Package, error) {
	f, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, err
	}

	filename := f.Name()
	defer os.Remove(filename)
	if err := f.Close(); err != nil {
		return nil, err
	}

	// Run the program.
	cmd := exec.Command(program, "-output", filename)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return nil, err
	}

	f, err = os.Open(filename)
	if err != nil {
		return nil, err
	}

	// Process output.
	var pkg model.Package
	if err := gob.NewDecoder(f).Decode(&pkg); err != nil {
		return nil, err
	}

	if err := f.Close(); err != nil {
		return nil, err
	}

	return &pkg, nil
}

// runInDir writes the given program into the given dir, runs it there, and
// parses the output as a model.Package.
func runInDir(program []byte, dir string) (*model.Package, error) {
	// We use TempDir instead of TempFile so we can control the filename.
	tmpDir, err := ioutil.TempDir(dir, "gomock_reflect_")
	if err != nil {
		return nil, err
	}
	defer func() {
		if err := os.RemoveAll(tmpDir); err != nil {
			log.Printf("failed to remove temp directory: %s", err)
		}
	}()
	const progSource = "prog.go"
	var progBinary = "prog.bin"
	if runtime.GOOS == "windows" {
		// Windows won't execute a program unless it has a ".exe" suffix.
		progBinary += ".exe"
	}

	if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil {
		return nil, err
	}

	cmdArgs := []string{}
	cmdArgs = append(cmdArgs, "build")
	if *buildFlags != "" {
		cmdArgs = append(cmdArgs, strings.Split(*buildFlags, " ")...)
	}
	cmdArgs = append(cmdArgs, "-o", progBinary, progSource)

	// Build the program.
	cmd := exec.Command("go", cmdArgs...)
	cmd.Dir = tmpDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return nil, err
	}
	return run(filepath.Join(tmpDir, progBinary))
}

func reflect(importPath string, symbols []string) (*model.Package, error) {
	// TODO: sanity check arguments

	if *execOnly != "" {
		return run(*execOnly)
	}

	program, err := writeProgram(importPath, symbols)
	if err != nil {
		return nil, err
	}

	if *progOnly {
		os.Stdout.Write(program)
		os.Exit(0)
	}

	wd, _ := os.Getwd()

	// Try to run the program in the same directory as the input package.
	if p, err := build.Import(importPath, wd, build.FindOnly); err == nil {
		dir := p.Dir
		if p, err := runInDir(program, dir); err == nil {
			return p, nil
		}
	}

	// Since that didn't work, try to run it in the current working directory.
	if p, err := runInDir(program, wd); err == nil {
		return p, nil
	}
	// Since that didn't work, try to run it in a standard temp directory.
	return runInDir(program, "")
}

type reflectData struct {
	ImportPath string
	Symbols    []string
}

// This program reflects on an interface value, and prints the
// gob encoding of a model.Package to standard output.
// JSON doesn't work because of the model.Type interface.
var reflectProgram = template.Must(template.New("program").Parse(`
package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"os"
	"path"
	"reflect"

	"github.com/golang/mock/mockgen/model"

	pkg_ {{printf "%q" .ImportPath}}
)

var output = flag.String("output", "", "The output file name, or empty to use stdout.")

func main() {
	flag.Parse()

	its := []struct{
		sym string
		typ reflect.Type
	}{
		{{range .Symbols}}
		{ {{printf "%q" .}}, reflect.TypeOf((*pkg_.{{.}})(nil)).Elem()},
		{{end}}
	}
	pkg := &model.Package{
		// NOTE: This behaves contrary to documented behaviour if the
		// package name is not the final component of the import path.
		// The reflect package doesn't expose the package name, though.
		Name: path.Base({{printf "%q" .ImportPath}}),
	}

	for _, it := range its {
		intf, err := model.InterfaceFromInterfaceType(it.typ)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
			os.Exit(1)
		}
		intf.Name = it.sym
		pkg.Interfaces = append(pkg.Interfaces, intf)
	}

	outfile := os.Stdout
	if len(*output) != 0 {
		var err error
		outfile, err = os.Create(*output)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to open output file %q", *output)
		}
		defer func() {
			if err := outfile.Close(); err != nil {
				fmt.Fprintf(os.Stderr, "failed to close output file %q", *output)
				os.Exit(1)
			}
		}()
	}

	if err := gob.NewEncoder(outfile).Encode(pkg); err != nil {
		fmt.Fprintf(os.Stderr, "gob encode: %v\n", err)
		os.Exit(1)
	}
}
`))
<|endoftext|>


<commit_before>package runtime

import "testing"

func TestStreamp(t *testing.T) {
	execTests(t, Streamp, []test{
		{
			exp:     `(streamp (standard-input))`,
			want:    `t`,
			wantErr: false,
		},
		{
			exp:     `(streamp '())`,
			want:    `nil`,
			wantErr: false,
		},
	})
}

func TestInputStreamP(t *testing.T) {
	execTests(t, InputStreamP, []test{
		{
			exp:     `(input-stream-p (standard-input))`,
			want:    `t`,
			wantErr: false,
		},
		{
			exp:     `(input-stream-p (standard-output))`,
			want:    `nil`,
			wantErr: false,
		},
		{
			exp:     `(streamp '(a b c))`,
			want:    `nil`,
			wantErr: false,
		},
	})
}

func TestOutputStreamP(t *testing.T) {
	execTests(t, OutputStreamP, []test{
		{
			exp:     `(output-stream-p (standard-input))`,
			want:    `nil`,
			wantErr: false,
		},
		{
			exp:     `(output-stream-p (standard-output))`,
			want:    `t`,
			wantErr: false,
		},
		{
			exp:     `(streamp "hello")`,
			want:    `nil`,
			wantErr: false,
		},
	})
}

func TestWithStandardInput(t *testing.T) {
	execTests(t, WithStandardInput, []test{
		{
			exp: `
			(with-standard-input (create-string-input-stream "this is a string")
 				(list (read) (read)))
			`,
			want:    `'(this is)`,
			wantErr: false,
		},
	})
}

func TestWithOpenIoFile(t *testing.T) {
	execTests(t, WithOpenIoFile, []test{
		{
			exp: `
			(with-open-output-file (outstream "__example.dat")
  				(format outstream "hello"))
			`,
			want:    `nil`,
			wantErr: false,
		},
		{
			exp: `
			(with-open-input-file (instream "__example.dat")
  				(read instream))
			`,
			want:    `'hello`,
			wantErr: false,
		},
	})
}

<commit_msg>Add test for craeting stream<commit_after>package runtime

import "testing"

func TestStreamp(t *testing.T) {
	execTests(t, Streamp, []test{
		{
			exp:     `(streamp (standard-input))`,
			want:    `t`,
			wantErr: false,
		},
		{
			exp:     `(streamp '())`,
			want:    `nil`,
			wantErr: false,
		},
	})
}

func TestInputStreamP(t *testing.T) {
	execTests(t, InputStreamP, []test{
		{
			exp:     `(input-stream-p (standard-input))`,
			want:    `t`,
			wantErr: false,
		},
		{
			exp:     `(input-stream-p (standard-output))`,
			want:    `nil`,
			wantErr: false,
		},
		{
			exp:     `(streamp '(a b c))`,
			want:    `nil`,
			wantErr: false,
		},
	})
}

func TestOutputStreamP(t *testing.T) {
	execTests(t, OutputStreamP, []test{
		{
			exp:     `(output-stream-p (standard-input))`,
			want:    `nil`,
			wantErr: false,
		},
		{
			exp:     `(output-stream-p (standard-output))`,
			want:    `t`,
			wantErr: false,
		},
		{
			exp:     `(streamp "hello")`,
			want:    `nil`,
			wantErr: false,
		},
	})
}

func TestWithStandardInput(t *testing.T) {
	execTests(t, WithStandardInput, []test{
		{
			exp: `
			(with-standard-input (create-string-input-stream "this is a string")
 				(list (read) (read)))
			`,
			want:    `'(this is)`,
			wantErr: false,
		},
	})
}

func TestWithOpenIoFile(t *testing.T) {
	execTests(t, WithOpenIoFile, []test{
		{
			exp: `
			(with-open-output-file (outstream "__example.dat")
  				(format outstream "hello"))
			`,
			want:    `nil`,
			wantErr: false,
		},
		{
			exp: `
			(with-open-input-file (instream "__example.dat")
  				(read instream))
			`,
			want:    `'hello`,
			wantErr: false,
		},
	})
}

func TestCreateStringInputStream(t *testing.T) {
	execTests(t, CreateStringInputStream, []test{
		{
			exp: `
			(let ((str (create-string-input-stream "this is a string")))
				(list (read str) (read str) (read str)))
			`,
			want:    `'(this is a)`,
			wantErr: false,
		},
	})
}

func TestCreateStringOutputStream(t *testing.T) {
	execTests(t, CreateStringOutputStream, []test{
		{
			exp: `
			(let ((str (create-string-output-stream)))
			  (format str "hello")
			  (format str "world")
			  (get-output-stream-string str))
			`,
			want:    `"helloworld"`,
			wantErr: false,
		},
	})
}

func TestGetOutputStreamString(t *testing.T) {
	execTests(t, GetOutputStreamString, []test{
		{
			exp: `
			(let ((out-str (create-string-output-stream)))
			  (format out-str "This is a string")
			  (let ((part1 (get-output-stream-string out-str)))
			    (format out-str "right!")
			    (list part1 (get-output-stream-string out-str))))
			`,
			want:    `'("This is a string" "right!")`,
			wantErr: false,
		},
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// set up metrics
var (
	requestCount = promauto.NewCounter(prometheus.CounterOpts{
		Name: "go_request_count",
		Help: "total request count",
	})
	failedRequestCount = promauto.NewCounter(prometheus.CounterOpts{
		Name: "go_failed_request_count",
		Help: "failed request count",
	})
	responseLatency = promauto.NewHistogram(prometheus.HistogramOpts{
		Name: "go_response_latency",
		Help: "response latencies",
	})
)

func main() {
	log.Printf("main function")
	http.HandleFunc("/", handle)
	http.Handle("/metrics", promhttp.Handler())
	log.Fatal(http.ListenAndServe(":8081", nil))
}

func handle(w http.ResponseWriter, r *http.Request) {
	requestCount.Inc()
	// start timer
	// fail
	fmt.Fprintf(w, "Hello")
}
<commit_msg>finished prometheus go example<commit_after>package main

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// set up metrics
var (
	requestCount = promauto.NewCounter(prometheus.CounterOpts{
		Name: "go_request_count",
		Help: "total request count",
	})
	failedRequestCount = promauto.NewCounter(prometheus.CounterOpts{
		Name: "go_failed_request_count",
		Help: "failed request count",
	})
	responseLatency = promauto.NewHistogram(prometheus.HistogramOpts{
		Name: "go_response_latency",
		Help: "response latencies",
	})
)

func main() {
	log.Printf("main function")
	http.HandleFunc("/", handle)
	http.Handle("/metrics", promhttp.Handler())
	log.Fatal(http.ListenAndServe(":8081", nil))
}

func handle(w http.ResponseWriter, r *http.Request) {
	requestReceived := time.Now()
	requestCount.Inc()

	// fail 10% of the time
	if rand.Intn(100) > 90 {
		failedRequestCount.Inc()
		fmt.Fprintf(w, "error!")
		responseLatency.Observe(time.Since(requestReceived).Seconds())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	} else {
		time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
		responseLatency.Observe(time.Since(requestReceived).Seconds())
		fmt.Fprintf(w, "Hello")
	}
}
<|endoftext|>


<commit_before>package qdisksync

import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

//read from snapshot and copy
func SyncVolumeData(srcVolume string, destVolume string, bufferSize int64, workerCount int32) {
	runtime.GOMAXPROCS(runtime.NumCPU())
	cacheFile := "qdisksync.cache"
	cacheFileH, err := os.Open(cacheFile)
	if err != nil {
		L.Error("Load cache file `%s' failed", cacheFile)
		return
	}
	defer cacheFileH.Close()

	bReader := bufio.NewScanner(cacheFileH)
	bReader.Split(bufio.ScanLines)
	//init channel
	var allWorkers int32 = 0
	syncStart := time.Now()
	L.Informational("Sync `%s' -> `'%s start from `%s'", srcVolume, destVolume, syncStart.String())
	syncDone := make(chan bool)
	//receive the sync result
	go func() {
		<-syncDone
	}()
	for bReader.Scan() {
		line := bReader.Text()
		//split to name and size
		items := strings.Split(line, "\t")
		if len(items) != 3 {
			L.Error("Line data `%s'' error of cache file `%s'", line, cacheFile)
			continue
		}
		fname := items[0]
		fsize, err := strconv.ParseInt(items[1], 10, 64)
		if err != nil {
			L.Error("File length error `%s' for line `%s'", items[1], line)
			continue
		}
		fperm, err := strconv.ParseInt(items[2], 10, 64)
		if err != nil {
			L.Error("File perm error `%s' for line `%s'", items[2], line)
			continue
		}
		//join the path
		srcFullPath := filepath.Join(srcVolume, fname)
		destFullPath := filepath.Join(destVolume, fname)
		//check src and dest file
		srcFileH, srcErr := os.Open(srcFullPath)
		if srcErr != nil {
			L.Error("Open src file `%s' error `%s'", srcFullPath, srcErr.Error())
			continue
		}
		//create path if necessary
		lastSlashIndex := strings.LastIndex(destFullPath, "/")
		destFullPathBase := destFullPath[:lastSlashIndex]
		if err := os.MkdirAll(destFullPathBase, 0775); err != nil {
			L.Error("Failed to create dir `%s' due to error `%s'", destFullPathBase, err.Error())
			continue
		}
		destFileH, destErr := os.OpenFile(destFullPath, os.O_CREATE|os.O_RDWR, os.FileMode(fperm))
		if destErr != nil {
			L.Error("Open dest file `%s' error `%s'", destFullPath, destErr.Error())
			continue
		}

		//check whether it's time to run copy
		for {
			curWorkers := atomic.LoadInt32(&allWorkers)
			L.Debug("Current Workers: `%d'", curWorkers)
			if curWorkers < workerCount {
				atomic.AddInt32(&allWorkers, 1)
				go copy(srcFileH, destFileH, fsize, bufferSize, srcFullPath, destFullPath, &allWorkers)
				break
			} else {
				//wait some time to avoid infinite cycle
				<-time.After(time.Microsecond * 1)
			}
		}
	}
	for {
		L.Debug("Remaing workers: `%s'", atomic.LoadInt32(&allWorkers))
		if atomic.LoadInt32(&allWorkers) == 0 {
			syncDone <- true
			syncEnd := time.Now()
			L.Informational("Sync `%s' -> `%s' end at `%s'", srcVolume, destVolume, syncEnd.String())
			L.Informational("Sync `%s' -> `%s' last for `%s'", srcVolume, destVolume, time.Since(syncStart))
			break
		} else {
			<-time.After(time.Second * 5)
		}
	}
}

func copy(srcFileH, destFileH *os.File, fsize int64, bufferSize int64, srcFullPath, destFullPath string, allWorkers *int32) {
	defer func() {
		atomic.AddInt32(allWorkers, -1)
		runtime.Gosched()
	}()
	L.Debug("Copying from `%s' to `%s'", srcFullPath, destFullPath)
	buffer := make([]byte, bufferSize)
	var cpErr error
	var cpNum int64
	for {
		numRead, errRead := srcFileH.Read(buffer)
		if errRead == io.EOF {
			break
		} else {
			if errRead != nil {
				L.Error("Read from `%s' error: `%s'", srcFullPath, errRead.Error())
				cpErr = errRead
				break
			} else {
				numWrite, errWrite := destFileH.Write(buffer[:numRead])
				if errWrite != nil {
					L.Error("Write to `%s' error: `%s'", destFullPath, errWrite.Error())
					cpErr = errWrite
					break
				}
				cpNum += int64(numWrite)
			}
		}
	}
	defer srcFileH.Close()
	defer destFileH.Close()
	if cpErr != nil || cpNum != fsize {
		L.Error("Copy from `%s' to `%s' failed, error: `%s'", srcFullPath, destFullPath, cpErr.Error())
	} else {
		L.Debug("Copy from `%s' to `%s' succcess", srcFullPath, destFullPath)
	}

}
<commit_msg>Move the file open operation to go routine.<commit_after>package qdisksync

import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

//read from snapshot and copy
func SyncVolumeData(srcVolume string, destVolume string, bufferSize int64, workerCount int32) {
	runtime.GOMAXPROCS(runtime.NumCPU())
	cacheFile := "qdisksync.cache"
	cacheFileH, err := os.Open(cacheFile)
	if err != nil {
		L.Error("Load cache file `%s' failed", cacheFile)
		return
	}
	defer cacheFileH.Close()

	bReader := bufio.NewScanner(cacheFileH)
	bReader.Split(bufio.ScanLines)
	//init channel
	var allWorkers int32 = 0
	syncStart := time.Now()
	L.Informational("Sync `%s' -> `'%s start from `%s'", srcVolume, destVolume, syncStart.String())
	syncDone := make(chan bool)
	//receive the sync result
	go func() {
		<-syncDone
	}()
	for bReader.Scan() {
		line := bReader.Text()
		//split to name and size
		items := strings.Split(line, "\t")
		if len(items) != 3 {
			L.Error("Line data `%s'' error of cache file `%s'", line, cacheFile)
			continue
		}
		fname := items[0]
		fsize, err := strconv.ParseInt(items[1], 10, 64)
		if err != nil {
			L.Error("File length error `%s' for line `%s'", items[1], line)
			continue
		}
		fperm, err := strconv.ParseInt(items[2], 10, 64)
		if err != nil {
			L.Error("File perm error `%s' for line `%s'", items[2], line)
			continue
		}
		//join the path
		srcFullPath := filepath.Join(srcVolume, fname)
		destFullPath := filepath.Join(destVolume, fname)
		//check whether it's time to run copy
		for {
			curWorkers := atomic.LoadInt32(&allWorkers)
			L.Debug("Current Workers: `%d'", curWorkers)
			if curWorkers < workerCount {
				atomic.AddInt32(&allWorkers, 1)
				go copy(srcFullPath, destFullPath, fsize, os.FileMode(fperm), bufferSize, &allWorkers)
				break
			} else {
				//wait some time to avoid infinite cycle
				<-time.After(time.Microsecond * 1)
			}
		}
	}
	for {
		L.Debug("Remaing workers: `%s'", atomic.LoadInt32(&allWorkers))
		if atomic.LoadInt32(&allWorkers) == 0 {
			syncDone <- true
			syncEnd := time.Now()
			L.Informational("Sync `%s' -> `%s' end at `%s'", srcVolume, destVolume, syncEnd.String())
			L.Informational("Sync `%s' -> `%s' last for `%s'", srcVolume, destVolume, time.Since(syncStart))
			break
		} else {
			<-time.After(time.Second * 5)
		}
	}
}

func copy(srcFullPath, destFullPath string, fsize int64, fperm os.FileMode, bufferSize int64, allWorkers *int32) {
	defer func() {
		atomic.AddInt32(allWorkers, -1)
		runtime.Gosched()
	}()
	//check src and dest file
	srcFileH, srcErr := os.Open(srcFullPath)
	if srcErr != nil {
		L.Error("Open src file `%s' error `%s'", srcFullPath, srcErr.Error())
		return
	}
	//create path if necessary
	lastSlashIndex := strings.LastIndex(destFullPath, "/")
	destFullPathBase := destFullPath[:lastSlashIndex]
	if err := os.MkdirAll(destFullPathBase, 0775); err != nil {
		L.Error("Failed to create dir `%s' due to error `%s'", destFullPathBase, err.Error())
		return
	}
	destFileH, destErr := os.OpenFile(destFullPath, os.O_CREATE|os.O_RDWR, os.FileMode(fperm))
	if destErr != nil {
		L.Error("Open dest file `%s' error `%s'", destFullPath, destErr.Error())
		return
	}

	L.Debug("Copying from `%s' to `%s'", srcFullPath, destFullPath)
	buffer := make([]byte, bufferSize)
	var cpErr error
	var cpNum int64
	for {
		numRead, errRead := srcFileH.Read(buffer)
		if errRead == io.EOF {
			break
		} else {
			if errRead != nil {
				L.Error("Read from `%s' error: `%s'", srcFullPath, errRead.Error())
				cpErr = errRead
				break
			} else {
				numWrite, errWrite := destFileH.Write(buffer[:numRead])
				if errWrite != nil {
					L.Error("Write to `%s' error: `%s'", destFullPath, errWrite.Error())
					cpErr = errWrite
					break
				}
				cpNum += int64(numWrite)
			}
		}
	}
	defer srcFileH.Close()
	defer destFileH.Close()
	if cpErr != nil || cpNum != fsize {
		L.Error("Copy from `%s' to `%s' failed, error: `%s'", srcFullPath, destFullPath, cpErr.Error())
	} else {
		L.Debug("Copy from `%s' to `%s' succcess", srcFullPath, destFullPath)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestRedirectToAdminOnSlash(t *testing.T) {
	req, err := http.NewRequest("GET", "http://example.com", nil)
	assert.Nil(t, err)

	w := httptest.NewRecorder()
	defaultHandler(w, req)
	assert.Equal(t, w.Code, http.StatusMovedPermanently)
}

func TestNoRedirectToAdminOnSlash(t *testing.T) {
	req, err := http.NewRequest("GET", "http://example.com/", nil)
	assert.Nil(t, err)

	w := httptest.NewRecorder()
	defaultHandler(w, req)
	assert.Equal(t, http.StatusMovedPermanently, w.Code)
	assert.Equal(t, "/admin/", w.Header().Get("Location"))
}

func TestAppAccess(t *testing.T) {
	req, err := http.NewRequest("GET", "http://example.com/foobar", nil)
	assert.Nil(t, err)

	w := httptest.NewRecorder()
	defaultHandler(w, req)
	assert.NotEqual(t, http.StatusMovedPermanently, w.Code)
}

func TestAdminAccess(t *testing.T) {
	req, err := http.NewRequest("GET", "http://example.com/admin/", nil)
	assert.Nil(t, err)

	w := httptest.NewRecorder()
	defaultHandler(w, req)
	assert.NotEqual(t, http.StatusMovedPermanently, w.Code)
}

func TestAdminAccessRedirectSlash(t *testing.T) {
	req, err := http.NewRequest("GET", "http://example.com/admin", nil)
	assert.Nil(t, err)

	w := httptest.NewRecorder()
	defaultHandler(w, req)
	assert.Equal(t, http.StatusMovedPermanently, w.Code)
	assert.Equal(t, "/admin/", w.Header().Get("Location"))
}

func TestMain(m *testing.M) {
	target_url, _ := url.Parse("http://example.com/")
	apps_proxy = newSwitchingProxy(target_url)
	management_proxy = httputil.NewSingleHostReverseProxy(target_url)
	exit := m.Run()
	os.Exit(exit)
}
<commit_msg>remove dokku tests<commit_after><|endoftext|>


<commit_before>package engineio

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestServer(t *testing.T) {
	t1 := newFakeTransportCreater(true, "t1")
	t2 := newFakeTransportCreater(true, "t2")
	t3 := newFakeTransportCreater(false, "t3")
	registerTransport("t1", true, t1)
	registerTransport("t2", false, t2)
	registerTransport("t3", true, t3)

	Convey("Setup server", t, func() {
		server, err := NewServer(nil)
		So(err, ShouldBeNil)
		server.SetPingInterval(time.Second)
		So(server.config.PingInterval, ShouldEqual, time.Second)
		server.SetPingTimeout(10 * time.Second)
		So(server.config.PingTimeout, ShouldEqual, 10*time.Second)
		f := func(*http.Request) error { return nil }
		server.SetAllowRequest(f)
		So(server.config.AllowRequest, ShouldEqual, f)
		server.SetAllowUpgrades(false)
		So(server.config.AllowUpgrades, ShouldBeFalse)
		server.SetCookie("prefix")
		So(server.config.Cookie, ShouldEqual, "prefix")
	})

	Convey("Create server", t, func() {
		server, err := NewServer([]string{"t1", "t2", "t3"})
		So(err, ShouldBeNil)

		Convey("Test new id", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			id1 := server.newId(req)
			id2 := server.newId(req)
			So(id1, ShouldNotEqual, id2)
		})

		Convey("Test on close", func() {
			req, err := http.NewRequest("GET", "/", nil)
			t, err := t1(req)
			So(err, ShouldBeNil)
			id := "abc"
			conn, err := newConn(id, server, t, req)
			So(err, ShouldBeNil)
			server.sessions.Set(id, conn)
			server.onClose(conn)
			So(server.sessions.Get(id), ShouldBeNil)
		})

		Convey("Test serve http", func() {

			Convey("Normal request", func() {
				check := make(chan bool)
				id := ""
				go func() {
					conn, _ := server.Accept()
					id = conn.Id()
					check <- true
					conn.Close()
					check <- true
				}()

				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t1")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusOK)

				p.Set("sid", id)
				r, err = http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w = httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusOK)

				<-check
				<-check

				r, err = http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w = httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
			})

			Convey("Not allowed", func() {
				s, err := NewServer(nil)
				So(err, ShouldBeNil)
				s.SetAllowRequest(func(*http.Request) error {
					return errors.New("not allowed")
				})

				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t1")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				s.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "not allowed\n")
			})

			Convey("Wrong transport", func() {
				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "notexist")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "invalid transport\n")
			})

			Convey("Transport error", func() {
				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t3")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "transport t3 error\n")
			})

			Convey("Wrong session id", func() {
				check := make(chan bool)
				var conn Conn
				go func() {
					conn, _ = server.Accept()
					check <- true
					conn.Close()
					check <- true
				}()

				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t1")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusOK)

				p.Set("sid", conn.Id()+"abc")
				r, err = http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w = httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "invalid sid\n")

				<-check
				<-check
			})

		})

	})
}
<commit_msg>fix race test<commit_after>package engineio

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestServer(t *testing.T) {
	t1 := newFakeTransportCreater(true, "t1")
	t2 := newFakeTransportCreater(true, "t2")
	t3 := newFakeTransportCreater(false, "t3")
	registerTransport("t1", true, t1)
	registerTransport("t2", false, t2)
	registerTransport("t3", true, t3)

	Convey("Setup server", t, func() {
		server, err := NewServer(nil)
		So(err, ShouldBeNil)
		server.SetPingInterval(time.Second)
		So(server.config.PingInterval, ShouldEqual, time.Second)
		server.SetPingTimeout(10 * time.Second)
		So(server.config.PingTimeout, ShouldEqual, 10*time.Second)
		f := func(*http.Request) error { return nil }
		server.SetAllowRequest(f)
		So(server.config.AllowRequest, ShouldEqual, f)
		server.SetAllowUpgrades(false)
		So(server.config.AllowUpgrades, ShouldBeFalse)
		server.SetCookie("prefix")
		So(server.config.Cookie, ShouldEqual, "prefix")
	})

	Convey("Create server", t, func() {
		server, err := NewServer([]string{"t1", "t2", "t3"})
		So(err, ShouldBeNil)

		Convey("Test new id", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			id1 := server.newId(req)
			id2 := server.newId(req)
			So(id1, ShouldNotEqual, id2)
		})

		Convey("Test on close", func() {
			req, err := http.NewRequest("GET", "/", nil)
			t, err := t1(req)
			So(err, ShouldBeNil)
			id := "abc"
			conn, err := newConn(id, server, t, req)
			So(err, ShouldBeNil)
			server.sessions.Set(id, conn)
			server.onClose(conn)
			So(server.sessions.Get(id), ShouldBeNil)
		})

		Convey("Test serve http", func() {

			Convey("Normal request", func() {
				check := make(chan bool)
				id := ""
				go func() {
					conn, _ := server.Accept()
					id = conn.Id()
					check <- true
					check <- true
					conn.Close()
					check <- true
				}()

				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t1")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusOK)

				<-check

				p.Set("sid", id)
				r, err = http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w = httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusOK)

				<-check
				<-check

				r, err = http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w = httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
			})

			Convey("Not allowed", func() {
				s, err := NewServer(nil)
				So(err, ShouldBeNil)
				s.SetAllowRequest(func(*http.Request) error {
					return errors.New("not allowed")
				})

				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t1")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				s.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "not allowed\n")
			})

			Convey("Wrong transport", func() {
				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "notexist")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "invalid transport\n")
			})

			Convey("Transport error", func() {
				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t3")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "transport t3 error\n")
			})

			Convey("Wrong session id", func() {
				check := make(chan bool)
				var id string
				var conn Conn
				go func() {
					conn, _ = server.Accept()
					id = conn.Id()
					check <- true
					check <- true
					conn.Close()
					check <- true
				}()

				p := make(url.Values)
				p.Set("EIO", fmt.Sprintf("%d", Protocol))
				p.Set("transport", "t1")
				p.Set("t", fmt.Sprintf("%d-0", time.Now().Unix()))

				r, err := http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w := httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusOK)

				<-check

				p.Set("sid", id+"abc")
				r, err = http.NewRequest("GET", "/?"+p.Encode(), bytes.NewBuffer(nil))
				So(err, ShouldBeNil)
				w = httptest.NewRecorder()

				server.ServeHTTP(w, r)
				So(w.Code, ShouldEqual, http.StatusBadRequest)
				So(w.Body.String(), ShouldEqual, "invalid sid\n")

				<-check
				<-check
			})

		})

	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"golang.org/x/net/html"
	"io"
	"mime"
	"net/http"
	"net/url"
	"strings"
)

func main() {
	server := http.Server{
		Addr:    ":8000",
		Handler: &myHandler{},
	}
	server.ListenAndServe()
}

type myHandler struct{}

func buildRootURLString(url *url.URL) string {
	return url.Scheme + "://" + url.Host
}

func normalizeUrl(targetHost *url.URL, url string) string {
	return "/" + buildRootURLString(targetHost) + url
}

func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	reqUrl := r.URL.String()
	reqUrl = reqUrl[1:]
	client := &http.Client{}
	if !strings.HasPrefix(reqUrl, "http") {
		if ref := r.Header.Get("Referer"); ref != "" {
            // FIXME: Security: diable cookie caching
            fmt.Println(ref)
			refUrl, err := url.Parse(ref)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			fmt.Print("Raw path ")
			fmt.Println(refUrl.Path)
			refUrl, err = url.Parse(refUrl.Path[1:])
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			fmt.Print("Patching url by ref to ")
			reqUrl = normalizeUrl(refUrl, "/"+reqUrl)[1:]
			fmt.Println(reqUrl)
		}
	}
	req, err := http.NewRequest(r.Method, reqUrl, nil)
	if err != nil {
		// FIXME
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	//req.Header = r.Header
	resp, err := client.Do(req)
	if err != nil {
		// FIXME
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()
	ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	switch ct {
	case "text/html":
		fmt.Println("html")
		root, err := html.Parse(resp.Body)
		if err != nil {
			fmt.Println(err)
			return
		}
		var f func(*html.Node)
		f = func(node *html.Node) {
			if node.Type == html.ElementNode {
				if node.Data == "form" || node.Data == "script" || node.Data == "link" || node.Data == "a" || node.Data == "img" {
					for i, attr := range node.Attr {
						if attr.Key == "href" || attr.Key == "src" || attr.Key == "action" {
							if strings.HasPrefix(attr.Val, "//") {
								node.Attr[i].Val = "/" + req.URL.Scheme + ":" + attr.Val
							} else if strings.HasPrefix(attr.Val, "http://") || strings.HasPrefix(attr.Val, "https://") {
								node.Attr[i].Val = "/" + attr.Val
							} else if strings.HasPrefix(attr.Val, "/") {
								node.Attr[i].Val = normalizeUrl(req.URL, attr.Val)
							}
						}
					}
				}
			}
			for c := node.FirstChild; c != nil; c = c.NextSibling {
				f(c)
			}
		}
		f(root)
		html.Render(w, root)
	default:
		w.Header().Set("Content-Type", req.Header.Get("Content-Type"))
		io.Copy(w, resp.Body)
	}
}
<commit_msg>ADD: logging and configuration by cli parameters<commit_after>package main

import (
	"flag"
	"github.com/Sirupsen/logrus"
	"golang.org/x/net/html"
	"io"
	"mime"
	"net/http"
	"net/url"
	"strings"
)

var mainLog = logrus.WithField("module", "main")

type LogLevelFlag struct {
    // flag.Value
    lvl logrus.Level
}

func (f LogLevelFlag) String() string {
    return f.lvl.String()
}
func (f LogLevelFlag) Set(val string) error {
    l, err := logrus.ParseLevel(val)
    if err != nil {
        f.lvl = l
    }
    return err
}

func main() {
	var (
		logLevel     LogLevelFlag = LogLevelFlag{logrus.DebugLevel}
		listenString string
	)

	flag.Var(&logLevel, "log.level", "possible values: debug, info, warning, error, fatal, panic")
	flag.StringVar(&listenString, "listen", ":8080", "net.Listen() string, e.g. addr:port")

	flag.Parse()
	logrus.SetLevel(logLevel.lvl)

	server := http.Server{
		Addr:    listenString,
		Handler: &myHandler{},
	}
    mainLog.Info("Startup")
	server.ListenAndServe()
}

type myHandler struct{}

func buildRootURLString(url *url.URL) string {
	return url.Scheme + "://" + url.Host
}

func normalizeUrl(targetHost *url.URL, url string) string {
	return "/" + buildRootURLString(targetHost) + url
}

func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	reqUrl := r.URL.String()
	reqUrl = reqUrl[1:]
	client := &http.Client{}
	if !strings.HasPrefix(reqUrl, "http") {
		if ref := r.Header.Get("Referer"); ref != "" {
			// FIXME: Security: diable cookie caching
            mainLog.Debug("Referer: ", ref)
			refUrl, err := url.Parse(ref)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			mainLog.Debug("Referer URL: ", refUrl.Path)
            refUrl, err = url.Parse(refUrl.Path[1:])
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			reqUrl = normalizeUrl(refUrl, "/"+reqUrl)[1:]
			mainLog.Debug("Patching url by ref to ", reqUrl)
		}
	}
	req, err := http.NewRequest(r.Method, reqUrl, nil)
	if err != nil {
		// FIXME
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	//req.Header = r.Header
	resp, err := client.Do(req)
	if err != nil {
		// FIXME
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()
	ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	switch ct {
	case "text/html":
		root, err := html.Parse(resp.Body)
		if err != nil {
			mainLog.Error(err)
			return
		}
		var f func(*html.Node)
		f = func(node *html.Node) {
			if node.Type == html.ElementNode {
				if node.Data == "form" || node.Data == "script" || node.Data == "link" || node.Data == "a" || node.Data == "img" {
					for i, attr := range node.Attr {
						if attr.Key == "href" || attr.Key == "src" || attr.Key == "action" {
							if strings.HasPrefix(attr.Val, "//") {
								node.Attr[i].Val = "/" + req.URL.Scheme + ":" + attr.Val
							} else if strings.HasPrefix(attr.Val, "http://") || strings.HasPrefix(attr.Val, "https://") {
								node.Attr[i].Val = "/" + attr.Val
							} else if strings.HasPrefix(attr.Val, "/") {
								node.Attr[i].Val = normalizeUrl(req.URL, attr.Val)
							}
						}
					}
				}
			}
			for c := node.FirstChild; c != nil; c = c.NextSibling {
				f(c)
			}
		}
		f(root)
		html.Render(w, root)
	default:
		w.Header().Set("Content-Type", req.Header.Get("Content-Type"))
		io.Copy(w, resp.Body)
	}
}
<|endoftext|>