<commit_before>// Copyright 2021 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 localbgp_test

import (
	"testing"
	"time"

	"github.com/openconfig/featureprofiles/internal/attrs"
	"github.com/openconfig/featureprofiles/internal/confirm"
	"github.com/openconfig/featureprofiles/internal/deviations"
	"github.com/openconfig/featureprofiles/internal/fptest"
	"github.com/openconfig/ondatra"
	"github.com/openconfig/ondatra/telemetry"
	"github.com/openconfig/ygot/ygot"
)

func TestMain(m *testing.M) {
	fptest.RunTests(m)
}

var (
	dutAttrs = attrs.Attributes{
		Desc:    "To ATE",
		IPv4:    "192.0.2.1",
		IPv4Len: 30,
	}
	ateAttrs = attrs.Attributes{
		Desc:    "To DUT",
		IPv4:    "192.0.2.2",
		IPv4Len: 30,
	}
)

const (
	dutAS = 64500
	ateAS = 64501
)

func bgpWithNbr(as uint32, routerID string, nbr *telemetry.NetworkInstance_Protocol_Bgp_Neighbor) *telemetry.NetworkInstance_Protocol_Bgp {
	bgp := &telemetry.NetworkInstance_Protocol_Bgp{}
	bgp.GetOrCreateGlobal().As = ygot.Uint32(as)
	if routerID != "" {
		bgp.Global.RouterId = ygot.String(routerID)
	}
	bgp.AppendNeighbor(nbr)
	return bgp
}

func TestEstablish(t *testing.T) {
	// Configure interfaces
	dut := ondatra.DUT(t, "dut1")
	dutPortName := dut.Port(t, "port1").Name()
	intf1 := dutAttrs.NewInterface(dutPortName)
	dut.Config().Interface(intf1.GetName()).Replace(t, intf1)
	ate := ondatra.DUT(t, "dut2")
	atePortName := ate.Port(t, "port1").Name()
	intf2 := ateAttrs.NewInterface(atePortName)
	ate.Config().Interface(intf2.GetName()).Replace(t, intf2)
	// Get BGP paths
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateAttrs.IPv4)
	// Remove any existing BGP config
	dutConfPath.Replace(t, nil)
	ateConfPath.Replace(t, nil)
	startTime := uint64(time.Now().Unix())
	// Start a new session
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateAttrs.IPv4),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutAttrs.IPv4),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)

	dutState := statePath.Get(t)
	confirm.State(t, dutConf, dutState)
	nbr := dutState.GetNeighbor(ateAttrs.IPv4)

	if !nbr.GetEnabled() {
		t.Errorf("Expected neighbor %v to be enabled", ateAttrs.IPv4)
	}
	if got, want := nbr.GetLastEstablished(), startTime*1000000000; got < want {
		t.Errorf("Bad last-established timestamp: got %v, want >= %v", got, want)
	}
	if got := nbr.GetEstablishedTransitions(); got != 1 {
		t.Errorf("Wrong established-transitions: got %v, want 1", got)
	}

	capabilities := map[telemetry.E_BgpTypes_BGP_CAPABILITY]bool{
		telemetry.BgpTypes_BGP_CAPABILITY_ROUTE_REFRESH: false,
		telemetry.BgpTypes_BGP_CAPABILITY_ASN32:         false,
		telemetry.BgpTypes_BGP_CAPABILITY_MPBGP:         false,
	}
	for _, cap := range nbrPath.SupportedCapabilities().Get(t) {
		capabilities[cap] = true
	}
	for cap, present := range capabilities {
		if !present {
			t.Errorf("Capability not reported: %v", cap)
		}
	}
}

func TestDisconnect(t *testing.T) {
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	nbrPath := statePath.Neighbor(ateIP)

	// Clear any existing config
	dutConfPath.Replace(t, nil)
	ateConfPath.Replace(t, nil)

	// Apply simple config
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateIP),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutIP),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
	// ateConfPath.Replace(t, nil)
	ateConfPath.Neighbor(dutIP).Enabled().Replace(t, false)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ACTIVE)
	code := nbrPath.Messages().Received().LastNotificationErrorCode().Get(t)
	if code != telemetry.BgpTypes_BGP_ERROR_CODE_CEASE {
		t.Errorf("On disconnect: expected error code %v, got %v", telemetry.BgpTypes_BGP_ERROR_CODE_CEASE, code)
	}
}

func TestParameters(t *testing.T) {
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateIP)

	cases := []struct {
		name      string
		dutConf   *telemetry.NetworkInstance_Protocol_Bgp
		ateConf   *telemetry.NetworkInstance_Protocol_Bgp
		wantState *telemetry.NetworkInstance_Protocol_Bgp
		skipMsg   string
	}{
		{
			name: "basic internal",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "basic external",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit AS",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				LocalAs:         ygot.Uint32(100),
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(100),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit router id",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "password",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				AuthPassword:    ygot.String("password"),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				AuthPassword:    ygot.String("password"),
			}),
		},
		{
			name: "hold-time",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(100),
					KeepaliveInterval: ygot.Uint16(50),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "connect-retry",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					ConnectRetry: ygot.Uint16(100),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
			skipMsg: "Not currently supported by EOS (see b/186141921)",
		},
		{
			name: "hold time negotiated",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(100),
					KeepaliveInterval: ygot.Uint16(50),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(135),
					KeepaliveInterval: ygot.Uint16(45),
				},
			}),
			wantState: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:           ygot.Uint16(100),
					NegotiatedHoldTime: ygot.Uint16(100),
					KeepaliveInterval:  ygot.Uint16(50),
				},
			}),
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if len(tc.skipMsg) > 0 {
				t.Skip(tc.skipMsg)
			}
			// Disable BGP
			dutConfPath.Replace(t, nil)
			ateConfPath.Replace(t, nil)
			// Renable and wait to establish
			dutConfPath.Replace(t, tc.dutConf)
			ateConfPath.Replace(t, tc.ateConf)
			nbrPath.SessionState().Await(t, time.Second*30, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
			stateDut := statePath.Get(t)
			wantState := tc.wantState
			if wantState == nil {
				// if you don't specify a wantState, we just validate that the state corresponding to each config value is what it should be.
				wantState = tc.dutConf
			}
			confirm.State(t, wantState, stateDut)
		})
	}
}
<commit_msg>BGP keepalive timer should be 1/3 of hold timer (#439)<commit_after>// Copyright 2021 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 localbgp_test

import (
	"testing"
	"time"

	"github.com/openconfig/featureprofiles/internal/attrs"
	"github.com/openconfig/featureprofiles/internal/confirm"
	"github.com/openconfig/featureprofiles/internal/deviations"
	"github.com/openconfig/featureprofiles/internal/fptest"
	"github.com/openconfig/ondatra"
	"github.com/openconfig/ondatra/telemetry"
	"github.com/openconfig/ygot/ygot"
)

func TestMain(m *testing.M) {
	fptest.RunTests(m)
}

var (
	dutAttrs = attrs.Attributes{
		Desc:    "To ATE",
		IPv4:    "192.0.2.1",
		IPv4Len: 30,
	}
	ateAttrs = attrs.Attributes{
		Desc:    "To DUT",
		IPv4:    "192.0.2.2",
		IPv4Len: 30,
	}
)

const (
	dutAS = 64500
	ateAS = 64501

	keepAlive = 50
	holdTime  = keepAlive * 3 // Should be 3x keepAlive, see RFC 4271 - A Border Gateway Protocol 4, Sec. 10
)

func bgpWithNbr(as uint32, routerID string, nbr *telemetry.NetworkInstance_Protocol_Bgp_Neighbor) *telemetry.NetworkInstance_Protocol_Bgp {
	bgp := &telemetry.NetworkInstance_Protocol_Bgp{}
	bgp.GetOrCreateGlobal().As = ygot.Uint32(as)
	if routerID != "" {
		bgp.Global.RouterId = ygot.String(routerID)
	}
	bgp.AppendNeighbor(nbr)
	return bgp
}

func TestEstablish(t *testing.T) {
	// Configure interfaces
	dut := ondatra.DUT(t, "dut1")
	dutPortName := dut.Port(t, "port1").Name()
	intf1 := dutAttrs.NewInterface(dutPortName)
	dut.Config().Interface(intf1.GetName()).Replace(t, intf1)
	ate := ondatra.DUT(t, "dut2")
	atePortName := ate.Port(t, "port1").Name()
	intf2 := ateAttrs.NewInterface(atePortName)
	ate.Config().Interface(intf2.GetName()).Replace(t, intf2)
	// Get BGP paths
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateAttrs.IPv4)
	// Remove any existing BGP config
	dutConfPath.Replace(t, nil)
	ateConfPath.Replace(t, nil)
	startTime := uint64(time.Now().Unix())
	// Start a new session
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateAttrs.IPv4),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutAttrs.IPv4),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)

	dutState := statePath.Get(t)
	confirm.State(t, dutConf, dutState)
	nbr := dutState.GetNeighbor(ateAttrs.IPv4)

	if !nbr.GetEnabled() {
		t.Errorf("Expected neighbor %v to be enabled", ateAttrs.IPv4)
	}
	if got, want := nbr.GetLastEstablished(), startTime*1000000000; got < want {
		t.Errorf("Bad last-established timestamp: got %v, want >= %v", got, want)
	}
	if got := nbr.GetEstablishedTransitions(); got != 1 {
		t.Errorf("Wrong established-transitions: got %v, want 1", got)
	}

	capabilities := map[telemetry.E_BgpTypes_BGP_CAPABILITY]bool{
		telemetry.BgpTypes_BGP_CAPABILITY_ROUTE_REFRESH: false,
		telemetry.BgpTypes_BGP_CAPABILITY_ASN32:         false,
		telemetry.BgpTypes_BGP_CAPABILITY_MPBGP:         false,
	}
	for _, cap := range nbrPath.SupportedCapabilities().Get(t) {
		capabilities[cap] = true
	}
	for cap, present := range capabilities {
		if !present {
			t.Errorf("Capability not reported: %v", cap)
		}
	}
}

func TestDisconnect(t *testing.T) {
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	nbrPath := statePath.Neighbor(ateIP)

	// Clear any existing config
	dutConfPath.Replace(t, nil)
	ateConfPath.Replace(t, nil)

	// Apply simple config
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateIP),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutIP),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
	// ateConfPath.Replace(t, nil)
	ateConfPath.Neighbor(dutIP).Enabled().Replace(t, false)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ACTIVE)
	code := nbrPath.Messages().Received().LastNotificationErrorCode().Get(t)
	if code != telemetry.BgpTypes_BGP_ERROR_CODE_CEASE {
		t.Errorf("On disconnect: expected error code %v, got %v", telemetry.BgpTypes_BGP_ERROR_CODE_CEASE, code)
	}
}

