-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Pattern-based request multiplexer
- Loading branch information
Showing
1 changed file
with
79 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package runtime | ||
|
||
import ( | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/golang/glog" | ||
) | ||
|
||
// A HandlerFunc handles a specific pair of path pattern and HTTP method. | ||
type HandlerFunc func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) | ||
|
||
// ServeMux is a request multiplexer for grpc-gateway. | ||
// It matches http requests to patterns and invokes the corresponding handler. | ||
type ServeMux struct { | ||
// handlers maps HTTP method to a list of handlers. | ||
handlers map[string][]handler | ||
} | ||
|
||
// NewServeMux returns a new MuxHandler whose internal mapping is empty. | ||
func NewServeMux() *ServeMux { | ||
return &ServeMux{ | ||
handlers: make(map[string][]handler), | ||
} | ||
} | ||
|
||
// Handle associates "h" to the pair of HTTP method and path pattern. | ||
func (s *ServeMux) Handle(meth string, pat Pattern, h HandlerFunc) { | ||
s.handlers[meth] = append(s.handlers[meth], handler{pat: pat, h: h}) | ||
} | ||
|
||
// 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) { | ||
path := r.URL.Path | ||
if !strings.HasPrefix(path, "/") { | ||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) | ||
return | ||
} | ||
|
||
components := strings.Split(path[1:], "/") | ||
l := len(components) | ||
var verb string | ||
if idx := strings.LastIndex(components[l-1], ":"); idx == 0 { | ||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) | ||
return | ||
} else if idx > 0 { | ||
c := components[l-1] | ||
verb, components[l-1] = c[:idx], c[idx+1:] | ||
} | ||
|
||
for _, h := range s.handlers[r.Method] { | ||
pathParams, err := h.pat.Match(components, verb) | ||
if err != nil { | ||
glog.V(3).Infof("path mismatch: %q to %q", path, h.pat) | ||
continue | ||
} | ||
h.h(w, r, pathParams) | ||
return | ||
} | ||
|
||
// lookup other methods to determine if it is MethodNotAllowed | ||
for m, handlers := range s.handlers { | ||
if m == r.Method { | ||
continue | ||
} | ||
for _, h := range handlers { | ||
if _, err := h.pat.Match(components, verb); err == nil { | ||
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) | ||
return | ||
} | ||
} | ||
} | ||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) | ||
} | ||
|
||
type handler struct { | ||
pat Pattern | ||
h HandlerFunc | ||
} |
d8bcb11
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yugui - Hi, I'm using this code and I'm running into a weird issue where the code won't parse URLs with a
:
in the path. It's not a huge deal, I can just remove the colon from my path, but I'm curious, could you help me understand why we do thisstrings.LastIndex(components[l-1], ":")
? Is this part of some protocol I'm not familiar with?d8bcb11
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please see #224
d8bcb11
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thank you!