<commit_before>package broker

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"testing"
	"time"

	jsonpb "github.com/golang/protobuf/jsonpb"
	proto "github.com/golang/protobuf/proto"
	emulators "google/emulators"
)

// Test-only client.
type httpJsonClient struct {
	port   int
	client http.Client
}

func (c *httpJsonClient) get(url string, output proto.Message) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return errors.New("Request failed: " + resp.Status)
	}
	if output != nil {
		err = jsonpb.Unmarshal(resp.Body, output)
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *httpJsonClient) post(url string, req proto.Message, output proto.Message) error {
	return c.do("POST", url, req, output)
}

// req, err := http.NewRequest("PATCH", url, strings.NewReader(body))
// if err != nil {
//   return nil, err
// }
// resp, err := c.client.Do(req)

func (c *httpJsonClient) patch(url string, req proto.Message, output proto.Message) error {
	return c.do("PATCH", url, req, output)
}

func (c *httpJsonClient) do(method string, url string, req proto.Message, output proto.Message) error {
	var reqBody io.Reader = nil
	if req != nil {
		m := jsonpb.Marshaler{}
		body, err := m.MarshalToString(req)
		if err != nil {
			return nil
		}
		reqBody = bytes.NewBufferString(body)
	}
	httpReq, err := http.NewRequest(method, url, reqBody)
	if err != nil {
		return err
	}
	resp, err := c.client.Do(httpReq)
	if err != nil {
		return nil
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("Request failed [%v]: %v", req, resp.Status)
		}
		return fmt.Errorf("Request failed [%v]: %v: %s", req, resp.Status, b)
	}
	if output != nil {
		err = jsonpb.Unmarshal(resp.Body, output)
		if err != nil {
			return err
		}
	}
	return nil
}

// Waits until the HTTP server is ready. Requests sent before the server is
// ready may result in a variety of error responses (4xx and 5xx).
func (c *httpJsonClient) awaitReady(timeout time.Duration) error {
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {
		_, err := c.listEmulators()
		if err == nil {
			return nil
		}
		time.Sleep(100 * time.Millisecond)
	}
	return errors.New("Timed-out waiting for client to become ready!")
}

func (c *httpJsonClient) createEmulator(emu *emulators.Emulator) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators", c.port)
	return c.post(url, emu, EmptyPb)
}

func (c *httpJsonClient) getEmulator(id string) (*emulators.Emulator, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s", c.port, id)
	emu := &emulators.Emulator{}
	err := c.get(url, emu)
	return emu, err
}

func (c *httpJsonClient) listEmulators() (*emulators.ListEmulatorsResponse, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators", c.port)
	listResp := &emulators.ListEmulatorsResponse{}
	err := c.get(url, listResp)
	return listResp, err
}

func (c *httpJsonClient) startEmulator(id string) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s:start", c.port, id)
	return c.post(url, nil, nil)
}

func (c *httpJsonClient) reportEmulatorOnline(report *emulators.ReportEmulatorOnlineRequest) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s:report_online", c.port, report.EmulatorId)
	return c.post(url, report, nil)
}

func (c *httpJsonClient) stopEmulator(id string) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s:stop", c.port, id)
	return c.post(url, nil, nil)
}

func (c *httpJsonClient) createResolveRule(rule *emulators.ResolveRule) error {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules", c.port)
	return c.post(url, rule, nil)
}

func (c *httpJsonClient) getResolveRule(id string) (*emulators.ResolveRule, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules/%s", c.port, id)
	rule := &emulators.ResolveRule{}
	err := c.get(url, rule)
	return rule, err
}

func (c *httpJsonClient) listResolveRules() (*emulators.ListResolveRulesResponse, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules", c.port)
	listResp := &emulators.ListResolveRulesResponse{}
	err := c.get(url, listResp)
	return listResp, err
}

func (c *httpJsonClient) updateResolveRule(rule *emulators.ResolveRule) (*emulators.ResolveRule, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules/%s", c.port, rule.RuleId)
	updatedRule := &emulators.ResolveRule{}
	err := c.patch(url, rule, updatedRule)
	return updatedRule, err
}

func (c *httpJsonClient) resolve(req *emulators.ResolveRequest) (*emulators.ResolveResponse, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules:resolve", c.port)
	resp := &emulators.ResolveResponse{}
	err := c.post(url, req, resp)
	return resp, err
}

// Sanity check HTTP/Json access to the emulators resource.
// Tests create, get, and list.
func TestHttpJson_EmulatorsResource(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	listResp, err := c.listEmulators()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Emulators) != 0 {
		t.Fatalf("Expected zero emulators: %v", listResp)
	}
	err = c.createEmulator(dummyEmulator)
	if err != nil {
		t.Fatal(err)
	}
	emu, err := c.getEmulator(dummyEmulator.EmulatorId)
	if !proto.Equal(emu, dummyEmulator) {
		t.Fatalf("Expected %v: %v", dummyEmulator, emu)
	}
	listResp, err = c.listEmulators()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Emulators) != 1 {
		t.Fatalf("Expected 1 emulator: %v", listResp)
	}
}

// Sanity check HTTP/Json access to the resolve_rules resource.
// Tests create, get, list, and update.
func TestHttpJson_ResolveRulesResource(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	listResp, err := c.listResolveRules()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Rules) != 0 {
		t.Fatalf("Expected zero rule: %v", listResp)
	}
	err = c.createResolveRule(dummyEmulator.Rule)
	if err != nil {
		t.Fatal(err)
	}
	rule, err := c.getResolveRule(dummyEmulator.Rule.RuleId)
	if err != nil {
		t.Fatal(err)
	}
	listResp, err = c.listResolveRules()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Rules) != 1 {
		t.Fatalf("Expected 1 rule: %v", listResp)
	}
	rule.TargetPatterns = append(rule.TargetPatterns, "new")
	updatedRule, err := c.updateResolveRule(rule)
	if err != nil {
		t.Fatal(err)
	}
	if !unorderedEqual(updatedRule.TargetPatterns, rule.TargetPatterns) {
		t.Fatalf("Expected %v: %v", rule.TargetPatterns, updatedRule.TargetPatterns)
	}
}

// Tests emulators:start, :report online, and :stop.
func TestHttpJson_EmulatorsCustomMethods(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	realNoReg := proto.Clone(realEmulator).(*emulators.Emulator)
	realNoReg.StartCommand.Args = []string{"--port={port:real}"}
	err = c.createEmulator(realNoReg)
	if err != nil {
		t.Fatal(err)
	}

	// Start the emulator, and wait until it is ready to be reported online.
	done := make(chan bool)
	go func() {
		err = c.startEmulator(realNoReg.EmulatorId)
		if err != nil {
			t.Fatal(err)
		}
		done <- true
	}()
	err = b.s.waitForStarting(realNoReg.EmulatorId, time.Now().Add(2*time.Second))
	if err != nil {
		t.Fatal(err)
	}

	// Report the emulator online.
	report := &emulators.ReportEmulatorOnlineRequest{
		EmulatorId:   realNoReg.EmulatorId,
		ResolvedHost: "foo"}
	err = c.reportEmulatorOnline(report)
	if err != nil {
		t.Fatal(err)
	}
	select {
	case <-time.After(5 * time.Second):
		t.Fatalf("Timed-out starting emulator!")
	case <-done:
		break
	}
	emu, err := c.getEmulator(realNoReg.EmulatorId)
	if err != nil {
		t.Fatal(err)
	}
	if emu.State != emulators.Emulator_ONLINE {
		t.Fatalf("Expected %v: %v", emulators.Emulator_ONLINE, emu.State)
	}

	// Stop the emulator.
	err = c.stopEmulator(realNoReg.EmulatorId)
	if err != nil {
		t.Fatal(err)
	}
	emu, err = c.getEmulator(realNoReg.EmulatorId)
	if err != nil {
		t.Fatal(err)
	}
	if emu.State != emulators.Emulator_OFFLINE {
		t.Fatalf("Expected %v: %v", emulators.Emulator_OFFLINE, emu.State)
	}
}

// Tests resolve_rules:resolve.
func TestHttpJson_Resolve(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	rule := &emulators.ResolveRule{RuleId: "r0", TargetPatterns: []string{"foo"}, ResolvedHost: "bar"}
	err = c.createResolveRule(rule)
	if err != nil {
		t.Fatal(err)
	}
	_, err = c.getResolveRule("r0")
	if err != nil {
		t.Fatal(err)
	}
	resolveResp, err := c.resolve(&emulators.ResolveRequest{Target: "foo"})
	if err != nil {
		t.Fatal(err)
	}
	if resolveResp.Target != "bar" {
		t.Fatalf("Expected bar: %v", resolveResp.Target)
	}
}
<commit_msg>More test logging - show Json.<commit_after>package broker

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"testing"
	"time"

	glog "github.com/golang/glog"
	jsonpb "github.com/golang/protobuf/jsonpb"
	proto "github.com/golang/protobuf/proto"
	emulators "google/emulators"
)

// Test-only client.
type httpJsonClient struct {
	port   int
	client http.Client
}

func (c *httpJsonClient) get(url string, output proto.Message) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return errors.New("Request failed: " + resp.Status)
	}
	if output != nil {
		err = jsonpb.Unmarshal(resp.Body, output)
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *httpJsonClient) post(url string, req proto.Message, output proto.Message) error {
	return c.do("POST", url, req, output)
}

// req, err := http.NewRequest("PATCH", url, strings.NewReader(body))
// if err != nil {
//   return nil, err
// }
// resp, err := c.client.Do(req)

func (c *httpJsonClient) patch(url string, req proto.Message, output proto.Message) error {
	return c.do("PATCH", url, req, output)
}

func (c *httpJsonClient) do(method string, url string, req proto.Message, output proto.Message) error {
	var reqBody io.Reader = nil
	if req != nil {
		m := jsonpb.Marshaler{}
		body, err := m.MarshalToString(req)
		if err != nil {
			return err
		}
		glog.Infof("Request body: %v", body)
		reqBody = bytes.NewBufferString(body)
	}
	httpReq, err := http.NewRequest(method, url, reqBody)
	if err != nil {
		return err
	}
	resp, err := c.client.Do(httpReq)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("Request failed [%v]: %v", req, resp.Status)
		}
		return fmt.Errorf("Request failed [%v]: %v: %s", req, resp.Status, b)
	}
	if output != nil {
		err = jsonpb.Unmarshal(resp.Body, output)
		if err != nil {
			return err
		}
	}
	return nil
}