func TestParameters(t *testing.T) {
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateIP)

	cases := []struct {
		name      string
		dutConf   *telemetry.NetworkInstance_Protocol_Bgp
		ateConf   *telemetry.NetworkInstance_Protocol_Bgp
		wantState *telemetry.NetworkInstance_Protocol_Bgp
		skipMsg   string
	}{
		{
			name: "basic internal",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "basic external",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit AS",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				LocalAs:         ygot.Uint32(100),
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(100),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit router id",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "password",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				AuthPassword:    ygot.String("password"),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				AuthPassword:    ygot.String("password"),
			}),
		},
		{
			name: "hold-time",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(holdTime),
					KeepaliveInterval: ygot.Uint16(keepAlive),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "connect-retry",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					ConnectRetry: ygot.Uint16(100),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
			skipMsg: "Not currently supported by EOS (see b/186141921)",
		},
		{
			name: "hold time negotiated",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(holdTime),
					KeepaliveInterval: ygot.Uint16(keepAlive),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(135),
					KeepaliveInterval: ygot.Uint16(45),
				},
			}),
			wantState: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:           ygot.Uint16(holdTime),
					NegotiatedHoldTime: ygot.Uint16(135),
					KeepaliveInterval:  ygot.Uint16(keepAlive),
				},
			}),
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if len(tc.skipMsg) > 0 {
				t.Skip(tc.skipMsg)
			}
			// Disable BGP
			dutConfPath.Replace(t, nil)
			ateConfPath.Replace(t, nil)
			// Renable and wait to establish
			dutConfPath.Replace(t, tc.dutConf)
			ateConfPath.Replace(t, tc.ateConf)
			nbrPath.SessionState().Await(t, time.Second*30, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
			stateDut := statePath.Get(t)
			wantState := tc.wantState
			if wantState == nil {
				// if you don't specify a wantState, we just validate that the state corresponding to each config value is what it should be.
				wantState = tc.dutConf
			}
			confirm.State(t, wantState, stateDut)
		})
	}
}
<|endoftext|>


<commit_before>package ntypes_test

import (
	"encoding/json"
	"testing"

	"github.com/golang/protobuf/proto"
	"github.com/piotrkowalczuk/ntypes"
)

func TestInt64_ProtoMessage(t *testing.T) {
	var (
		buf []byte
		err error
		tmp ntypes.Int64
	)
	success := []ntypes.Int64{
		{Int64: 1, Valid: true},
		{Int64: 0, Valid: false},
		{Int64: 13123, Valid: false},
		{Int64: -1241223, Valid: true},
	}

	for _, given := range success {
		buf, err = proto.Marshal(&given)
		if err != nil {
			t.Errorf("marshal returned unexpected error: %s", err.Error())
			continue
		}

		err = proto.Unmarshal(buf, &tmp)
		if err != nil {
			t.Errorf("unmarshal returned unexpected error: %s", err.Error())
			continue
		}

		if tmp.Int64 != given.Int64 {
			t.Errorf("integers are not equal expected %d, got %d", given.Int64, tmp.Int64)
		}

		if tmp.Valid != given.Valid {
			t.Errorf("booleans are not equal expected %t, got %t", given.Valid, tmp.Valid)
		}
	}
}

func TestInt64_MarshalJSON(t *testing.T) {
	cases := map[string]struct {
		given    *ntypes.Int64
		expected string
	}{

		"nil": {
			given:    nil,
			expected: "null",
		},
		"zero value": {
			given:    &ntypes.Int64{},
			expected: "null",
		},
		"valid": {
			given:    &ntypes.Int64{Valid: true},
			expected: "0",
		},
		"invalid": {
			given:    &ntypes.Int64{Valid: false},
			expected: "null",
		},
		"non zero valid value": {
			given:    &ntypes.Int64{Int64: 123, Valid: true},
			expected: "123",
		},
		"non zero valid max value": {
			given:    &ntypes.Int64{Int64: 9223372036854775807, Valid: true},
			expected: "9223372036854775807",
		},
		"non zero valid min value": {
			given:    &ntypes.Int64{Int64: -9223372036854775808, Valid: true},
			expected: "-9223372036854775808",
		},
		"non zero invalid max value": {
			given:    &ntypes.Int64{Int64: 9223372036854775807, Valid: false},
			expected: "null",
		},
		"non zero invalid min value": {
			given:    &ntypes.Int64{Int64: -9223372036854775808, Valid: false},
			expected: "null",
		},
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			b, err := json.Marshal(c.given)
			if err != nil {
				t.Fatalf("%s: unexpected error: %s", d, err.Error())
			}
			if string(b) != c.expected {
				t.Errorf("%s: wrong output, expected %s but got %s", d, c.expected, string(b))
			}
		})
	}

	type within struct {
		ID *ntypes.Int64 `json:"id"`
	}
}

func TestUint32_Scan(t *testing.T) {
	testUint32_Scan_success(t, nil, 0, false)
}

func TestUint32_Scan_string(t *testing.T) {
	success := map[uint32]string{
		100:        "100",
		4294967295: "4294967295",
		0:          "0",
	}

	for expected, given := range success {
		testUint32_Scan_success(t, given, expected, true)
	}
}

func TestUint32_Scan_int64(t *testing.T) {
	success := map[uint32]int64{
		100:        100,
		4294967295: 4294967295,
		0:          0,
	}

	for expected, given := range success {
		testUint32_Scan_success(t, given, expected, true)
	}
}

func TestUint32_Scan_bytes(t *testing.T) {
	success := map[uint32][]byte{
		100:        []byte("100"),
		4294967295: []byte("4294967295"),
		0:          []byte("0"),
	}

	for expected, given := range success {
		testUint32_Scan_success(t, given, expected, true)
	}
}

func testUint32_Scan_success(t *testing.T, given interface{}, expectedValue uint32, expectedValid bool) {
	var u ntypes.Uint32
	if err := u.Scan(given); err != nil {
		t.Errorf("unexpected error: %s", err.Error())
		return
	}

	if u.Valid != expectedValid {
		t.Errorf("wrong valid property value, got %t but expected %t", u.Valid, expectedValid)
	}

	if u.Uint32 != expectedValue {
		t.Errorf("wrong uint32 property value, got %d but expected %d", u.Uint32, expectedValue)
	}
}

func TestUint32_MarshalJSON(t *testing.T) {
	cases := map[string]struct {
		given    *ntypes.Uint32
		expected string
	}{

		"nil": {
			given:    nil,
			expected: "null",
		},
		"zero value": {
			given:    &ntypes.Uint32{},
			expected: "null",
		},
		"valid": {
			given:    &ntypes.Uint32{Valid: true},
			expected: "0",
		},
		"invalid": {
			given:    &ntypes.Uint32{Valid: false},
			expected: "null",
		},
		"non zero valid value": {
			given:    &ntypes.Uint32{Uint32: 123, Valid: true},
			expected: "123",
		},
		"non zero valid max value": {
			given:    &ntypes.Uint32{Uint32: 4294967295, Valid: true},
			expected: "4294967295",
		},
		"non zero valid min value": {
			given:    &ntypes.Uint32{Uint32: 0, Valid: true},
			expected: "0",
		},
		"non zero invalid max value": {
			given:    &ntypes.Uint32{Uint32: 4294967295, Valid: false},
			expected: "null",
		},
		"non zero invalid min value": {
			given:    &ntypes.Uint32{Uint32: 0, Valid: false},
			expected: "null",
		},
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			b, err := json.Marshal(c.given)
			if err != nil {
				t.Fatalf("%s: unexpected error: %s", d, err.Error())
			}
			if string(b) != c.expected {
				t.Errorf("%s: wrong output, expected %s but got %s", d, c.expected, string(b))
			}
		})
	}

	type within struct {
		ID *ntypes.Uint32 `json:"id"`
	}
}

func TestBool_MarshalJSON(t *testing.T) {
	cases := map[string]*ntypes.Bool{
		"nil":         nil,
		"zero value":  {},
		"valid":       {Valid: true},
		"invalid":     {Valid: false},
		"true true":   {Bool: true, Valid: true},
		"true false":  {Bool: true, Valid: false},
		"false false": {Bool: false, Valid: false},
		"false true":  {Bool: false, Valid: true},
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			_, err := json.Marshal(c)
			if err != nil {
				t.Fatalf("simple: %s: unexpected error: %s", d, err.Error())
			}
		})
	}

	type within struct {
		Exists *ntypes.Bool `json:"exists"`
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			w := within{Exists: c}
			_, err := json.Marshal(w)
			if err != nil {
				t.Errorf("within: %s: unexpected error: %s", d, err.Error())
			}
		})
	}
}

func TestString_Value(t *testing.T) {
	t.Run("valid", func(t *testing.T) {
		given := ntypes.String{String: "something", Valid: true}
		val, err := given.Value()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if _, ok := val.(string); !ok {
			t.Fatalf("unexpected output type, expected string but got %T", val)
		}
	})
	t.Run("invalid", func(t *testing.T) {
		given := ntypes.String{String: "something", Valid: false}
		val, err := given.Value()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if val != nil {
			t.Fatalf("unexpected value, expected nil but got %v", val)
		}
	})
}

func TestString_MarshalJSON(t *testing.T) {
	t.Run("valid", func(t *testing.T) {
		expected := `"test"`
		given := &ntypes.String{String: "test", Valid: true}
		got, err := given.MarshalJSON()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if string(got) != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, string(got))
		}
	})
	t.Run("invalid", func(t *testing.T) {
		expected := `null`
		given := &ntypes.String{String: "test", Valid: false}
		got, err := given.MarshalJSON()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if string(got) != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, string(got))
		}
	})
	t.Run("nil", func(t *testing.T) {
		expected := `null`
		var given *ntypes.String
		got, err := given.MarshalJSON()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if string(got) != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, string(got))
		}
	})
}

func TestString_Scan(t *testing.T) {
	cases := map[string]struct {
		given    interface{}
		expected string
	}{
		"bytes": {
			given:    []byte("some byte slice"),
			expected: "some byte slice",
		},
		"string": {
			given:    "some string",
			expected: "some string",
		},
		"nil": {
			expected: "",
		},
	}

	for hint, c := range cases {
		t.Run(hint, func(t *testing.T) {
			str := &ntypes.String{}
			err := str.Scan(c.given)
			if err != nil {
				t.Fatalf("unexpected error: %s")
			}
			if c.expected != str.String {
				t.Fatalf("wrong output, expected %s but got %s", c.expected, str.String)
			}

		})
	}
}
func TestString_StringOr(t *testing.T) {
	t.Run("valid", func(t *testing.T) {
		expected := "test"
		given := &ntypes.String{String: expected, Valid: true}
		got := given.StringOr("alternative")

		if got != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, got)
		}
	})
	t.Run("invalid", func(t *testing.T) {
		expected := "alternative"
		given := &ntypes.String{String: "test", Valid: false}
		got := given.StringOr(expected)

		if got != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, got)
		}
	})
	t.Run("nil", func(t *testing.T) {
		expected := "alternative"
		var given *ntypes.String
		got := given.StringOr(expected)

		if got != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, got)
		}
	})
}
<commit_msg>XXXOr method test coverage<commit_after>package ntypes_test

import (
	"encoding/json"
	"testing"

	"github.com/golang/protobuf/proto"
	"github.com/piotrkowalczuk/ntypes"
)

func TestInt64_ProtoMessage(t *testing.T) {
	var (
		buf []byte
		err error
		tmp ntypes.Int64
	)
	success := []ntypes.Int64{
		{Int64: 1, Valid: true},
		{Int64: 0, Valid: false},
		{Int64: 13123, Valid: false},
		{Int64: -1241223, Valid: true},
	}

	for _, given := range success {
		buf, err = proto.Marshal(&given)
		if err != nil {
			t.Errorf("marshal returned unexpected error: %s", err.Error())
			continue
		}

		err = proto.Unmarshal(buf, &tmp)
		if err != nil {
			t.Errorf("unmarshal returned unexpected error: %s", err.Error())
			continue
		}

		if tmp.Int64 != given.Int64 {
			t.Errorf("integers are not equal expected %d, got %d", given.Int64, tmp.Int64)
		}

		if tmp.Valid != given.Valid {
			t.Errorf("booleans are not equal expected %t, got %t", given.Valid, tmp.Valid)
		}
	}
}

