<commit_before>/*
 * Copyright (c) 2013 Dave Collins <dave@davec.name>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package spew_test

import (
	"fmt"
	"github.com/davecgh/go-spew/spew"
)

type Flag int

const (
	flagOne Flag = iota
	flagTwo
)

var flagStrings = map[Flag]string{
	flagOne: "flagOne",
	flagTwo: "flagTwo",
}

func (f Flag) String() string {
	if s, ok := flagStrings[f]; ok {
		return s
	}
	return fmt.Sprintf("Unknown flag (%d)", int(f))
}

type Bar struct {
	flag Flag
	data uintptr
}

type Foo struct {
	unexportedField Bar
	ExportedField   map[interface{}]interface{}
}

// This example demonstrates how to use Dump to dump variables to stdout.
func ExampleDump() {
	// The following package level declarations are assumed for this example:
	/*
		type Flag int

		const (
			flagOne Flag = iota
			flagTwo
		)

		var flagStrings = map[Flag]string{
			flagOne: "flagOne",
			flagTwo: "flagTwo",
		}

		func (f Flag) String() string {
			if s, ok := flagStrings[f]; ok {
				return s
			}
			return fmt.Sprintf("Unknown flag (%d)", int(f))
		}

		type Bar struct {
			flag Flag
			data uintptr
		}

		type Foo struct {
			unexportedField Bar
			ExportedField   map[interface{}]interface{}
		}
	*/

	// Setup some sample data structures for the example.
	bar := Bar{Flag(flagTwo), uintptr(0)}
	s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
	f := Flag(5)

	// Dump!
	spew.Dump(s1, f)

	// Output:
	// (spew_test.Foo) {
	//  unexportedField: (spew_test.Bar) {
	//   flag: (spew_test.Flag) flagTwo,
	//   data: (uintptr) <nil>
	//  },
	//  ExportedField: (map[interface {}]interface {}) {
	//   (string) "one": (bool) true
	//  }
	// }
	// (spew_test.Flag) Unknown flag (5)
	//
}

// This example demonstrates how to use Printf to display a variable with a
// format string and inline formatting.
func ExamplePrintf() {
	// Create a double pointer to a uint 8.
	ui8 := uint8(5)
	pui8 := &ui8
	ppui8 := &pui8

	// Create a circular data type.
	type circular struct {
		ui8 uint8
		c   *circular
	}
	c := circular{ui8: 1}
	c.c = &c

	// Print!
	spew.Printf("ppui8: %v\n", ppui8)
	spew.Printf("circular: %v\n", c)

	// Output:
	// ppui8: <**>5
	// circular: {1 <*>{1 <*><shown>}}
}

// This example demonstrates how to use a ConfigState.
func ExampleConfigState() {
	// Modify the indent level of the ConfigState only.  The global
	// configuration is not modified.
	scs := spew.ConfigState{Indent: "\t"}

	// Output using the ConfigState instance.
	v := map[string]int{"one": 1}
	scs.Printf("v: %v\n", v)
	scs.Dump(v)

	// Output:
	// v: map[one:1]
	// (map[string]int) {
	// 	(string) "one": (int) 1
	// }
}

// This example demonstrates how to use ConfigState.Dump to dump variables to
// stdout
func ExampleConfigState_Dump() {
	// See the top-level Dump example for details on the types used in this
	// example.

	// Create two ConfigState instances with different indentation.
	scs := spew.ConfigState{Indent: "\t"}
	scs2 := spew.ConfigState{Indent: " "}

	// Setup some sample data structures for the example.
	bar := Bar{Flag(flagTwo), uintptr(0)}
	s1 := Foo{bar, map[interface{}]interface{}{"one": true}}

	// Dump using the ConfigState instances.
	scs.Dump(s1)
	scs2.Dump(s1)

	// Output:
	// (spew_test.Foo) {
	// 	unexportedField: (spew_test.Bar) {
	// 		flag: (spew_test.Flag) flagTwo,
	// 		data: (uintptr) <nil>
	// 	},
	// 	ExportedField: (map[interface {}]interface {}) {
	//		(string) "one": (bool) true
	// 	}
	// }
	// (spew_test.Foo) {
	//  unexportedField: (spew_test.Bar) {
	//   flag: (spew_test.Flag) flagTwo,
	//   data: (uintptr) <nil>
	//  },
	//  ExportedField: (map[interface {}]interface {}) {
	//   (string) "one": (bool) true
	//  }
	// }
	//
}

// This example demonstrates how to use ConfigState.Printf to display a variable
// with a format string and inline formatting.
func ExampleConfigState_Printf() {
	// See the top-level Dump example for details on the types used in this
	// example.

	// Create two ConfigState instances and modify the method handling of the
	// first ConfigState only.
	scs := spew.NewDefaultConfig()
	scs2 := spew.NewDefaultConfig()
	scs.DisableMethods = true

	// Alternatively
	// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
	// scs2 := spew.ConfigState{Indent: " "}

	// This is of type Flag which implements a Stringer and has raw value 1.
	f := flagTwo

	// Dump using the ConfigState instances.
	scs.Printf("f: %v\n", f)
	scs2.Printf("f: %v\n", f)

	// Output:
	// f: 1
	// f: flagTwo
}
<commit_msg>Add byte slice to Dump example.<commit_after>/*
 * Copyright (c) 2013 Dave Collins <dave@davec.name>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package spew_test

import (
	"fmt"
	"github.com/davecgh/go-spew/spew"
)

type Flag int

const (
	flagOne Flag = iota
	flagTwo
)

var flagStrings = map[Flag]string{
	flagOne: "flagOne",
	flagTwo: "flagTwo",
}

func (f Flag) String() string {
	if s, ok := flagStrings[f]; ok {
		return s
	}
	return fmt.Sprintf("Unknown flag (%d)", int(f))
}

type Bar struct {
	flag Flag
	data uintptr
}

type Foo struct {
	unexportedField Bar
	ExportedField   map[interface{}]interface{}
}

// This example demonstrates how to use Dump to dump variables to stdout.
func ExampleDump() {
	// The following package level declarations are assumed for this example:
	/*
		type Flag int

		const (
			flagOne Flag = iota
			flagTwo
		)

		var flagStrings = map[Flag]string{
			flagOne: "flagOne",
			flagTwo: "flagTwo",
		}

		func (f Flag) String() string {
			if s, ok := flagStrings[f]; ok {
				return s
			}
			return fmt.Sprintf("Unknown flag (%d)", int(f))
		}

		type Bar struct {
			flag Flag
			data uintptr
		}

		type Foo struct {
			unexportedField Bar
			ExportedField   map[interface{}]interface{}
		}
	*/

	// Setup some sample data structures for the example.
	bar := Bar{Flag(flagTwo), uintptr(0)}
	s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
	f := Flag(5)
	b := []byte{
		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
		0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
		0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
		0x31, 0x32,
	}

	// Dump!
	spew.Dump(s1, f, b)

	// Output:
	// (spew_test.Foo) {
	//  unexportedField: (spew_test.Bar) {
	//   flag: (spew_test.Flag) flagTwo,
	//   data: (uintptr) <nil>
	//  },
	//  ExportedField: (map[interface {}]interface {}) {
	//   (string) "one": (bool) true
	//  }
	// }
	// (spew_test.Flag) Unknown flag (5)
	// ([]uint8) {
	//  00000000  11 12 13 14 15 16 17 18  19 1a 1b 1c 1d 1e 1f 20  |............... |
	//  00000010  21 22 23 24 25 26 27 28  29 2a 2b 2c 2d 2e 2f 30  |!"#$%&'()*+,-./0|
	//  00000020  31 32                                             |12|
	// }
	//
}

// This example demonstrates how to use Printf to display a variable with a
// format string and inline formatting.
func ExamplePrintf() {
	// Create a double pointer to a uint 8.
	ui8 := uint8(5)
	pui8 := &ui8
	ppui8 := &pui8

	// Create a circular data type.
	type circular struct {
		ui8 uint8
		c   *circular
	}
	c := circular{ui8: 1}
	c.c = &c

	// Print!
	spew.Printf("ppui8: %v\n", ppui8)
	spew.Printf("circular: %v\n", c)

	// Output:
	// ppui8: <**>5
	// circular: {1 <*>{1 <*><shown>}}
}

// This example demonstrates how to use a ConfigState.
func ExampleConfigState() {
	// Modify the indent level of the ConfigState only.  The global
	// configuration is not modified.
	scs := spew.ConfigState{Indent: "\t"}

	// Output using the ConfigState instance.
	v := map[string]int{"one": 1}
	scs.Printf("v: %v\n", v)
	scs.Dump(v)

	// Output:
	// v: map[one:1]
	// (map[string]int) {
	// 	(string) "one": (int) 1
	// }
}

// This example demonstrates how to use ConfigState.Dump to dump variables to
// stdout
func ExampleConfigState_Dump() {
	// See the top-level Dump example for details on the types used in this
	// example.

	// Create two ConfigState instances with different indentation.
	scs := spew.ConfigState{Indent: "\t"}
	scs2 := spew.ConfigState{Indent: " "}

	// Setup some sample data structures for the example.
	bar := Bar{Flag(flagTwo), uintptr(0)}
	s1 := Foo{bar, map[interface{}]interface{}{"one": true}}

	// Dump using the ConfigState instances.
	scs.Dump(s1)
	scs2.Dump(s1)

	// Output:
	// (spew_test.Foo) {
	// 	unexportedField: (spew_test.Bar) {
	// 		flag: (spew_test.Flag) flagTwo,
	// 		data: (uintptr) <nil>
	// 	},
	// 	ExportedField: (map[interface {}]interface {}) {
	//		(string) "one": (bool) true
	// 	}
	// }
	// (spew_test.Foo) {
	//  unexportedField: (spew_test.Bar) {
	//   flag: (spew_test.Flag) flagTwo,
	//   data: (uintptr) <nil>
	//  },
	//  ExportedField: (map[interface {}]interface {}) {
	//   (string) "one": (bool) true
	//  }
	// }
	//
}

// This example demonstrates how to use ConfigState.Printf to display a variable
// with a format string and inline formatting.
func ExampleConfigState_Printf() {
	// See the top-level Dump example for details on the types used in this
	// example.

	// Create two ConfigState instances and modify the method handling of the
	// first ConfigState only.
	scs := spew.NewDefaultConfig()
	scs2 := spew.NewDefaultConfig()
	scs.DisableMethods = true

	// Alternatively
	// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
	// scs2 := spew.ConfigState{Indent: " "}

	// This is of type Flag which implements a Stringer and has raw value 1.
	f := flagTwo

	// Dump using the ConfigState instances.
	scs.Printf("f: %v\n", f)
	scs2.Printf("f: %v\n", f)

	// Output:
	// f: 1
	// f: flagTwo
}
<|endoftext|>


<commit_before>// Package application contains the server-side application code.
package application

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

const cookieName = "reCAPTCHA"

var Cookie = &http.Cookie{Name: cookieName, Value: "1", HttpOnly: true, Path: "/"}

type RecaptchaMiddleware struct {
	Configuration Configuration
}

