func (m *MockTransport) RegisterResponder(method, url string, responder Responder) {
	if isRegexpURL(url) {
		m.registerRegexpResponder(regexpResponder{
			origRx:    url,
			method:    method,
			rx:        regexp.MustCompile(url[2:]),
			responder: responder,
		})
		return
	}
	key := routeKey{
		Method: method,
		URL:    url,
	}
	m.mu.Lock()
	m.responders[key] = responder
	m.callCountInfo[key] = 0
	m.mu.Unlock()
}
RegisterResponder adds a new responder, associated with a given
HTTP method and URL (or path).

When a request comes in that matches, the responder will be called
and the response returned to the client.

If url contains query parameters, their order matters as well as
their content. All following URLs are here considered as different:
http://z.tld?a=1&b=1
http://z.tld?b=1&a=1
http://z.tld?a&b
http://z.tld?a=&b=

If url begins with "=~", the following chars are considered as a
regular expression. If this regexp can not be compiled, it panics.
Note that the "=~" prefix remains in statistics returned by
GetCallCountInfo(). As 2 regexps can match the same URL, the regexp
responders are tested in the order they are registered. Registering
an already existing regexp responder (same method & same regexp
string) replaces its responder but does not change its position.

See RegisterRegexpResponder() to directly pass a *regexp.Regexp.

