Skip to content

Commit

Permalink
capture on miss request in spy mode (#1126)
Browse files Browse the repository at this point in the history
  • Loading branch information
kapishmalik committed May 3, 2024
1 parent a16e3bc commit 2cb8acc
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 13 deletions.
1 change: 1 addition & 0 deletions core/handlers/v2/views.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type ModeArgumentsView struct {
MatchingStrategy *string `json:"matchingStrategy,omitempty"`
Stateful bool `json:"stateful,omitempty"`
OverwriteDuplicate bool `json:"overwriteDuplicate,omitempty"`
CaptureOnMiss bool `json:"captureOnMiss,omitempty"`
}

type IsWebServerView struct {
Expand Down
4 changes: 4 additions & 0 deletions core/hoverfly_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,10 @@ func (hf *Hoverfly) Save(request *models.RequestDetails, response *models.Respon
hf.Simulation.AddPair(&pair)
}

if hf.Cfg.GetMode() == modes.Spy {
_, _ = hf.CacheMatcher.SaveRequestMatcherResponsePair(*request, &pair, nil)
}

return nil
}

Expand Down
1 change: 1 addition & 0 deletions core/hoverfly_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (hf *Hoverfly) SetModeWithArguments(modeView v2.ModeView) error {
MatchingStrategy: matchingStrategy,
Stateful: modeView.Arguments.Stateful,
OverwriteDuplicate: modeView.Arguments.OverwriteDuplicate,
CaptureOnMiss: modeView.Arguments.CaptureOnMiss,
}

hf.modeMap[hf.Cfg.GetMode()].SetArguments(modeArguments)
Expand Down
1 change: 1 addition & 0 deletions core/modes/modes.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type ModeArguments struct {
MatchingStrategy *string
Stateful bool
OverwriteDuplicate bool
CaptureOnMiss bool
}

type ProcessResult struct {
Expand Down
50 changes: 39 additions & 11 deletions core/modes/spy_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/SpectoLabs/hoverfly/core/errors"
v2 "github.com/SpectoLabs/hoverfly/core/handlers/v2"
"github.com/SpectoLabs/hoverfly/core/util"

log "github.com/sirupsen/logrus"

Expand All @@ -15,27 +16,40 @@ type HoverflySpy interface {
GetResponse(models.RequestDetails) (*models.ResponseDetails, *errors.HoverflyError)
ApplyMiddleware(models.RequestResponsePair) (models.RequestResponsePair, error)
DoRequest(*http.Request) (*http.Response, error)
Save(*models.RequestDetails, *models.ResponseDetails, *ModeArguments) error
}

type SpyMode struct {
Hoverfly HoverflySpy
MatchingStrategy string
Hoverfly HoverflySpy
Arguments ModeArguments
}

func (this *SpyMode) View() v2.ModeView {
return v2.ModeView{
Mode: Spy,
Arguments: v2.ModeArgumentsView{
MatchingStrategy: &this.MatchingStrategy,
MatchingStrategy: this.Arguments.MatchingStrategy,
CaptureOnMiss: this.Arguments.CaptureOnMiss,
Stateful: this.Arguments.Stateful,
Headers: this.Arguments.Headers,
OverwriteDuplicate: this.Arguments.OverwriteDuplicate,
},
}
}

func (this *SpyMode) SetArguments(arguments ModeArguments) {
if arguments.MatchingStrategy == nil {
this.MatchingStrategy = "strongest"
var matchingStrategy string
if arguments.MatchingStrategy == nil || *arguments.MatchingStrategy == "" {
matchingStrategy = "strongest"
} else {
this.MatchingStrategy = *arguments.MatchingStrategy
matchingStrategy = *arguments.MatchingStrategy
}
this.Arguments = ModeArguments{
MatchingStrategy: &matchingStrategy,
Headers: arguments.Headers,
Stateful: arguments.Stateful,
OverwriteDuplicate: arguments.OverwriteDuplicate,
CaptureOnMiss: arguments.CaptureOnMiss,
}
}

Expand All @@ -55,6 +69,24 @@ func (this SpyMode) Process(request *http.Request, details models.RequestDetails
}
response, err := this.Hoverfly.DoRequest(modifiedRequest)
if err == nil {

if this.Arguments.CaptureOnMiss {
respBody, _ := util.GetResponseBody(response)
respHeaders := util.GetResponseHeaders(response)

responseObj := &models.ResponseDetails{
Status: response.StatusCode,
Body: respBody,
Headers: respHeaders,
}
if this.Arguments.Headers == nil {
this.Arguments.Headers = []string{}
}
err = this.Hoverfly.Save(&pair.Request, responseObj, &this.Arguments)
if err != nil {
return ReturnErrorAndLog(request, err, &pair, "There was an error when saving request and response", Spy)
}
}
log.Info("Going to return response from real server")
return newProcessResult(response, 0, nil), nil
} else {
Expand All @@ -69,13 +101,9 @@ func (this SpyMode) Process(request *http.Request, details models.RequestDetails
return ReturnErrorAndLog(request, err, &pair, "There was an error when executing middleware", Spy)
}

return newProcessResultWithPostServeActionInputDetails(
return newProcessResult(
ReconstructResponse(request, pair),
pair.Response.FixedDelay,
pair.Response.LogNormalDelay,
&PostServeActionInputDetails{
PostServeAction: pair.Response.PostServeAction,
Pair: &pair,
},
), nil
}
4 changes: 4 additions & 0 deletions core/modes/spy_mode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ func (this hoverflySpyStub) ApplyMiddleware(pair models.RequestResponsePair) (mo
return pair, nil
}

func (this hoverflySpyStub) Save(request *models.RequestDetails, response *models.ResponseDetails, arguments *modes.ModeArguments) error {
return nil
}

func Test_SpyMode_WhenGivenAMatchingRequestItReturnsTheCorrectResponse(t *testing.T) {
RegisterTestingT(t)

Expand Down
25 changes: 23 additions & 2 deletions hoverctl/cmd/mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"strings"

"github.com/SpectoLabs/hoverfly/core/handlers/v2"
v2 "github.com/SpectoLabs/hoverfly/core/handlers/v2"
"github.com/SpectoLabs/hoverfly/core/modes"
"github.com/SpectoLabs/hoverfly/hoverctl/wrapper"
"github.com/spf13/cobra"
Expand All @@ -15,6 +15,7 @@ var allHeaders bool
var stateful bool
var overwriteDuplicate bool
var matchingStrategy string
var captureOnMiss bool

var modeCmd = &cobra.Command{
Use: "mode [capture|diff|simulate|spy|modify|synthesize (optional)]",
Expand Down Expand Up @@ -55,6 +56,13 @@ mode is shown.
case modes.Diff:
setHeaderArgument(modeView)
break
case modes.Spy:
modeView.Arguments.MatchingStrategy = &matchingStrategy
modeView.Arguments.Stateful = stateful
modeView.Arguments.OverwriteDuplicate = overwriteDuplicate
modeView.Arguments.CaptureOnMiss = captureOnMiss
setHeaderArgument(modeView)
break
}

mode, err := wrapper.SetModeWithArguments(*target, modeView)
Expand Down Expand Up @@ -100,6 +108,18 @@ func getExtraInfo(mode *v2.ModeView) string {
}
}
break
case modes.Spy:
if mode.Arguments.CaptureOnMiss {
extraInfo = "and will capture on not finding the match"
}
if len(mode.Arguments.Headers) > 0 {
if len(mode.Arguments.Headers) == 1 && mode.Arguments.Headers[0] == "*" {
extraInfo = "and also will capture all request headers"
} else {
extraInfo = fmt.Sprintf("and also will capture the following request headers: %s", mode.Arguments.Headers)
}
}
break
}

return extraInfo
Expand All @@ -111,11 +131,12 @@ func init() {
modeCmd.PersistentFlags().StringVar(&specificHeaders, "headers", "",
"A comma separated list of request headers to record (for capture mode) or response headers to ignore (for diff mode) `Content-Type,Authorization`")
modeCmd.PersistentFlags().BoolVar(&allHeaders, "all-headers", false,
"Record all request headers (for capture mode) or ignore all response headers (for diff mode)")
"Record all request headers (for capture/spy mode) or ignore all response headers (for diff mode)")
modeCmd.PersistentFlags().StringVar(&matchingStrategy, "matching-strategy", "strongest",
"Sets the matching strategy - 'strongest | first'")
modeCmd.PersistentFlags().BoolVar(&stateful, "stateful", false,
"Record stateful responses as a sequence in capture mode")
modeCmd.PersistentFlags().BoolVar(&overwriteDuplicate, "overwrite-duplicate", false,
"Overwrite duplicate requests in capture mode")
modeCmd.PersistentFlags().BoolVar(&captureOnMiss, "capture-on-miss", false, "Capture the request on miss in spy mode")
}

0 comments on commit 2cb8acc

Please sign in to comment.