func (middle RecaptchaMiddleware) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		recaptchaCookie, _ := r.Cookie(cookieName)

		if recaptchaCookie == nil {
			challenge := r.URL.Query().Get("c")

			catpcha := recaptcha.Recaptcha{PrivateKey: middle.Configuration.Recaptcha.PrivateKey}
			recaptchaResponse, _ := catpcha.Verify(challenge, "127.0.0.1")

			if recaptchaResponse.Success == false {
				w.WriteHeader(403)
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}
<commit_msg>Added documentation to the middleware file<commit_after>// Package application contains the server-side application code.
package application

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

// cookieName specifies the cookie name that will hold the verification of wether the reCAPTCHA passed or not.
const cookieName = "reCAPTCHA"

// Cookie is a single instance of the reCAPTCHA cookie to avoid instantiation
// FIXME We will want to set an expire date so clearly this won't work :P
var Cookie = &http.Cookie{Name: cookieName, Value: "1", HttpOnly: true, Path: "/"}

// RecaptchaMiddleware will allow us to chain the reCAPTCHA validation into the request processing
type RecaptchaMiddleware struct {
	// Configuration holds the application configuration. In this context only the reCAPTCHA configuration is used
	Configuration Configuration
}

// Middleware will perform the validation of reCAPTCHA challenge that the user should solve before passing
// control to the actual function that processes the full request and returns the result.
// TODO Don't hardcode the IPAddress when verifying the challenge
func (middle RecaptchaMiddleware) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		recaptchaCookie, _ := r.Cookie(cookieName)

		if recaptchaCookie == nil {
			challenge := r.URL.Query().Get("c")

			catpcha := recaptcha.Recaptcha{PrivateKey: middle.Configuration.Recaptcha.PrivateKey}
			recaptchaResponse, _ := catpcha.Verify(challenge, "127.0.0.1")

			if recaptchaResponse.Success == false {
				w.WriteHeader(403)
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>// This program provides a functionality similar to what `pgrep` does un Linux:
// it lists all the processes whose name matches a given regexp.
package main

import (
	"flag"
	"fmt"
	"github.com/mozilla/masche/listlibs"
	"log"
	"regexp"
)

var reg = flag.String("r", ".*", "Regular Expression to use.")

func main() {
	flag.Parse()

	r, err := regexp.Compile(*reg)
	if err != nil {
		log.Fatal(err)
	}

	ps, _, err := listlibs.GrepProcesses(r)
	if err != nil {
		log.Fatal(err)
	}

	for _, p := range ps {
		fmt.Printf("Process: %s\nPid: %d\n\n", p.Filename, p.Pid)
	}
}
<commit_msg>Update pgrep example to work with Process library<commit_after>// This program provides a functionality similar to what `pgrep` does un Linux:
// it lists all the processes whose name matches a given regexp.
package main

import (
	"flag"
	"fmt"
	"github.com/mozilla/masche/process"
	"log"
	"regexp"
)

var reg = flag.String("r", ".*", "Regular Expression to use.")

func main() {
	flag.Parse()

	r, err := regexp.Compile(*reg)
	if err != nil {
		log.Fatal(err)
	}

	ps, hard, soft := process.OpenByName(r)
	if hard != nil {
		log.Fatal(err)
	}
	if soft != nil {
		for _, err := range soft {
			log.Println(err)
		}
	}

	for _, p := range ps {
		name, hard, soft := p.Name()
		if hard != nil {
			log.Fatal(err)
		}
		if soft != nil {
			for _, err := range soft {
				log.Println(err)
			}
		}
		fmt.Printf("Process: %s\nPid: %d\n\n", name, p.Pid())
	}
}
<|endoftext|>


<commit_before>package clockwork

import (
	"reflect"
	"testing"
	"time"
)

func TestFakeClockAfter(t *testing.T) {
	fc := &fakeClock{}

	zero := fc.After(0)
	select {
	case <-zero:
	default:
		t.Errorf("zero did not return!")
	}
	one := fc.After(1)
	two := fc.After(2)
	six := fc.After(6)
	ten := fc.After(10)
	fc.Advance(1)
	select {
	case <-one:
	default:
		t.Errorf("one did not return!")
	}
	select {
	case <-two:
		t.Errorf("two returned prematurely!")
	case <-six:
		t.Errorf("six returned prematurely!")
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(1)
	select {
	case <-two:
	default:
		t.Errorf("two did not return!")
	}
	select {
	case <-six:
		t.Errorf("six returned prematurely!")
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(1)
	select {
	case <-six:
		t.Errorf("six returned prematurely!")
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(3)
	select {
	case <-six:
	default:
		t.Errorf("six did not return!")
	}
	select {
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(100)
	select {
	case <-ten:
	default:
		t.Errorf("ten did not return!")
	}
}

func TestNotifyBlockers(t *testing.T) {
	b1 := &blocker{1, make(chan struct{})}
	b2 := &blocker{2, make(chan struct{})}
	b3 := &blocker{5, make(chan struct{})}
	b4 := &blocker{10, make(chan struct{})}
	b5 := &blocker{10, make(chan struct{})}
	bs := []*blocker{b1, b2, b3, b4, b5}
	bs1 := notifyBlockers(bs, 2)
	if n := len(bs1); n != 4 {
		t.Fatalf("got %d blockers, want %d", n, 4)
	}
	select {
	case <-b2.ch:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for channel close!")
	}
	bs2 := notifyBlockers(bs1, 10)
	if n := len(bs2); n != 2 {
		t.Fatalf("got %d blockers, want %d", n, 2)
	}
	select {
	case <-b4.ch:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for channel close!")
	}
	select {
	case <-b5.ch:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for channel close!")
	}
}

func TestNewFakeClock(t *testing.T) {
	fc := NewFakeClock()
	now := fc.Now()
	if now.IsZero() {
		t.Fatalf("fakeClock.Now() fulfills IsZero")
	}

	now2 := fc.Now()
	if !reflect.DeepEqual(now, now2) {
		t.Fatalf("fakeClock.Now() returned different value: want=%#v got=%#v", now, now2)
	}
}

func TestNewFakeClockAt(t *testing.T) {
	t1 := time.Date(1999, time.February, 3, 4, 5, 6, 7, time.UTC)
	fc := NewFakeClockAt(t1)
	now := fc.Now()
	if !reflect.DeepEqual(now, t1) {
		t.Fatalf("fakeClock.Now() returned unexpected non-initialised value: want=%#v, got %#v", t1, now)
	}
}

func TestFakeClockSince(t *testing.T) {
	fc := NewFakeClock()
	now := fc.Now()
	elapsedTime := time.Second
	fc.Advance(elapsedTime)
	if fc.Since(now) != elapsedTime {
		t.Fatalf("fakeClock.Since() returned unexpected duration, got: %d, want: %d", fc.Since(now), elapsedTime)
	}
}
<commit_msg>Added failing test<commit_after>package clockwork

import (
	"reflect"
	"testing"
	"time"
)

func TestFakeClockAfter(t *testing.T) {
	fc := &fakeClock{}

	neg := fc.After(-1)
	select {
	case <-neg:
	default:
		t.Errorf("negative did not return!")
	}

	zero := fc.After(0)
	select {
	case <-zero:
	default:
		t.Errorf("zero did not return!")
	}
	one := fc.After(1)
	two := fc.After(2)
	six := fc.After(6)
	ten := fc.After(10)
	fc.Advance(1)
	select {
	case <-one:
	default:
		t.Errorf("one did not return!")
	}
	select {
	case <-two:
		t.Errorf("two returned prematurely!")
	case <-six:
		t.Errorf("six returned prematurely!")
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(1)
	select {
	case <-two:
	default:
		t.Errorf("two did not return!")
	}
	select {
	case <-six:
		t.Errorf("six returned prematurely!")
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(1)
	select {
	case <-six:
		t.Errorf("six returned prematurely!")
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(3)
	select {
	case <-six:
	default:
		t.Errorf("six did not return!")
	}
	select {
	case <-ten:
		t.Errorf("ten returned prematurely!")
	default:
	}
	fc.Advance(100)
	select {
	case <-ten:
	default:
		t.Errorf("ten did not return!")
	}
}

func TestNotifyBlockers(t *testing.T) {
	b1 := &blocker{1, make(chan struct{})}
	b2 := &blocker{2, make(chan struct{})}
	b3 := &blocker{5, make(chan struct{})}
	b4 := &blocker{10, make(chan struct{})}
	b5 := &blocker{10, make(chan struct{})}
	bs := []*blocker{b1, b2, b3, b4, b5}
	bs1 := notifyBlockers(bs, 2)
	if n := len(bs1); n != 4 {
		t.Fatalf("got %d blockers, want %d", n, 4)
	}
	select {
	case <-b2.ch:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for channel close!")
	}
	bs2 := notifyBlockers(bs1, 10)
	if n := len(bs2); n != 2 {
		t.Fatalf("got %d blockers, want %d", n, 2)
	}
	select {
	case <-b4.ch:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for channel close!")
	}
	select {
	case <-b5.ch:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for channel close!")
	}
}

func TestNewFakeClock(t *testing.T) {
	fc := NewFakeClock()
	now := fc.Now()
	if now.IsZero() {
		t.Fatalf("fakeClock.Now() fulfills IsZero")
	}

	now2 := fc.Now()
	if !reflect.DeepEqual(now, now2) {
		t.Fatalf("fakeClock.Now() returned different value: want=%#v got=%#v", now, now2)
	}
}

func TestNewFakeClockAt(t *testing.T) {
	t1 := time.Date(1999, time.February, 3, 4, 5, 6, 7, time.UTC)
	fc := NewFakeClockAt(t1)
	now := fc.Now()
	if !reflect.DeepEqual(now, t1) {
		t.Fatalf("fakeClock.Now() returned unexpected non-initialised value: want=%#v, got %#v", t1, now)
	}
}

func TestFakeClockSince(t *testing.T) {
	fc := NewFakeClock()
	now := fc.Now()
	elapsedTime := time.Second
	fc.Advance(elapsedTime)
	if fc.Since(now) != elapsedTime {
		t.Fatalf("fakeClock.Since() returned unexpected duration, got: %d, want: %d", fc.Since(now), elapsedTime)
	}
}
<|endoftext|>


<commit_before>package clocky

import (
	"io"
	"strconv"
	"strings"
	"template"
	"time"
	"xml"

	"appengine"
	"appengine/memcache"
)

type prediction struct {
	EpochTime   int64 `xml:"attr"`
	IsDeparture bool  `xml:"attr"`
}

func (p prediction) ToString() string {
	s := p.EpochTime/1000 - time.Seconds()
	if s < 60 {
		return "now"
	}
	result := strconv.Itoa64(s / 60)
	if s < 600 && s%60 >= 30 {
		result += "½"
	}
	return result
}

var BoringMuniMessages = map[string]bool{
	"PROOF OF PAYMENT\nis required when\non a Muni vehicle\nor in a station.": true,
	"sfmta.com or 3 1 1\nfor Muni info":                                       true,
}

func NextBus(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "nextbus")
	if err != nil {
		c.Errorf("%s", err)
		return
	}

	data := struct {
		Predictions []struct {
			RouteTag  string `xml:"attr"`
			Direction []struct {
				Title      string `xml:"attr"`
				Prediction []prediction
			}
			Message []struct {
				Text string `xml:"attr"`
			}
		}
	}{}
	if err := xml.Unmarshal(strings.NewReader(string(item.Value)), &data); err != nil {
		c.Errorf("%s", err)
		return
	}

	for _, p := range data.Predictions {
		for _, d := range p.Direction {
			io.WriteString(w, `<div class=bus><div class=route>`)
			template.HTMLEscape(w, []byte(p.RouteTag))
			io.WriteString(w, ` <span class=smaller>`)
			title := d.Title
			title = strings.Replace(title, "Inbound", "inbound", -1)
			title = strings.Replace(title, "Outbound", "outbound", -1)
			title = strings.Replace(title, "Downtown", "downtown", -1)
			title = strings.Replace(title, "Disrict", "District", -1)
			template.HTMLEscape(w, []byte(title))
			io.WriteString(w, `</span></div><div>`)
			for i, pp := range d.Prediction {
				if pp.IsDeparture && i == 0 {
					io.WriteString(w, "departs ")
				}
				s := pp.ToString()
				io.WriteString(w, s)
				if i == len(d.Prediction)-1 {
					if s == "1" {
						io.WriteString(w, ` minute`)
					} else {
						io.WriteString(w, ` minutes`)
					}
				} else {
					io.WriteString(w, `, `)
				}
			}
			io.WriteString(w, `</div>`)
		}
		for _, m := range p.Message {
			if BoringMuniMessages[m.Text] {
				continue
			}
			io.WriteString(w, `<div class=munimessage>`)
			template.HTMLEscape(w, []byte(m.Text))
			io.WriteString(w, `</div>`)
		}
	}
}
<commit_msg>Suppress boring message about watching your valuables.<commit_after>package clocky

import (
	"io"
	"strconv"
	"strings"
	"template"
	"time"
	"xml"

	"appengine"
	"appengine/memcache"
)

type prediction struct {
	EpochTime   int64 `xml:"attr"`
	IsDeparture bool  `xml:"attr"`
}

func (p prediction) ToString() string {
	s := p.EpochTime/1000 - time.Seconds()
	if s < 60 {
		return "now"
	}
	result := strconv.Itoa64(s / 60)
	if s < 600 && s%60 >= 30 {
		result += "½"
	}
	return result
}

var BoringMuniMessages = map[string]bool{
	"On board, watch\nyour valuables.":                                        true,
	"PROOF OF PAYMENT\nis required when\non a Muni vehicle\nor in a station.": true,
	"sfmta.com or 3 1 1\nfor Muni info":                                       true,
}

func NextBus(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "nextbus")
	if err != nil {
		c.Errorf("%s", err)
		return
	}

	data := struct {
		Predictions []struct {
			RouteTag  string `xml:"attr"`
			Direction []struct {
				Title      string `xml:"attr"`
				Prediction []prediction
			}
			Message []struct {
				Text string `xml:"attr"`
			}
		}
	}{}
	if err := xml.Unmarshal(strings.NewReader(string(item.Value)), &data); err != nil {
		c.Errorf("%s", err)
		return
	}

	for _, p := range data.Predictions {
		for _, d := range p.Direction {
			io.WriteString(w, `<div class=bus><div class=route>`)
			template.HTMLEscape(w, []byte(p.RouteTag))
			io.WriteString(w, ` <span class=smaller>`)
			title := d.Title
			title = strings.Replace(title, "Inbound", "inbound", -1)
			title = strings.Replace(title, "Outbound", "outbound", -1)
			title = strings.Replace(title, "Downtown", "downtown", -1)
			title = strings.Replace(title, "Disrict", "District", -1)
			template.HTMLEscape(w, []byte(title))
			io.WriteString(w, `</span></div><div>`)
			for i, pp := range d.Prediction {
				if pp.IsDeparture && i == 0 {
					io.WriteString(w, "departs ")
				}
				s := pp.ToString()
				io.WriteString(w, s)
				if i == len(d.Prediction)-1 {
					if s == "1" {
						io.WriteString(w, ` minute`)
					} else {
						io.WriteString(w, ` minutes`)
					}
				} else {
					io.WriteString(w, `, `)
				}
			}
			io.WriteString(w, `</div>`)
		}
		for _, m := range p.Message {
			if BoringMuniMessages[m.Text] {
				continue
			}
			io.WriteString(w, `<div class=munimessage>`)
			template.HTMLEscape(w, []byte(m.Text))
			io.WriteString(w, `</div>`)
		}
	}
}
<|endoftext|>


<commit_before>package gcsx

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"time"

	"cloud.google.com/go/storage"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"google.golang.org/api/option"
)

type storageClient struct {
	client *storage.Client
}

type storageClientConfig struct {
	disableHTTP2        bool
	maxConnsPerHost     int
	maxIdleConnsPerHost int
	tokenSrc            oauth2.TokenSource
}

// GetStorageClientHandle returns the handle of Go storage client containing
// customized http client. We can configure the http client using the
// storageClientConfig parameter.
func GetStorageClientHandle(ctx context.Context,
		scConfig storageClientConfig) (sh *storageClient, err error) {
	var transport *http.Transport
	// Disabling the http2 makes the client more performant.
	if scConfig.disableHTTP2 {
		transport = &http.Transport{
			MaxConnsPerHost:     scConfig.maxConnsPerHost,
			MaxIdleConnsPerHost: scConfig.maxIdleConnsPerHost,
			// This disables HTTP/2 in transport.
			TLSNextProto: make(
				map[string]func(string, *tls.Conn) http.RoundTripper,
			),
		}
	} else {
		// For http2, change in MaxConnsPerHost doesn't affect the performance.
		transport = &http.Transport{
			DisableKeepAlives: true,
			MaxConnsPerHost:   scConfig.maxConnsPerHost,
			ForceAttemptHTTP2: true,
		}
	}

	// Custom http client for Go Client.
	httpClient := &http.Client{Transport: &oauth2.Transport{
		Base:   transport,
		Source: scConfig.tokenSrc,
	},
		Timeout: 800 * time.Millisecond,
	}

	var sc *storage.Client
	sc, err = storage.NewClient(ctx, option.WithHTTPClient(httpClient))
	if err != nil {
		err = fmt.Errorf("go storage client creation: %w", err)
		return
	}

	sh = &storageClient{sc}
	return
}
<commit_msg>Resolving linting issue<commit_after>package gcsx

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"time"

	"cloud.google.com/go/storage"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"google.golang.org/api/option"
)

type storageClient struct {
	client *storage.Client
}

type storageClientConfig struct {
	disableHTTP2        bool
	maxConnsPerHost     int
	maxIdleConnsPerHost int
	tokenSrc            oauth2.TokenSource
}

// GetStorageClientHandle returns the handle of Go storage client containing
// customized http client. We can configure the http client using the
// storageClientConfig parameter.
func GetStorageClientHandle(ctx context.Context,
	scConfig storageClientConfig) (sh *storageClient, err error) {
	var transport *http.Transport
	// Disabling the http2 makes the client more performant.
	if scConfig.disableHTTP2 {
		transport = &http.Transport{
			MaxConnsPerHost:     scConfig.maxConnsPerHost,
			MaxIdleConnsPerHost: scConfig.maxIdleConnsPerHost,
			// This disables HTTP/2 in transport.
			TLSNextProto: make(
				map[string]func(string, *tls.Conn) http.RoundTripper,
			),
		}
	} else {
		// For http2, change in MaxConnsPerHost doesn't affect the performance.
		transport = &http.Transport{
			DisableKeepAlives: true,
			MaxConnsPerHost:   scConfig.maxConnsPerHost,
			ForceAttemptHTTP2: true,
		}
	}

	// Custom http client for Go Client.
	httpClient := &http.Client{Transport: &oauth2.Transport{
		Base:   transport,
		Source: scConfig.tokenSrc,
	},
		Timeout: 800 * time.Millisecond,
	}

	var sc *storage.Client
	sc, err = storage.NewClient(ctx, option.WithHTTPClient(httpClient))
	if err != nil {
		err = fmt.Errorf("go storage client creation: %w", err)
		return
	}

	sh = &storageClient{sc}
	return
}
<|endoftext|>


<commit_before>package github

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"

	"github.com/bradleyfalzon/gopherci/internal/analyser"
	"github.com/google/go-github/github"
	"github.com/pkg/errors"
)

type Installation struct {
	client *github.Client
}

func (g *GitHub) NewInstallation(installationID int) (*Installation, error) {

	// TODO reuse installations, so we maintain rate limit state between webhooks
	installation, err := g.db.GetGHInstallation(installationID)
	if err != nil {
		return nil, err
	}
	if installation == nil {
		return nil, nil
	}
	if !installation.IsEnabled() {
		log.Printf("ignoring disabled installation: %+v", installation)
		return nil, nil
	}

	log.Printf("found installation: %+v", installation)
	itr, err := g.newInstallationTransport(installation.InstallationID)
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("could not initialise transport for installation id %v", installation.InstallationID))
	}
	client := github.NewClient(&http.Client{Transport: itr})

	// Allow overwriting of baseURL for tests
	if client.BaseURL, err = url.Parse(g.baseURL); err != nil {
		return nil, err
	}

	return &Installation{client: client}, nil
}

// StatusState is the state of a GitHub Status API as defined in
// https://developer.github.com/v3/repos/statuses/
type StatusState string

const (
	StatusStatePending StatusState = "pending"
	StatusStateSuccess StatusState = "success"
	StatusStateError   StatusState = "error"
	StatusStateFailure StatusState = "failure"
)

// SetStatus sets the CI Status API
func (i *Installation) SetStatus(statusURL string, status StatusState, description string) error {
	s := struct {
		State       string `json:"state,omitempty"`
		TargetURL   string `json:"target_url,omitempty"`
		Description string `json:"description,omitempty"`
		Context     string `json:"context,omitempty"`
	}{
		string(status), "", description, "ci/gopherci",
	}
	log.Printf("status: %#v", status)

	js, err := json.Marshal(&s)
	if err != nil {
		return errors.Wrap(err, "could not marshal status")
	}

	req, err := http.NewRequest("POST", statusURL, bytes.NewBuffer(js))
	if err != nil {
		return err
	}
	resp, err := i.client.Do(req, nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("received status code %v", resp.StatusCode)
	}
	return nil
}

// maxIssueComments is the maximum number of comments that will be written
// on a pull request by writeissues. a pr may have more comments written if
// writeissues is called multiple times, such is multiple syncronise events.
const maxIssueComments = 10

// WriteIssues takes a slice of issues and creates a pull request comment for
// each issue on a given owner, repo, pr and commit hash. Returns on the first
// error encountered.
func (i *Installation) WriteIssues(owner, repo string, prNumber int, commit string, issues []analyser.Issue) (suppressed int, err error) {
	// TODO make this idempotent, so don't post the same issue twice
	// which may occur when we support additional commits to a PR (synchronize
	// api event)
	for n, issue := range issues {
		if n >= maxIssueComments {
			suppressed = len(issues) - maxIssueComments
			break
		}
		comment := &github.PullRequestComment{
			Body:     github.String(issue.Issue),
			CommitID: github.String(commit),
			Path:     github.String(issue.File),
			Position: github.Int(issue.HunkPos),
		}
		_, resp, err := i.client.PullRequests.CreateComment(owner, repo, prNumber, comment)
		if err != nil {
			return suppressed, errors.Wrapf(err, "github api response rate: %v", resp.Rate)
		}
	}
	return suppressed, nil
}
<commit_msg>Remove comment about idempotent WriteIssues<commit_after>package github

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"

	"github.com/bradleyfalzon/gopherci/internal/analyser"
	"github.com/google/go-github/github"
	"github.com/pkg/errors"
)

type Installation struct {
	client *github.Client
}

func (g *GitHub) NewInstallation(installationID int) (*Installation, error) {

	// TODO reuse installations, so we maintain rate limit state between webhooks
	installation, err := g.db.GetGHInstallation(installationID)
	if err != nil {
		return nil, err
	}
	if installation == nil {
		return nil, nil
	}
	if !installation.IsEnabled() {
		log.Printf("ignoring disabled installation: %+v", installation)
		return nil, nil
	}

	log.Printf("found installation: %+v", installation)
	itr, err := g.newInstallationTransport(installation.InstallationID)
	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("could not initialise transport for installation id %v", installation.InstallationID))
	}
	client := github.NewClient(&http.Client{Transport: itr})

	// Allow overwriting of baseURL for tests
	if client.BaseURL, err = url.Parse(g.baseURL); err != nil {
		return nil, err
	}

	return &Installation{client: client}, nil
}