func TestInt64_MarshalJSON(t *testing.T) {
	cases := map[string]struct {
		given    *ntypes.Int64
		expected string
	}{

		"nil": {
			given:    nil,
			expected: "null",
		},
		"zero value": {
			given:    &ntypes.Int64{},
			expected: "null",
		},
		"valid": {
			given:    &ntypes.Int64{Valid: true},
			expected: "0",
		},
		"invalid": {
			given:    &ntypes.Int64{Valid: false},
			expected: "null",
		},
		"non zero valid value": {
			given:    &ntypes.Int64{Int64: 123, Valid: true},
			expected: "123",
		},
		"non zero valid max value": {
			given:    &ntypes.Int64{Int64: 9223372036854775807, Valid: true},
			expected: "9223372036854775807",
		},
		"non zero valid min value": {
			given:    &ntypes.Int64{Int64: -9223372036854775808, Valid: true},
			expected: "-9223372036854775808",
		},
		"non zero invalid max value": {
			given:    &ntypes.Int64{Int64: 9223372036854775807, Valid: false},
			expected: "null",
		},
		"non zero invalid min value": {
			given:    &ntypes.Int64{Int64: -9223372036854775808, Valid: false},
			expected: "null",
		},
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			b, err := json.Marshal(c.given)
			if err != nil {
				t.Fatalf("%s: unexpected error: %s", d, err.Error())
			}
			if string(b) != c.expected {
				t.Errorf("%s: wrong output, expected %s but got %s", d, c.expected, string(b))
			}
		})
	}

	type within struct {
		ID *ntypes.Int64 `json:"id"`
	}
}

func TestUint32_Scan(t *testing.T) {
	testUint32_Scan_success(t, nil, 0, false)
}

func TestUint32_Scan_string(t *testing.T) {
	success := map[uint32]string{
		100:        "100",
		4294967295: "4294967295",
		0:          "0",
	}

	for expected, given := range success {
		testUint32_Scan_success(t, given, expected, true)
	}
}

func TestUint32_Scan_int64(t *testing.T) {
	success := map[uint32]int64{
		100:        100,
		4294967295: 4294967295,
		0:          0,
	}

	for expected, given := range success {
		testUint32_Scan_success(t, given, expected, true)
	}
}

func TestUint32_Scan_bytes(t *testing.T) {
	success := map[uint32][]byte{
		100:        []byte("100"),
		4294967295: []byte("4294967295"),
		0:          []byte("0"),
	}

	for expected, given := range success {
		testUint32_Scan_success(t, given, expected, true)
	}
}

func testUint32_Scan_success(t *testing.T, given interface{}, expectedValue uint32, expectedValid bool) {
	var u ntypes.Uint32
	if err := u.Scan(given); err != nil {
		t.Errorf("unexpected error: %s", err.Error())
		return
	}

	if u.Valid != expectedValid {
		t.Errorf("wrong valid property value, got %t but expected %t", u.Valid, expectedValid)
	}

	if u.Uint32 != expectedValue {
		t.Errorf("wrong uint32 property value, got %d but expected %d", u.Uint32, expectedValue)
	}
}

func TestUint32_MarshalJSON(t *testing.T) {
	cases := map[string]struct {
		given    *ntypes.Uint32
		expected string
	}{

		"nil": {
			given:    nil,
			expected: "null",
		},
		"zero value": {
			given:    &ntypes.Uint32{},
			expected: "null",
		},
		"valid": {
			given:    &ntypes.Uint32{Valid: true},
			expected: "0",
		},
		"invalid": {
			given:    &ntypes.Uint32{Valid: false},
			expected: "null",
		},
		"non zero valid value": {
			given:    &ntypes.Uint32{Uint32: 123, Valid: true},
			expected: "123",
		},
		"non zero valid max value": {
			given:    &ntypes.Uint32{Uint32: 4294967295, Valid: true},
			expected: "4294967295",
		},
		"non zero valid min value": {
			given:    &ntypes.Uint32{Uint32: 0, Valid: true},
			expected: "0",
		},
		"non zero invalid max value": {
			given:    &ntypes.Uint32{Uint32: 4294967295, Valid: false},
			expected: "null",
		},
		"non zero invalid min value": {
			given:    &ntypes.Uint32{Uint32: 0, Valid: false},
			expected: "null",
		},
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			b, err := json.Marshal(c.given)
			if err != nil {
				t.Fatalf("%s: unexpected error: %s", d, err.Error())
			}
			if string(b) != c.expected {
				t.Errorf("%s: wrong output, expected %s but got %s", d, c.expected, string(b))
			}
		})
	}

	type within struct {
		ID *ntypes.Uint32 `json:"id"`
	}
}

func TestBool_MarshalJSON(t *testing.T) {
	cases := map[string]*ntypes.Bool{
		"nil":         nil,
		"zero value":  {},
		"valid":       {Valid: true},
		"invalid":     {Valid: false},
		"true true":   {Bool: true, Valid: true},
		"true false":  {Bool: true, Valid: false},
		"false false": {Bool: false, Valid: false},
		"false true":  {Bool: false, Valid: true},
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			_, err := json.Marshal(c)
			if err != nil {
				t.Fatalf("simple: %s: unexpected error: %s", d, err.Error())
			}
		})
	}

	type within struct {
		Exists *ntypes.Bool `json:"exists"`
	}

	for d, c := range cases {
		t.Run(d, func(t *testing.T) {
			w := within{Exists: c}
			_, err := json.Marshal(w)
			if err != nil {
				t.Errorf("within: %s: unexpected error: %s", d, err.Error())
			}
		})
	}
}

func TestString_Value(t *testing.T) {
	t.Run("valid", func(t *testing.T) {
		given := ntypes.String{String: "something", Valid: true}
		val, err := given.Value()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if _, ok := val.(string); !ok {
			t.Fatalf("unexpected output type, expected string but got %T", val)
		}
	})
	t.Run("invalid", func(t *testing.T) {
		given := ntypes.String{String: "something", Valid: false}
		val, err := given.Value()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if val != nil {
			t.Fatalf("unexpected value, expected nil but got %v", val)
		}
	})
}

func TestString_MarshalJSON(t *testing.T) {
	t.Run("valid", func(t *testing.T) {
		expected := `"test"`
		given := &ntypes.String{String: "test", Valid: true}
		got, err := given.MarshalJSON()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if string(got) != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, string(got))
		}
	})
	t.Run("invalid", func(t *testing.T) {
		expected := `null`
		given := &ntypes.String{String: "test", Valid: false}
		got, err := given.MarshalJSON()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if string(got) != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, string(got))
		}
	})
	t.Run("nil", func(t *testing.T) {
		expected := `null`
		var given *ntypes.String
		got, err := given.MarshalJSON()
		if err != nil {
			t.Fatalf("unexpected error: %s", err.Error())
		}
		if string(got) != expected {
			t.Fatalf("wrong output, expected %s but got %s", expected, string(got))
		}
	})
}

func TestString_Scan(t *testing.T) {
	cases := map[string]struct {
		given    interface{}
		expected string
	}{
		"bytes": {
			given:    []byte("some byte slice"),
			expected: "some byte slice",
		},
		"string": {
			given:    "some string",
			expected: "some string",
		},
		"nil": {
			expected: "",
		},
	}

	for hint, c := range cases {
		t.Run(hint, func(t *testing.T) {
			str := &ntypes.String{}
			err := str.Scan(c.given)
			if err != nil {
				t.Fatalf("unexpected error: %s")
			}
			if c.expected != str.String {
				t.Fatalf("wrong output, expected %s but got %s", c.expected, str.String)
			}

		})
	}
}
func TestString_StringOr(t *testing.T) {
	cases := map[string]struct {
		expected string
		or       string
		given    *ntypes.String
	}{
		"valid": {
			expected: "test",
			given:    &ntypes.String{String: "test", Valid: true},
			or:       "alternative",
		},
		"invalid": {
			expected: "alternative",
			given:    &ntypes.String{String: "test", Valid: false},
			or:       "alternative",
		},
		"nil": {
			expected: "alternative",
			or:       "alternative",
		},
	}

	for hint, c := range cases {
		t.Run(hint, func(t *testing.T) {
			got := c.given.StringOr(c.or)

			if got != c.expected {
				t.Fatalf("wrong output, expected %s but got %s", c.expected, got)
			}
		})
	}
}

func TestFloat64_Float64Or(t *testing.T) {
	cases := map[string]struct {
		expected float64
		or       float64
		given    *ntypes.Float64
	}{
		"valid": {
			expected: 0.1,
			given:    &ntypes.Float64{Float64: 0.1, Valid: true},
			or:       0.2,
		},
		"invalid": {
			expected: 0.2,
			given:    &ntypes.Float64{Float64: 0.1, Valid: false},
			or:       0.2,
		},
		"nil": {
			expected: 0.3,
			or:       0.3,
		},
	}

	for hint, c := range cases {
		t.Run(hint, func(t *testing.T) {
			got := c.given.Float64Or(c.or)

			if got != c.expected {
				t.Fatalf("wrong output, expected %s but got %s", c.expected, got)
			}
		})
	}
}

func TestFloat32_Float32Or(t *testing.T) {
	cases := map[string]struct {
		expected float32
		or       float32
		given    *ntypes.Float32
	}{
		"valid": {
			expected: 0.1,
			given:    &ntypes.Float32{Float32: 0.1, Valid: true},
			or:       0.2,
		},
		"invalid": {
			expected: 0.2,
			given:    &ntypes.Float32{Float32: 0.1, Valid: false},
			or:       0.2,
		},
		"nil": {
			expected: 0.3,
			or:       0.3,
		},
	}

	for hint, c := range cases {
		t.Run(hint, func(t *testing.T) {
			got := c.given.Float32Or(c.or)

			if got != c.expected {
				t.Fatalf("wrong output, expected %s but got %s", c.expected, got)
			}
		})
	}
}

func TestBool_BoolOr(t *testing.T) {
	cases := map[string]struct {
		expected bool
		or       bool
		given    *ntypes.Bool
	}{
		"valid": {
			expected: true,
			given:    &ntypes.Bool{Bool: true, Valid: true},
			or:       false,
		},
		"invalid": {
			expected: false,
			given:    &ntypes.Bool{Bool: true, Valid: false},
			or:       false,
		},
		"nil": {
			expected: true,
			or:       true,
		},
	}

	for hint, c := range cases {
		t.Run(hint, func(t *testing.T) {
			got := c.given.BoolOr(c.or)

			if got != c.expected {
				t.Fatalf("wrong output, expected %s but got %s", c.expected, got)
			}
		})
	}
}
<|endoftext|>


<commit_before>package webgo

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"
	//"sync"
	"bytes"
)

type App struct {
	router        Router
	definitions   Definitions
	templates     *template.Template
	staticDir     string
	modules       Modules
	workDir       string
	tmpDir        string
	maxBodyLength int64
}

