Пример #1
0
// Creates a new recorder
func New(cassetteName string) (*Recorder, error) {
	var mode RecorderMode
	var c *cassette.Cassette
	var wg sync.WaitGroup
	cassetteFile := fmt.Sprintf("%s.yaml", cassetteName)

	// Depending on whether the cassette file exists or not we
	// either create a new empty cassette or load from file
	if _, err := os.Stat(cassetteFile); os.IsNotExist(err) {
		// Create new cassette and enter in recording mode
		c = cassette.New(cassetteName)
		mode = ModeRecording
	} else {
		// Load cassette from file and enter replay mode
		c, err = cassette.Load(cassetteName)
		if err != nil {
			return nil, err
		}
		mode = ModeReplaying
		wg.Add(len(c.UnclosedRequests))
	}

	rec := &Recorder{
		mode:     mode,
		cassette: c,
		wg:       &wg,
	}

	doneRequests := make(map[string]struct{})
	var doneRequestMu sync.RWMutex

	// Handler for client requests
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		if rec.mode == ModeReplaying && c.HasRequest(r.URL.String()) {
			doneRequestMu.RLock()
			_, duplicate := doneRequests[r.URL.String()]
			doneRequestMu.RUnlock()

			if duplicate != true {
				doneRequestMu.Lock()
				doneRequests[r.URL.String()] = struct{}{}
				doneRequestMu.Unlock()
			}

			cn, ok := w.(http.CloseNotifier)
			if !ok {
				log.Fatal("don't support CloseNotifier")
			}

			<-cn.CloseNotify()
			if duplicate != true {
				wg.Done()
			}

			return
		}

		interaction, err := requestHandler(r, c, mode)

		if err != nil {
			panic(fmt.Errorf("Failed to process request for URL:\n%s\n%s", r.URL, err))
		}

		w.WriteHeader(interaction.Response.Code)
		body := strings.TrimSuffix(interaction.Response.Body, "\n")
		fmt.Fprintln(w, body)
	})

	// HTTP server used to mock requests
	rec.server = httptest.NewServer(handler)

	// A proxy function which routes all requests through our HTTP server
	// Can be used by clients to inject into their own transports
	proxyUrl, err := url.Parse(rec.server.URL)
	if err != nil {
		return nil, err
	}

	// A transport which can be used by clients to inject
	rec.Transport = &http.Transport{
		Proxy: http.ProxyURL(proxyUrl),
	}

	return rec, nil
}