func newHandlerFromConfig()

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
}