// StatusState is the state of a GitHub Status API as defined in
// https://developer.github.com/v3/repos/statuses/
type StatusState string

const (
	StatusStatePending StatusState = "pending"
	StatusStateSuccess StatusState = "success"
	StatusStateError   StatusState = "error"
	StatusStateFailure StatusState = "failure"
)

// SetStatus sets the CI Status API
func (i *Installation) SetStatus(statusURL string, status StatusState, description string) error {
	s := struct {
		State       string `json:"state,omitempty"`
		TargetURL   string `json:"target_url,omitempty"`
		Description string `json:"description,omitempty"`
		Context     string `json:"context,omitempty"`
	}{
		string(status), "", description, "ci/gopherci",
	}
	log.Printf("status: %#v", status)

	js, err := json.Marshal(&s)
	if err != nil {
		return errors.Wrap(err, "could not marshal status")
	}

	req, err := http.NewRequest("POST", statusURL, bytes.NewBuffer(js))
	if err != nil {
		return err
	}
	resp, err := i.client.Do(req, nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("received status code %v", resp.StatusCode)
	}
	return nil
}

// maxIssueComments is the maximum number of comments that will be written
// on a pull request by writeissues. a pr may have more comments written if
// writeissues is called multiple times, such is multiple syncronise events.
const maxIssueComments = 10

