func TestPat(t *testing.T) { t.Parallel() for _, test := range PatTests { pat := New(test.pat) if str := pat.String(); str != test.pat { t.Errorf("[%q %q] String()=%q, expected=%q", test.pat, test.req, str, test.pat) } ctx := pat.Match(mustReq("GET", test.req)) if (ctx != nil) != test.match { t.Errorf("[%q %q] match=%v, expected=%v", test.pat, test.req, ctx != nil, test.match) } if ctx == nil { continue } if path := pattern.Path(ctx); path != test.path { t.Errorf("[%q %q] path=%q, expected=%q", test.pat, test.req, path, test.path) } vars := ctx.Value(pattern.AllVariables) if (vars != nil) != (test.vars != nil) { t.Errorf("[%q %q] vars=%#v, expected=%#v", test.pat, test.req, vars, test.vars) } if vars == nil { continue } if tvars := vars.(map[pattern.Variable]interface{}); !reflect.DeepEqual(tvars, test.vars) { t.Errorf("[%q %q] vars=%v, expected=%v", test.pat, test.req, tvars, test.vars) } } }
func (l *Logger) printRequest(ctx context.Context, r *http.Request) { var buf bytes.Buffer if l.Debug { buf.WriteString("[DEBUG]") } buf.WriteString("Serving route: ") // Goji routing details colorWrite(&buf, bGreen, "%s", pattern.Path(ctx)) // Server details buf.WriteString(fmt.Sprintf(" from %s ", r.RemoteAddr)) // Request details buf.WriteString("for ") colorWrite(&buf, bMagenta, "%s ", r.Method) urlStr := r.URL.String() // if not in debug mode, remove Query params from logging as not to include any // sensitive information inadvertantly into user's logs if !l.Debug && r.URL.RawQuery != "" { tempURL := &url.URL{} *tempURL = *r.URL tempURL.RawQuery = "<omitted>" urlStr = tempURL.String() } colorWrite(&buf, bBlue, "%q", urlStr) log.Print(buf.String()) }
/* Match runs the Pat pattern on the given request, returning a non-nil context if the request matches the request. This function satisfies goji.Pattern. */ func (p *Pattern) Match(ctx context.Context, r *http.Request) context.Context { path := pattern.Path(ctx) var scratch []string if p.wildcard { scratch = make([]string, len(p.pats)+1) } else if len(p.pats) > 0 { scratch = make([]string, len(p.pats)) } for i := range p.pats { sli := p.literals[i] if !strings.HasPrefix(path, sli) { return nil } path = path[len(sli):] m := 0 bc := p.breaks[i] for ; m < len(path); m++ { if path[m] == bc || path[m] == '/' { break } } if m == 0 { // Empty strings are not matches, otherwise routes like // "/:foo" would match the path "/" return nil } scratch[i] = path[:m] path = path[m:] } // There's exactly one more literal than pat. tail := p.literals[len(p.pats)] if p.wildcard { if !strings.HasPrefix(path, tail) { return nil } scratch[len(p.pats)] = path[len(tail)-1:] } else if path != tail { return nil } for i := range p.pats { var err error scratch[i], err = unescape(scratch[i]) if err != nil { // If we encounter an encoding error here, there's // really not much we can do about it with our current // API, and I'm not really interested in supporting // clients that misencode URLs anyways. return nil } } return &match{ctx, p, scratch} }
func TestExistingContext(t *testing.T) { t.Parallel() pat := New("/hi/:c/:a/:r/:l") req, err := http.NewRequest("GET", "/hi/foo/bar/baz/quux", nil) if err != nil { panic(err) } ctx := context.Background() ctx = pattern.SetPath(ctx, req.URL.EscapedPath()) ctx = context.WithValue(ctx, pattern.AllVariables, map[pattern.Variable]interface{}{ "hello": "world", "c": "nope", }) ctx = context.WithValue(ctx, pattern.Variable("user"), "carl") req = req.WithContext(ctx) req = pat.Match(req) if req == nil { t.Fatalf("expected pattern to match") } ctx = req.Context() expected := map[pattern.Variable]interface{}{ "c": "foo", "a": "bar", "r": "baz", "l": "quux", } for k, v := range expected { if p := Param(req, string(k)); p != v { t.Errorf("expected %s=%q, got %q", k, v, p) } } expected["hello"] = "world" all := ctx.Value(pattern.AllVariables).(map[pattern.Variable]interface{}) if !reflect.DeepEqual(all, expected) { t.Errorf("expected %v, got %v", expected, all) } if path := pattern.Path(ctx); path != "" { t.Errorf("expected path=%q, got %q", "", path) } if user := ctx.Value(pattern.Variable("user")); user != "carl" { t.Errorf("expected user=%q, got %q", "carl", user) } }
// IntegrationHandler handles webhooks to integration handlers in the integration // package by calling their hook function with the request. func IntegrationHandler(ctx context.Context, rw http.ResponseWriter, r *http.Request) { // integration is push only with status code returns. no response output. dataRender := data.FromContext(ctx) dataRender.Type = data.DataNoRender // get the handler name (as the first part) handlerName := pattern.Path(ctx) impl := integration.Implementations[strings.Split(handlerName, "/")[0]] if impl == nil { http.NotFound(rw, r) } impl.Hook(r) rw.WriteHeader(200) }
func StaticHandler(ctx context.Context, rw http.ResponseWriter, r *http.Request) { file := pattern.Path(ctx) f, err := staticDir.Open(file) if err != nil { panic(ErrNotFound) // assume not found } defer f.Close() fi, err := f.Stat() if err != nil { panic(err) // this is weird } // try to serve index file if fi.IsDir() { // redirect if missing trailing slash if !strings.HasSuffix(r.URL.Path, "/") { http.Redirect(rw, r, r.URL.Path+"/", http.StatusFound) return } file = path.Join(file, indexFile) f, err = staticDir.Open(file) if err != nil { panic(ErrNotFound) // just hide its existence } defer f.Close() fi, err = f.Stat() if err != nil || fi.IsDir() { panic(ErrNotFound) // go away } } // okay, we don't need to render this. dataRender := data.FromContext(ctx) dataRender.Type = data.DataNoRender http.ServeContent(rw, r, file, fi.ModTime(), f) }