// Waits until the HTTP server is ready. Requests sent before the server is
// ready may result in a variety of error responses (4xx and 5xx).
func (c *httpJsonClient) awaitReady(timeout time.Duration) error {
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {
		_, err := c.listEmulators()
		if err == nil {
			return nil
		}
		time.Sleep(100 * time.Millisecond)
	}
	return errors.New("Timed-out waiting for client to become ready!")
}

func (c *httpJsonClient) createEmulator(emu *emulators.Emulator) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators", c.port)
	return c.post(url, emu, EmptyPb)
}

func (c *httpJsonClient) getEmulator(id string) (*emulators.Emulator, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s", c.port, id)
	emu := &emulators.Emulator{}
	err := c.get(url, emu)
	return emu, err
}

func (c *httpJsonClient) listEmulators() (*emulators.ListEmulatorsResponse, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators", c.port)
	listResp := &emulators.ListEmulatorsResponse{}
	err := c.get(url, listResp)
	return listResp, err
}

func (c *httpJsonClient) startEmulator(id string) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s:start", c.port, id)
	return c.post(url, nil, nil)
}

func (c *httpJsonClient) reportEmulatorOnline(report *emulators.ReportEmulatorOnlineRequest) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s:report_online", c.port, report.EmulatorId)
	return c.post(url, report, nil)
}

func (c *httpJsonClient) stopEmulator(id string) error {
	url := fmt.Sprintf("http://localhost:%d/v1/emulators/%s:stop", c.port, id)
	return c.post(url, nil, nil)
}

func (c *httpJsonClient) createResolveRule(rule *emulators.ResolveRule) error {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules", c.port)
	return c.post(url, rule, nil)
}

func (c *httpJsonClient) getResolveRule(id string) (*emulators.ResolveRule, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules/%s", c.port, id)
	rule := &emulators.ResolveRule{}
	err := c.get(url, rule)
	return rule, err
}

func (c *httpJsonClient) listResolveRules() (*emulators.ListResolveRulesResponse, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules", c.port)
	listResp := &emulators.ListResolveRulesResponse{}
	err := c.get(url, listResp)
	return listResp, err
}

func (c *httpJsonClient) updateResolveRule(rule *emulators.ResolveRule) (*emulators.ResolveRule, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules/%s", c.port, rule.RuleId)
	updatedRule := &emulators.ResolveRule{}
	err := c.patch(url, rule, updatedRule)
	return updatedRule, err
}

func (c *httpJsonClient) resolve(req *emulators.ResolveRequest) (*emulators.ResolveResponse, error) {
	url := fmt.Sprintf("http://localhost:%d/v1/resolve_rules:resolve", c.port)
	resp := &emulators.ResolveResponse{}
	err := c.post(url, req, resp)
	return resp, err
}

// Sanity check HTTP/Json access to the emulators resource.
// Tests create, get, and list.
func TestHttpJson_EmulatorsResource(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	listResp, err := c.listEmulators()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Emulators) != 0 {
		t.Fatalf("Expected zero emulators: %v", listResp)
	}
	err = c.createEmulator(dummyEmulator)
	if err != nil {
		t.Fatal(err)
	}
	emu, err := c.getEmulator(dummyEmulator.EmulatorId)
	if !proto.Equal(emu, dummyEmulator) {
		t.Fatalf("Expected %v: %v", dummyEmulator, emu)
	}
	listResp, err = c.listEmulators()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Emulators) != 1 {
		t.Fatalf("Expected 1 emulator: %v", listResp)
	}
}

// Sanity check HTTP/Json access to the resolve_rules resource.
// Tests create, get, list, and update.
func TestHttpJson_ResolveRulesResource(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	listResp, err := c.listResolveRules()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Rules) != 0 {
		t.Fatalf("Expected zero rule: %v", listResp)
	}
	err = c.createResolveRule(dummyEmulator.Rule)
	if err != nil {
		t.Fatal(err)
	}
	rule, err := c.getResolveRule(dummyEmulator.Rule.RuleId)
	if err != nil {
		t.Fatal(err)
	}
	listResp, err = c.listResolveRules()
	if err != nil {
		t.Fatal(err)
	}
	if len(listResp.Rules) != 1 {
		t.Fatalf("Expected 1 rule: %v", listResp)
	}
	rule.TargetPatterns = append(rule.TargetPatterns, "new")
	updatedRule, err := c.updateResolveRule(rule)
	if err != nil {
		t.Fatal(err)
	}
	if !unorderedEqual(updatedRule.TargetPatterns, rule.TargetPatterns) {
		t.Fatalf("Expected %v: %v", rule.TargetPatterns, updatedRule.TargetPatterns)
	}
}

// Tests emulators:start, :report online, and :stop.
func TestHttpJson_EmulatorsCustomMethods(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	realNoReg := proto.Clone(realEmulator).(*emulators.Emulator)
	realNoReg.StartCommand.Args = []string{"--port={port:real}"}
	err = c.createEmulator(realNoReg)
	if err != nil {
		t.Fatal(err)
	}

	// Start the emulator, and wait until it is ready to be reported online.
	done := make(chan bool)
	go func() {
		err = c.startEmulator(realNoReg.EmulatorId)
		if err != nil {
			t.Fatal(err)
		}
		done <- true
	}()
	err = b.s.waitForStarting(realNoReg.EmulatorId, time.Now().Add(2*time.Second))
	if err != nil {
		t.Fatal(err)
	}

	// Report the emulator online.
	report := &emulators.ReportEmulatorOnlineRequest{
		EmulatorId:   realNoReg.EmulatorId,
		ResolvedHost: "foo"}
	err = c.reportEmulatorOnline(report)
	if err != nil {
		t.Fatal(err)
	}
	select {
	case <-time.After(5 * time.Second):
		t.Fatalf("Timed-out starting emulator!")
	case <-done:
		break
	}
	emu, err := c.getEmulator(realNoReg.EmulatorId)
	if err != nil {
		t.Fatal(err)
	}
	if emu.State != emulators.Emulator_ONLINE {
		t.Fatalf("Expected %v: %v", emulators.Emulator_ONLINE, emu.State)
	}

	// Stop the emulator.
	err = c.stopEmulator(realNoReg.EmulatorId)
	if err != nil {
		t.Fatal(err)
	}
	emu, err = c.getEmulator(realNoReg.EmulatorId)
	if err != nil {
		t.Fatal(err)
	}
	if emu.State != emulators.Emulator_OFFLINE {
		t.Fatalf("Expected %v: %v", emulators.Emulator_OFFLINE, emu.State)
	}
}