// WriteIssues takes a slice of issues and creates a pull request comment for
// each issue on a given owner, repo, pr and commit hash. Returns on the first
// error encountered.
func (i *Installation) WriteIssues(owner, repo string, prNumber int, commit string, issues []analyser.Issue) (suppressed int, err error) {
	for n, issue := range issues {
		if n >= maxIssueComments {
			suppressed = len(issues) - maxIssueComments
			break
		}
		comment := &github.PullRequestComment{
			Body:     github.String(issue.Issue),
			CommitID: github.String(commit),
			Path:     github.String(issue.File),
			Position: github.Int(issue.HunkPos),
		}
		_, resp, err := i.client.PullRequests.CreateComment(owner, repo, prNumber, comment)
		if err != nil {
			return suppressed, errors.Wrapf(err, "github api response rate: %v", resp.Rate)
		}
	}
	return suppressed, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go 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 impl_test

import (
	"fmt"
	"testing"

	"github.com/golang/protobuf/proto"
	cmp "github.com/google/go-cmp/cmp"
	testpb "google.golang.org/protobuf/internal/testprotos/test"
	pref "google.golang.org/protobuf/reflect/protoreflect"
)

func TestExtensionType(t *testing.T) {
	cmpOpts := cmp.Options{
		cmp.Comparer(func(x, y proto.Message) bool {
			return proto.Equal(x, y)
		}),
	}
	for _, test := range []struct {
		xt    pref.ExtensionType
		value interface{}
	}{
		{
			xt:    testpb.E_OptionalInt32Extension,
			value: int32(0),
		},
		{
			xt:    testpb.E_OptionalInt64Extension,
			value: int64(0),
		},
		{
			xt:    testpb.E_OptionalUint32Extension,
			value: uint32(0),
		},
		{
			xt:    testpb.E_OptionalUint64Extension,
			value: uint64(0),
		},
		{
			xt:    testpb.E_OptionalFloatExtension,
			value: float32(0),
		},
		{
			xt:    testpb.E_OptionalDoubleExtension,
			value: float64(0),
		},
		{
			xt:    testpb.E_OptionalBoolExtension,
			value: true,
		},
		{
			xt:    testpb.E_OptionalStringExtension,
			value: "",
		},
		{
			xt:    testpb.E_OptionalBytesExtension,
			value: []byte{},
		},
		{
			xt:    testpb.E_OptionalNestedMessageExtension,
			value: &testpb.TestAllTypes_NestedMessage{},
		},
		{
			xt:    testpb.E_OptionalNestedEnumExtension,
			value: testpb.TestAllTypes_FOO,
		},
		{
			xt:    testpb.E_RepeatedInt32Extension,
			value: []int32{0},
		},
		{
			xt:    testpb.E_RepeatedInt64Extension,
			value: []int64{0},
		},
		{
			xt:    testpb.E_RepeatedUint32Extension,
			value: []uint32{0},
		},
		{
			xt:    testpb.E_RepeatedUint64Extension,
			value: []uint64{0},
		},
		{
			xt:    testpb.E_RepeatedFloatExtension,
			value: []float32{0},
		},
		{
			xt:    testpb.E_RepeatedDoubleExtension,
			value: []float64{0},
		},
		{
			xt:    testpb.E_RepeatedBoolExtension,
			value: []bool{true},
		},
		{
			xt:    testpb.E_RepeatedStringExtension,
			value: []string{""},
		},
		{
			xt:    testpb.E_RepeatedBytesExtension,
			value: [][]byte{nil},
		},
		{
			xt:    testpb.E_RepeatedNestedMessageExtension,
			value: []*testpb.TestAllTypes_NestedMessage{{}},
		},
		{
			xt:    testpb.E_RepeatedNestedEnumExtension,
			value: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO},
		},
	} {
		name := test.xt.TypeDescriptor().FullName()
		t.Run(fmt.Sprint(name), func(t *testing.T) {
			if !test.xt.IsValidInterface(test.value) {
				t.Fatalf("IsValidInterface(%[1]T(%[1]v)) = false, want true", test.value)
			}
			v := test.xt.ValueOf(test.value)
			if !test.xt.IsValidValue(v) {
				t.Fatalf("IsValidValue(%[1]T(%[1]v)) = false, want true", v)
			}
			if got, want := test.xt.InterfaceOf(v), test.value; !cmp.Equal(got, want, cmpOpts) {
				t.Fatalf("round trip InterfaceOf(ValueOf(x)) = %v, want %v", got, want)
			}
		})
	}
}
<commit_msg>internal/impl: use current proto package, not old one<commit_after>// Copyright 2019 The Go 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 impl_test

import (
	"fmt"
	"testing"

	cmp "github.com/google/go-cmp/cmp"
	testpb "google.golang.org/protobuf/internal/testprotos/test"
	"google.golang.org/protobuf/proto"
	pref "google.golang.org/protobuf/reflect/protoreflect"
)

func TestExtensionType(t *testing.T) {
	cmpOpts := cmp.Options{
		cmp.Comparer(func(x, y proto.Message) bool {
			return proto.Equal(x, y)
		}),
	}
	for _, test := range []struct {
		xt    pref.ExtensionType
		value interface{}
	}{
		{
			xt:    testpb.E_OptionalInt32Extension,
			value: int32(0),
		},
		{
			xt:    testpb.E_OptionalInt64Extension,
			value: int64(0),
		},
		{
			xt:    testpb.E_OptionalUint32Extension,
			value: uint32(0),
		},
		{
			xt:    testpb.E_OptionalUint64Extension,
			value: uint64(0),
		},
		{
			xt:    testpb.E_OptionalFloatExtension,
			value: float32(0),
		},
		{
			xt:    testpb.E_OptionalDoubleExtension,
			value: float64(0),
		},
		{
			xt:    testpb.E_OptionalBoolExtension,
			value: true,
		},
		{
			xt:    testpb.E_OptionalStringExtension,
			value: "",
		},
		{
			xt:    testpb.E_OptionalBytesExtension,
			value: []byte{},
		},
		{
			xt:    testpb.E_OptionalNestedMessageExtension,
			value: &testpb.TestAllTypes_NestedMessage{},
		},
		{
			xt:    testpb.E_OptionalNestedEnumExtension,
			value: testpb.TestAllTypes_FOO,
		},
		{
			xt:    testpb.E_RepeatedInt32Extension,
			value: []int32{0},
		},
		{
			xt:    testpb.E_RepeatedInt64Extension,
			value: []int64{0},
		},
		{
			xt:    testpb.E_RepeatedUint32Extension,
			value: []uint32{0},
		},
		{
			xt:    testpb.E_RepeatedUint64Extension,
			value: []uint64{0},
		},
		{
			xt:    testpb.E_RepeatedFloatExtension,
			value: []float32{0},
		},
		{
			xt:    testpb.E_RepeatedDoubleExtension,
			value: []float64{0},
		},
		{
			xt:    testpb.E_RepeatedBoolExtension,
			value: []bool{true},
		},
		{
			xt:    testpb.E_RepeatedStringExtension,
			value: []string{""},
		},
		{
			xt:    testpb.E_RepeatedBytesExtension,
			value: [][]byte{nil},
		},
		{
			xt:    testpb.E_RepeatedNestedMessageExtension,
			value: []*testpb.TestAllTypes_NestedMessage{{}},
		},
		{
			xt:    testpb.E_RepeatedNestedEnumExtension,
			value: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO},
		},
	} {
		name := test.xt.TypeDescriptor().FullName()
		t.Run(fmt.Sprint(name), func(t *testing.T) {
			if !test.xt.IsValidInterface(test.value) {
				t.Fatalf("IsValidInterface(%[1]T(%[1]v)) = false, want true", test.value)
			}
			v := test.xt.ValueOf(test.value)
			if !test.xt.IsValidValue(v) {
				t.Fatalf("IsValidValue(%[1]T(%[1]v)) = false, want true", v)
			}
			if got, want := test.xt.InterfaceOf(v), test.value; !cmp.Equal(got, want, cmpOpts) {
				t.Fatalf("round trip InterfaceOf(ValueOf(x)) = %v, want %v", got, want)
			}
		})
	}
}
<|endoftext|>


<commit_before>package edgectl

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/datawire/ambassador/pkg/k8s"
	"github.com/datawire/ambassador/pkg/supervisor"
)

var simpleTransport = &http.Transport{
	// #nosec G402
	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	Proxy:           nil,
	DialContext: (&net.Dialer{
		Timeout:   10 * time.Second,
		KeepAlive: 1 * time.Second,
		DualStack: true,
	}).DialContext,
	DisableKeepAlives: true,
}

var hClient = &http.Client{
	Transport: simpleTransport,
	Timeout:   15 * time.Second,
}

// Connect the daemon to a cluster
func (d *Daemon) Connect(
	p *supervisor.Process, out *Emitter, rai *RunAsInfo,
	context, namespace, managerNs string, kargs []string,
	installID string, isCI bool,
) error {
	// Sanity checks
	if d.cluster != nil {
		out.Println("Already connected")
		out.Send("connect", "Already connected")
		return nil
	}
	if d.bridge != nil {
		out.Println("Not ready: Trying to disconnect")
		out.Send("connect", "Not ready: Trying to disconnect")
		return nil
	}
	if d.network == nil {
		out.Println("Not ready: Network overrides are paused (use \"edgectl resume\")")
		out.Send("connect", "Not ready: Paused")
		return nil
	}
	if !d.network.IsOkay() {
		out.Println("Not ready: Establishing network overrides")
		out.Send("connect", "Not ready: Establishing network overrides")
		return nil
	}

	out.Printf("Connecting to traffic manager in namespace %s...\n", managerNs)
	out.Send("connect", "Connecting...")
	cluster, err := TrackKCluster(p, rai, context, namespace, kargs)
	if err != nil {
		out.Println(err.Error())
		out.Send("failed", err.Error())
		out.SendExit(1)
		return nil
	}
	d.cluster = cluster

	previewHost, err := getClusterPreviewHostname(p, cluster)
	if err != nil {
		p.Logf("get preview URL hostname: %+v", err)
		previewHost = ""
	}

	bridge, err := CheckedRetryingCommand(
		p,
		"bridge",
		[]string{GetExe(), "teleproxy", "bridge", cluster.context, cluster.namespace},
		rai,
		checkBridge,
		15*time.Second,
	)
	if err != nil {
		out.Println(err.Error())
		out.Send("failed", err.Error())
		out.SendExit(1)
		d.cluster.Close()
		d.cluster = nil
		return nil
	}
	d.bridge = bridge
	d.cluster.SetBridgeCheck(d.bridge.IsOkay)

	out.Printf(
		"Connected to context %s (%s)\n", d.cluster.Context(), d.cluster.Server(),
	)
	out.Send("cluster.context", d.cluster.Context())
	out.Send("cluster.server", d.cluster.Server())

	tmgr, err := NewTrafficManager(p, d.cluster, managerNs, installID, isCI)
	if err != nil {
		out.Println()
		out.Println("Unable to connect to the traffic manager in your cluster.")
		out.Println("The intercept feature will not be available.")
		out.Println("Error was:", err)
		// out.Println("Use <some command> to set up the traffic manager.") // FIXME
		out.Send("intercept", false)
	} else {
		tmgr.previewHost = previewHost
		d.trafficMgr = tmgr
		out.Send("intercept", true)
	}
	return nil
}

