in internal/httpserver/httpserver.go [116:254]
func newHandlerFromConfig(config *config, notFoundHandler http.HandlerFunc, logger *zap.SugaredLogger) (http.Handler, error) {
router := mux.NewRouter()
var buf bytes.Buffer
var currInSeq int
var posInSeq int
for i, rule := range config.Rules {
rule := rule
var count int
i := i
if i > 0 {
posInSeq += len(config.Rules[i-1].Responses)
}
posInSeq := posInSeq
logger.Debugf("Setting up rule #%d for path %q", i, rule.Path)
route := router.HandleFunc(rule.Path, func(w http.ResponseWriter, r *http.Request) {
isNext := currInSeq == posInSeq+count
if config.AsSequence && !isNext {
logger.Fatalf("expecting to match request #%d in sequence, matched rule #%d instead, exiting", currInSeq, posInSeq+count)
}
response := func() *response {
switch len(rule.Responses) {
case 0:
return nil
case 1:
return &rule.Responses[0]
}
return &rule.Responses[count%len(rule.Responses)]
}()
count++
currInSeq++
logger.Debug(fmt.Sprintf("Rule #%d matched: request #%d => %s", i, count, strRequest(r)))
data := map[string]interface{}{
"req_num": count,
"request": map[string]interface{}{
"vars": mux.Vars(r),
"url": r.URL,
"headers": r.Header,
},
}
if response != nil {
for k, tpls := range response.Headers {
for _, tpl := range tpls {
buf.Reset()
if err := tpl.Execute(&buf, data); err != nil {
logger.Errorf("executing header template %s: %s, %v", k, tpl.Root.String(), err)
continue
}
w.Header().Add(k, buf.String())
}
}
w.WriteHeader(response.StatusCode)
if err := response.Body.Execute(w, data); err != nil {
logger.Errorf("executing body template %s: %v", response.Body.Root.String(), err)
}
}
})
route.Methods(rule.Methods...)
exclude := make(map[string]bool)
for key, vals := range rule.QueryParams {
if len(vals) == 0 { // Cannot use nil since ucfg interprets null as an empty slice instead of nil.
exclude[key] = true
continue
}
for _, v := range vals {
route.Queries(key, v)
}
}
route.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
for key := range exclude {
if r.URL.Query().Has(key) {
return false
}
}
return true
})
for key, vals := range rule.RequestHeaders {
for _, v := range vals {
route.HeadersRegexp(key, v)
}
}
route.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
user, password, _ := r.BasicAuth()
if rule.User != "" && user != rule.User {
return false
}
if rule.Password != "" && password != rule.Password {
return false
}
return true
})
var bodyRE *regexp.Regexp
if strings.HasPrefix(rule.RequestBody, "/") && strings.HasSuffix(rule.RequestBody, "/") {
re := strings.TrimPrefix(strings.TrimSuffix(rule.RequestBody, "/"), "/")
var err error
bodyRE, err = regexp.Compile(re)
if err != nil {
logger.Errorf("compiling body match regexp: %s", re, err)
}
}
route.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
if rule.RequestBody == "" {
return true
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return false
}
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
if bodyRE != nil {
return bodyRE.Match(body)
}
return rule.RequestBody == string(body)
})
}
router.NotFoundHandler = notFoundHandler
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// merge together form params into the url ones to make checks easier
_ = r.ParseForm()
r.URL.RawQuery = r.Form.Encode()
router.ServeHTTP(w, r)
}), nil
}