const (
	CT_JSON      = "application/json"
	CT_FORM      = "application/x-www-form-urlencoded"
	CT_MULTIPART = "multipart/form-data"
)

var app App
var LOGGER *Logger

func init() {

	// Init LOGGER
	LOGGER = NewLogger()

	cp := consoleProvider{}
	ep := emailProvider{}

	LOGGER.RegisterProvider(cp)
	LOGGER.RegisterProvider(ep)

	LOGGER.AddLogProvider(PROVIDER_CONSOLE)
	LOGGER.AddErrorProvider(PROVIDER_CONSOLE, PROVIDER_EMAIL)
	LOGGER.AddFatalProvider(PROVIDER_CONSOLE, PROVIDER_EMAIL)
	LOGGER.AddDebugProvider(PROVIDER_CONSOLE)

	// Init App
	templates := template.New("template")
	filepath.Walk("templates", func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(path, ".html") {
			templates.ParseFiles(path)
		}
		return nil
	})
	app = App{}
	app.router = Router{make(Routes)}
	app.definitions = Definitions{}
	app.templates = templates
	app.staticDir = "public"
	app.modules = Modules{}

	app.workDir, _ = os.Getwd()
	app.tmpDir = app.workDir + "/tmp"
	app.maxBodyLength = 131072

	//TODO: Проверить папку tmp, создать если необходимо
}

func parseRequest(ctx *Context, limit int64) (errorCode int, err error) {
	var body []byte

	defer func() {
		r := recover()
		if r != nil {
			errorCode = 400
			err = errors.New("Bad Request")
		}
	}()
	ctx.Request.Body = http.MaxBytesReader(ctx.Response, ctx.Request.Body, limit)

	if ctx.Request.Method == "GET" {
		err = ctx.Request.ParseForm()
		if err != nil {
			errorCode = 400
			return
		}

		// Копируем данные
		for i := range ctx.Request.Form {
			ctx.Query[i] = ctx.Request.Form[i]
		}

		return
	}

	switch ctx.ContentType {
	case CT_JSON:
		body, err = ioutil.ReadAll(ctx.Request.Body)
		if err != nil {
			errorCode = 400
			return
		}

		var data interface{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			errorCode = 400
			return
		}
		ctx._Body = body
		ctx.Body = data.(map[string]interface{})

		return
	case CT_FORM:
		err = ctx.Request.ParseForm()
		if err != nil {
			errorCode = 400
			return
		}

	case CT_MULTIPART:
		err = ctx.Request.ParseMultipartForm(limit)
		if err != nil {
			//TODO: 400 or 413
			errorCode = 400
			return
		}

		for _, fheaders := range ctx.Request.MultipartForm.File {
			for _, hdr := range fheaders {
				var infile multipart.File
				if infile, err = hdr.Open(); nil != err {
					errorCode = 500
					return
				}

				var outfile *os.File
				if outfile, err = os.Create(app.tmpDir + "/" + hdr.Filename); nil != err {
					errorCode = 500
					return
				}
				// 32K buffer copy
				var written int64
				if written, err = io.Copy(outfile, infile); nil != err {
					errorCode = 500
					return
				}

				ctx.Files = append(ctx.Files, File{FileName: hdr.Filename, Size: int64(written)})
			}
		}

	default:
		err = errors.New("Bad Request")
		errorCode = 400
		return
	}

	for i := range ctx.Request.Form {
		ctx.Body[i] = ctx.Request.Form[i]
	}

	return
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//cn, ok := w.(http.CloseNotifier)
	//if !ok {
	//	LOGGER.Fatal("don't support CloseNotifier")
	//}

	var vc reflect.Value
	var Action reflect.Value
	var middlewareGroup string

	method := r.Method
	path := r.URL.Path

	// TODO как отдавать статику?
	/*// Отдаем статику если был запрошен файл
	ext := filepath.Ext(path)
	if ext != "" {
		http.ServeFile(w, r, app.staticDir+filepath.Clean(path))
		return
	}*/

	if len(path) > 1 && path[len(path)-1:] == "/" {
		http.Redirect(w, r, path[:len(path)-1], 301)
		return
	}

	route := a.router.Match(method, path)
	if route == nil {
		http.Error(w, "", 404)
		return
	}

	if route.Options.Timeout == 0 {
		route.Options.Timeout = 2
	}
	//timeout := time.After(route.Options.Timeout * time.Second)
	//done := make(chan bool)

	vc = reflect.New(route.ControllerType)
	Action = vc.MethodByName(route.Options.Action)
	middlewareGroup = route.Options.MiddlewareGroup

	var err error
	ctx := Context{Action: route.Options.Action, Response: w, Request: r, Query: make(map[string]interface{}), Body: make(map[string]interface{}), Params: route.Params, Method: method}
	ctx.ContentType = ctx.Request.Header.Get("Content-Type")
	ctx.ContentType, _, err = mime.ParseMediaType(ctx.ContentType)

	if err != nil && method != "GET" {
		http.Error(w, "", 400)
		return
	}

	if route.Options.ContentType != "" && (method == "POST" || method == "PUT") {
		if route.Options.ContentType != ctx.ContentType {
			http.Error(w, "", 400)
			return
		}
	}

	Controller, ok := vc.Interface().(ControllerInterface)
	if !ok {
		LOGGER.Error(errors.New("controller is not ControllerInterface"))
		http.Error(w, "", 500)
		return
	}

	// Парсим запрос
	var maxBodyLength int64 = app.maxBodyLength
	if route.Options.BodyLength > 0 {
		maxBodyLength = route.Options.BodyLength
	}

	code, err := parseRequest(&ctx, maxBodyLength)
	if err != nil {
		http.Error(w, "", code)
		return
	}

	// Инициализация контекста
	Controller.Init(&ctx)

	// Запуск предобработчика
	if !Controller.Prepare() {
		Controller.exec()
		return
	}

	// Запуск цепочки middleware
	if middlewareGroup != "" {
		isNext := app.definitions.Run(middlewareGroup, &ctx)
		if !isNext {
			return
		}
	}

	in := make([]reflect.Value, 0)
	Action.Call(in)
	//go func () {
	//	in := make([]reflect.Value, 0)
	//	Action.Call(in)
	//	done <- true
	//}()

	// Запуск постобработчика

	Controller.Finish()

	if ctx.ContentType == "multipart/form-data" {
		err = ctx.Files.RemoveAll()
		if err != nil {
			LOGGER.Error(err)
		}

		err = ctx.Request.MultipartForm.RemoveAll()
		if err != nil {
			LOGGER.Error(err)
		}
	}

	Controller.exec()

	//select {
	//case <-timeout:
	//	ctx.close = true
	//	w.WriteHeader(503)
	//	w.Write([]byte(""))
	//	return
	//case <-cn.CloseNotify():
	//	//TODO: НИХРЕНА НЕПОНЯТНО!!!
	//	ctx.close = true
	//	w.WriteHeader(503)
	//	w.Write([]byte(""))
	//	return
	//case <-done:
	//	// TODO: Обработать ошибки
	//	if ctx.error != nil {
	//		if ctx.code == 0 {
	//			ctx.code = 500
	//		}
	//		ctx.Response.WriteHeader(ctx.code)
	//		ctx.Response.Write(ctx.output)
	//		return
	//	}
	//
	//	// Проверяем редирект
	//	if ctx.IsRedirect(){
	//		ctx.Response.WriteHeader(ctx.code)
	//		return
	//	}
	//
	//	// Выводим данные
	//	if ctx.code == 0 {
	//		ctx.code = 200
	//	}
	//	ctx.Response.WriteHeader(ctx.code)
	//	ctx.Response.Write(ctx.output)
	//	return
	//}

}

func RegisterMiddleware(name string, plugins ...MiddlewareInterface) {
	for _, plugin := range plugins {
		app.definitions.Register(name, plugin)
	}
}
func RegisterModule(name string, module ModuleInterface) {
	app.modules.RegisterModule(name, module)
}
func Get(url string, opts RouteOptions) {
	app.router.addRoute("GET", url, &opts)
}
func Post(url string, opts RouteOptions) {
	app.router.addRoute("POST", url, &opts)
}
func Put(url string, opts RouteOptions) {
	app.router.addRoute("PUT", url, &opts)
}
func Delete(url string, opts RouteOptions) {
	app.router.addRoute("DELETE", url, &opts)
}
func Options(url string, opts RouteOptions) {
	app.router.addRoute("OPTIONS", url, &opts)
}

func GetModule(str string) ModuleInterface {
	return app.modules[str]
}
func Mail(address string, subject string, tpl string, model interface{}) (err error) {
	var message []byte
	bytes := bytes.NewBufferString("")
	err = app.templates.ExecuteTemplate(bytes, tpl+".html", model)
	if err != nil {
		return
	}
	message, err = ioutil.ReadAll(bytes)

	mail := NewMail(address, subject, string(message))
	err = mail.SendMail()
	return
}
func Run() {
	var r *int = flag.Int("r", 0, "read timeout")
	var w *int = flag.Int("w", 0, "write timeout")

	port := CFG.Int("port")

	if port == 0 {
		port = 80
	}

	host := CFG.Str("host")
	if host == "" {
		host = "127.0.0.1"
	}

	address := fmt.Sprintf("%s:%d", host, port)
	fmt.Println("WebGO start ", address)

	server := http.Server{
		Addr:         address,
		ReadTimeout:  time.Duration(*r) * time.Second,
		WriteTimeout: time.Duration(*w) * time.Second,
		Handler:      &app,
	}

	//server.SetKeepAlivesEnabled(false)

	err := server.ListenAndServe()
	if err != nil {
		LOGGER.Fatal(err)
	}

}
<commit_msg>Fix body parse<commit_after>package webgo

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"
	//"sync"
	"bytes"
)

type App struct {
	router        Router
	definitions   Definitions
	templates     *template.Template
	staticDir     string
	modules       Modules
	workDir       string
	tmpDir        string
	maxBodyLength int64
}

const (
	CT_JSON      = "application/json"
	CT_FORM      = "application/x-www-form-urlencoded"
	CT_MULTIPART = "multipart/form-data"
)

var app App
var LOGGER *Logger

func init() {

	// Init LOGGER
	LOGGER = NewLogger()

	cp := consoleProvider{}
	ep := emailProvider{}

	LOGGER.RegisterProvider(cp)
	LOGGER.RegisterProvider(ep)

	LOGGER.AddLogProvider(PROVIDER_CONSOLE)
	LOGGER.AddErrorProvider(PROVIDER_CONSOLE, PROVIDER_EMAIL)
	LOGGER.AddFatalProvider(PROVIDER_CONSOLE, PROVIDER_EMAIL)
	LOGGER.AddDebugProvider(PROVIDER_CONSOLE)

	// Init App
	templates := template.New("template")
	filepath.Walk("templates", func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(path, ".html") {
			templates.ParseFiles(path)
		}
		return nil
	})
	app = App{}
	app.router = Router{make(Routes)}
	app.definitions = Definitions{}
	app.templates = templates
	app.staticDir = "public"
	app.modules = Modules{}

	app.workDir, _ = os.Getwd()
	app.tmpDir = app.workDir + "/tmp"
	app.maxBodyLength = 131072

	//TODO: Проверить папку tmp, создать если необходимо
}

