Skip to content

Commit

Permalink
Add mux.HandlePath method (#1449)
Browse files Browse the repository at this point in the history
* add HandlePath

* add HandlePath test and docs
  • Loading branch information
ti authored Jun 9, 2020
1 parent e3513c9 commit c4b0f84
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 1 deletion.
53 changes: 53 additions & 0 deletions docs/_docs/inject_router.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
category: documentation
title: Adding custom routes to the mux
---

# Adding custom routes to the mux

The gRPC-gateway allows you to add custom routes to the serve mux, for example if you want to support a use case that isn't supported by the grpc-gateway, like file uploads.

## Example

```go
package main

import (
"context"
"net/http"

pb "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/helloworld"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
)

func main() {
ctx := context.TODO()
mux := runtime.NewServeMux()
// Register generated routes to mux
err := pb.RegisterGreeterHandlerServer(ctx, mux, &GreeterServer{})
if err != nil {
panic(err)
}
// Register custom route for GET /hello/{name}
err = mux.HandlePath("GET", "/hello/{name}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
w.Write([]byte("hello " + pathParams["name"]))
})
if err != nil {
panic(err)
}
http.ListenAndServe(":8080", mux)
}


// GreeterServer is the server API for Greeter service.
type GreeterServer struct {

}

// SayHello implement to say hello
func (h *GreeterServer) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{
Message: "hello " + req.Name,
}, nil
}
```
1 change: 1 addition & 0 deletions runtime/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ go_library(
],
importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/runtime",
deps = [
"//internal/httprule:go_default_library",
"//utilities:go_default_library",
"@com_github_golang_protobuf//ptypes:go_default_library_gen",
"@go_googleapis//google/api:httpbody_go_proto",
Expand Down
19 changes: 18 additions & 1 deletion runtime/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
"net/http"
"net/textproto"
"strings"


"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -164,6 +165,22 @@ func (s *ServeMux) Handle(meth string, pat Pattern, h HandlerFunc) {
s.handlers[meth] = append([]handler{{pat: pat, h: h}}, s.handlers[meth]...)
}

// HandlePath allows users to configure custom path handlers.
// refer: https://grpc-ecosystem.github.io/grpc-gateway/docs/inject_router.html
func (s *ServeMux) HandlePath(meth string, pathPattern string, h HandlerFunc) error {
compiler, err := httprule.Parse(pathPattern)
if err != nil {
return fmt.Errorf("parsing path pattern: %w", err)
}
tp := compiler.Compile()
pattern, err := NewPattern(tp.Version, tp.OpCodes, tp.Pool, tp.Verb)
if err != nil {
return fmt.Errorf("creating new pattern: %w", err)
}
s.Handle(meth, pattern, h)
return nil
}

// ServeHTTP dispatches the request to the first handler whose pattern matches to r.Method and r.Path.
func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
Expand Down
52 changes: 52 additions & 0 deletions runtime/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,55 @@ func TestDefaultHeaderMatcher(t *testing.T) {
})
}
}



var defaultRouteMatcherTests = []struct {
name string
method string
path string
valid bool
}{
{
"Simple Endpoint",
"GET",
"/v1/{bucket}/do:action",
true,
},
{
"Complex Endpoint",
"POST",
"/v1/b/{bucket_name=buckets/*}/o/{name}",
true,
},
{
"Wildcard Endpoint",
"GET",
"/v1/endpoint/*",
true,
},
{
"Invalid Endpoint",
"POST",
"v1/b/:name/do",
false,
},
}

func TestServeMux_HandlePath(t *testing.T) {
mux := runtime.NewServeMux()
testFn := func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
}
for _, tt := range defaultRouteMatcherTests {
t.Run(tt.name, func(t *testing.T) {
err := mux.HandlePath(tt.method, tt.path, testFn)
if tt.valid && err != nil {
t.Errorf("The route %v with method %v and path %v invalid, got %v", tt.name, tt.method, tt.path, err)
}
if !tt.valid && err == nil {
t.Errorf("The route %v with method %v and path %v should be invalid", tt.name, tt.method, tt.path)
}
})
}

}

0 comments on commit c4b0f84

Please sign in to comment.