Example:
func TestFetchArticles(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

httpmock.RegisterResponder("GET", "http://example.com/",
httpmock.NewStringResponder(200, "hello world"))

httpmock.RegisterResponder("GET", "/path/only",
httpmock.NewStringResponder("any host hello world", 200))

httpmock.RegisterResponder("GET", `=~^/item/id/\d+\z`,
httpmock.NewStringResponder("any item get", 200))

// requests to http://example.com/ will now return "hello world" and
// requests to any host with path /path/only will return "any host hello world"
// requests to any host with path matching ^/item/id/\d+\z regular expression will return "any item get"
}
package httpmock
import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
)
const regexpPrefix = "=~"
var NoResponderFound = errors.New("no responder found")  
type routeKey struct {
	Method string
	URL    string
}
var noResponder routeKey
func (r routeKey) String() string {
	if r == noResponder {
		return "NO_RESPONDER"
	}
	return r.Method + " " + r.URL
}
func ConnectionFailure(*http.Request) (*http.Response, error) {
	return nil, NoResponderFound
}
func NewMockTransport() *MockTransport {
	return &MockTransport{
		responders:    make(map[routeKey]Responder),
		callCountInfo: make(map[routeKey]int),
	}
}
type regexpResponder struct {
	origRx    string
	method    string
	rx        *regexp.Regexp
	responder Responder
}
type MockTransport struct {
	mu               sync.RWMutex
	responders       map[routeKey]Responder
	regexpResponders []regexpResponder
	noResponder      Responder
	callCountInfo    map[routeKey]int
	totalCallCount   int
}
func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	url := req.URL.String()
	method := req.Method
	if method == "" {
		method = http.MethodGet
	}
	var (
		responder  Responder
		respKey    routeKey
		submatches []string
	)
	key := routeKey{
		Method: method,
	}
	for _, getResponder := range []func(routeKey) (Responder, routeKey, []string){
		m.responderForKey,        
		m.regexpResponderForKey,  
	} {
		key.URL = url
		responder, respKey, submatches = getResponder(key)
		if responder != nil {
			break
		}
		query := sortedQuery(req.URL.Query())
		if query != "" {
			key.URL = strings.Replace(url, req.URL.RawQuery, query, 1)
			responder, respKey, submatches = getResponder(key)
			if responder != nil {
				break
			}
		}
		strippedURL := *req.URL
		strippedURL.RawQuery = ""
		strippedURL.Fragment = ""
		surl := strings.TrimSuffix(strippedURL.String(), "?")
		hasQueryString := url != surl
		if hasQueryString {
			key.URL = surl
			responder, respKey, submatches = getResponder(key)
			if responder != nil {
				break
			}
		}
		pathAlone := req.URL.Path
		if hasQueryString {
			key.URL = pathAlone + strings.TrimPrefix(url, surl)  
			responder, respKey, submatches = getResponder(key)
			if responder != nil {
				break
			}
			key.URL = pathAlone + "?" + sortedQuery(req.URL.Query())
			if req.URL.Fragment != "" {
				key.URL += "#" + req.URL.Fragment
			}
			responder, respKey, submatches = getResponder(key)
			if responder != nil {
				break
			}
		}
		key.URL = pathAlone
		responder, respKey, submatches = getResponder(key)
		if responder != nil {
			break
		}
	}
	m.mu.Lock()
	if responder != nil {
		m.callCountInfo[key]++
		if key != respKey {
			m.callCountInfo[respKey]++
		}
		m.totalCallCount++
	} else {
		if m.noResponder != nil {
			m.callCountInfo[noResponder]++
			m.totalCallCount++
			responder = m.noResponder
		}
	}
	m.mu.Unlock()
	if responder == nil {
		return ConnectionFailure(req)
	}
	return runCancelable(responder, setSubmatches(req, submatches))
}
func runCancelable(responder Responder, req *http.Request) (*http.Response, error) {
	ctx := req.Context()
	if req.Cancel == nil && ctx.Done() == nil {  
		resp, err := responder(req)
		return resp, checkStackTracer(req, err)
	}
	type result struct {
		response *http.Response
		err      error
	}
	resultch := make(chan result, 1)
	done := make(chan struct{}, 1)
	go func() {
		select {
		case <-req.Cancel:  
			resultch <- result{
				response: nil,
				err:      errors.New("request canceled"),
			}
		case <-ctx.Done():
			resultch <- result{
				response: nil,
				err:      ctx.Err(),
			}
		case <-done:
		}
	}()
	go func() {
		defer func() {
			if err := recover(); err != nil {
				resultch <- result{
					response: nil,
					err:      fmt.Errorf("panic in responder: got %q", err),
				}
			}
		}()
		response, err := responder(req)
		resultch <- result{
			response: response,
			err:      err,
		}
	}()
	r := <-resultch
	done <- struct{}{}
	return r.response, checkStackTracer(req, r.err)
}
type stackTracer struct {
	customFn func(...interface{})
	err      error
}
func (n stackTracer) Error() string {
	if n.err == nil {
		return ""
	}
	return n.err.Error()
}
func checkStackTracer(req *http.Request, err error) error {
	if nf, ok := err.(stackTracer); ok {
		if nf.customFn != nil {
			pc := make([]uintptr, 128)
			npc := runtime.Callers(2, pc)
			pc = pc[:npc]
			var mesg bytes.Buffer
			var netHTTPBegin, netHTTPEnd bool
			for {
				frames := runtime.CallersFrames(pc)
				var lastFn string
				for {
					frame, more := frames.Next()
					if !netHTTPEnd {
						if netHTTPBegin {
							netHTTPEnd = !strings.HasPrefix(frame.Function, "net/http.")
						} else {
							netHTTPBegin = strings.HasPrefix(frame.Function, "net/http.")
						}
					}
					if netHTTPEnd {
						if lastFn != "" {
							if mesg.Len() == 0 {
								if nf.err != nil {
									mesg.WriteString(nf.err.Error())
								} else {
									fmt.Fprintf(&mesg, "%s %s", req.Method, req.URL)
								}
								mesg.WriteString("\nCalled from ")
							} else {
								mesg.WriteString("\n  ")
							}
							fmt.Fprintf(&mesg, "%s()\n    at %s:%d", lastFn, frame.File, frame.Line)
						}
					}
					lastFn = frame.Function
					if !more {
						break
					}
				}
				if mesg.Len() > 0 {
					break
				}
				netHTTPEnd = true  
			}
			nf.customFn(mesg.String())
		}
		err = nf.err
	}
	return err
}
func (m *MockTransport) responderForKey(key routeKey) (Responder, routeKey, []string) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.responders[key], key, nil
}
func (m *MockTransport) regexpResponderForKey(key routeKey) (Responder, routeKey, []string) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	for _, regInfo := range m.regexpResponders {
		if regInfo.method == key.Method {
			if sm := regInfo.rx.FindStringSubmatch(key.URL); sm != nil {
				if len(sm) == 1 {
					sm = nil
				} else {
					sm = sm[1:]
				}
				return regInfo.responder, routeKey{
					Method: key.Method,
					URL:    regInfo.origRx,
				}, sm
			}
		}
	}
	return nil, key, nil
}
func isRegexpURL(url string) bool {
	return strings.HasPrefix(url, regexpPrefix)
}