// Tests resolve_rules:resolve.
func TestHttpJson_Resolve(t *testing.T) {
	b, err := startNewBroker(brokerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer b.Shutdown()

	c := httpJsonClient{port: b.Port()}
	err = c.awaitReady(2 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	rule := &emulators.ResolveRule{RuleId: "r0", TargetPatterns: []string{"foo"}, ResolvedHost: "bar"}
	err = c.createResolveRule(rule)
	if err != nil {
		t.Fatal(err)
	}
	_, err = c.getResolveRule("r0")
	if err != nil {
		t.Fatal(err)
	}
	resolveResp, err := c.resolve(&emulators.ResolveRequest{Target: "foo"})
	if err != nil {
		t.Fatal(err)
	}
	if resolveResp.Target != "bar" {
		t.Fatalf("Expected bar: %v", resolveResp.Target)
	}
}
<|endoftext|>


<commit_before>package revel

import (
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strings"
)

type Route struct {
	Method      string   // e.g. GET
	Path        string   // e.g. /app/{id}
	Action      string   // e.g. Application.ShowApp
	FixedParams []string // e.g. "arg1","arg2","arg3" (CSV formatting)

	pathPattern   *regexp.Regexp // for matching the url path
	args          []*arg         // e.g. {id} from path /app/{id}
	actionPattern *regexp.Regexp
}

type RouteMatch struct {
	Action         string // e.g. Application.ShowApp
	ControllerName string // e.g. Application
	MethodName     string // e.g. ShowApp
	FixedParams    []string
	Params         map[string]string // e.g. {id: 123}
}

type arg struct {
	name       string
	index      int
	constraint *regexp.Regexp
}

var (
	nakedPathParamRegex = regexp.MustCompile(`\{([a-zA-Z_][a-zA-Z_0-9]*)\}`)
	argsPattern         = regexp.MustCompile(`\{<(?P<pattern>[^>]+)>(?P<var>[a-zA-Z_0-9]+)\}`)
)

// Prepares the route to be used in matching.
func NewRoute(method, path, action, fixedArgs string) (r *Route) {
	// Handle fixed arguments
	argsReader := strings.NewReader(fixedArgs)
	csv := csv.NewReader(argsReader)
	fargs, err := csv.Read()
	if err != nil && err != io.EOF {
		ERROR.Printf("Invalid fixed parameters (%v): for string '%v'", err.Error(), fixedArgs)
	}

	r = &Route{
		Method:      strings.ToUpper(method),
		Path:        path,
		Action:      action,
		FixedParams: fargs,
	}

	// URL pattern
	// TODO: Support non-absolute paths
	if !strings.HasPrefix(r.Path, "/") {
		ERROR.Print("Absolute URL required.")
		return
	}

	// Handle embedded arguments

	// Convert path arguments with unspecified regexes to standard form.
	// e.g. "/customer/{id}" => "/customer/{<[^/]+>id}
	normPath := nakedPathParamRegex.ReplaceAllStringFunc(r.Path, func(m string) string {
		var argMatches []string = nakedPathParamRegex.FindStringSubmatch(m)
		return "{<[^/]+>" + argMatches[1] + "}"
	})

	// Go through the arguments
	r.args = make([]*arg, 0, 3)
	for i, m := range argsPattern.FindAllStringSubmatch(normPath, -1) {
		r.args = append(r.args, &arg{
			name:       string(m[2]),
			index:      i,
			constraint: regexp.MustCompile(string(m[1])),
		})
	}

	// Now assemble the entire path regex, including the embedded parameters.
	// e.g. /app/{<[^/]+>id} => /app/(?P<id>[^/]+)
	pathPatternStr := argsPattern.ReplaceAllStringFunc(normPath, func(m string) string {
		var argMatches []string = argsPattern.FindStringSubmatch(m)
		return "(?P<" + argMatches[2] + ">" + argMatches[1] + ")"
	})
	r.pathPattern = regexp.MustCompile(pathPatternStr + "$")

	// Handle action
	var actionPatternStr string = strings.Replace(r.Action, ".", `\.`, -1)
	for _, arg := range r.args {
		var argName string = "{" + arg.name + "}"
		if argIndex := strings.Index(actionPatternStr, argName); argIndex != -1 {
			actionPatternStr = strings.Replace(actionPatternStr, argName,
				"(?P<"+arg.name+">"+arg.constraint.String()+")", -1)
		}
	}
	r.actionPattern = regexp.MustCompile(actionPatternStr)
	return
}

// Return nil if no match.
func (r *Route) Match(method string, reqPath string) *RouteMatch {
	// Check the Method
	if r.Method != "*" && method != r.Method && !(method == "HEAD" && r.Method == "GET") {
		return nil
	}

	// Check the Path
	var matches []string = r.pathPattern.FindStringSubmatch(reqPath)
	if len(matches) == 0 || len(matches[0]) != len(reqPath) {
		return nil
	}

	// Figure out the Param names.
	params := make(map[string]string)
	for i, m := range matches[1:] {
		params[r.pathPattern.SubexpNames()[i+1]] = m
	}

	// If the action is variablized, replace into it with the captured args.
	action := r.Action
	if strings.Contains(action, "{") {
		for key, value := range params {
			action = strings.Replace(action, "{"+key+"}", value, -1)
		}
	}

	// Special handling for explicit 404's.
	if action == "404" {
		return &RouteMatch{
			Action: "404",
		}
	}

	// Split the action into controller and method
	actionSplit := strings.Split(action, ".")
	if len(actionSplit) != 2 {
		ERROR.Printf("Failed to split action: %s (matching route: %s)", action, r.Action)
		return nil
	}

	return &RouteMatch{
		Action:         action,
		ControllerName: actionSplit[0],
		MethodName:     actionSplit[1],
		Params:         params,
		FixedParams:    r.FixedParams,
	}
}

type Router struct {
	Routes []*Route
	path   string
}

func (router *Router) Route(req *http.Request) *RouteMatch {
	for _, route := range router.Routes {
		if m := route.Match(req.Method, req.URL.Path); m != nil {
			return m
		}
	}
	return nil
}

// Refresh re-reads the routes file and re-calculates the routing table.
// Returns an error if a specified action could not be found.
func (router *Router) Refresh() (err *Error) {
	router.Routes, err = parseRoutesFile(router.path, true)
	return
}

// parseRoutesFile reads the given routes file and returns the contained routes.
func parseRoutesFile(routesPath string, validate bool) ([]*Route, *Error) {
	contentBytes, err := ioutil.ReadFile(routesPath)
	if err != nil {
		return nil, &Error{
			Title:       "Failed to load routes file",
			Description: err.Error(),
		}
	}
	return parseRoutes(routesPath, string(contentBytes), validate)
}

// parseRoutes reads the content of a routes file into the routing table.
func parseRoutes(routesPath, content string, validate bool) ([]*Route, *Error) {
	var routes []*Route

	// For each line..
	for n, line := range strings.Split(content, "\n") {
		line = strings.TrimSpace(line)
		if len(line) == 0 || line[0] == '#' {
			continue
		}

		// Handle included routes from modules.
		// e.g. "module:testrunner" imports all routes from that module.
		if strings.HasPrefix(line, "module:") {
			moduleRoutes, err := getModuleRoutes(line[len("module:"):], validate)
			if err != nil {
				return nil, routeError(err, routesPath, content, n)
			}
			routes = append(routes, moduleRoutes...)
			continue
		}

		// A single route
		method, path, action, fixedArgs, found := parseRouteLine(line)
		if !found {
			continue
		}

		route := NewRoute(method, path, action, fixedArgs)
		routes = append(routes, route)

		if validate {
			if err := validateRoute(route); err != nil {
				return nil, routeError(err, routesPath, content, n)
			}
		}
	}

	return routes, nil
}

// validateRoute checks that every specified action exists.
func validateRoute(route *Route) error {
	// Skip variable routes.
	if strings.ContainsAny(route.Action, "{}") {
		return nil
	}

	// Skip 404s
	if route.Action == "404" {
		return nil
	}

	// We should be able to load the action.
	parts := strings.Split(route.Action, ".")
	if len(parts) != 2 {
		return fmt.Errorf("Expected two parts (Controller.Action), but got %d: %s",
			len(parts), route.Action)
	}

	var c Controller
	if err := c.SetAction(parts[0], parts[1]); err != nil {
		return err
	}

	return nil
}

// routeError adds context to a simple error message.
func routeError(err error, routesPath, content string, n int) *Error {
	if revelError, ok := err.(*Error); ok {
		return revelError
	}
	return &Error{
		Title:       "Route validation error",
		Description: err.Error(),
		Path:        routesPath,
		Line:        n + 1,
		SourceLines: strings.Split(content, "\n"),
	}
}

// getModuleRoutes loads the routes file for the given module and returns the
// list of routes.
func getModuleRoutes(moduleName string, validate bool) ([]*Route, *Error) {
	// Look up the module.  It may be not found due to the common case of e.g. the
	// testrunner module being active only in dev mode.
	module, found := ModuleByName(moduleName)
	if !found {
		INFO.Println("Skipping routes for inactive module", moduleName)
		return nil, nil
	}
	return parseRoutesFile(path.Join(module.Path, "conf", "routes"), validate)
}

// Groups:
// 1: method
// 4: path
// 5: action
// 6: fixedargs
var routePattern *regexp.Regexp = regexp.MustCompile(
	"(?i)^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|WS|\\*)" +
		"[(]?([^)]*)(\\))?[ \t]+" +
		"(.*/[^ \t]*)[ \t]+([^ \t(]+)" +
		`\(?([^)]*)\)?[ \t]*$`)

func parseRouteLine(line string) (method, path, action, fixedArgs string, found bool) {
	var matches []string = routePattern.FindStringSubmatch(line)
	if matches == nil {
		return
	}
	method, path, action, fixedArgs = matches[1], matches[4], matches[5], matches[6]
	found = true
	return
}

func NewRouter(routesPath string) *Router {
	return &Router{
		path: routesPath,
	}
}

type ActionDefinition struct {
	Host, Method, Url, Action string
	Star                      bool
	Args                      map[string]string
}

func (a *ActionDefinition) String() string {
	return a.Url
}

func (router *Router) Reverse(action string, argValues map[string]string) *ActionDefinition {

NEXT_ROUTE:
	// Loop through the routes.
	for _, route := range router.Routes {
		if route.actionPattern == nil {
			continue
		}

		var matches []string = route.actionPattern.FindStringSubmatch(action)
		if len(matches) == 0 {
			continue
		}

		for i, match := range matches[1:] {
			argValues[route.actionPattern.SubexpNames()[i+1]] = match
		}

		// Create a lookup for the route args.
		routeArgs := make(map[string]*arg)
		for _, arg := range route.args {
			routeArgs[arg.name] = arg
		}

		// Enforce the constraints on the arg values.
		for argKey, argValue := range argValues {
			arg, ok := routeArgs[argKey]
			if ok && !arg.constraint.MatchString(argValue) {
				continue NEXT_ROUTE
			}
		}

		// Build up the URL.
		var queryValues url.Values = make(url.Values)
		// Handle optional trailing slashes (e.g. "/?") by removing the question mark.
		path := strings.Replace(route.Path, "?", "", -1)
		for argKey, argValue := range argValues {
			if _, ok := routeArgs[argKey]; ok {
				// If this arg goes into the path, put it in.
				path = regexp.MustCompile(`\{(<[^>]+>)?`+regexp.QuoteMeta(argKey)+`\}`).
					ReplaceAllString(path, url.QueryEscape(string(argValue)))
			} else {
				// Else, add it to the query string.
				queryValues.Set(argKey, argValue)
			}
		}

		// Calculate the final URL and Method
		url := path
		if len(queryValues) > 0 {
			url += "?" + queryValues.Encode()
		}

		method := route.Method
		star := false
		if route.Method == "*" {
			method = "GET"
			star = true
		}

		return &ActionDefinition{
			Url:    url,
			Method: method,
			Star:   star,
			Action: action,
			Args:   argValues,
			Host:   "TODO",
		}
	}
	ERROR.Println("Failed to find reverse route:", action, argValues)
	return nil
}

func init() {
	OnAppStart(func() {
		MainRouter = NewRouter(path.Join(BasePath, "conf", "routes"))
		if MainWatcher != nil && Config.BoolDefault("watch.routes", true) {
			MainWatcher.Listen(MainRouter, MainRouter.path)
		} else {
			MainRouter.Refresh()
		}
	})
}

var RouterFilter = func(c *Controller, fc []Filter) {
	// Figure out the Controller/Action
	var route *RouteMatch = MainRouter.Route(c.Request.Request)
	if route == nil {
		c.Result = c.NotFound("No matching route found")
		return
	}

	// The route may want to explicitly return a 404.
	if route.Action == "404" {
		c.Result = c.NotFound("(intentionally)")
		return
	}

	// Set the action.
	if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
		c.Result = c.NotFound(err.Error())
		return
	}

	// Add the route and fixed params to the Request Params.
	for k, v := range route.Params {
		if c.Params.Route == nil {
			c.Params.Route = make(map[string][]string)
		}
		c.Params.Route[k] = []string{v}
	}

	// Add the fixed parameters mapped by name.
	c.Params.Fixed = make(url.Values)
	for i, value := range route.FixedParams {
		if i < len(c.MethodType.Args) {
			arg := c.MethodType.Args[i]
			c.Params.Fixed.Set(arg.Name, value)
		} else {
			WARN.Println("Too many parameters to", route.Action, "trying to add", value)
			break
		}
	}

	fc[0](c, fc[1:])
}
<commit_msg>Router: only create a new map for Fixed params if necessary<commit_after>package revel

import (
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strings"
)

type Route struct {
	Method      string   // e.g. GET
	Path        string   // e.g. /app/{id}
	Action      string   // e.g. Application.ShowApp
	FixedParams []string // e.g. "arg1","arg2","arg3" (CSV formatting)

	pathPattern   *regexp.Regexp // for matching the url path
	args          []*arg         // e.g. {id} from path /app/{id}
	actionPattern *regexp.Regexp
}

type RouteMatch struct {
	Action         string // e.g. Application.ShowApp
	ControllerName string // e.g. Application
	MethodName     string // e.g. ShowApp
	FixedParams    []string
	Params         map[string]string // e.g. {id: 123}
}

type arg struct {
	name       string
	index      int
	constraint *regexp.Regexp
}

var (
	nakedPathParamRegex = regexp.MustCompile(`\{([a-zA-Z_][a-zA-Z_0-9]*)\}`)
	argsPattern         = regexp.MustCompile(`\{<(?P<pattern>[^>]+)>(?P<var>[a-zA-Z_0-9]+)\}`)
)

// Prepares the route to be used in matching.
func NewRoute(method, path, action, fixedArgs string) (r *Route) {
	// Handle fixed arguments
	argsReader := strings.NewReader(fixedArgs)
	csv := csv.NewReader(argsReader)
	fargs, err := csv.Read()
	if err != nil && err != io.EOF {
		ERROR.Printf("Invalid fixed parameters (%v): for string '%v'", err.Error(), fixedArgs)
	}

	r = &Route{
		Method:      strings.ToUpper(method),
		Path:        path,
		Action:      action,
		FixedParams: fargs,
	}

	// URL pattern
	// TODO: Support non-absolute paths
	if !strings.HasPrefix(r.Path, "/") {
		ERROR.Print("Absolute URL required.")
		return
	}

	// Handle embedded arguments

	// Convert path arguments with unspecified regexes to standard form.
	// e.g. "/customer/{id}" => "/customer/{<[^/]+>id}
	normPath := nakedPathParamRegex.ReplaceAllStringFunc(r.Path, func(m string) string {
		var argMatches []string = nakedPathParamRegex.FindStringSubmatch(m)
		return "{<[^/]+>" + argMatches[1] + "}"
	})

	// Go through the arguments
	r.args = make([]*arg, 0, 3)
	for i, m := range argsPattern.FindAllStringSubmatch(normPath, -1) {
		r.args = append(r.args, &arg{
			name:       string(m[2]),
			index:      i,
			constraint: regexp.MustCompile(string(m[1])),
		})
	}

	// Now assemble the entire path regex, including the embedded parameters.
	// e.g. /app/{<[^/]+>id} => /app/(?P<id>[^/]+)
	pathPatternStr := argsPattern.ReplaceAllStringFunc(normPath, func(m string) string {
		var argMatches []string = argsPattern.FindStringSubmatch(m)
		return "(?P<" + argMatches[2] + ">" + argMatches[1] + ")"
	})
	r.pathPattern = regexp.MustCompile(pathPatternStr + "$")

	// Handle action
	var actionPatternStr string = strings.Replace(r.Action, ".", `\.`, -1)
	for _, arg := range r.args {
		var argName string = "{" + arg.name + "}"
		if argIndex := strings.Index(actionPatternStr, argName); argIndex != -1 {
			actionPatternStr = strings.Replace(actionPatternStr, argName,
				"(?P<"+arg.name+">"+arg.constraint.String()+")", -1)
		}
	}
	r.actionPattern = regexp.MustCompile(actionPatternStr)
	return
}

// Return nil if no match.
func (r *Route) Match(method string, reqPath string) *RouteMatch {
	// Check the Method
	if r.Method != "*" && method != r.Method && !(method == "HEAD" && r.Method == "GET") {
		return nil
	}

	// Check the Path
	var matches []string = r.pathPattern.FindStringSubmatch(reqPath)
	if len(matches) == 0 || len(matches[0]) != len(reqPath) {
		return nil
	}

	// Figure out the Param names.
	params := make(map[string]string)
	for i, m := range matches[1:] {
		params[r.pathPattern.SubexpNames()[i+1]] = m
	}

	// If the action is variablized, replace into it with the captured args.
	action := r.Action
	if strings.Contains(action, "{") {
		for key, value := range params {
			action = strings.Replace(action, "{"+key+"}", value, -1)
		}
	}

	// Special handling for explicit 404's.
	if action == "404" {
		return &RouteMatch{
			Action: "404",
		}
	}

	// Split the action into controller and method
	actionSplit := strings.Split(action, ".")
	if len(actionSplit) != 2 {
		ERROR.Printf("Failed to split action: %s (matching route: %s)", action, r.Action)
		return nil
	}

	return &RouteMatch{
		Action:         action,
		ControllerName: actionSplit[0],
		MethodName:     actionSplit[1],
		Params:         params,
		FixedParams:    r.FixedParams,
	}
}

type Router struct {
	Routes []*Route
	path   string
}

func (router *Router) Route(req *http.Request) *RouteMatch {
	for _, route := range router.Routes {
		if m := route.Match(req.Method, req.URL.Path); m != nil {
			return m
		}
	}
	return nil
}

// Refresh re-reads the routes file and re-calculates the routing table.
// Returns an error if a specified action could not be found.
func (router *Router) Refresh() (err *Error) {
	router.Routes, err = parseRoutesFile(router.path, true)
	return
}

// parseRoutesFile reads the given routes file and returns the contained routes.
func parseRoutesFile(routesPath string, validate bool) ([]*Route, *Error) {
	contentBytes, err := ioutil.ReadFile(routesPath)
	if err != nil {
		return nil, &Error{
			Title:       "Failed to load routes file",
			Description: err.Error(),
		}
	}
	return parseRoutes(routesPath, string(contentBytes), validate)
}

// parseRoutes reads the content of a routes file into the routing table.
func parseRoutes(routesPath, content string, validate bool) ([]*Route, *Error) {
	var routes []*Route

	// For each line..
	for n, line := range strings.Split(content, "\n") {
		line = strings.TrimSpace(line)
		if len(line) == 0 || line[0] == '#' {
			continue
		}

		// Handle included routes from modules.
		// e.g. "module:testrunner" imports all routes from that module.
		if strings.HasPrefix(line, "module:") {
			moduleRoutes, err := getModuleRoutes(line[len("module:"):], validate)
			if err != nil {
				return nil, routeError(err, routesPath, content, n)
			}
			routes = append(routes, moduleRoutes...)
			continue
		}

		// A single route
		method, path, action, fixedArgs, found := parseRouteLine(line)
		if !found {
			continue
		}

		route := NewRoute(method, path, action, fixedArgs)
		routes = append(routes, route)

		if validate {
			if err := validateRoute(route); err != nil {
				return nil, routeError(err, routesPath, content, n)
			}
		}
	}

	return routes, nil
}

// validateRoute checks that every specified action exists.
func validateRoute(route *Route) error {
	// Skip variable routes.
	if strings.ContainsAny(route.Action, "{}") {
		return nil
	}

	// Skip 404s
	if route.Action == "404" {
		return nil
	}

	// We should be able to load the action.
	parts := strings.Split(route.Action, ".")
	if len(parts) != 2 {
		return fmt.Errorf("Expected two parts (Controller.Action), but got %d: %s",
			len(parts), route.Action)
	}

	var c Controller
	if err := c.SetAction(parts[0], parts[1]); err != nil {
		return err
	}

	return nil
}

// routeError adds context to a simple error message.
func routeError(err error, routesPath, content string, n int) *Error {
	if revelError, ok := err.(*Error); ok {
		return revelError
	}
	return &Error{
		Title:       "Route validation error",
		Description: err.Error(),
		Path:        routesPath,
		Line:        n + 1,
		SourceLines: strings.Split(content, "\n"),
	}
}

// getModuleRoutes loads the routes file for the given module and returns the
// list of routes.
func getModuleRoutes(moduleName string, validate bool) ([]*Route, *Error) {
	// Look up the module.  It may be not found due to the common case of e.g. the
	// testrunner module being active only in dev mode.
	module, found := ModuleByName(moduleName)
	if !found {
		INFO.Println("Skipping routes for inactive module", moduleName)
		return nil, nil
	}
	return parseRoutesFile(path.Join(module.Path, "conf", "routes"), validate)
}

// Groups:
// 1: method
// 4: path
// 5: action
// 6: fixedargs
var routePattern *regexp.Regexp = regexp.MustCompile(
	"(?i)^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|WS|\\*)" +
		"[(]?([^)]*)(\\))?[ \t]+" +
		"(.*/[^ \t]*)[ \t]+([^ \t(]+)" +
		`\(?([^)]*)\)?[ \t]*$`)

func parseRouteLine(line string) (method, path, action, fixedArgs string, found bool) {
	var matches []string = routePattern.FindStringSubmatch(line)
	if matches == nil {
		return
	}
	method, path, action, fixedArgs = matches[1], matches[4], matches[5], matches[6]
	found = true
	return
}

func NewRouter(routesPath string) *Router {
	return &Router{
		path: routesPath,
	}
}

type ActionDefinition struct {
	Host, Method, Url, Action string
	Star                      bool
	Args                      map[string]string
}

func (a *ActionDefinition) String() string {
	return a.Url
}

func (router *Router) Reverse(action string, argValues map[string]string) *ActionDefinition {

NEXT_ROUTE:
	// Loop through the routes.
	for _, route := range router.Routes {
		if route.actionPattern == nil {
			continue
		}

		var matches []string = route.actionPattern.FindStringSubmatch(action)
		if len(matches) == 0 {
			continue
		}

		for i, match := range matches[1:] {
			argValues[route.actionPattern.SubexpNames()[i+1]] = match
		}

		// Create a lookup for the route args.
		routeArgs := make(map[string]*arg)
		for _, arg := range route.args {
			routeArgs[arg.name] = arg
		}

		// Enforce the constraints on the arg values.
		for argKey, argValue := range argValues {
			arg, ok := routeArgs[argKey]
			if ok && !arg.constraint.MatchString(argValue) {
				continue NEXT_ROUTE
			}
		}

		// Build up the URL.
		var queryValues url.Values = make(url.Values)
		// Handle optional trailing slashes (e.g. "/?") by removing the question mark.
		path := strings.Replace(route.Path, "?", "", -1)
		for argKey, argValue := range argValues {
			if _, ok := routeArgs[argKey]; ok {
				// If this arg goes into the path, put it in.
				path = regexp.MustCompile(`\{(<[^>]+>)?`+regexp.QuoteMeta(argKey)+`\}`).
					ReplaceAllString(path, url.QueryEscape(string(argValue)))
			} else {
				// Else, add it to the query string.
				queryValues.Set(argKey, argValue)
			}
		}

		// Calculate the final URL and Method
		url := path
		if len(queryValues) > 0 {
			url += "?" + queryValues.Encode()
		}

		method := route.Method
		star := false
		if route.Method == "*" {
			method = "GET"
			star = true
		}

		return &ActionDefinition{
			Url:    url,
			Method: method,
			Star:   star,
			Action: action,
			Args:   argValues,
			Host:   "TODO",
		}
	}
	ERROR.Println("Failed to find reverse route:", action, argValues)
	return nil
}

func init() {
	OnAppStart(func() {
		MainRouter = NewRouter(path.Join(BasePath, "conf", "routes"))
		if MainWatcher != nil && Config.BoolDefault("watch.routes", true) {
			MainWatcher.Listen(MainRouter, MainRouter.path)
		} else {
			MainRouter.Refresh()
		}
	})
}

var RouterFilter = func(c *Controller, fc []Filter) {
	// Figure out the Controller/Action
	var route *RouteMatch = MainRouter.Route(c.Request.Request)
	if route == nil {
		c.Result = c.NotFound("No matching route found")
		return
	}

	// The route may want to explicitly return a 404.
	if route.Action == "404" {
		c.Result = c.NotFound("(intentionally)")
		return
	}

	// Set the action.
	if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
		c.Result = c.NotFound(err.Error())
		return
	}

	// Add the route and fixed params to the Request Params.
	for k, v := range route.Params {
		if c.Params.Route == nil {
			c.Params.Route = make(map[string][]string)
		}
		c.Params.Route[k] = []string{v}
	}

	// Add the fixed parameters mapped by name.
	for i, value := range route.FixedParams {
		if c.Params.Fixed == nil {
			c.Params.Fixed = make(url.Values)
		}
		if i < len(c.MethodType.Args) {
			arg := c.MethodType.Args[i]
			c.Params.Fixed.Set(arg.Name, value)
		} else {
			WARN.Println("Too many parameters to", route.Action, "trying to add", value)
			break
		}
	}

	fc[0](c, fc[1:])
}
<|endoftext|>