// Disconnect from the connected cluster
func (d *Daemon) Disconnect(p *supervisor.Process, out *Emitter) error {
	// Sanity checks
	if d.cluster == nil {
		out.Println("Not connected (use 'edgectl connect' to connect to your cluster)")
		out.Send("disconnect", "Not connected")
		return nil
	}

	_ = d.ClearIntercepts(p)
	if d.bridge != nil {
		d.cluster.SetBridgeCheck(nil) // Stop depending on this bridge
		_ = d.bridge.Close()
		d.bridge = nil
	}
	if d.trafficMgr != nil {
		_ = d.trafficMgr.Close()
		d.trafficMgr = nil
	}
	err := d.cluster.Close()
	d.cluster = nil

	out.Println("Disconnected")
	out.Send("disconnect", "Disconnected")
	return err
}

// getClusterPreviewHostname returns the hostname of the first Host resource it
// finds that has Preview URLs enabled with a supported URL type.
func getClusterPreviewHostname(p *supervisor.Process, cluster *KCluster) (hostname string, err error) {
	p.Log("Looking for a Host with Preview URLs enabled")

	// kubectl get hosts, in all namespaces or in this namespace
	var outBytes []byte
	outBytes, err = func() ([]byte, error) {
		clusterCmd := cluster.GetKubectlCmdNoNamespace(p, "get", "host", "-o", "yaml", "--all-namespaces")
		if outBytes, err := clusterCmd.CombinedOutput(); err == nil {
			return outBytes, nil
		}

		nsCmd := cluster.GetKubectlCmd(p, "get", "host", "-o", "yaml")
		if outBytes, err := nsCmd.CombinedOutput(); err == nil {
			return outBytes, nil
		} else {
			return nil, err
		}
	}()
	if err != nil {
		return
	}

	// Parse the output
	hostLists, kerr := k8s.ParseResources("get hosts", string(outBytes))
	if kerr != nil {
		err = kerr
		return
	}
	if len(hostLists) != 1 {
		err = errors.Errorf("weird result with length %d", len(hostLists))
		return
	}

	// Grab the "items" slice, as the result should be a list of Host resources
	hostItems := k8s.Map(hostLists[0]).GetMaps("items")
	p.Logf("Found %d Host resources", len(hostItems))

	// Loop over Hosts looking for a Preview URL hostname
	for _, hostItem := range hostItems {
		host := k8s.Resource(hostItem)
		logEntry := fmt.Sprintf("- Host %s / %s: %%s", host.Namespace(), host.Name())

		previewUrlSpec := host.Spec().GetMap("previewUrl")
		if len(previewUrlSpec) == 0 {
			p.Logf(logEntry, "no preview URL config")
			continue
		}

		if enabled, ok := previewUrlSpec["enabled"].(bool); !ok || !enabled {
			p.Logf(logEntry, "preview URL not enabled")
			continue
		}

		if pType, ok := previewUrlSpec["type"].(string); !ok || pType != "Path" {
			p.Logf(logEntry+": %#v", "unsupported preview URL type", previewUrlSpec["type"])
			continue
		}

		if hostname = host.Spec().GetString("hostname"); hostname == "" {
			p.Logf(logEntry, "empty hostname???")
			continue
		}

		p.Logf(logEntry+": %q", "SUCCESS! Hostname is", hostname)
		return
	}

	p.Logf("No appropriate Host resource found.")
	return
}

// checkBridge checks the status of teleproxy bridge by doing the equivalent of
//  curl http://traffic-proxy.svc.cluster.local:8022.
// Note there is no namespace specified, as we are checking for bridge status in the
// current namespace.
func checkBridge(p *supervisor.Process) error {
	address := "traffic-proxy.svc.cluster.local:8022"
	conn, err := net.DialTimeout("tcp", address, 15*time.Second)
	if err != nil {
		return errors.Wrap(err, "tcp connect")
	}
	if conn != nil {
		defer conn.Close()
		msg, _, err := bufio.NewReader(conn).ReadLine()
		if err != nil {
			return errors.Wrap(err, "tcp read")
		}
		if !strings.Contains(string(msg), "SSH") {
			return fmt.Errorf("expected SSH prompt, got: %v", string(msg))
		}
	} else {
		return fmt.Errorf("fail to establish tcp connection to %v", address)
	}
	return nil
}

// TrafficManager is a handle to access the Traffic Manager in a
// cluster.
type TrafficManager struct {
	crc            Resource
	apiPort        int
	sshPort        int
	namespace      string
	interceptables []string
	totalClusCepts int
	snapshotSent   bool
	installID      string // edgectl's install ID
	connectCI      bool   // whether --ci was passed to connect
	apiErr         error  // holds the latest traffic-manager API error
	licenseInfo    string // license information from traffic-manager
	previewHost    string // hostname to use for preview URLs, if enabled
}

// NewTrafficManager returns a TrafficManager resource for the given
// cluster if it has a Traffic Manager service.
func NewTrafficManager(p *supervisor.Process, cluster *KCluster, managerNs string, installID string, isCI bool) (*TrafficManager, error) {
	cmd := cluster.GetKubectlCmd(p, "get", "-n", managerNs, "svc/telepresence-proxy", "deploy/telepresence-proxy")
	err := cmd.Run()
	if err != nil {
		return nil, errors.Wrap(err, "kubectl get svc/deploy telepresency-proxy")
	}

	apiPort, err := GetFreePort()
	if err != nil {
		return nil, errors.Wrap(err, "get free port for API")
	}
	sshPort, err := GetFreePort()
	if err != nil {
		return nil, errors.Wrap(err, "get free port for ssh")
	}
	kpfArgStr := fmt.Sprintf("port-forward -n %s svc/telepresence-proxy %d:8022 %d:8081", managerNs, sshPort, apiPort)
	kpfArgs := cluster.GetKubectlArgs(strings.Fields(kpfArgStr)...)
	tm := &TrafficManager{
		apiPort:   apiPort,
		sshPort:   sshPort,
		namespace: managerNs,
		installID: installID,
		connectCI: isCI,
	}

	pf, err := CheckedRetryingCommand(p, "traffic-kpf", kpfArgs, cluster.RAI(), tm.check, 15*time.Second)
	if err != nil {
		return nil, err
	}
	tm.crc = pf
	return tm, nil
}

func (tm *TrafficManager) check(p *supervisor.Process) error {
	body, code, err := tm.request("GET", "state", []byte{})
	if err != nil {
		return err
	}

	if code != http.StatusOK {
		tm.apiErr = fmt.Errorf("%v: %v", code, body)
		return tm.apiErr
	}
	tm.apiErr = nil

	var state map[string]interface{}
	if err := json.Unmarshal([]byte(body), &state); err != nil {
		p.Logf("check: bad JSON from tm: %v", err)
		p.Logf("check: JSON data is: %q", body)
		return err
	}
	if licenseInfo, ok := state["LicenseInfo"]; ok {
		tm.licenseInfo = licenseInfo.(string)
	}
	deployments, ok := state["Deployments"].(map[string]interface{})
	if !ok {
		p.Log("check: failed to get deployment info")
		p.Logf("check: JSON data is: %q", body)
	}
	tm.interceptables = make([]string, len(deployments))
	tm.totalClusCepts = 0
	idx := 0
	for deployment := range deployments {
		tm.interceptables[idx] = deployment
		idx++
		info, ok := deployments[deployment].(map[string]interface{})
		if !ok {
			continue
		}
		cepts, ok := info["Intercepts"].([]interface{})
		if ok {
			tm.totalClusCepts += len(cepts)
		}
	}

	if !tm.snapshotSent {
		p.Log("trying to send snapshot")
		tm.snapshotSent = true // don't try again, even if this fails
		body, code, err := tm.request("GET", "snapshot", []byte{})
		if err != nil || code != 200 {
			p.Logf("snapshot request failed: %v", err)
			return nil
		}
		resp, err := hClient.Post("http://teleproxy/api/tables/", "application/json", strings.NewReader(body))
		if err != nil {
			p.Logf("snapshot post failed: %v", err)
			return nil
		}
		_, _ = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		p.Log("snapshot sent!")
	}

	return nil
}

func (tm *TrafficManager) request(method, path string, data []byte) (result string, code int, err error) {
	url := fmt.Sprintf("http://127.0.0.1:%d/%s", tm.apiPort, path)
	req, err := http.NewRequest(method, url, bytes.NewBuffer(data))
	if err != nil {
		return
	}

	req.Header.Set("edgectl-install-id", tm.installID)
	req.Header.Set("edgectl-connect-ci", strconv.FormatBool(tm.connectCI))

	resp, err := hClient.Do(req)
	if err != nil {
		err = errors.Wrap(err, "get")
		return
	}
	defer resp.Body.Close()
	code = resp.StatusCode
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		err = errors.Wrap(err, "read body")
		return
	}
	result = string(body)
	return
}

// Name implements Resource
func (tm *TrafficManager) Name() string {
	return "trafficMgr"
}

// IsOkay implements Resource
func (tm *TrafficManager) IsOkay() bool {
	return tm.crc.IsOkay()
}

// Close implements Resource
func (tm *TrafficManager) Close() error {
	return tm.crc.Close()
}
<commit_msg>edgectl: Support default (missing) Preview URL type too<commit_after>package edgectl

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/datawire/ambassador/pkg/k8s"
	"github.com/datawire/ambassador/pkg/supervisor"
)

var simpleTransport = &http.Transport{
	// #nosec G402
	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	Proxy:           nil,
	DialContext: (&net.Dialer{
		Timeout:   10 * time.Second,
		KeepAlive: 1 * time.Second,
		DualStack: true,
	}).DialContext,
	DisableKeepAlives: true,
}

var hClient = &http.Client{
	Transport: simpleTransport,
	Timeout:   15 * time.Second,
}

// Connect the daemon to a cluster
func (d *Daemon) Connect(
	p *supervisor.Process, out *Emitter, rai *RunAsInfo,
	context, namespace, managerNs string, kargs []string,
	installID string, isCI bool,
) error {
	// Sanity checks
	if d.cluster != nil {
		out.Println("Already connected")
		out.Send("connect", "Already connected")
		return nil
	}
	if d.bridge != nil {
		out.Println("Not ready: Trying to disconnect")
		out.Send("connect", "Not ready: Trying to disconnect")
		return nil
	}
	if d.network == nil {
		out.Println("Not ready: Network overrides are paused (use \"edgectl resume\")")
		out.Send("connect", "Not ready: Paused")
		return nil
	}
	if !d.network.IsOkay() {
		out.Println("Not ready: Establishing network overrides")
		out.Send("connect", "Not ready: Establishing network overrides")
		return nil
	}

	out.Printf("Connecting to traffic manager in namespace %s...\n", managerNs)
	out.Send("connect", "Connecting...")
	cluster, err := TrackKCluster(p, rai, context, namespace, kargs)
	if err != nil {
		out.Println(err.Error())
		out.Send("failed", err.Error())
		out.SendExit(1)
		return nil
	}
	d.cluster = cluster

	previewHost, err := getClusterPreviewHostname(p, cluster)
	if err != nil {
		p.Logf("get preview URL hostname: %+v", err)
		previewHost = ""
	}

	bridge, err := CheckedRetryingCommand(
		p,
		"bridge",
		[]string{GetExe(), "teleproxy", "bridge", cluster.context, cluster.namespace},
		rai,
		checkBridge,
		15*time.Second,
	)
	if err != nil {
		out.Println(err.Error())
		out.Send("failed", err.Error())
		out.SendExit(1)
		d.cluster.Close()
		d.cluster = nil
		return nil
	}
	d.bridge = bridge
	d.cluster.SetBridgeCheck(d.bridge.IsOkay)

	out.Printf(
		"Connected to context %s (%s)\n", d.cluster.Context(), d.cluster.Server(),
	)
	out.Send("cluster.context", d.cluster.Context())
	out.Send("cluster.server", d.cluster.Server())

	tmgr, err := NewTrafficManager(p, d.cluster, managerNs, installID, isCI)
	if err != nil {
		out.Println()
		out.Println("Unable to connect to the traffic manager in your cluster.")
		out.Println("The intercept feature will not be available.")
		out.Println("Error was:", err)
		// out.Println("Use <some command> to set up the traffic manager.") // FIXME
		out.Send("intercept", false)
	} else {
		tmgr.previewHost = previewHost
		d.trafficMgr = tmgr
		out.Send("intercept", true)
	}
	return nil
}