func parseRequest(ctx *Context, limit int64) (errorCode int, err error) {
	var body []byte

	defer func() {
		r := recover()
		if r != nil {
			errorCode = 400
			err = errors.New("Bad Request")
		}
	}()
	ctx.Request.Body = http.MaxBytesReader(ctx.Response, ctx.Request.Body, limit)

	if ctx.Request.Method == "GET" {
		err = ctx.Request.ParseForm()
		if err != nil {
			errorCode = 400
			return
		}

		// Копируем данные
		for i := range ctx.Request.Form {
			ctx.Query[i] = ctx.Request.Form[i]
		}

		return
	}

	switch ctx.ContentType {
	case CT_JSON:
		body, err = ioutil.ReadAll(ctx.Request.Body)
		if err != nil {
			errorCode = 400
			return
		}

		var data interface{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			errorCode = 400
			return
		}
		ctx._Body = body
		ctx.Body = data.(map[string]interface{})

		return
	case CT_FORM:
		err = ctx.Request.ParseForm()
		if err != nil {
			errorCode = 400
			return
		}

	case CT_MULTIPART:
		err = ctx.Request.ParseMultipartForm(limit)
		if err != nil {
			//TODO: 400 or 413
			errorCode = 400
			return
		}

		for _, fheaders := range ctx.Request.MultipartForm.File {
			for _, hdr := range fheaders {
				var infile multipart.File
				if infile, err = hdr.Open(); nil != err {
					errorCode = 500
					return
				}

				var outfile *os.File
				if outfile, err = os.Create(app.tmpDir + "/" + hdr.Filename); nil != err {
					errorCode = 500
					return
				}
				// 32K buffer copy
				var written int64
				if written, err = io.Copy(outfile, infile); nil != err {
					errorCode = 500
					return
				}

				ctx.Files = append(ctx.Files, File{FileName: hdr.Filename, Size: int64(written)})
			}
		}

	default:
		if ctx.Request.ContentLength > 0 {
			err = errors.New("Bad Request")
			errorCode = 400
		}

		return
	}

	for i := range ctx.Request.Form {
		ctx.Body[i] = ctx.Request.Form[i]
	}

	return
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//cn, ok := w.(http.CloseNotifier)
	//if !ok {
	//	LOGGER.Fatal("don't support CloseNotifier")
	//}

	var vc reflect.Value
	var Action reflect.Value
	var middlewareGroup string

	method := r.Method
	path := r.URL.Path

	// TODO как отдавать статику?
	/*// Отдаем статику если был запрошен файл
	ext := filepath.Ext(path)
	if ext != "" {
		http.ServeFile(w, r, app.staticDir+filepath.Clean(path))
		return
	}*/

	if len(path) > 1 && path[len(path)-1:] == "/" {
		http.Redirect(w, r, path[:len(path)-1], 301)
		return
	}

	route := a.router.Match(method, path)
	if route == nil {
		http.Error(w, "", 404)
		return
	}

	if route.Options.Timeout == 0 {
		route.Options.Timeout = 2
	}
	//timeout := time.After(route.Options.Timeout * time.Second)
	//done := make(chan bool)

	vc = reflect.New(route.ControllerType)
	Action = vc.MethodByName(route.Options.Action)
	middlewareGroup = route.Options.MiddlewareGroup

	var err error
	ctx := Context{Action: route.Options.Action, Response: w, Request: r, Query: make(map[string]interface{}), Body: make(map[string]interface{}), Params: route.Params, Method: method}
	ctx.ContentType = ctx.Request.Header.Get("Content-Type")
	ctx.ContentType, _, err = mime.ParseMediaType(ctx.ContentType)

	if ctx.Request.ContentLength > 0 && (err != nil || route.Options.ContentType != ctx.ContentType){
		http.Error(w, "", 400)
		return
	}

	Controller, ok := vc.Interface().(ControllerInterface)
	if !ok {
		LOGGER.Error(errors.New("controller is not ControllerInterface"))
		http.Error(w, "", 500)
		return
	}

	// Парсим запрос
	var maxBodyLength int64 = app.maxBodyLength
	if route.Options.BodyLength > 0 {
		maxBodyLength = route.Options.BodyLength
	}

	code, err := parseRequest(&ctx, maxBodyLength)
	if err != nil {
		http.Error(w, "", code)
		return
	}

	// Инициализация контекста
	Controller.Init(&ctx)

	// Запуск предобработчика
	if !Controller.Prepare() {
		Controller.exec()
		return
	}

	// Запуск цепочки middleware
	if middlewareGroup != "" {
		isNext := app.definitions.Run(middlewareGroup, &ctx)
		if !isNext {
			return
		}
	}

	in := make([]reflect.Value, 0)
	Action.Call(in)
	//go func () {
	//	in := make([]reflect.Value, 0)
	//	Action.Call(in)
	//	done <- true
	//}()

	// Запуск постобработчика

	Controller.Finish()

	if ctx.ContentType == "multipart/form-data" {
		err = ctx.Files.RemoveAll()
		if err != nil {
			LOGGER.Error(err)
		}

		err = ctx.Request.MultipartForm.RemoveAll()
		if err != nil {
			LOGGER.Error(err)
		}
	}

	Controller.exec()

	//select {
	//case <-timeout:
	//	ctx.close = true
	//	w.WriteHeader(503)
	//	w.Write([]byte(""))
	//	return
	//case <-cn.CloseNotify():
	//	//TODO: НИХРЕНА НЕПОНЯТНО!!!
	//	ctx.close = true
	//	w.WriteHeader(503)
	//	w.Write([]byte(""))
	//	return
	//case <-done:
	//	// TODO: Обработать ошибки
	//	if ctx.error != nil {
	//		if ctx.code == 0 {
	//			ctx.code = 500
	//		}
	//		ctx.Response.WriteHeader(ctx.code)
	//		ctx.Response.Write(ctx.output)
	//		return
	//	}
	//
	//	// Проверяем редирект
	//	if ctx.IsRedirect(){
	//		ctx.Response.WriteHeader(ctx.code)
	//		return
	//	}
	//
	//	// Выводим данные
	//	if ctx.code == 0 {
	//		ctx.code = 200
	//	}
	//	ctx.Response.WriteHeader(ctx.code)
	//	ctx.Response.Write(ctx.output)
	//	return
	//}

}

func RegisterMiddleware(name string, plugins ...MiddlewareInterface) {
	for _, plugin := range plugins {
		app.definitions.Register(name, plugin)
	}
}
func RegisterModule(name string, module ModuleInterface) {
	app.modules.RegisterModule(name, module)
}
func Get(url string, opts RouteOptions) {
	app.router.addRoute("GET", url, &opts)
}
func Post(url string, opts RouteOptions) {
	app.router.addRoute("POST", url, &opts)
}
func Put(url string, opts RouteOptions) {
	app.router.addRoute("PUT", url, &opts)
}
func Delete(url string, opts RouteOptions) {
	app.router.addRoute("DELETE", url, &opts)
}
func Options(url string, opts RouteOptions) {
	app.router.addRoute("OPTIONS", url, &opts)
}

func GetModule(str string) ModuleInterface {
	return app.modules[str]
}
func Mail(address string, subject string, tpl string, model interface{}) (err error) {
	var message []byte
	bytes := bytes.NewBufferString("")
	err = app.templates.ExecuteTemplate(bytes, tpl+".html", model)
	if err != nil {
		return
	}
	message, err = ioutil.ReadAll(bytes)

	mail := NewMail(address, subject, string(message))
	err = mail.SendMail()
	return
}
func Run() {
	var r *int = flag.Int("r", 0, "read timeout")
	var w *int = flag.Int("w", 0, "write timeout")

	port := CFG.Int("port")

	if port == 0 {
		port = 80
	}

	host := CFG.Str("host")
	if host == "" {
		host = "127.0.0.1"
	}

	address := fmt.Sprintf("%s:%d", host, port)
	fmt.Println("WebGO start ", address)

	server := http.Server{
		Addr:         address,
		ReadTimeout:  time.Duration(*r) * time.Second,
		WriteTimeout: time.Duration(*w) * time.Second,
		Handler:      &app,
	}

	//server.SetKeepAlivesEnabled(false)

	err := server.ListenAndServe()
	if err != nil {
		LOGGER.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/jra-go/mqtt"
	"log"
	"net"
)

type mlRes struct {
	conn net.Conn
	err  error
}

type multiListener struct {
	listeners []net.Listener
	next      chan mlRes
}

func newMultiListener(l ...net.Listener) *multiListener {
	ml := &multiListener{
		listeners: make([]net.Listener, len(l)),
		next:      make(chan mlRes, len(l)),
	}
	copy(ml.listeners, l)

	for _, l := range ml.listeners {
		go func(l net.Listener, next chan mlRes) {
			for {
				res := mlRes{}
				res.conn, res.err = l.Accept()
				next <- res
			}
		}(l, ml.next)
	}

	return ml
}

func (ml *multiListener) Accept() (net.Conn, error) {
	res := <-ml.next
	return res.conn, res.err
}

func (ml *multiListener) Close() error {
	for _, l := range ml.listeners {
		l.Close()
	}
	return nil
}

func (ml *multiListener) Addr() net.Addr {
	return ml.listeners[0].Addr()
}

func main() {
	l, err := net.Listen("tcp", ":1883")
	if err != nil {
		log.Print("listen: ", err)
		return
	}
	svr := mqtt.NewServer(l)
	svr.Start()
	<-svr.Done
}
<commit_msg>Added remote pprof to mqttsrv.<commit_after>package main

import (
	"code.google.com/p/jra-go/mqtt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof"
)

type mlRes struct {
	conn net.Conn
	err  error
}

type multiListener struct {
	listeners []net.Listener
	next      chan mlRes
}

func newMultiListener(l ...net.Listener) *multiListener {
	ml := &multiListener{
		listeners: make([]net.Listener, len(l)),
		next:      make(chan mlRes, len(l)),
	}
	copy(ml.listeners, l)

	for _, l := range ml.listeners {
		go func(l net.Listener, next chan mlRes) {
			for {
				res := mlRes{}
				res.conn, res.err = l.Accept()
				next <- res
			}
		}(l, ml.next)
	}

	return ml
}

func (ml *multiListener) Accept() (net.Conn, error) {
	res := <-ml.next
	return res.conn, res.err
}

func (ml *multiListener) Close() error {
	for _, l := range ml.listeners {
		l.Close()
	}
	return nil
}

func (ml *multiListener) Addr() net.Addr {
	return ml.listeners[0].Addr()
}

func main() {
	// see godoc net/http/pprof
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	l, err := net.Listen("tcp", ":1883")
	if err != nil {
		log.Print("listen: ", err)
		return
	}
	svr := mqtt.NewServer(l)
	svr.Start()
	<-svr.Done
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Mute Communications Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package msg_test

import (
	"bytes"
	"errors"
	"io"
	"testing"

	"github.com/agl/ed25519"
	"github.com/mutecomm/mute/cipher"
	"github.com/mutecomm/mute/encode/base64"
	"github.com/mutecomm/mute/keyserver/hashchain"
	"github.com/mutecomm/mute/log"
	"github.com/mutecomm/mute/msg"
	"github.com/mutecomm/mute/msg/memstore"
	"github.com/mutecomm/mute/uid"
	"github.com/mutecomm/mute/util/fuzzer"
	"github.com/mutecomm/mute/util/msgs"
	"github.com/mutecomm/mute/util/times"
)

func init() {
	if err := log.Init("info", "msg  ", "", true); err != nil {
		panic(err)
	}
}

func encrypt(sign bool, flipUIDs bool) (
	sender, recipient *uid.Message,
	w bytes.Buffer,
	recipientTemp *uid.KeyEntry,
	privateKey string,
	err error,
) {
	sender, err = uid.Create("alice@mute.berlin", false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		return
	}
	recipient, err = uid.Create("bob@mute.berlin", false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		return
	}
	if flipUIDs {
		sender, recipient = recipient, sender
	}
	r := bytes.NewBufferString(msgs.Message1)
	now := uint64(times.Now())
	recipientKI, _, privateKey, err := recipient.KeyInit(1, now+times.Day, now-times.Day,
		false, "mute.berlin", "", "", cipher.RandReader)
	if err != nil {
		return
	}
	recipientTemp, err = recipientKI.KeyEntryECDHE25519(recipient.SigPubKey())
	if err != nil {
		return
	}
	// encrypt
	var privateSigKey *[64]byte
	if sign {
		privateSigKey = sender.PrivateSigKey64()
	}
	ms := memstore.New()
	ms.AddPublicKeyEntry(recipient.Identity(), recipientTemp)
	args := &msg.EncryptArgs{
		Writer: &w,
		From:   sender,
		To:     recipient,
		SenderLastKeychainHash: hashchain.TestEntry,
		PrivateSigKey:          privateSigKey,
		Reader:                 r,
		Rand:                   cipher.RandReader,
		KeyStore:               ms,
	}
	if _, err = msg.Encrypt(args); err != nil {
		return
	}
	return
}

func decrypt(sender, recipient *uid.Message, r io.Reader, recipientTemp *uid.KeyEntry,
	privateKey string, sign bool, chkMsg bool) error {
	// decrypt
	var res bytes.Buffer
	identities := []string{recipient.Identity()}
	recipientIdentities := []*uid.KeyEntry{recipient.PubKey()}
	input := base64.NewDecoder(r)
	version, preHeader, err := msg.ReadFirstOuterHeader(input)
	if err != nil {
		return err
	}
	if version != msg.Version {
		return errors.New("wrong version")
	}
	ms := memstore.New()
	if err := recipientTemp.SetPrivateKey(privateKey); err != nil {
		return err
	}
	ms.AddPrivateKeyEntry(recipientTemp)
	args := &msg.DecryptArgs{
		Writer:              &res,
		Identities:          identities,
		RecipientIdentities: recipientIdentities,
		PreHeader:           preHeader,
		Reader:              input,
		Rand:                cipher.RandReader,
		KeyStore:            ms,
	}
	_, sig, err := msg.Decrypt(args)
	if err != nil {
		return err
	}
	// do not compare messages when fuzzing, because messages have to be different!
	if chkMsg && res.String() != msgs.Message1 {
		return errors.New("messages differ")
	}
	if sign {
		contentHash := cipher.SHA512(res.Bytes())
		decSig, err := base64.Decode(sig)
		if err != nil {
			return err
		}
		if len(decSig) != ed25519.SignatureSize {
			return errors.New("signature has wrong length")
		}
		var sigBuf [ed25519.SignatureSize]byte
		copy(sigBuf[:], decSig)
		if !ed25519.Verify(sender.PublicSigKey32(), contentHash, &sigBuf) {
			return errors.New("signature verification failed")
		}
	}
	return nil
}

func encryptAndDecrypt(t *testing.T, sign, flipUIDs bool) {
	// encrypt
	sender, recipient, w, recipientTemp, privateKey, err := encrypt(sign, flipUIDs)
	if err != nil {
		t.Fatal(err)
	}
	// decrypt
	err = decrypt(sender, recipient, &w, recipientTemp, privateKey, sign, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestUnsignedMsg(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, false, false)
}

func TestSignedMsg(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, true, false)
}

func TestUnsignedMsgFlip(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, false, true)
}

func TestSignedMsgFlip(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, true, true)
}