<commit_before>package amber

import (
	"os"
	"fmt"
	"strings"
	"regexp"
	"net/http"
	"reflect"
	"runtime"
)

type route struct {
	pattern					string
	method					string
	isController 		bool
	controllerType 	reflect.Type
	cName						string
	cAction					string
	handler 				interface{}
	router 					*router
	regex 					*regexp.Regexp
}

type router struct {
	namedRoutes 	map[string]*route
	routes				[]*route
}

// Router functions ----------------------------------------------
func newRouter() *router {
	return &router{namedRoutes: make(map[string]*route)}
}

func (r *router) AddRoute(method string, pattern string, handler Handler) *route {
	route := newRoute(method, pattern, handler, r)
	if route.isController {
		route.Name(route.cName + "#" + route.cAction)
		r.routes = append(r.routes, route)

		return route
	}

	return nil
}

func (r *router) AddRouteFunc(method string, pattern string, handler func(http.ResponseWriter, *http.Request)) * route {
	route := newRoute(method, pattern, handler, r)
	r.routes = append(r.routes, route)

	return route
}

func (r *router) searchRoute(method string, request string) (*route, Param) {
	for i, route := range r.routes {
		if route.method == method || method == "Any" {
			matches := route.regex.FindStringSubmatch(request)
			if len(matches) > 0 && matches[0] == request {
				params := make(Param)
				for i := 1; i < len(matches)-1; i++ {
					params[route.regex.SubexpNames()[i]] = matches[i]
				}
				return r.routes[i], params
			}
		}
	}
	return nil, nil
}