// Disconnect from the connected cluster
func (d *Daemon) Disconnect(p *supervisor.Process, out *Emitter) error {
	// Sanity checks
	if d.cluster == nil {
		out.Println("Not connected (use 'edgectl connect' to connect to your cluster)")
		out.Send("disconnect", "Not connected")
		return nil
	}

	_ = d.ClearIntercepts(p)
	if d.bridge != nil {
		d.cluster.SetBridgeCheck(nil) // Stop depending on this bridge
		_ = d.bridge.Close()
		d.bridge = nil
	}
	if d.trafficMgr != nil {
		_ = d.trafficMgr.Close()
		d.trafficMgr = nil
	}
	err := d.cluster.Close()
	d.cluster = nil

	out.Println("Disconnected")
	out.Send("disconnect", "Disconnected")
	return err
}

// getClusterPreviewHostname returns the hostname of the first Host resource it
// finds that has Preview URLs enabled with a supported URL type.
func getClusterPreviewHostname(p *supervisor.Process, cluster *KCluster) (hostname string, err error) {
	p.Log("Looking for a Host with Preview URLs enabled")

	// kubectl get hosts, in all namespaces or in this namespace
	var outBytes []byte
	outBytes, err = func() ([]byte, error) {
		clusterCmd := cluster.GetKubectlCmdNoNamespace(p, "get", "host", "-o", "yaml", "--all-namespaces")
		if outBytes, err := clusterCmd.CombinedOutput(); err == nil {
			return outBytes, nil
		}

		nsCmd := cluster.GetKubectlCmd(p, "get", "host", "-o", "yaml")
		if outBytes, err := nsCmd.CombinedOutput(); err == nil {
			return outBytes, nil
		} else {
			return nil, err
		}
	}()
	if err != nil {
		return
	}

	// Parse the output
	hostLists, kerr := k8s.ParseResources("get hosts", string(outBytes))
	if kerr != nil {
		err = kerr
		return
	}
	if len(hostLists) != 1 {
		err = errors.Errorf("weird result with length %d", len(hostLists))
		return
	}

	// Grab the "items" slice, as the result should be a list of Host resources
	hostItems := k8s.Map(hostLists[0]).GetMaps("items")
	p.Logf("Found %d Host resources", len(hostItems))

	// Loop over Hosts looking for a Preview URL hostname
	for _, hostItem := range hostItems {
		host := k8s.Resource(hostItem)
		logEntry := fmt.Sprintf("- Host %s / %s: %%s", host.Namespace(), host.Name())

		previewUrlSpec := host.Spec().GetMap("previewUrl")
		if len(previewUrlSpec) == 0 {
			p.Logf(logEntry, "no preview URL config")
			continue
		}

		if enabled, ok := previewUrlSpec["enabled"].(bool); !ok || !enabled {
			p.Logf(logEntry, "preview URL not enabled")
			continue
		}

		// missing type, default is "Path" --> success
		// type is present, set to "Path" --> success
		// otherwise --> failure
		if pType, ok := previewUrlSpec["type"].(string); ok && pType != "Path" {
			p.Logf(logEntry+": %#v", "unsupported preview URL type", previewUrlSpec["type"])
			continue
		}

		if hostname = host.Spec().GetString("hostname"); hostname == "" {
			p.Logf(logEntry, "empty hostname???")
			continue
		}

		p.Logf(logEntry+": %q", "SUCCESS! Hostname is", hostname)
		return
	}

	p.Logf("No appropriate Host resource found.")
	return
}

// checkBridge checks the status of teleproxy bridge by doing the equivalent of
//  curl http://traffic-proxy.svc.cluster.local:8022.
// Note there is no namespace specified, as we are checking for bridge status in the
// current namespace.
func checkBridge(p *supervisor.Process) error {
	address := "traffic-proxy.svc.cluster.local:8022"
	conn, err := net.DialTimeout("tcp", address, 15*time.Second)
	if err != nil {
		return errors.Wrap(err, "tcp connect")
	}
	if conn != nil {
		defer conn.Close()
		msg, _, err := bufio.NewReader(conn).ReadLine()
		if err != nil {
			return errors.Wrap(err, "tcp read")
		}
		if !strings.Contains(string(msg), "SSH") {
			return fmt.Errorf("expected SSH prompt, got: %v", string(msg))
		}
	} else {
		return fmt.Errorf("fail to establish tcp connection to %v", address)
	}
	return nil
}

// TrafficManager is a handle to access the Traffic Manager in a
// cluster.
type TrafficManager struct {
	crc            Resource
	apiPort        int
	sshPort        int
	namespace      string
	interceptables []string
	totalClusCepts int
	snapshotSent   bool
	installID      string // edgectl's install ID
	connectCI      bool   // whether --ci was passed to connect
	apiErr         error  // holds the latest traffic-manager API error
	licenseInfo    string // license information from traffic-manager
	previewHost    string // hostname to use for preview URLs, if enabled
}

// NewTrafficManager returns a TrafficManager resource for the given
// cluster if it has a Traffic Manager service.
func NewTrafficManager(p *supervisor.Process, cluster *KCluster, managerNs string, installID string, isCI bool) (*TrafficManager, error) {
	cmd := cluster.GetKubectlCmd(p, "get", "-n", managerNs, "svc/telepresence-proxy", "deploy/telepresence-proxy")
	err := cmd.Run()
	if err != nil {
		return nil, errors.Wrap(err, "kubectl get svc/deploy telepresency-proxy")
	}

	apiPort, err := GetFreePort()
	if err != nil {
		return nil, errors.Wrap(err, "get free port for API")
	}
	sshPort, err := GetFreePort()
	if err != nil {
		return nil, errors.Wrap(err, "get free port for ssh")
	}
	kpfArgStr := fmt.Sprintf("port-forward -n %s svc/telepresence-proxy %d:8022 %d:8081", managerNs, sshPort, apiPort)
	kpfArgs := cluster.GetKubectlArgs(strings.Fields(kpfArgStr)...)
	tm := &TrafficManager{
		apiPort:   apiPort,
		sshPort:   sshPort,
		namespace: managerNs,
		installID: installID,
		connectCI: isCI,
	}

	pf, err := CheckedRetryingCommand(p, "traffic-kpf", kpfArgs, cluster.RAI(), tm.check, 15*time.Second)
	if err != nil {
		return nil, err
	}
	tm.crc = pf
	return tm, nil
}

func (tm *TrafficManager) check(p *supervisor.Process) error {
	body, code, err := tm.request("GET", "state", []byte{})
	if err != nil {
		return err
	}

	if code != http.StatusOK {
		tm.apiErr = fmt.Errorf("%v: %v", code, body)
		return tm.apiErr
	}
	tm.apiErr = nil

	var state map[string]interface{}
	if err := json.Unmarshal([]byte(body), &state); err != nil {
		p.Logf("check: bad JSON from tm: %v", err)
		p.Logf("check: JSON data is: %q", body)
		return err
	}
	if licenseInfo, ok := state["LicenseInfo"]; ok {
		tm.licenseInfo = licenseInfo.(string)
	}
	deployments, ok := state["Deployments"].(map[string]interface{})
	if !ok {
		p.Log("check: failed to get deployment info")
		p.Logf("check: JSON data is: %q", body)
	}
	tm.interceptables = make([]string, len(deployments))
	tm.totalClusCepts = 0
	idx := 0
	for deployment := range deployments {
		tm.interceptables[idx] = deployment
		idx++
		info, ok := deployments[deployment].(map[string]interface{})
		if !ok {
			continue
		}
		cepts, ok := info["Intercepts"].([]interface{})
		if ok {
			tm.totalClusCepts += len(cepts)
		}
	}

	if !tm.snapshotSent {
		p.Log("trying to send snapshot")
		tm.snapshotSent = true // don't try again, even if this fails
		body, code, err := tm.request("GET", "snapshot", []byte{})
		if err != nil || code != 200 {
			p.Logf("snapshot request failed: %v", err)
			return nil
		}
		resp, err := hClient.Post("http://teleproxy/api/tables/", "application/json", strings.NewReader(body))
		if err != nil {
			p.Logf("snapshot post failed: %v", err)
			return nil
		}
		_, _ = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		p.Log("snapshot sent!")
	}

	return nil
}

func (tm *TrafficManager) request(method, path string, data []byte) (result string, code int, err error) {
	url := fmt.Sprintf("http://127.0.0.1:%d/%s", tm.apiPort, path)
	req, err := http.NewRequest(method, url, bytes.NewBuffer(data))
	if err != nil {
		return
	}

	req.Header.Set("edgectl-install-id", tm.installID)
	req.Header.Set("edgectl-connect-ci", strconv.FormatBool(tm.connectCI))

	resp, err := hClient.Do(req)
	if err != nil {
		err = errors.Wrap(err, "get")
		return
	}
	defer resp.Body.Close()
	code = resp.StatusCode
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		err = errors.Wrap(err, "read body")
		return
	}
	result = string(body)
	return
}

// Name implements Resource
func (tm *TrafficManager) Name() string {
	return "trafficMgr"
}

// IsOkay implements Resource
func (tm *TrafficManager) IsOkay() bool {
	return tm.crc.IsOkay()
}

// Close implements Resource
func (tm *TrafficManager) Close() error {
	return tm.crc.Close()
}
<|endoftext|>


<commit_before>package message

import (
	"fmt"
	"sort"

	cbor "github.com/britram/borat"
	log "github.com/inconshreveable/log15"

	"github.com/netsec-ethz/rains/internal/pkg/query"
	"github.com/netsec-ethz/rains/internal/pkg/section"
	"github.com/netsec-ethz/rains/internal/pkg/signature"
	"github.com/netsec-ethz/rains/internal/pkg/token"
)

const (
	rainsTag = 0xE99BA8
)

//Message represents a Message
type Message struct {
	//Capabilities is a slice of capabilities or the hash thereof which the server originating the
	//message has.
	Capabilities []Capability
	//Token is used to identify a message
	Token token.Token
	//Content is a slice of
	Content []section.Section
	//Signatures authenticate the content of this message. An encoding of Message is signed by the infrastructure key of the originating server.
	Signatures []signature.Sig
}

// MarshalCBOR writes the RAINS message to the provided writer.
// Implements the CBORMarshaler interface.
func (rm *Message) MarshalCBOR(w *cbor.CBORWriter) error {
	if err := w.WriteTag(cbor.CBORTag(rainsTag)); err != nil {
		return err
	}

	m := make(map[int]interface{})
	if len(rm.Signatures) > 0 {
		m[0] = rm.Signatures
	}

	if len(rm.Capabilities) > 0 {
		caps := make([]string, len(rm.Capabilities))
		for i, cap := range rm.Capabilities {
			caps[i] = string(cap)
		}
		m[1] = caps
	}
	m[2] = rm.Token

	msgsect := make([][2]interface{}, 0)
	for _, sect := range rm.Content {
		switch sect.(type) {
		case *section.Assertion:
			msgsect = append(msgsect, [2]interface{}{1, sect})
		case *section.Shard:
			msgsect = append(msgsect, [2]interface{}{2, sect})
		case *section.Pshard:
			msgsect = append(msgsect, [2]interface{}{3, sect})
		case *section.Zone:
			msgsect = append(msgsect, [2]interface{}{4, sect})
		case *query.Name:
			msgsect = append(msgsect, [2]interface{}{5, sect})
		case *section.Notification:
			msgsect = append(msgsect, [2]interface{}{23, sect})
		default:
			return fmt.Errorf("unknown section type: %T", sect)
		}
	}
	m[23] = msgsect
	return w.WriteIntMap(m)
}