func encryptAndDecryptFuzzing(t *testing.T, sign bool) {
	// encrypt
	sender, recipient, w, recipientTemp, privateKey, err := encrypt(sign, false)
	if err != nil {
		t.Fatal(err)
	}

	// decrypt func
	testFunc := func(b []byte) error {
		err := decrypt(sender, recipient, bytes.NewBuffer(b), recipientTemp, privateKey, sign, false)
		if err != nil {
			return err
		}
		return nil
	}

	// check length of encrypted message
	if w.Len() != msg.EncodedMsgSize {
		t.Errorf("w.Len() = %d != %d = msg.EncodedMsgSize)",
			w.Len(), msg.EncodedMsgSize)
	}

	// fuzzer: fuzz everything except for most of the message padding
	fzzr := &fuzzer.SequentialFuzzer{
		Data:     w.Bytes(),
		End:      8000 * 8,
		TestFunc: testFunc,
	}
	if ok := fzzr.Fuzz(); !ok {
		t.Error("fuzzer failed")
	}

	fzzr = &fuzzer.SequentialFuzzer{
		Data:     w.Bytes(),
		Start:    msg.EncodedMsgSize*8 - 1000,
		TestFunc: testFunc,
	}
	if ok := fzzr.Fuzz(); !ok {
		t.Error("fuzzer failed")
	}
}

func TestFuzzedUnsignedMsg(t *testing.T) {
	t.Parallel()
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	encryptAndDecryptFuzzing(t, false)
}

func TestFuzzedSignedMsg(t *testing.T) {
	t.Parallel()
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	encryptAndDecryptFuzzing(t, true)
}

func TestUnencodedMsgSize(t *testing.T) {
	t.Parallel()
	if msg.UnencodedMsgSize != 49152 {
		t.Errorf("unencodedMsgSize = %d != %d", msg.UnencodedMsgSize, 49152)
	}
}

func TestMaxContentLength(t *testing.T) {
	t.Parallel()
	if msg.MaxContentLength != 41691 {
		t.Errorf("msg.MaxContentLength = %d != %d", msg.MaxContentLength, 41691)
	}
}
<commit_msg>msg: add TestReflection()<commit_after>// Copyright (c) 2015 Mute Communications Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package msg_test

import (
	"bytes"
	"errors"
	"io"
	"testing"

	"github.com/agl/ed25519"
	"github.com/mutecomm/mute/cipher"
	"github.com/mutecomm/mute/encode/base64"
	"github.com/mutecomm/mute/keyserver/hashchain"
	"github.com/mutecomm/mute/log"
	"github.com/mutecomm/mute/msg"
	"github.com/mutecomm/mute/msg/memstore"
	"github.com/mutecomm/mute/uid"
	"github.com/mutecomm/mute/util/fuzzer"
	"github.com/mutecomm/mute/util/msgs"
	"github.com/mutecomm/mute/util/times"
)

func init() {
	if err := log.Init("info", "msg  ", "", true); err != nil {
		panic(err)
	}
}

func encrypt(sign bool, flipUIDs bool) (
	sender, recipient *uid.Message,
	w bytes.Buffer,
	recipientTemp *uid.KeyEntry,
	privateKey string,
	err error,
) {
	sender, err = uid.Create("alice@mute.berlin", false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		return
	}
	recipient, err = uid.Create("bob@mute.berlin", false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		return
	}
	if flipUIDs {
		sender, recipient = recipient, sender
	}
	r := bytes.NewBufferString(msgs.Message1)
	now := uint64(times.Now())
	recipientKI, _, privateKey, err := recipient.KeyInit(1, now+times.Day, now-times.Day,
		false, "mute.berlin", "", "", cipher.RandReader)
	if err != nil {
		return
	}
	recipientTemp, err = recipientKI.KeyEntryECDHE25519(recipient.SigPubKey())
	if err != nil {
		return
	}
	// encrypt
	var privateSigKey *[64]byte
	if sign {
		privateSigKey = sender.PrivateSigKey64()
	}
	ms := memstore.New()
	ms.AddPublicKeyEntry(recipient.Identity(), recipientTemp)
	args := &msg.EncryptArgs{
		Writer: &w,
		From:   sender,
		To:     recipient,
		SenderLastKeychainHash: hashchain.TestEntry,
		PrivateSigKey:          privateSigKey,
		Reader:                 r,
		Rand:                   cipher.RandReader,
		KeyStore:               ms,
	}
	if _, err = msg.Encrypt(args); err != nil {
		return
	}
	return
}

func decrypt(sender, recipient *uid.Message, r io.Reader, recipientTemp *uid.KeyEntry,
	privateKey string, sign bool, chkMsg bool) error {
	// decrypt
	var res bytes.Buffer
	identities := []string{recipient.Identity()}
	recipientIdentities := []*uid.KeyEntry{recipient.PubKey()}
	input := base64.NewDecoder(r)
	version, preHeader, err := msg.ReadFirstOuterHeader(input)
	if err != nil {
		return err
	}
	if version != msg.Version {
		return errors.New("wrong version")
	}
	ms := memstore.New()
	if err := recipientTemp.SetPrivateKey(privateKey); err != nil {
		return err
	}
	ms.AddPrivateKeyEntry(recipientTemp)
	args := &msg.DecryptArgs{
		Writer:              &res,
		Identities:          identities,
		RecipientIdentities: recipientIdentities,
		PreHeader:           preHeader,
		Reader:              input,
		Rand:                cipher.RandReader,
		KeyStore:            ms,
	}
	_, sig, err := msg.Decrypt(args)
	if err != nil {
		return err
	}
	// do not compare messages when fuzzing, because messages have to be different!
	if chkMsg && res.String() != msgs.Message1 {
		return errors.New("messages differ")
	}
	if sign {
		contentHash := cipher.SHA512(res.Bytes())
		decSig, err := base64.Decode(sig)
		if err != nil {
			return err
		}
		if len(decSig) != ed25519.SignatureSize {
			return errors.New("signature has wrong length")
		}
		var sigBuf [ed25519.SignatureSize]byte
		copy(sigBuf[:], decSig)
		if !ed25519.Verify(sender.PublicSigKey32(), contentHash, &sigBuf) {
			return errors.New("signature verification failed")
		}
	}
	return nil
}

func encryptAndDecrypt(t *testing.T, sign, flipUIDs bool) {
	// encrypt
	sender, recipient, w, recipientTemp, privateKey, err := encrypt(sign, flipUIDs)
	if err != nil {
		t.Fatal(err)
	}
	// decrypt
	err = decrypt(sender, recipient, &w, recipientTemp, privateKey, sign, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestUnsignedMsg(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, false, false)
}

func TestSignedMsg(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, true, false)
}

func TestUnsignedMsgFlip(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, false, true)
}

func TestSignedMsgFlip(t *testing.T) {
	t.Parallel()
	encryptAndDecrypt(t, true, true)
}