func (r *router) getReverseUrl(name string, param []interface{}) string {
	route := r.getNamedRoute(name)
	nParam := len(param)

	if route != nil {
		i := -1
		regex := regexp.MustCompile("{.*}")
		url := regex.ReplaceAllStringFunc(route.pattern, func(str string) string {
			i = i + 1
			if i <= nParam - 1 {
				return param[i].(string)
			} else {
				return ""
			}
		})

		return url
	}

	return ""
}

func (r *router) getNamedRoute(name string) *route {
	return r.namedRoutes[strings.ToLower(name)]
}

// Route functions ---------------------------------------------
func newRoute(method string, pattern string, handler Handler, router *router) *route {
	regex := regexp.MustCompile("{[a-zA-Z0-9]+}")
	regexPattern := regex.ReplaceAllStringFunc(pattern, func(s string) string {
		return fmt.Sprintf("(?P<%s>[a-z]+)", s[1:len(s)-1])
	})
	regexPattern = regexPattern + "(\\?.*)?"

	cName := ""
	cAction := ""
	isController := false
	var controllerType reflect.Type

	if ok, t := isControllerHandler(handler); ok {
		var fn *runtime.Func
	  if fn = runtime.FuncForPC(reflect.ValueOf(handler).Pointer()); fn == nil {
			logger.Println("![Warning]! Failed to add route. Can't fetch controller function")
			return nil
	  }

	  controllerType = t
	  isController = true
		token := strings.Split(fn.Name(), ".")
		cName = token[1][2:len(token[1])-1]
		cAction = token[2]
	}

	return &route{pattern, method, isController, controllerType, cName, cAction, handler, router, regexp.MustCompile(regexPattern)}
}

func (r *route) Name(name string) {
	r.router.namedRoutes[strings.ToLower(name)] = r
}

func servePublic(rw http.ResponseWriter, req *http.Request) {
	var file http.File
	var err error
	var stat os.FileInfo
	fname := req.URL.Path[len("/public/"):]

	if !strings.HasPrefix(fname, ".") {
		if file, err = http.Dir("public").Open(fname); err == nil {
			if stat, err = file.Stat(); err == nil {
				if !stat.IsDir() {
					http.ServeContent(rw, req, req.URL.Path, stat.ModTime(), file)
					file.Close()
					return
				}
			}
		}
	}

	http.NotFound(rw, req)
}<commit_msg>fixed url params to accept alphanumerical values<commit_after>package amber

import (
	"os"
	"fmt"
	"strings"
	"regexp"
	"net/http"
	"reflect"
	"runtime"
)

type route struct {
	pattern					string
	method					string
	isController 		bool
	controllerType 	reflect.Type
	cName						string
	cAction					string
	handler 				interface{}
	router 					*router
	regex 					*regexp.Regexp
}

type router struct {
	namedRoutes 	map[string]*route
	routes				[]*route
}

// Router functions ----------------------------------------------
func newRouter() *router {
	return &router{namedRoutes: make(map[string]*route)}
}

func (r *router) AddRoute(method string, pattern string, handler Handler) *route {
	route := newRoute(method, pattern, handler, r)
	if route.isController {
		route.Name(route.cName + "#" + route.cAction)
		r.routes = append(r.routes, route)

		return route
	}

	return nil
}

func (r *router) AddRouteFunc(method string, pattern string, handler func(http.ResponseWriter, *http.Request)) * route {
	route := newRoute(method, pattern, handler, r)
	r.routes = append(r.routes, route)

	return route
}

func (r *router) searchRoute(method string, request string) (*route, Param) {
	for i, route := range r.routes {
		if route.method == method || method == "Any" {
			matches := route.regex.FindStringSubmatch(request)
			if len(matches) > 0 && matches[0] == request {
				params := make(Param)
				for i := 1; i < len(matches)-1; i++ {
					params[route.regex.SubexpNames()[i]] = matches[i]
				}
				return r.routes[i], params
			}
		}
	}
	return nil, nil
}

func (r *router) getReverseUrl(name string, param []interface{}) string {
	route := r.getNamedRoute(name)
	nParam := len(param)

	if route != nil {
		i := -1
		regex := regexp.MustCompile("{.*}")
		url := regex.ReplaceAllStringFunc(route.pattern, func(str string) string {
			i = i + 1
			if i <= nParam - 1 {
				return param[i].(string)
			} else {
				return ""
			}
		})

		return url
	}

	return ""
}

func (r *router) getNamedRoute(name string) *route {
	return r.namedRoutes[strings.ToLower(name)]
}

// Route functions ---------------------------------------------
func newRoute(method string, pattern string, handler Handler, router *router) *route {
	regex := regexp.MustCompile("{[a-zA-Z0-9]+}")
	regexPattern := regex.ReplaceAllStringFunc(pattern, func(s string) string {
		return fmt.Sprintf("(?P<%s>[a-zA-Z0-9]+)", s[1:len(s)-1])
	})
	regexPattern = regexPattern + "(\\?.*)?"

	cName := ""
	cAction := ""
	isController := false
	var controllerType reflect.Type

	if ok, t := isControllerHandler(handler); ok {
		var fn *runtime.Func
	  if fn = runtime.FuncForPC(reflect.ValueOf(handler).Pointer()); fn == nil {
			logger.Println("![Warning]! Failed to add route. Can't fetch controller function")
			return nil
	  }

	  controllerType = t
	  isController = true
		token := strings.Split(fn.Name(), ".")
		cName = token[1][2:len(token[1])-1]
		cAction = token[2]
	}

	return &route{pattern, method, isController, controllerType, cName, cAction, handler, router, regexp.MustCompile(regexPattern)}
}

func (r *route) Name(name string) {
	r.router.namedRoutes[strings.ToLower(name)] = r
}

func servePublic(rw http.ResponseWriter, req *http.Request) {
	var file http.File
	var err error
	var stat os.FileInfo
	fname := req.URL.Path[len("/public/"):]

	if !strings.HasPrefix(fname, ".") {
		if file, err = http.Dir("public").Open(fname); err == nil {
			if stat, err = file.Stat(); err == nil {
				if !stat.IsDir() {
					http.ServeContent(rw, req, req.URL.Path, stat.ModTime(), file)
					file.Close()
					return
				}
			}
		}
	}

	http.NotFound(rw, req)
}<|endoftext|>


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

/*
Package router 0.2.6 provides fast HTTP request router.

The router matches incoming requests by the request method and the path.
If a handle is registered for this path and method, the router delegates the
request to defined handler.
The router package is useful to prepare a RESTful API for Go services.
It has JSON output, which bind automatically for relevant type of data.
The router has timer feature to display duration of request handling in the header

Simplest example (serve static route):

	package main

	import (
		"github.com/takama/router"
	)

	func Hello(c *router.Control) {
		c.Body("Hello")
	}

	func main() {
		r := router.New()
		r.GET("/hello", Hello)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Check it:

	curl -i http://localhost:8888/hello

Serve dynamic route with parameter:

	func main() {
		r := router.New()
		r.GET("/hello/:name", func(c *router.Control) {
			c.Code(200).Body("Hello " + c.Get(":name"))
		})

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Checks JSON Content-Type automatically:

	func main() {
		r := router.New()
		r.GET("/settings/database/:db", func(c *router.Control) {
			data := map[string]map[string]string{
				"Database settings": {
					"database": c.Get(":db"),
					"host":     "localhost",
					"port":     "3306",
				},
			}
			c.Code(200).Body(data)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Go Router
*/
package router

import (
	"log"
	"net/http"
	"strings"
)