func (rm *Message) UnmarshalCBOR(r *cbor.CBORReader) error {
	tag, err := r.ReadTag()
	if err != nil {
		return fmt.Errorf("failed to read tag: %v", err)
	}
	if tag != cbor.CBORTag(rainsTag) {
		return fmt.Errorf("expected tag for RAINS message but got: %v", tag)
	}
	m, err := r.ReadIntMapUntagged()
	if err != nil {
		return fmt.Errorf("failed to read map: %v", err)
	}

	if sigs, ok := m[0]; ok {
		rm.Signatures = make([]signature.Sig, len(sigs.([]interface{})))
		for i, sig := range sigs.([]interface{}) {
			if err := rm.Signatures[i].UnmarshalArray(sig.([]interface{})); err != nil {
				return err
			}
		}
	}

	if caps, ok := m[1]; ok {
		rm.Capabilities = make([]Capability, len(caps.([]interface{})))
		for i, cap := range caps.([]interface{}) {
			rm.Capabilities[i] = Capability(cap.(string))
		}
	}

	if _, ok := m[2]; !ok {
		return fmt.Errorf("token missing from RAINS message: %v", m)
	}
	for i, val := range m[2].([]interface{}) {
		rm.Token[i] = byte(val.(int))
	}

	for _, elem := range m[23].([]interface{}) {
		elem := elem.([]interface{})
		t := elem[0].(int)
		switch t {
		case 1:
			a := &section.Assertion{}
			if err := a.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, a)
		case 2:
			s := &section.Shard{}
			if err := s.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, s)
		case 7:
			s := &section.Pshard{}
			if err := s.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, s)
		case 3:
			z := &section.Zone{}
			if err := z.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, z)
		case 4:
			q := &query.Name{}
			if err := q.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, q)
		case 23:
			n := &section.Notification{}
			if err := n.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, n)
		}
	}
	return nil
}

//Sort sorts the sections in m.Content first by Message Section Type Codes (see RAINS Protocol Specification) and
//second the sections of equal type according to their sort function.
func (m *Message) Sort() {
	var assertions []*section.Assertion
	var shards []*section.Shard
	var pshards []*section.Pshard
	var zones []*section.Zone
	var queries []*query.Name
	var notifications []*section.Notification
	for _, sec := range m.Content {
		sec.Sort()
		switch sec := sec.(type) {
		case *section.Assertion:
			assertions = append(assertions, sec)
		case *section.Shard:
			shards = append(shards, sec)
		case *section.Pshard:
			pshards = append(pshards, sec)
		case *section.Zone:
			zones = append(zones, sec)
		case *query.Name:
			queries = append(queries, sec)
		case *section.Notification:
			notifications = append(notifications, sec)
		default:
			log.Warn("Unsupported section type", "type", fmt.Sprintf("%T", sec))
		}
	}
	sort.Slice(assertions, func(i, j int) bool { return assertions[i].CompareTo(assertions[j]) < 0 })
	sort.Slice(shards, func(i, j int) bool { return shards[i].CompareTo(shards[j]) < 0 })
	sort.Slice(pshards, func(i, j int) bool { return pshards[i].CompareTo(pshards[j]) < 0 })
	sort.Slice(zones, func(i, j int) bool { return zones[i].CompareTo(zones[j]) < 0 })
	sort.Slice(queries, func(i, j int) bool { return queries[i].CompareTo(queries[j]) < 0 })
	sort.Slice(notifications, func(i, j int) bool { return notifications[i].CompareTo(notifications[j]) < 0 })
	m.Content = []section.Section{}
	for _, section := range assertions {
		m.Content = append(m.Content, section)
	}
	for _, section := range shards {
		m.Content = append(m.Content, section)
	}
	for _, section := range pshards {
		m.Content = append(m.Content, section)
	}
	for _, section := range zones {
		m.Content = append(m.Content, section)
	}
	for _, section := range queries {
		m.Content = append(m.Content, section)
	}
	for _, section := range notifications {
		m.Content = append(m.Content, section)
	}
}

//Query returns the first section in the messages content if it is a query. It panics if the message
//has no content.
func (m *Message) Query() *query.Name {
	if q, ok := m.Content[0].(*query.Name); ok {
		return q
	}
	return nil
}

//Capability is a urn of a capability
type Capability string

const (
	//NoCapability is used when the server does not listen for any connections
	NoCapability Capability = "urn:x-rains:nocapability"
	//TLSOverTCP is used when the server listens for tls over tcp connections
	TLSOverTCP Capability = "urn:x-rains:tlssrv"
)
<commit_msg>fixes wrong section mapping in message's cbor unmarshaller<commit_after>package message

import (
	"fmt"
	"sort"

	cbor "github.com/britram/borat"
	log "github.com/inconshreveable/log15"

	"github.com/netsec-ethz/rains/internal/pkg/query"
	"github.com/netsec-ethz/rains/internal/pkg/section"
	"github.com/netsec-ethz/rains/internal/pkg/signature"
	"github.com/netsec-ethz/rains/internal/pkg/token"
)

const (
	rainsTag = 0xE99BA8
)

//Message represents a Message
type Message struct {
	//Capabilities is a slice of capabilities or the hash thereof which the server originating the
	//message has.
	Capabilities []Capability
	//Token is used to identify a message
	Token token.Token
	//Content is a slice of
	Content []section.Section
	//Signatures authenticate the content of this message. An encoding of Message is signed by the infrastructure key of the originating server.
	Signatures []signature.Sig
}

// MarshalCBOR writes the RAINS message to the provided writer.
// Implements the CBORMarshaler interface.
func (rm *Message) MarshalCBOR(w *cbor.CBORWriter) error {
	if err := w.WriteTag(cbor.CBORTag(rainsTag)); err != nil {
		return err
	}

	m := make(map[int]interface{})
	if len(rm.Signatures) > 0 {
		m[0] = rm.Signatures
	}

	if len(rm.Capabilities) > 0 {
		caps := make([]string, len(rm.Capabilities))
		for i, cap := range rm.Capabilities {
			caps[i] = string(cap)
		}
		m[1] = caps
	}
	m[2] = rm.Token

	msgsect := make([][2]interface{}, 0)
	for _, sect := range rm.Content {
		switch sect.(type) {
		case *section.Assertion:
			msgsect = append(msgsect, [2]interface{}{1, sect})
		case *section.Shard:
			msgsect = append(msgsect, [2]interface{}{2, sect})
		case *section.Pshard:
			msgsect = append(msgsect, [2]interface{}{3, sect})
		case *section.Zone:
			msgsect = append(msgsect, [2]interface{}{4, sect})
		case *query.Name:
			msgsect = append(msgsect, [2]interface{}{5, sect})
		case *section.Notification:
			msgsect = append(msgsect, [2]interface{}{23, sect})
		default:
			return fmt.Errorf("unknown section type: %T", sect)
		}
	}
	m[23] = msgsect
	return w.WriteIntMap(m)
}

func (rm *Message) UnmarshalCBOR(r *cbor.CBORReader) error {
	tag, err := r.ReadTag()
	if err != nil {
		return fmt.Errorf("failed to read tag: %v", err)
	}
	if tag != cbor.CBORTag(rainsTag) {
		return fmt.Errorf("expected tag for RAINS message but got: %v", tag)
	}
	m, err := r.ReadIntMapUntagged()
	if err != nil {
		return fmt.Errorf("failed to read map: %v", err)
	}

	if sigs, ok := m[0]; ok {
		rm.Signatures = make([]signature.Sig, len(sigs.([]interface{})))
		for i, sig := range sigs.([]interface{}) {
			if err := rm.Signatures[i].UnmarshalArray(sig.([]interface{})); err != nil {
				return err
			}
		}
	}

	if caps, ok := m[1]; ok {
		rm.Capabilities = make([]Capability, len(caps.([]interface{})))
		for i, cap := range caps.([]interface{}) {
			rm.Capabilities[i] = Capability(cap.(string))
		}
	}

	if _, ok := m[2]; !ok {
		return fmt.Errorf("token missing from RAINS message: %v", m)
	}
	for i, val := range m[2].([]interface{}) {
		rm.Token[i] = byte(val.(int))
	}

	for _, elem := range m[23].([]interface{}) {
		elem := elem.([]interface{})
		t := elem[0].(int)
		switch t {
		case 1:
			a := &section.Assertion{}
			if err := a.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, a)
		case 2:
			s := &section.Shard{}
			if err := s.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, s)
		case 3:
			s := &section.Pshard{}
			if err := s.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, s)
		case 4:
			z := &section.Zone{}
			if err := z.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, z)
		case 5:
			q := &query.Name{}
			if err := q.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, q)
		case 23:
			n := &section.Notification{}
			if err := n.UnmarshalMap(elem[1].(map[int]interface{})); err != nil {
				return err
			}
			rm.Content = append(rm.Content, n)
		}
	}
	return nil
}

//Sort sorts the sections in m.Content first by Message Section Type Codes (see RAINS Protocol Specification) and
//second the sections of equal type according to their sort function.
func (m *Message) Sort() {
	var assertions []*section.Assertion
	var shards []*section.Shard
	var pshards []*section.Pshard
	var zones []*section.Zone
	var queries []*query.Name
	var notifications []*section.Notification
	for _, sec := range m.Content {
		sec.Sort()
		switch sec := sec.(type) {
		case *section.Assertion:
			assertions = append(assertions, sec)
		case *section.Shard:
			shards = append(shards, sec)
		case *section.Pshard:
			pshards = append(pshards, sec)
		case *section.Zone:
			zones = append(zones, sec)
		case *query.Name:
			queries = append(queries, sec)
		case *section.Notification:
			notifications = append(notifications, sec)
		default:
			log.Warn("Unsupported section type", "type", fmt.Sprintf("%T", sec))
		}
	}
	sort.Slice(assertions, func(i, j int) bool { return assertions[i].CompareTo(assertions[j]) < 0 })
	sort.Slice(shards, func(i, j int) bool { return shards[i].CompareTo(shards[j]) < 0 })
	sort.Slice(pshards, func(i, j int) bool { return pshards[i].CompareTo(pshards[j]) < 0 })
	sort.Slice(zones, func(i, j int) bool { return zones[i].CompareTo(zones[j]) < 0 })
	sort.Slice(queries, func(i, j int) bool { return queries[i].CompareTo(queries[j]) < 0 })
	sort.Slice(notifications, func(i, j int) bool { return notifications[i].CompareTo(notifications[j]) < 0 })
	m.Content = []section.Section{}
	for _, section := range assertions {
		m.Content = append(m.Content, section)
	}
	for _, section := range shards {
		m.Content = append(m.Content, section)
	}
	for _, section := range pshards {
		m.Content = append(m.Content, section)
	}
	for _, section := range zones {
		m.Content = append(m.Content, section)
	}
	for _, section := range queries {
		m.Content = append(m.Content, section)
	}
	for _, section := range notifications {
		m.Content = append(m.Content, section)
	}
}

//Query returns the first section in the messages content if it is a query. It panics if the message
//has no content.
func (m *Message) Query() *query.Name {
	if q, ok := m.Content[0].(*query.Name); ok {
		return q
	}
	return nil
}

//Capability is a urn of a capability
type Capability string