func encryptAndDecryptFuzzing(t *testing.T, sign bool) {
	// encrypt
	sender, recipient, w, recipientTemp, privateKey, err := encrypt(sign, false)
	if err != nil {
		t.Fatal(err)
	}

	// decrypt func
	testFunc := func(b []byte) error {
		err := decrypt(sender, recipient, bytes.NewBuffer(b), recipientTemp, privateKey, sign, false)
		if err != nil {
			return err
		}
		return nil
	}

	// check length of encrypted message
	if w.Len() != msg.EncodedMsgSize {
		t.Errorf("w.Len() = %d != %d = msg.EncodedMsgSize)",
			w.Len(), msg.EncodedMsgSize)
	}

	// fuzzer: fuzz everything except for most of the message padding
	fzzr := &fuzzer.SequentialFuzzer{
		Data:     w.Bytes(),
		End:      8000 * 8,
		TestFunc: testFunc,
	}
	if ok := fzzr.Fuzz(); !ok {
		t.Error("fuzzer failed")
	}

	fzzr = &fuzzer.SequentialFuzzer{
		Data:     w.Bytes(),
		Start:    msg.EncodedMsgSize*8 - 1000,
		TestFunc: testFunc,
	}
	if ok := fzzr.Fuzz(); !ok {
		t.Error("fuzzer failed")
	}
}

func TestFuzzedUnsignedMsg(t *testing.T) {
	t.Parallel()
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	encryptAndDecryptFuzzing(t, false)
}

func TestFuzzedSignedMsg(t *testing.T) {
	t.Parallel()
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	encryptAndDecryptFuzzing(t, true)
}

func TestUnencodedMsgSize(t *testing.T) {
	t.Parallel()
	if msg.UnencodedMsgSize != 49152 {
		t.Errorf("unencodedMsgSize = %d != %d", msg.UnencodedMsgSize, 49152)
	}
}

func TestMaxContentLength(t *testing.T) {
	t.Parallel()
	if msg.MaxContentLength != 41691 {
		t.Errorf("msg.MaxContentLength = %d != %d", msg.MaxContentLength, 41691)
	}
}

func TestReflection(t *testing.T) {
	alice := "alice@mute.berlin"
	aliceUID, err := uid.Create(alice, false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		t.Fatal(err)
	}
	bob := "bob@mute.berlin"
	bobUID, err := uid.Create(bob, false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		t.Fatal(err)
	}
	var encMsg bytes.Buffer
	aliceKeyStore := memstore.New()
	aliceKeyStore.AddPublicKeyEntry(bob, bobUID.PubKey()) // duplicate key
	encryptArgs := &msg.EncryptArgs{
		Writer: &encMsg,
		From:   aliceUID,
		To:     bobUID,
		SenderLastKeychainHash: hashchain.TestEntry,
		Reader:                 bytes.NewBufferString(msgs.Message1),
		Rand:                   cipher.RandReader,
		KeyStore:               aliceKeyStore,
	}
	if _, err = msg.Encrypt(encryptArgs); err != msg.ErrReflection {
		t.Error("should fail with msg.ErrReflection")
	}
}
<|endoftext|>


<commit_before>// +build linux

package namespaces

/*
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <getopt.h>

static const kBufSize = 256;

void get_args(int *argc, char ***argv) {
	// Read argv
	int fd = open("/proc/self/cmdline", O_RDONLY);

	// Read the whole commandline.
	ssize_t contents_size = 0;
	ssize_t contents_offset = 0;
	char *contents = NULL;
	ssize_t bytes_read = 0;
	do {
		contents_size += kBufSize;
		contents = (char *) realloc(contents, contents_size);
		bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset);
		contents_offset += bytes_read;
	} while (bytes_read > 0);
	close(fd);

	// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
	ssize_t i;
	*argc = 0;
	for (i = 0; i < contents_offset; i++) {
		if (contents[i] == '\0') {
			(*argc)++;
		}
	}
	*argv = (char **) malloc(sizeof(char *) * ((*argc) + 1));
	int idx;
	for (idx = 0; idx < (*argc); idx++) {
		(*argv)[idx] = contents;
		contents += strlen(contents) + 1;
	}
	(*argv)[*argc] = NULL;
}

// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
#define _GNU_SOURCE
#include <sched.h>
#include "syscall.h"
#ifdef SYS_setns
int setns(int fd, int nstype) {
  return syscall(SYS_setns, fd, nstype);
}
#endif
#endif

void print_usage() {
	fprintf(stderr, "<binary> nsenter --nspid <pid> --containerjson <container_json> -- cmd1 arg1 arg2...\n");
}

void nsenter() {
	int argc;
	char **argv;
	get_args(&argc, &argv);

	// Ignore if this is not for us.
	if (argc < 2 || strcmp(argv[1], "nsenter") != 0) {
		return;
	}

	// USAGE: <binary> nsenter <PID> <process label> <container JSON> <argv>...
	if (argc < 6) {
		fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n");
		exit(1);
	}

	static const struct option longopts[] = {
		{ "nspid",         required_argument, NULL, 'n' },
		{ "containerjson", required_argument, NULL, 'c' },
                { "console",       required_argument, NULL, 't' },
		{ NULL,            0,                 NULL,  0  }
	};

	int c;
	pid_t init_pid = -1;
	char *init_pid_str = NULL;
	char *container_json = NULL;
        char *console = NULL;
	while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
		switch (c) {
		case 'n':
			init_pid_str = optarg;
			break;
		case 'c':
			container_json = optarg;
			break;
		case 't':
			console = optarg;
			break;
		}
	}

	if (container_json == NULL || init_pid_str == NULL) {
		print_usage();
		exit(1);
	}

	init_pid = strtol(init_pid_str, NULL, 10);
	if (errno != 0 || init_pid <= 0) {
		fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno));
		print_usage();
		exit(1);
	}

	argc -= 3;
	argv += 3;

        if (setsid() == -1) {
               fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
               exit(1);
        }

        // before we setns we need to dup the console
        int consolefd = -1;
        if (console != NULL) {
               consolefd = open(console, O_RDWR);
               if (consolefd < 0) {
                    fprintf(stderr, "nsenter: failed to open console %s\n", console, strerror(errno));
                    exit(1);
              }
        }

	// Setns on all supported namespaces.
	char ns_dir[PATH_MAX];
	memset(ns_dir, 0, PATH_MAX);
	snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);
	struct dirent *dent;
	DIR *dir = opendir(ns_dir);
	if (dir == NULL) {
		fprintf(stderr, "nsenter: Failed to open directory \"%s\" with error: \"%s\"\n", ns_dir, strerror(errno));
		exit(1);
	}

	while((dent = readdir(dir)) != NULL) {
		if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0 || strcmp(dent->d_name, "user") == 0) {
			continue;
		}

		// Get and open the namespace for the init we are joining..
		char buf[PATH_MAX];
		memset(buf, 0, PATH_MAX);
		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, dent->d_name);
		int fd = open(buf, O_RDONLY);
		if (fd == -1) {
			fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, dent->d_name, strerror(errno));
			exit(1);
		}

		// Set the namespace.
		if (setns(fd, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", dent->d_name, strerror(errno));
			exit(1);
		}
		close(fd);
	}
	closedir(dir);

	// We must fork to actually enter the PID namespace.
	int child = fork();
	if (child == 0) {
       if (consolefd != -1) {
        if (dup2(consolefd, STDIN_FILENO) != 0) {
            fprintf(stderr, "nsenter: failed to dup 0 %s\n",  strerror(errno));
            exit(1);
        }
        if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
            fprintf(stderr, "nsenter: failed to dup 1 %s\n",  strerror(errno));
            exit(1);
        }
        if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
            fprintf(stderr, "nsenter: failed to dup 2 %s\n",  strerror(errno));
            exit(1);
        }
}

		// Finish executing, let the Go runtime take over.
		return;
	} else {
		// Parent, wait for the child.
		int status = 0;
		if (waitpid(child, &status, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to waitpid with error: \"%s\"\n", strerror(errno));
			exit(1);
		}

		// Forward the child's exit code or re-send its death signal.
		if (WIFEXITED(status)) {
			exit(WEXITSTATUS(status));
		} else if (WIFSIGNALED(status)) {
			kill(getpid(), WTERMSIG(status));
		}
		exit(1);
	}

	return;
}

__attribute__((constructor)) init() {
	nsenter();
}
*/
import "C"
<commit_msg>Correct nsenter fprintf syntax Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@docker.com> (github: crosbymichael)<commit_after>// +build linux

package namespaces

/*
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <getopt.h>

static const kBufSize = 256;

void get_args(int *argc, char ***argv) {
	// Read argv
	int fd = open("/proc/self/cmdline", O_RDONLY);

	// Read the whole commandline.
	ssize_t contents_size = 0;
	ssize_t contents_offset = 0;
	char *contents = NULL;
	ssize_t bytes_read = 0;
	do {
		contents_size += kBufSize;
		contents = (char *) realloc(contents, contents_size);
		bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset);
		contents_offset += bytes_read;
	} while (bytes_read > 0);
	close(fd);

	// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
	ssize_t i;
	*argc = 0;
	for (i = 0; i < contents_offset; i++) {
		if (contents[i] == '\0') {
			(*argc)++;
		}
	}
	*argv = (char **) malloc(sizeof(char *) * ((*argc) + 1));
	int idx;
	for (idx = 0; idx < (*argc); idx++) {
		(*argv)[idx] = contents;
		contents += strlen(contents) + 1;
	}
	(*argv)[*argc] = NULL;
}

// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
#define _GNU_SOURCE
#include <sched.h>
#include "syscall.h"
#ifdef SYS_setns
int setns(int fd, int nstype) {
  return syscall(SYS_setns, fd, nstype);
}
#endif
#endif

void print_usage() {
	fprintf(stderr, "<binary> nsenter --nspid <pid> --containerjson <container_json> -- cmd1 arg1 arg2...\n");
}

void nsenter() {
	int argc;
	char **argv;
	get_args(&argc, &argv);

	// Ignore if this is not for us.
	if (argc < 2 || strcmp(argv[1], "nsenter") != 0) {
		return;
	}

	// USAGE: <binary> nsenter <PID> <process label> <container JSON> <argv>...
	if (argc < 6) {
		fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n");
		exit(1);
	}

	static const struct option longopts[] = {
		{ "nspid",         required_argument, NULL, 'n' },
		{ "containerjson", required_argument, NULL, 'c' },
                { "console",       required_argument, NULL, 't' },
		{ NULL,            0,                 NULL,  0  }
	};

	int c;
	pid_t init_pid = -1;
	char *init_pid_str = NULL;
	char *container_json = NULL;
        char *console = NULL;
	while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
		switch (c) {
		case 'n':
			init_pid_str = optarg;
			break;
		case 'c':
			container_json = optarg;
			break;
		case 't':
			console = optarg;
			break;
		}
	}

	if (container_json == NULL || init_pid_str == NULL) {
		print_usage();
		exit(1);
	}

	init_pid = strtol(init_pid_str, NULL, 10);
	if (errno != 0 || init_pid <= 0) {
		fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno));
		print_usage();
		exit(1);
	}

	argc -= 3;
	argv += 3;

        if (setsid() == -1) {
               fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
               exit(1);
        }

        // before we setns we need to dup the console
        int consolefd = -1;
        if (console != NULL) {
               consolefd = open(console, O_RDWR);
               if (consolefd < 0) {
                    fprintf(stderr, "nsenter: failed to open console %s %s\n", console, strerror(errno));
                    exit(1);
              }
        }

	// Setns on all supported namespaces.
	char ns_dir[PATH_MAX];
	memset(ns_dir, 0, PATH_MAX);
	snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);
	struct dirent *dent;
	DIR *dir = opendir(ns_dir);
	if (dir == NULL) {
		fprintf(stderr, "nsenter: Failed to open directory \"%s\" with error: \"%s\"\n", ns_dir, strerror(errno));
		exit(1);
	}

	while((dent = readdir(dir)) != NULL) {
		if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0 || strcmp(dent->d_name, "user") == 0) {
			continue;
		}

		// Get and open the namespace for the init we are joining..
		char buf[PATH_MAX];
		memset(buf, 0, PATH_MAX);
		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, dent->d_name);
		int fd = open(buf, O_RDONLY);
		if (fd == -1) {
			fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, dent->d_name, strerror(errno));
			exit(1);
		}

		// Set the namespace.
		if (setns(fd, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", dent->d_name, strerror(errno));
			exit(1);
		}
		close(fd);
	}
	closedir(dir);

	// We must fork to actually enter the PID namespace.
	int child = fork();
	if (child == 0) {
       if (consolefd != -1) {
        if (dup2(consolefd, STDIN_FILENO) != 0) {
            fprintf(stderr, "nsenter: failed to dup 0 %s\n",  strerror(errno));
            exit(1);
        }
        if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
            fprintf(stderr, "nsenter: failed to dup 1 %s\n",  strerror(errno));
            exit(1);
        }
        if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
            fprintf(stderr, "nsenter: failed to dup 2 %s\n",  strerror(errno));
            exit(1);
        }
}

		// Finish executing, let the Go runtime take over.
		return;
	} else {
		// Parent, wait for the child.
		int status = 0;
		if (waitpid(child, &status, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to waitpid with error: \"%s\"\n", strerror(errno));
			exit(1);
		}

		// Forward the child's exit code or re-send its death signal.
		if (WIFEXITED(status)) {
			exit(WEXITSTATUS(status));
		} else if (WIFSIGNALED(status)) {
			kill(getpid(), WTERMSIG(status));
		}
		exit(1);
	}

	return;
}

__attribute__((constructor)) init() {
	nsenter();
}
*/
import "C"
<|endoftext|>