// Router represents a multiplexer for HTTP requests.
type Router struct {
	// List of handlers which accociated with known http methods (GET, POST ...)
	handlers map[string]*parser

	// NotFound is called when unknown HTTP method or a handler not found.
	// If it is not set, http.NotFound is used.
	// Please overwrite this if need your own NotFound handler.
	NotFound Handle

	// PanicHandler is called when panic happen.
	// The handler prevents your server from crashing and should be used to return
	// http status code http.StatusInternalServerError (500)
	PanicHandler Handle

	// Logger activates logging for each requests
	Logger bool
}

// Handle type is aliased to type of handler function.
type Handle func(*Control)

// New it returns a new multiplexer (Router).
func New() *Router {
	return &Router{handlers: make(map[string]*parser)}
}

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

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

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

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

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

// Handle registers a new request handle with the given path and method.
func (r *Router) Handle(method, path string, h Handle) {
	if r.handlers[method] == nil {
		r.handlers[method] = newParser()
	}
	r.handlers[method].register(path, h)
}

// Handler allows the usage of an http.Handler as a request handle.
func (r *Router) Handler(method, path string, handler http.Handler) {
	r.Handle(method, path,
		func(c *Control) {
			handler.ServeHTTP(c.Writer, c.Request)
		},
	)
}

// HandlerFunc allows the usage of an http.HandlerFunc as a request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(c *Control) {
			handler(c.Writer, c.Request)
		},
	)
}

// Lookup returns handler and URL parameters that associated with path.
func (r *Router) Lookup(method, path string) (Handle, []Param, bool) {
	if parser := r.handlers[method]; parser != nil {
		return parser.get(path)
	}
	return nil, nil, false
}

// Listen and serve on requested host and port.
func (r *Router) Listen(hostPort string) {
	if err := http.ListenAndServe(hostPort, r); err != nil {
		log.Fatal(err)
	}
}

// ServeHTTP implements http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if recovery := recover(); recovery != nil {
			if r.PanicHandler != nil {
				c := &Control{Request: req, Writer: w}
				r.PanicHandler(c)
			} else {
				log.Println("Recovered in handler:", req.Method, req.URL.Path)
			}
		}
	}()
	if r.Logger {
		log.Println(req.RemoteAddr, req.Method, req.URL.Path)
	}
	if _, ok := r.handlers[req.Method]; ok {
		if handle, params, ok := r.handlers[req.Method].get(req.URL.Path); ok {
			c := &Control{Request: req, Writer: w}
			if len(params) > 0 {
				c.Params = append(c.Params, params...)
			}
			handle(c)
			return
		}
	}
	allowed := make([]string, 0, len(r.handlers))
	for method, parser := range r.handlers {
		if _, _, ok := parser.get(req.URL.Path); ok {
			allowed = append(allowed, method)
		}
	}

	if len(allowed) == 0 {
		if r.NotFound != nil {
			c := &Control{Request: req, Writer: w}
			r.NotFound(c)
		} else {
			http.NotFound(w, req)
		}
		return
	}

	w.Header().Add("Allow", strings.Join(allowed, ", "))
	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}
<commit_msg>Bumped version number to 0.2.7<commit_after>// Copyright 2014 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package router 0.2.7 provides fast HTTP request router.

The router matches incoming requests by the request method and the path.
If a handle is registered for this path and method, the router delegates the
request to defined handler.
The router package is useful to prepare a RESTful API for Go services.
It has JSON output, which bind automatically for relevant type of data.
The router has timer feature to display duration of request handling in the header

Simplest example (serve static route):

	package main

	import (
		"github.com/takama/router"
	)

	func Hello(c *router.Control) {
		c.Body("Hello")
	}

	func main() {
		r := router.New()
		r.GET("/hello", Hello)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Check it:

	curl -i http://localhost:8888/hello

Serve dynamic route with parameter:

	func main() {
		r := router.New()
		r.GET("/hello/:name", func(c *router.Control) {
			c.Code(200).Body("Hello " + c.Get(":name"))
		})

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Checks JSON Content-Type automatically:

	func main() {
		r := router.New()
		r.GET("/settings/database/:db", func(c *router.Control) {
			data := map[string]map[string]string{
				"Database settings": {
					"database": c.Get(":db"),
					"host":     "localhost",
					"port":     "3306",
				},
			}
			c.Code(200).Body(data)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Go Router
*/
package router

import (
	"log"
	"net/http"
	"strings"
)

// Router represents a multiplexer for HTTP requests.
type Router struct {
	// List of handlers which accociated with known http methods (GET, POST ...)
	handlers map[string]*parser

	// NotFound is called when unknown HTTP method or a handler not found.
	// If it is not set, http.NotFound is used.
	// Please overwrite this if need your own NotFound handler.
	NotFound Handle

	// PanicHandler is called when panic happen.
	// The handler prevents your server from crashing and should be used to return
	// http status code http.StatusInternalServerError (500)
	PanicHandler Handle

	// Logger activates logging for each requests
	Logger bool
}

// Handle type is aliased to type of handler function.
type Handle func(*Control)

// New it returns a new multiplexer (Router).
func New() *Router {
	return &Router{handlers: make(map[string]*parser)}
}

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

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

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

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

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

// Handle registers a new request handle with the given path and method.
func (r *Router) Handle(method, path string, h Handle) {
	if r.handlers[method] == nil {
		r.handlers[method] = newParser()
	}
	r.handlers[method].register(path, h)
}

// Handler allows the usage of an http.Handler as a request handle.
func (r *Router) Handler(method, path string, handler http.Handler) {
	r.Handle(method, path,
		func(c *Control) {
			handler.ServeHTTP(c.Writer, c.Request)
		},
	)
}

// HandlerFunc allows the usage of an http.HandlerFunc as a request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(c *Control) {
			handler(c.Writer, c.Request)
		},
	)
}

// Lookup returns handler and URL parameters that associated with path.
func (r *Router) Lookup(method, path string) (Handle, []Param, bool) {
	if parser := r.handlers[method]; parser != nil {
		return parser.get(path)
	}
	return nil, nil, false
}

// Listen and serve on requested host and port.
func (r *Router) Listen(hostPort string) {
	if err := http.ListenAndServe(hostPort, r); err != nil {
		log.Fatal(err)
	}
}

// ServeHTTP implements http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if recovery := recover(); recovery != nil {
			if r.PanicHandler != nil {
				c := &Control{Request: req, Writer: w}
				r.PanicHandler(c)
			} else {
				log.Println("Recovered in handler:", req.Method, req.URL.Path)
			}
		}
	}()
	if r.Logger {
		log.Println(req.RemoteAddr, req.Method, req.URL.Path)
	}
	if _, ok := r.handlers[req.Method]; ok {
		if handle, params, ok := r.handlers[req.Method].get(req.URL.Path); ok {
			c := &Control{Request: req, Writer: w}
			if len(params) > 0 {
				c.Params = append(c.Params, params...)
			}
			handle(c)
			return
		}
	}
	allowed := make([]string, 0, len(r.handlers))
	for method, parser := range r.handlers {
		if _, _, ok := parser.get(req.URL.Path); ok {
			allowed = append(allowed, method)
		}
	}

	if len(allowed) == 0 {
		if r.NotFound != nil {
			c := &Control{Request: req, Writer: w}
			r.NotFound(c)
		} else {
			http.NotFound(w, req)
		}
		return
	}

	w.Header().Add("Allow", strings.Join(allowed, ", "))
	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}
<|endoftext|>


<commit_before>package lion

import (
	"net/http"
	"os"
	"sync"

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

// Router is the main component of Lion. It is responsible for registering handlers and middlewares
type Router struct {
	rm RegisterMatcher

	router *Router

	middlewares Middlewares

	handler Handler // TODO: create a handler

	pattern string

	notFoundHandler Handler

	registeredHandlers []registeredHandler // Used for Mount()

	pool sync.Pool

	namedMiddlewares map[string]Middlewares
}

// New creates a new router instance
func New(mws ...Middleware) *Router {
	r := &Router{
		middlewares:      Middlewares{},
		rm:               newRadixMatcher(),
		namedMiddlewares: make(map[string]Middlewares),
	}
	r.pool.New = func() interface{} {
		return NewContext()
	}
	r.router = r
	r.Use(mws...)
	return r
}

// Group creates a subrouter with parent pattern provided.
func (r *Router) Group(pattern string, mws ...Middleware) *Router {
	p := r.pattern + pattern
	if pattern == "/" && r.pattern != "/" {
		p = r.pattern
	}
	validatePattern(p)

	nr := &Router{
		router:           r,
		rm:               r.rm,
		pattern:          p,
		middlewares:      Middlewares{},
		namedMiddlewares: make(map[string]Middlewares),
	}
	nr.Use(mws...)
	return nr
}

// Get registers an http GET method receiver with the provided Handler
func (r *Router) Get(pattern string, handler Handler) {
	r.Handle("GET", pattern, handler)
}

// Head registers an http HEAD method receiver with the provided Handler
func (r *Router) Head(pattern string, handler Handler) {
	r.Handle("HEAD", pattern, handler)
}

// Post registers an http POST method receiver with the provided Handler
func (r *Router) Post(pattern string, handler Handler) {
	r.Handle("POST", pattern, handler)
}

// Put registers an http PUT method receiver with the provided Handler
func (r *Router) Put(pattern string, handler Handler) {
	r.Handle("PUT", pattern, handler)
}

// Delete registers an http DELETE method receiver with the provided Handler
func (r *Router) Delete(pattern string, handler Handler) {
	r.Handle("DELETE", pattern, handler)
}

// Trace registers an http TRACE method receiver with the provided Handler
func (r *Router) Trace(pattern string, handler Handler) {
	r.Handle("TRACE", pattern, handler)
}

// Options registers an http OPTIONS method receiver with the provided Handler
func (r *Router) Options(pattern string, handler Handler) {
	r.Handle("OPTIONS", pattern, handler)
}

// Connect registers an http CONNECT method receiver with the provided Handler
func (r *Router) Connect(pattern string, handler Handler) {
	r.Handle("CONNECT", pattern, handler)
}

// Patch registers an http PATCH method receiver with the provided Handler
func (r *Router) Patch(pattern string, handler Handler) {
	r.Handle("PATCH", pattern, handler)
}

// GetFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) GetFunc(pattern string, fn HandlerFunc) {
	r.Get(pattern, HandlerFunc(fn))
}

// HeadFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) HeadFunc(pattern string, fn HandlerFunc) {
	r.Head(pattern, HandlerFunc(fn))
}

// PostFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PostFunc(pattern string, fn HandlerFunc) {
	r.Post(pattern, HandlerFunc(fn))
}

// PutFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PutFunc(pattern string, fn HandlerFunc) {
	r.Put(pattern, HandlerFunc(fn))
}

// DeleteFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) DeleteFunc(pattern string, fn HandlerFunc) {
	r.Delete(pattern, HandlerFunc(fn))
}

// TraceFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) TraceFunc(pattern string, fn HandlerFunc) {
	r.Trace(pattern, HandlerFunc(fn))
}

// OptionsFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) OptionsFunc(pattern string, fn HandlerFunc) {
	r.Options(pattern, HandlerFunc(fn))
}

// ConnectFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) ConnectFunc(pattern string, fn HandlerFunc) {
	r.Connect(pattern, HandlerFunc(fn))
}

// PatchFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PatchFunc(pattern string, fn HandlerFunc) {
	r.Patch(pattern, HandlerFunc(fn))
}

// Use registers middlewares to be used
func (r *Router) Use(middlewares ...Middleware) {
	r.middlewares = append(r.middlewares, middlewares...)
}

// UseFunc wraps a MiddlewareFunc as a Middleware and registers it middlewares to be used
func (r *Router) UseFunc(middlewareFuncs ...MiddlewareFunc) {
	for _, fn := range middlewareFuncs {
		r.Use(MiddlewareFunc(fn))
	}
}

type negroniHandler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

type negroniHandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h negroniHandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}

func (r *Router) UseNegroni(n negroniHandler) {
	r.Use(MiddlewareFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			n.ServeHTTP(w, r, HandlerFunc(func(_ context.Context, w http.ResponseWriter, r *http.Request) {
				next.ServeHTTPC(c, w, r)
			}).ServeHTTP)
		})
	}))
}

func (r *Router) UseNegroniFunc(n func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
	r.UseNegroni(negroniHandlerFunc(n))
}

// UseHandler uses
func (r *Router) UseHandler(handler Handler) {
	r.UseFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			handler.ServeHTTPC(c, w, r)
			next.ServeHTTPC(c, w, r)
		})
	})
}

// UseHandlerFunc uses
func (r *Router) UseHandlerFunc(fn HandlerFunc) {
	r.UseHandler(HandlerFunc(fn))
}

// Handle is the underling method responsible for registering a handler for a specific method and pattern.
func (r *Router) Handle(method, pattern string, handler Handler) {
	validatePattern(pattern)

	var p string
	if !r.isRoot() && pattern == "/" {
		p = r.pattern
	} else {
		p = r.pattern + pattern
	}

	built := r.buildMiddlewares(handler)
	r.registeredHandlers = append(r.registeredHandlers, registeredHandler{method, pattern, built})
	r.router.rm.Register(method, p, built)
}

type registeredHandler struct {
	method, pattern string
	handler         Handler
}

// Mount mounts a subrouter at the provided pattern
func (r *Router) Mount(pattern string, router *Router, mws ...Middleware) {
	sub := r.Group(pattern, mws...)
	for _, rh := range router.registeredHandlers {
		sub.Handle(rh.method, rh.pattern, rh.handler)
	}
}

func (r *Router) buildMiddlewares(handler Handler) Handler {
	handler = r.middlewares.BuildHandler(handler)
	if !r.isRoot() {
		handler = r.router.buildMiddlewares(handler)
	}
	return handler
}

func (r *Router) isRoot() bool {
	return r.router == r
}

// HandleFunc wraps a HandlerFunc and pass it to Handle method
func (r *Router) HandleFunc(method, pattern string, fn HandlerFunc) {
	r.Handle(method, pattern, HandlerFunc(fn))
}

// ServeHTTP calls ServeHTTPC with a context.Background()
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.ServeHTTPC(context.TODO(), w, req)
}

// ServeHTTPC finds the handler associated with the request's path.
// If it is not found it calls the NotFound handler
func (r *Router) ServeHTTPC(c context.Context, w http.ResponseWriter, req *http.Request) {
	ctx := r.pool.Get().(*Context)
	ctx.parent = c

	if ctx, h := r.router.rm.Match(ctx, req); h != nil {
		h.ServeHTTPC(ctx, w, req)
	} else {
		r.notFound(ctx, w, req) // r.middlewares.BuildHandler(HandlerFunc(r.NotFound)).ServeHTTPC
	}

	ctx.reset()
	r.pool.Put(ctx)
}

// NotFound calls NotFoundHandler() if it is set. Otherwise, it calls net/http.NotFound
func (r *Router) notFound(c context.Context, w http.ResponseWriter, req *http.Request) {
	if r.router.notFoundHandler != nil {
		r.router.notFoundHandler.ServeHTTPC(c, w, req)
	} else {
		http.NotFound(w, req)
	}
}

func (r *Router) NotFoundHandler(handler Handler) {
	r.notFoundHandler = handler
}

// ServeFiles serves files located in root http.FileSystem
//
// This can be used as shown below:
// 	r := New()
// 	r.ServeFiles("/static", http.Dir("static")) // This will serve files in the directory static with /static prefix
func (r *Router) ServeFiles(path string, root http.FileSystem) {
	fileServer := http.FileServer(root)
	r.Get(path, HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
		fileServer.ServeHTTP(w, r)
	}))
}

// GetH wraps a http.Handler
func (r *Router) GetH(pattern string, handler http.Handler) {
	r.Get(pattern, Wrap(handler))
}

// HeadH wraps a http.Handler
func (r *Router) HeadH(pattern string, handler http.Handler) {
	r.Head(pattern, Wrap(handler))
}

// PostH wraps a http.Handler
func (r *Router) PostH(pattern string, handler http.Handler) {
	r.Post(pattern, Wrap(handler))
}

// PutH wraps a http.Handler
func (r *Router) PutH(pattern string, handler http.Handler) {
	r.Put(pattern, Wrap(handler))
}

// DeleteH wraps a http.Handler
func (r *Router) DeleteH(pattern string, handler http.Handler) {
	r.Delete(pattern, Wrap(handler))
}

// TraceH wraps a http.Handler
func (r *Router) TraceH(pattern string, handler http.Handler) {
	r.Trace(pattern, Wrap(handler))
}

// OptionsH wraps a http.Handler
func (r *Router) OptionsH(pattern string, handler http.Handler) {
	r.Options(pattern, Wrap(handler))
}

// ConnectH wraps a http.Handler
func (r *Router) ConnectH(pattern string, handler http.Handler) {
	r.Connect(pattern, Wrap(handler))
}

// PatchH wraps a http.Handler
func (r *Router) PatchH(pattern string, handler http.Handler) {
	r.Patch(pattern, Wrap(handler))
}

// Run calls http.ListenAndServe for the current router.
// If no addresses are specified as arguments, it will use the PORT environnement variable if it is defined. Otherwise, it will listen on port 3000 of the localmachine
//
// 	r := New()
// 	r.Run() // will call
// 	r.Run(":8080")
func (r *Router) Run(addr ...string) {
	var a string

	if len(addr) == 0 {
		if p := os.Getenv("PORT"); p != "" {
			a = p
		} else {
			a = ":3000"
		}
	} else {
		a = addr[0]
	}

	lionLogger.Printf("listening on %s", a)
	lionLogger.Fatal(http.ListenAndServe(a, r))
}

// RunTLS calls http.ListenAndServeTLS for the current router
//
// 	r := New()
// 	r.RunTLS(":3443", "cert.pem", "key.pem")
func (r *Router) RunTLS(addr, certFile, keyFile string) {
	lionLogger.Printf("listening on %s", addr)
	lionLogger.Fatal(http.ListenAndServeTLS(addr, certFile, keyFile, r))
}

// Define registers some middleware using a name for reuse later using UseNamed method.
func (r *Router) Define(name string, mws ...Middleware) {
	r.namedMiddlewares[name] = append(r.namedMiddlewares[name], mws...)
}

// Define is a convenience wrapper for Define() to use MiddlewareFunc instead of a Middleware instance
func (r *Router) DefineFunc(name string, mws ...MiddlewareFunc) {
	for _, mw := range mws {
		r.Define(name, mw)
	}
}

// UseNamed adds a middleware already defined using Define method.
// If it cannot find it in the current router, it will look for it in the parent router.
func (r *Router) UseNamed(name string) {
	if r.hasNamed(name) { // Find if it this is registered in the current router
		r.Use(r.namedMiddlewares[name]...)
	} else if !r.isRoot() { // Otherwise, look for it in parent router.
		r.router.UseNamed(name)
	} else { // not found
		panic("Unknow named middlewares: " + name)
	}
}

func (r *Router) hasNamed(name string) bool {
	_, exist := r.namedMiddlewares[name]
	return exist
}

func validatePattern(pattern string) {
	if len(pattern) > 0 && pattern[0] != '/' {
		panic("path must start with '/' in path '" + pattern + "'")
	}
}
<commit_msg>Add wildcard suffix and http.StripPrefix to ServeFiles<commit_after>package lion

import (
	"net/http"
	"os"
	"path"
	"strings"
	"sync"

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

// Router is the main component of Lion. It is responsible for registering handlers and middlewares
type Router struct {
	rm RegisterMatcher

	router *Router

	middlewares Middlewares

	handler Handler // TODO: create a handler

	pattern string

	notFoundHandler Handler

	registeredHandlers []registeredHandler // Used for Mount()

	pool sync.Pool

	namedMiddlewares map[string]Middlewares
}

// New creates a new router instance
func New(mws ...Middleware) *Router {
	r := &Router{
		middlewares:      Middlewares{},
		rm:               newRadixMatcher(),
		namedMiddlewares: make(map[string]Middlewares),
	}
	r.pool.New = func() interface{} {
		return NewContext()
	}
	r.router = r
	r.Use(mws...)
	return r
}

// Group creates a subrouter with parent pattern provided.
func (r *Router) Group(pattern string, mws ...Middleware) *Router {
	p := r.pattern + pattern
	if pattern == "/" && r.pattern != "/" {
		p = r.pattern
	}
	validatePattern(p)

	nr := &Router{
		router:           r,
		rm:               r.rm,
		pattern:          p,
		middlewares:      Middlewares{},
		namedMiddlewares: make(map[string]Middlewares),
	}
	nr.Use(mws...)
	return nr
}

// Get registers an http GET method receiver with the provided Handler
func (r *Router) Get(pattern string, handler Handler) {
	r.Handle("GET", pattern, handler)
}

// Head registers an http HEAD method receiver with the provided Handler
func (r *Router) Head(pattern string, handler Handler) {
	r.Handle("HEAD", pattern, handler)
}

// Post registers an http POST method receiver with the provided Handler
func (r *Router) Post(pattern string, handler Handler) {
	r.Handle("POST", pattern, handler)
}

// Put registers an http PUT method receiver with the provided Handler
func (r *Router) Put(pattern string, handler Handler) {
	r.Handle("PUT", pattern, handler)
}

// Delete registers an http DELETE method receiver with the provided Handler
func (r *Router) Delete(pattern string, handler Handler) {
	r.Handle("DELETE", pattern, handler)
}

// Trace registers an http TRACE method receiver with the provided Handler
func (r *Router) Trace(pattern string, handler Handler) {
	r.Handle("TRACE", pattern, handler)
}

// Options registers an http OPTIONS method receiver with the provided Handler
func (r *Router) Options(pattern string, handler Handler) {
	r.Handle("OPTIONS", pattern, handler)
}

// Connect registers an http CONNECT method receiver with the provided Handler
func (r *Router) Connect(pattern string, handler Handler) {
	r.Handle("CONNECT", pattern, handler)
}

// Patch registers an http PATCH method receiver with the provided Handler
func (r *Router) Patch(pattern string, handler Handler) {
	r.Handle("PATCH", pattern, handler)
}

// GetFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) GetFunc(pattern string, fn HandlerFunc) {
	r.Get(pattern, HandlerFunc(fn))
}

// HeadFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) HeadFunc(pattern string, fn HandlerFunc) {
	r.Head(pattern, HandlerFunc(fn))
}

// PostFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PostFunc(pattern string, fn HandlerFunc) {
	r.Post(pattern, HandlerFunc(fn))
}

// PutFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PutFunc(pattern string, fn HandlerFunc) {
	r.Put(pattern, HandlerFunc(fn))
}

// DeleteFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) DeleteFunc(pattern string, fn HandlerFunc) {
	r.Delete(pattern, HandlerFunc(fn))
}

// TraceFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) TraceFunc(pattern string, fn HandlerFunc) {
	r.Trace(pattern, HandlerFunc(fn))
}

// OptionsFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) OptionsFunc(pattern string, fn HandlerFunc) {
	r.Options(pattern, HandlerFunc(fn))
}

// ConnectFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) ConnectFunc(pattern string, fn HandlerFunc) {
	r.Connect(pattern, HandlerFunc(fn))
}

// PatchFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PatchFunc(pattern string, fn HandlerFunc) {
	r.Patch(pattern, HandlerFunc(fn))
}

// Use registers middlewares to be used
func (r *Router) Use(middlewares ...Middleware) {
	r.middlewares = append(r.middlewares, middlewares...)
}

// UseFunc wraps a MiddlewareFunc as a Middleware and registers it middlewares to be used
func (r *Router) UseFunc(middlewareFuncs ...MiddlewareFunc) {
	for _, fn := range middlewareFuncs {
		r.Use(MiddlewareFunc(fn))
	}
}

type negroniHandler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

type negroniHandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h negroniHandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}

func (r *Router) UseNegroni(n negroniHandler) {
	r.Use(MiddlewareFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			n.ServeHTTP(w, r, HandlerFunc(func(_ context.Context, w http.ResponseWriter, r *http.Request) {
				next.ServeHTTPC(c, w, r)
			}).ServeHTTP)
		})
	}))
}

func (r *Router) UseNegroniFunc(n func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
	r.UseNegroni(negroniHandlerFunc(n))
}

// UseHandler uses
func (r *Router) UseHandler(handler Handler) {
	r.UseFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			handler.ServeHTTPC(c, w, r)
			next.ServeHTTPC(c, w, r)
		})
	})
}

// UseHandlerFunc uses
func (r *Router) UseHandlerFunc(fn HandlerFunc) {
	r.UseHandler(HandlerFunc(fn))
}

// Handle is the underling method responsible for registering a handler for a specific method and pattern.
func (r *Router) Handle(method, pattern string, handler Handler) {
	validatePattern(pattern)

	var p string
	if !r.isRoot() && pattern == "/" {
		p = r.pattern
	} else {
		p = r.pattern + pattern
	}

	built := r.buildMiddlewares(handler)
	r.registeredHandlers = append(r.registeredHandlers, registeredHandler{method, pattern, built})
	r.router.rm.Register(method, p, built)
}

type registeredHandler struct {
	method, pattern string
	handler         Handler
}

// Mount mounts a subrouter at the provided pattern
func (r *Router) Mount(pattern string, router *Router, mws ...Middleware) {
	sub := r.Group(pattern, mws...)
	for _, rh := range router.registeredHandlers {
		sub.Handle(rh.method, rh.pattern, rh.handler)
	}
}

func (r *Router) buildMiddlewares(handler Handler) Handler {
	handler = r.middlewares.BuildHandler(handler)
	if !r.isRoot() {
		handler = r.router.buildMiddlewares(handler)
	}
	return handler
}

func (r *Router) isRoot() bool {
	return r.router == r
}

// HandleFunc wraps a HandlerFunc and pass it to Handle method
func (r *Router) HandleFunc(method, pattern string, fn HandlerFunc) {
	r.Handle(method, pattern, HandlerFunc(fn))
}

// ServeHTTP calls ServeHTTPC with a context.Background()
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.ServeHTTPC(context.TODO(), w, req)
}

// ServeHTTPC finds the handler associated with the request's path.
// If it is not found it calls the NotFound handler
func (r *Router) ServeHTTPC(c context.Context, w http.ResponseWriter, req *http.Request) {
	ctx := r.pool.Get().(*Context)
	ctx.parent = c

	if ctx, h := r.router.rm.Match(ctx, req); h != nil {
		h.ServeHTTPC(ctx, w, req)
	} else {
		r.notFound(ctx, w, req) // r.middlewares.BuildHandler(HandlerFunc(r.NotFound)).ServeHTTPC
	}

	ctx.reset()
	r.pool.Put(ctx)
}

// NotFound calls NotFoundHandler() if it is set. Otherwise, it calls net/http.NotFound
func (r *Router) notFound(c context.Context, w http.ResponseWriter, req *http.Request) {
	if r.router.notFoundHandler != nil {
		r.router.notFoundHandler.ServeHTTPC(c, w, req)
	} else {
		http.NotFound(w, req)
	}
}

func (r *Router) NotFoundHandler(handler Handler) {
	r.notFoundHandler = handler
}

// ServeFiles serves files located in root http.FileSystem
//
// This can be used as shown below:
// 	r := New()
// 	r.ServeFiles("/static", http.Dir("static")) // This will serve files in the directory static with /static prefix
func (r *Router) ServeFiles(base string, root http.FileSystem) {
	if strings.IndexAny(base, ":*") != -1 {
		panic("Lion: ServeFiles cannot have url parameters")
	}

	pattern := path.Join(base, "/*")
	fileServer := http.StripPrefix(base, http.FileServer(root))

	r.GetH(pattern, fileServer)
}

// GetH wraps a http.Handler
func (r *Router) GetH(pattern string, handler http.Handler) {
	r.Get(pattern, Wrap(handler))
}

// HeadH wraps a http.Handler
func (r *Router) HeadH(pattern string, handler http.Handler) {
	r.Head(pattern, Wrap(handler))
}

// PostH wraps a http.Handler
func (r *Router) PostH(pattern string, handler http.Handler) {
	r.Post(pattern, Wrap(handler))
}

// PutH wraps a http.Handler
func (r *Router) PutH(pattern string, handler http.Handler) {
	r.Put(pattern, Wrap(handler))
}

// DeleteH wraps a http.Handler
func (r *Router) DeleteH(pattern string, handler http.Handler) {
	r.Delete(pattern, Wrap(handler))
}

// TraceH wraps a http.Handler
func (r *Router) TraceH(pattern string, handler http.Handler) {
	r.Trace(pattern, Wrap(handler))
}

// OptionsH wraps a http.Handler
func (r *Router) OptionsH(pattern string, handler http.Handler) {
	r.Options(pattern, Wrap(handler))
}

// ConnectH wraps a http.Handler
func (r *Router) ConnectH(pattern string, handler http.Handler) {
	r.Connect(pattern, Wrap(handler))
}

// PatchH wraps a http.Handler
func (r *Router) PatchH(pattern string, handler http.Handler) {
	r.Patch(pattern, Wrap(handler))
}

// Run calls http.ListenAndServe for the current router.
// If no addresses are specified as arguments, it will use the PORT environnement variable if it is defined. Otherwise, it will listen on port 3000 of the localmachine
//
// 	r := New()
// 	r.Run() // will call
// 	r.Run(":8080")
func (r *Router) Run(addr ...string) {
	var a string

	if len(addr) == 0 {
		if p := os.Getenv("PORT"); p != "" {
			a = p
		} else {
			a = ":3000"
		}
	} else {
		a = addr[0]
	}

	lionLogger.Printf("listening on %s", a)
	lionLogger.Fatal(http.ListenAndServe(a, r))
}

// RunTLS calls http.ListenAndServeTLS for the current router
//
// 	r := New()
// 	r.RunTLS(":3443", "cert.pem", "key.pem")
func (r *Router) RunTLS(addr, certFile, keyFile string) {
	lionLogger.Printf("listening on %s", addr)
	lionLogger.Fatal(http.ListenAndServeTLS(addr, certFile, keyFile, r))
}

// Define registers some middleware using a name for reuse later using UseNamed method.
func (r *Router) Define(name string, mws ...Middleware) {
	r.namedMiddlewares[name] = append(r.namedMiddlewares[name], mws...)
}

// Define is a convenience wrapper for Define() to use MiddlewareFunc instead of a Middleware instance
func (r *Router) DefineFunc(name string, mws ...MiddlewareFunc) {
	for _, mw := range mws {
		r.Define(name, mw)
	}
}

// UseNamed adds a middleware already defined using Define method.
// If it cannot find it in the current router, it will look for it in the parent router.
func (r *Router) UseNamed(name string) {
	if r.hasNamed(name) { // Find if it this is registered in the current router
		r.Use(r.namedMiddlewares[name]...)
	} else if !r.isRoot() { // Otherwise, look for it in parent router.
		r.router.UseNamed(name)
	} else { // not found
		panic("Unknow named middlewares: " + name)
	}
}

func (r *Router) hasNamed(name string) bool {
	_, exist := r.namedMiddlewares[name]
	return exist
}

func validatePattern(pattern string) {
	if len(pattern) > 0 && pattern[0] != '/' {
		panic("path must start with '/' in path '" + pattern + "'")
	}
}
<|endoftext|>