const (
	//NoCapability is used when the server does not listen for any connections
	NoCapability Capability = "urn:x-rains:nocapability"
	//TLSOverTCP is used when the server listens for tls over tcp connections
	TLSOverTCP Capability = "urn:x-rains:tlssrv"
)
<|endoftext|>


<commit_before>package ydls

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"github.com/wader/ydls/internal/timerange"
)

// RequestOptions request options
type RequestOptions struct {
	MediaRawURL string              // youtubedl media URL
	Format      *Format             // output format
	Codecs      []string            // force codecs
	Retranscode bool                // force retranscode even if same input codec
	TimeRange   timerange.TimeRange // time range limit
	Items       uint                // feed item limit
}

// NewRequestOptionsFromQuery /?url=...&format=...
func NewRequestOptionsFromQuery(v url.Values, formats Formats) (RequestOptions, error) {
	mediaRawURL := v.Get("url")
	if mediaRawURL == "" {
		return RequestOptions{}, fmt.Errorf("no url")
	}
	var timeRange timerange.TimeRange
	var timeRangeErr error
	if time := v.Get("time"); time != "" {
		timeRange, timeRangeErr = timerange.NewTimeRangeFromString(v.Get("time"))
		if timeRangeErr != nil {
			return RequestOptions{}, timeRangeErr
		}
	}

	var codecs []string
	var format *Format

	formatName := v.Get("format")
	if formatName != "" {
		qFormat, qFormatFound := formats.FindByName(formatName)
		if !qFormatFound {
			return RequestOptions{}, fmt.Errorf("unknown format \"%s\"", formatName)
		}
		format = &qFormat

		codecNames := map[string]bool{}
		for _, s := range format.Streams {
			for _, c := range s.Codecs {
				codecNames[c.Name] = true
			}
		}

		for _, codec := range v["codec"] {
			if _, ok := codecNames[codec]; !ok {
				return RequestOptions{}, fmt.Errorf("unknown codec \"%s\"", codec)
			}
			codecs = append(codecs, codec)
		}
	}

	items := uint(0)
	itemsStr := v.Get("items")
	if itemsStr != "" {
		itemsN, itemsNErr := strconv.Atoi(itemsStr)
		if itemsNErr != nil {
			return RequestOptions{}, fmt.Errorf("invalid items count")
		}
		items = uint(itemsN)
	}

	return RequestOptions{
		MediaRawURL: mediaRawURL,
		Format:      format,
		Codecs:      codecs,
		Retranscode: v.Get("retranscode") != "",
		TimeRange:   timeRange,
		Items:       items,
	}, nil
}

// NewRequestOptionsFromPath
// /format+opt+opt.../schema://host.domin/path?query
// /format+opt+opt.../host.domain/path?query
// /schema://host.domain/path?query
// /host.domain/path?query
func NewRequestOptionsFromPath(url *url.URL, formats Formats) (RequestOptions, error) {
	formatAndOpts := ""
	mediaRawURL := ""

	// /format+opt/url -> ["/", "format", "url"]
	// parts[0] always empty, path always starts with /
	parts := strings.SplitN(url.Path, "/", 3)
	parts = parts[1:]

	// format? part does not contains ":" or "."
	if !strings.Contains(parts[0], ":") && !strings.Contains(parts[0], ".") {
		formatAndOpts = parts[0]
		parts = parts[1:]
	}

	if len(parts) == 0 {
		return RequestOptions{}, fmt.Errorf("no url")
	}

	if len(parts) == 2 {
		// had schema:// but split has removed one /
		mediaRawURL = parts[0] + "/" + parts[1]
	} else {
		mediaRawURL = parts[0]
	}
	if url.RawQuery != "" {
		mediaRawURL += "?" + url.RawQuery
	}

	opts := []string{}
	if formatAndOpts != "" {
		opts = strings.Split(formatAndOpts, "+")
	}

	r, dErr := NewRequestOptionsFromOpts(opts, formats)
	if dErr != nil {
		return RequestOptions{}, dErr
	}

	r.MediaRawURL = mediaRawURL

	return r, nil
}

func NewRequestOptionsFromOpts(opts []string, formats Formats) (RequestOptions, error) {
	var format Format
	var formatFound bool
	formatIndex := -1
	for i, opt := range opts {
		format, formatFound = formats.FindByName(opt)
		if formatFound {
			formatIndex = i
			break
		}
	}

	codecNames := map[string]bool{}
	if formatFound {
		for _, s := range format.Streams {
			for _, c := range s.Codecs {
				codecNames[c.Name] = true
			}
		}
	}

	r := RequestOptions{}

	if formatFound {
		r.Format = &format
	}

	for i, opt := range opts {
		const itemsSuffix = "items"

		if i == formatIndex {
			// nop, skip format opt
		} else if opt == "retranscode" {
			r.Retranscode = true
		} else if strings.HasSuffix(opt, itemsSuffix) {
			itemsN, itemsNErr := strconv.Atoi(opt[0 : len(opt)-len(itemsSuffix)])
			if itemsNErr != nil {
				return RequestOptions{}, fmt.Errorf("invalid items count")
			}
			r.Items = uint(itemsN)
			strconv.ParseUint("", 10, 32)
		} else if _, ok := codecNames[opt]; ok {
			r.Codecs = append(r.Codecs, opt)
		} else if tr, trErr := timerange.NewTimeRangeFromString(opt); trErr == nil {
			r.TimeRange = tr
		} else {
			return RequestOptions{}, fmt.Errorf("unknown opt %s", opt)
		}
	}

	return r, nil
}

func (r RequestOptions) QueryValues() url.Values {
	v := url.Values{}
	if r.MediaRawURL != "" {
		v.Set("url", r.MediaRawURL)
	}
	if r.Format != nil {
		v.Set("format", r.Format.Name)
	}
	for _, codec := range r.Codecs {
		v.Add("codec", codec)
	}
	if r.Retranscode {
		v.Set("retranscode", "1")
	}
	if !r.TimeRange.IsZero() {
		v.Set("time", r.TimeRange.String())
	}
	if r.Items > 0 {
		v.Set("items", strconv.Itoa(int(r.Items)))
	}
	return v
}
<commit_msg>Better description of request options items<commit_after>package ydls

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"github.com/wader/ydls/internal/timerange"
)

// RequestOptions request options
type RequestOptions struct {
	MediaRawURL string              // youtubedl media URL
	Format      *Format             // output format
	Codecs      []string            // force codecs
	Retranscode bool                // force retranscode even if same input codec
	TimeRange   timerange.TimeRange // time range limit
	Items       uint                // feed item count limit
}

// NewRequestOptionsFromQuery /?url=...&format=...
func NewRequestOptionsFromQuery(v url.Values, formats Formats) (RequestOptions, error) {
	mediaRawURL := v.Get("url")
	if mediaRawURL == "" {
		return RequestOptions{}, fmt.Errorf("no url")
	}
	var timeRange timerange.TimeRange
	var timeRangeErr error
	if time := v.Get("time"); time != "" {
		timeRange, timeRangeErr = timerange.NewTimeRangeFromString(v.Get("time"))
		if timeRangeErr != nil {
			return RequestOptions{}, timeRangeErr
		}
	}

	var codecs []string
	var format *Format

	formatName := v.Get("format")
	if formatName != "" {
		qFormat, qFormatFound := formats.FindByName(formatName)
		if !qFormatFound {
			return RequestOptions{}, fmt.Errorf("unknown format \"%s\"", formatName)
		}
		format = &qFormat

		codecNames := map[string]bool{}
		for _, s := range format.Streams {
			for _, c := range s.Codecs {
				codecNames[c.Name] = true
			}
		}

		for _, codec := range v["codec"] {
			if _, ok := codecNames[codec]; !ok {
				return RequestOptions{}, fmt.Errorf("unknown codec \"%s\"", codec)
			}
			codecs = append(codecs, codec)
		}
	}

	items := uint(0)
	itemsStr := v.Get("items")
	if itemsStr != "" {
		itemsN, itemsNErr := strconv.Atoi(itemsStr)
		if itemsNErr != nil {
			return RequestOptions{}, fmt.Errorf("invalid items count")
		}
		items = uint(itemsN)
	}

	return RequestOptions{
		MediaRawURL: mediaRawURL,
		Format:      format,
		Codecs:      codecs,
		Retranscode: v.Get("retranscode") != "",
		TimeRange:   timeRange,
		Items:       items,
	}, nil
}

// NewRequestOptionsFromPath
// /format+opt+opt.../schema://host.domin/path?query
// /format+opt+opt.../host.domain/path?query
// /schema://host.domain/path?query
// /host.domain/path?query
func NewRequestOptionsFromPath(url *url.URL, formats Formats) (RequestOptions, error) {
	formatAndOpts := ""
	mediaRawURL := ""

	// /format+opt/url -> ["/", "format", "url"]
	// parts[0] always empty, path always starts with /
	parts := strings.SplitN(url.Path, "/", 3)
	parts = parts[1:]

	// format? part does not contains ":" or "."
	if !strings.Contains(parts[0], ":") && !strings.Contains(parts[0], ".") {
		formatAndOpts = parts[0]
		parts = parts[1:]
	}

	if len(parts) == 0 {
		return RequestOptions{}, fmt.Errorf("no url")
	}

	if len(parts) == 2 {
		// had schema:// but split has removed one /
		mediaRawURL = parts[0] + "/" + parts[1]
	} else {
		mediaRawURL = parts[0]
	}
	if url.RawQuery != "" {
		mediaRawURL += "?" + url.RawQuery
	}

	opts := []string{}
	if formatAndOpts != "" {
		opts = strings.Split(formatAndOpts, "+")
	}

	r, dErr := NewRequestOptionsFromOpts(opts, formats)
	if dErr != nil {
		return RequestOptions{}, dErr
	}

	r.MediaRawURL = mediaRawURL

	return r, nil
}

func NewRequestOptionsFromOpts(opts []string, formats Formats) (RequestOptions, error) {
	var format Format
	var formatFound bool
	formatIndex := -1
	for i, opt := range opts {
		format, formatFound = formats.FindByName(opt)
		if formatFound {
			formatIndex = i
			break
		}
	}

	codecNames := map[string]bool{}
	if formatFound {
		for _, s := range format.Streams {
			for _, c := range s.Codecs {
				codecNames[c.Name] = true
			}
		}
	}

	r := RequestOptions{}

	if formatFound {
		r.Format = &format
	}

	for i, opt := range opts {
		const itemsSuffix = "items"

		if i == formatIndex {
			// nop, skip format opt
		} else if opt == "retranscode" {
			r.Retranscode = true
		} else if strings.HasSuffix(opt, itemsSuffix) {
			itemsN, itemsNErr := strconv.Atoi(opt[0 : len(opt)-len(itemsSuffix)])
			if itemsNErr != nil {
				return RequestOptions{}, fmt.Errorf("invalid items count")
			}
			r.Items = uint(itemsN)
			strconv.ParseUint("", 10, 32)
		} else if _, ok := codecNames[opt]; ok {
			r.Codecs = append(r.Codecs, opt)
		} else if tr, trErr := timerange.NewTimeRangeFromString(opt); trErr == nil {
			r.TimeRange = tr
		} else {
			return RequestOptions{}, fmt.Errorf("unknown opt %s", opt)
		}
	}

	return r, nil
}

func (r RequestOptions) QueryValues() url.Values {
	v := url.Values{}
	if r.MediaRawURL != "" {
		v.Set("url", r.MediaRawURL)
	}
	if r.Format != nil {
		v.Set("format", r.Format.Name)
	}
	for _, codec := range r.Codecs {
		v.Add("codec", codec)
	}
	if r.Retranscode {
		v.Set("retranscode", "1")
	}
	if !r.TimeRange.IsZero() {
		v.Set("time", r.TimeRange.String())
	}
	if r.Items > 0 {
		v.Set("items", strconv.Itoa(int(r.Items)))
	}
	return v
}
<|endoftext|>