<commit_before><commit_msg>Add Leuzinger HS.<commit_after><|endoftext|>


<commit_before>package mux

import (
	"fmt"
	"gondola/defaults"
	"gondola/log"
	"net/http"
	"testing"
)

func helloHandler(ctx *Context) {
	ctx.Write([]byte("Hello world"))
}

func testReverse(t *testing.T, expected string, m *Mux, name string, args ...interface{}) {
	rev, err := m.Reverse(name, args...)
	if expected != "" {
		if err != nil {
			t.Error(err)
		}
	} else {
		if err == nil {
			t.Errorf("Expecting error while reversing %s with arguments %v", name, args)
		}
	}
	if rev != expected {
		t.Errorf("Error reversing %q with arguments %v, expected %q, got %q", name, args, expected, rev)
	} else {
		t.Logf("Reversed %q with %v to %q", name, args, rev)
	}
}

func TestReverse(t *testing.T) {
	m := New()
	m.HandleNamedFunc("^/program/(\\d+)/$", helloHandler, "program")
	m.HandleNamedFunc("^/program/(\\d+)/version/(\\d+)/$", helloHandler, "programversion")
	m.HandleNamedFunc("^/program/(?P<pid>\\d+)/version/(?P<vers>\\d+)/$", helloHandler, "programversionnamed")
	m.HandleNamedFunc("^/program/(\\d+)/(?:version/(\\d+)/)?$", helloHandler, "programoptversion")
	m.HandleNamedFunc("^/program/(\\d+)/(?:version/(\\d+)/)?(?:revision/(\\d+)/)?$", helloHandler, "programrevision")
	m.HandleNamedFunc("^/archive/(\\d+)?$", helloHandler, "archive")
	m.HandleNamedFunc("^/image/(\\w+)\\.(\\w+)$", helloHandler, "image")
	m.HandleNamedFunc("^/image/(\\w+)\\-(\\w+)$", helloHandler, "imagedash")
	m.HandleNamedFunc("^/image/(\\w+)\\\\(\\w+)$", helloHandler, "imageslash")

	testReverse(t, "/program/1/", m, "program", 1)
	testReverse(t, "/program/1/version/2/", m, "programversion", 1, 2)
	testReverse(t, "/program/1/version/2/", m, "programversionnamed", 1, 2)
	testReverse(t, "/program/1/", m, "programoptversion", 1)
	testReverse(t, "/program/1/version/2/", m, "programoptversion", 1, 2)
	testReverse(t, "/program/1/", m, "programrevision", 1)
	testReverse(t, "/program/1/version/2/", m, "programrevision", 1, 2)
	testReverse(t, "/program/1/version/2/revision/3/", m, "programrevision", 1, 2, 3)

	testReverse(t, "/archive/19700101", m, "archive", "19700101")
	testReverse(t, "/archive/", m, "archive")

	// TODO: These don't work
	/*
		m.HandleNamedFunc("^/section/(sub/(\\d+)/subsub(\\d+))?$", helloHandler, "section")
		testReverse(t, "/section/", m, "section")
		testReverse(t, "/section/sub/1/subsub/2", m, "section", 1, 2)
		testReverse(t, "/section/sub/1", m, "section", 1)
	*/

	// Test invalid reverses
	testReverse(t, "", m, "program")
	testReverse(t, "", m, "program", "foo")
	testReverse(t, "", m, "program", 1, 2)
	testReverse(t, "", m, "programrevision", 1, 2, 3, 4)

	// Dot, dash and slash
	testReverse(t, "/image/test.png", m, "image", "test", "png")
	testReverse(t, "/image/test-png", m, "imagedash", "test", "png")
	testReverse(t, "/image/test\\png", m, "imageslash", "test", "png")
}

func testMux(nolog bool) (*Mux, string) {
	if nolog {
		log.SetLevel(log.LNone)
	} else {
		log.SetLevel(log.LInfo)
	}
	mux := New()
	mux.HandleFunc("^/$", func(ctx *Context) {})
	url := fmt.Sprintf("http://localhost:%d/", defaults.Port())
	return mux, url
}

func benchmarkServe(b *testing.B, nolog bool) {
	m, url := testMux(nolog)
	go func() {
		m.ListenAndServe(-1)
	}()
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		_, err := http.Get(url)
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkServe(b *testing.B) {
	benchmarkServe(b, false)
}

func BenchmarkServeNoLog(b *testing.B) {
	benchmarkServe(b, true)
}

func benchmarkDirect(b *testing.B, nolog bool) {
	mux, url := testMux(nolog)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		b.Fatal(err)
	}
	for ii := 0; ii < b.N; ii++ {
		mux.ServeHTTP(nil, req)
	}
}

func BenchmarkDirect(b *testing.B) {
	benchmarkDirect(b, false)
}

func BenchmarkDirectNoLog(b *testing.B) {
	benchmarkDirect(b, true)
}
<commit_msg>Reset the timer before starting iterations<commit_after>package mux

import (
	"fmt"
	"gondola/defaults"
	"gondola/log"
	"net/http"
	"testing"
)

func helloHandler(ctx *Context) {
	ctx.Write([]byte("Hello world"))
}

func testReverse(t *testing.T, expected string, m *Mux, name string, args ...interface{}) {
	rev, err := m.Reverse(name, args...)
	if expected != "" {
		if err != nil {
			t.Error(err)
		}
	} else {
		if err == nil {
			t.Errorf("Expecting error while reversing %s with arguments %v", name, args)
		}
	}
	if rev != expected {
		t.Errorf("Error reversing %q with arguments %v, expected %q, got %q", name, args, expected, rev)
	} else {
		t.Logf("Reversed %q with %v to %q", name, args, rev)
	}
}

func TestReverse(t *testing.T) {
	m := New()
	m.HandleNamedFunc("^/program/(\\d+)/$", helloHandler, "program")
	m.HandleNamedFunc("^/program/(\\d+)/version/(\\d+)/$", helloHandler, "programversion")
	m.HandleNamedFunc("^/program/(?P<pid>\\d+)/version/(?P<vers>\\d+)/$", helloHandler, "programversionnamed")
	m.HandleNamedFunc("^/program/(\\d+)/(?:version/(\\d+)/)?$", helloHandler, "programoptversion")
	m.HandleNamedFunc("^/program/(\\d+)/(?:version/(\\d+)/)?(?:revision/(\\d+)/)?$", helloHandler, "programrevision")
	m.HandleNamedFunc("^/archive/(\\d+)?$", helloHandler, "archive")
	m.HandleNamedFunc("^/image/(\\w+)\\.(\\w+)$", helloHandler, "image")
	m.HandleNamedFunc("^/image/(\\w+)\\-(\\w+)$", helloHandler, "imagedash")
	m.HandleNamedFunc("^/image/(\\w+)\\\\(\\w+)$", helloHandler, "imageslash")

	testReverse(t, "/program/1/", m, "program", 1)
	testReverse(t, "/program/1/version/2/", m, "programversion", 1, 2)
	testReverse(t, "/program/1/version/2/", m, "programversionnamed", 1, 2)
	testReverse(t, "/program/1/", m, "programoptversion", 1)
	testReverse(t, "/program/1/version/2/", m, "programoptversion", 1, 2)
	testReverse(t, "/program/1/", m, "programrevision", 1)
	testReverse(t, "/program/1/version/2/", m, "programrevision", 1, 2)
	testReverse(t, "/program/1/version/2/revision/3/", m, "programrevision", 1, 2, 3)

	testReverse(t, "/archive/19700101", m, "archive", "19700101")
	testReverse(t, "/archive/", m, "archive")

	// TODO: These don't work
	/*
		m.HandleNamedFunc("^/section/(sub/(\\d+)/subsub(\\d+))?$", helloHandler, "section")
		testReverse(t, "/section/", m, "section")
		testReverse(t, "/section/sub/1/subsub/2", m, "section", 1, 2)
		testReverse(t, "/section/sub/1", m, "section", 1)
	*/

	// Test invalid reverses
	testReverse(t, "", m, "program")
	testReverse(t, "", m, "program", "foo")
	testReverse(t, "", m, "program", 1, 2)
	testReverse(t, "", m, "programrevision", 1, 2, 3, 4)

	// Dot, dash and slash
	testReverse(t, "/image/test.png", m, "image", "test", "png")
	testReverse(t, "/image/test-png", m, "imagedash", "test", "png")
	testReverse(t, "/image/test\\png", m, "imageslash", "test", "png")
}

func testMux(nolog bool) (*Mux, string) {
	if nolog {
		log.SetLevel(log.LNone)
	} else {
		log.SetLevel(log.LInfo)
	}
	mux := New()
	mux.HandleFunc("^/$", func(ctx *Context) {})
	url := fmt.Sprintf("http://localhost:%d/", defaults.Port())
	return mux, url
}

func benchmarkServe(b *testing.B, nolog bool) {
	m, url := testMux(nolog)
	go func() {
		m.ListenAndServe(-1)
	}()
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		_, err := http.Get(url)
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkServe(b *testing.B) {
	benchmarkServe(b, false)
}

func BenchmarkServeNoLog(b *testing.B) {
	benchmarkServe(b, true)
}

func benchmarkDirect(b *testing.B, nolog bool) {
	mux, url := testMux(nolog)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		b.Fatal(err)
	}
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		mux.ServeHTTP(nil, req)
	}
}

func BenchmarkDirect(b *testing.B) {
	benchmarkDirect(b, false)
}

func BenchmarkDirectNoLog(b *testing.B) {
	benchmarkDirect(b, true)
}
<|endoftext|>