diff --git a/core/templating/template_helpers.go b/core/templating/template_helpers.go index 2edd98729..b5b522f29 100644 --- a/core/templating/template_helpers.go +++ b/core/templating/template_helpers.go @@ -498,6 +498,24 @@ func (t templateHelpers) setStatusCode(statusCode string, options *raymond.Optio return "" } +func (t templateHelpers) setHeader(headerName string, headerValue string, options *raymond.Options) string { + if headerName == "" { + log.Error("header name cannot be empty") + return "" + } + internalVars := options.ValueFromAllCtx("InternalVars").(map[string]interface{}) + var headers map[string][]string + if h, ok := internalVars["setHeaders"]; ok { + headers = h.(map[string][]string) + } else { + headers = make(map[string][]string) + } + // Replace or add the header + headers[headerName] = []string{headerValue} + internalVars["setHeaders"] = headers + return "" +} + func (t templateHelpers) sum(numbers []string, format string) string { return sumNumbers(numbers, format) } diff --git a/core/templating/template_helpers_test.go b/core/templating/template_helpers_test.go index 905272506..4594c7fdb 100644 --- a/core/templating/template_helpers_test.go +++ b/core/templating/template_helpers_test.go @@ -3,10 +3,24 @@ package templating import ( "testing" "time" - . "github.com/onsi/gomega" ) + + +// mockRaymondOptions is a minimal mock for raymond.Options for testing +type mockRaymondOptions struct { + internalVars map[string]interface{} +} + +func (m *mockRaymondOptions) ValueFromAllCtx(key string) interface{} { + if key == "InternalVars" { + return m.internalVars + } + return nil +} + + func testNow() time.Time { parsedTime, _ := time.Parse("2006-01-02T15:04:05Z", "2018-01-01T00:00:00Z") return parsedTime diff --git a/core/templating/templating.go b/core/templating/templating.go index 801122d3e..9270e95be 100644 --- a/core/templating/templating.go +++ b/core/templating/templating.go @@ -105,6 +105,7 @@ func NewEnrichedTemplator(journal *journal.Journal) *Templator { helperMethodMap["journal"] = t.parseJournalBasedOnIndex helperMethodMap["hasJournalKey"] = t.hasJournalKey helperMethodMap["setStatusCode"] = t.setStatusCode + helperMethodMap["setHeader"] = t.setHeader helperMethodMap["sum"] = t.sum helperMethodMap["add"] = t.add helperMethodMap["subtract"] = t.subtract @@ -146,11 +147,20 @@ func (t *Templator) RenderTemplate(tpl *raymond.Template, requestDetails *models ctx := t.NewTemplatingData(requestDetails, literals, vars, state) result, err := tpl.Exec(ctx) - if err == nil { - statusCode, ok := ctx.InternalVars["statusCode"] - if ok && response != nil { + if err == nil && response != nil { + // Set status code if present + if statusCode, ok := ctx.InternalVars["statusCode"]; ok { response.Status = statusCode.(int) } + // Set headers if present + if setHeaders, ok := ctx.InternalVars["setHeaders"]; ok { + if response.Headers == nil { + response.Headers = make(map[string][]string) + } + for k, v := range setHeaders.(map[string][]string) { + response.Headers[k] = v + } + } } return result, err } diff --git a/core/templating/templating_test.go b/core/templating/templating_test.go index cbbbb2bf1..3cfd09747 100644 --- a/core/templating/templating_test.go +++ b/core/templating/templating_test.go @@ -917,6 +917,22 @@ func Test_ApplyTemplate_setStatusCode_should_handle_nil_response(t *testing.T) { Expect(template).To(Equal("")) } +func Test_ApplyTemplate_setHeader(t *testing.T) { + RegisterTestingT(t) + + templator := templating.NewTemplator() + + template, err := templator.ParseTemplate(`{{ setHeader "X-Test-Header" "HeaderValue" }}`) + Expect(err).To(BeNil()) + + response := &models.ResponseDetails{Headers: map[string][]string{}} + result, err := templator.RenderTemplate(template, &models.RequestDetails{}, response, &models.Literals{}, &models.Variables{}, make(map[string]string)) + + Expect(err).To(BeNil()) + Expect(result).To(Equal("")) + Expect(response.Headers).To(HaveKeyWithValue("X-Test-Header", []string{"HeaderValue"})) +} + func toInterfaceSlice(arguments []string) []interface{} { argumentsArray := make([]interface{}, len(arguments)) diff --git a/docs/pages/keyconcepts/templating/templating.rst b/docs/pages/keyconcepts/templating/templating.rst index 20e460061..3e1933689 100644 --- a/docs/pages/keyconcepts/templating/templating.rst +++ b/docs/pages/keyconcepts/templating/templating.rst @@ -654,6 +654,51 @@ To learn about more advanced templating functionality, such as looping and condi Global Literals and Variables ----------------------------- +Setting properties on the response +---------------------------------- + +Hoverfly provides helper functions to set properties on the HTTP response directly from your templates. This allows you to dynamically control the status code and headers based on request data or logic in your template. + +Setting the Status Code +~~~~~~~~~~~~~~~~~~~~~~~ +You can set the HTTP status code of the response using the ``setStatusCode`` helper. This is useful for conditional logic, such as returning a 404 if a resource is not found, or a 200 if an operation succeeds. + +.. code:: handlebars + + {{ setStatusCode 404 }} + +You can use this helper inside conditional blocks: + +.. code:: handlebars + + {{#equal (csvDeleteRows 'pets' 'category' 'cats' true) '0'}} + {{ setStatusCode 404 }} + {"Message":"Error no cats found"} + {{else}} + {{ setStatusCode 200 }} + {"Message":"All cats deleted"} + {{/equal}} + +If you provide an invalid status code (e.g., outside the range 100-599), it will be ignored. + +Setting Response Headers +~~~~~~~~~~~~~~~~~~~~~~~ +You can set or override HTTP response headers using the ``setHeader`` helper. This is useful for adding custom headers, controlling caching, or setting content types dynamically. + +.. code:: handlebars + + {{ setHeader "X-Custom-Header" "HeaderValue" }} + +You can use this helper multiple times to set different headers, or inside conditional blocks to set headers based on logic: + +.. code:: handlebars + + {{ setHeader "Content-Type" "application/json" }} + {{ setHeader "X-Request-Id" (randomUuid) }} + +If the header already exists, it will be overwritten with the new value. + +Both helpers do not output anything to the template result; they only affect the response properties. You can define global literals and variables for templated response. This comes in handy when you have a lot of templated responses that share the same constant values or helper methods. diff --git a/functional-tests/core/api/sandbox-1490498009/stdout b/functional-tests/core/api/sandbox-1490498009/stdout new file mode 100644 index 000000000..e69de29bb diff --git a/functional-tests/core/api/sandbox-1847794907/stdout b/functional-tests/core/api/sandbox-1847794907/stdout new file mode 100644 index 000000000..e69de29bb diff --git a/functional-tests/hoverctl/sandbox-2112582993/stdout b/functional-tests/hoverctl/sandbox-2112582993/stdout new file mode 100644 index 000000000..e69de29bb diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/add_random_delay.py b/functional-tests/hoverctl/sandbox-2112582993/testdata/add_random_delay.py new file mode 100755 index 000000000..be14d352c --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/add_random_delay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +import sys +import logging +import random +from time import sleep + +logging.basicConfig(filename='random_delay_middleware.log', level=logging.DEBUG) +logging.debug('Random delay middleware is called') + +# set delay to random value less than one second + +SLEEP_SECS = random.random() + +def main(): + + data = sys.stdin.readlines() + # this is a json string in one line so we are interested in that one line + payload = data[0] + logging.debug("sleeping for %s seconds" % SLEEP_SECS) + sleep(SLEEP_SECS) + + + # do not modifying payload, returning same one + print(payload) + +if __name__ == "__main__": + main() diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/cert.pem b/functional-tests/hoverctl/sandbox-2112582993/testdata/cert.pem new file mode 100644 index 000000000..d04f58b21 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWDCCAkCgAwIBAgIVALg7EVVQrv7N34JFuuJg9LeW3R+QMA0GCSqGSIb3DQEB +CwUAMC8xGzAZBgNVBAoTEkhvdmVyZmx5IEF1dGhvcml0eTEQMA4GA1UEAxMHY2F0 +LnBlbTAeFw0xNTExMjIxNzI4MjRaFw0xNzExMjExNzI4MjRaMC8xGzAZBgNVBAoT +EkhvdmVyZmx5IEF1dGhvcml0eTEQMA4GA1UEAxMHY2F0LnBlbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAN+OtQ5fRqcieaPPKMhoi4MwEi2b8cakfeQI +3hqm+N5eJUIK5gC4bRydTUy4I4+GVxDuqG2+Ec+Ue7sYzWeXrArF1bCw/1KoNskE +HKUIcpu3BXfMzOZpYdcC5oKo3j+GimgkNOnZYmOdRzbPj+5jOSC9jWKH3Fb+Jn+Y +hL5ey67cIXZoXbqC0rJVOoKKRqbCR1z+YJ4Tn3z4jn5WktsdL2LiE62oxhAiQCr/ +GkSuM3swvZ+5C55ftrCyq6vbTLwz39pQI1RM5QfQLQXiZgMW1K+sbdW0MUVGbjTp +uzP6QyRquajvRGP3uEhMyHLtVa/21y3XzDK/NL0IRwgS3pBEXPUCAwEAAaNrMGkw +DgYDVR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFCK64Dm2Eadc3j1Moo9zqqQtLYzPMBIGA1UdEQQLMAmC +B2NhdC5wZW0wDQYJKoZIhvcNAQELBQADggEBAL1ft3tMwtbaOJvwi7SlJ1mtNgUt +fnvGkgUkzz44/ppe+Bq0LT0PaE2iLfvjaJbB6C/aYZPVooUjdzM86DUkGD/nNwzn +RrcUEKI40j2BtCVLbwvK0opAQgNlrAUptwz8jXbf04lpVuXCJ470+qQxMRDPvBZi +T80suZMmplH+mGnrGFOnW7tiq182BGZ9DCBynMfoJJdZfgQFD11trhk/2fs/ebIi +0/k95fP7E+c5PoWy9DbTcP7XpTf2yxHsPZujrUhDSaXYfyBOZ79yzj4W366tV9dj +md6kIskFx/FyADwu43z4TUaVpeHcK1IXrsrbWilgtSKsP8eLMmdSXvj2sqw= +-----END CERTIFICATE----- diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/delays.json b/functional-tests/hoverctl/sandbox-2112582993/testdata/delays.json new file mode 100644 index 000000000..c270e6cef --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/delays.json @@ -0,0 +1,13 @@ +{ + "data": [ + { + "urlPattern": "host1", + "delay": 100 + }, + { + "httpMethod": "POST", + "urlPattern": "host2", + "delay": 110 + } + ] +} \ No newline at end of file diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/key.pem b/functional-tests/hoverctl/sandbox-2112582993/testdata/key.pem new file mode 100644 index 000000000..28afbdbe6 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA3461Dl9GpyJ5o88oyGiLgzASLZvxxqR95AjeGqb43l4lQgrm +ALhtHJ1NTLgjj4ZXEO6obb4Rz5R7uxjNZ5esCsXVsLD/Uqg2yQQcpQhym7cFd8zM +5mlh1wLmgqjeP4aKaCQ06dliY51HNs+P7mM5IL2NYofcVv4mf5iEvl7Lrtwhdmhd +uoLSslU6gopGpsJHXP5gnhOffPiOflaS2x0vYuITrajGECJAKv8aRK4zezC9n7kL +nl+2sLKrq9tMvDPf2lAjVEzlB9AtBeJmAxbUr6xt1bQxRUZuNOm7M/pDJGq5qO9E +Y/e4SEzIcu1Vr/bXLdfMMr80vQhHCBLekERc9QIDAQABAoIBAGUORGgHx49btTLI +PT5Ci/Y0b7MwUB2kU8gV/hh8K/mRAzSUap4ewCv0K3IntuN1LbxYtchN6A02qKvN +rWRLmpiQD2W8zN3XblR1yGENrYkYNZ+O83ygXHruM7cSkMyUi9JBs62V97Th7sQn +FYAEWFmlddj5Yy/r2QlKr47CmT4kKaw5DQsNjJnBLLvTkwHemT4tHDnIRrvKmR/I +lEh5OtI0AS8PxnpR54Mg2HOHlJteGPC5yw9FKetIoNkiQ8QpY278WV6tytRoRwA+ +BFT7tELTXJjeOoMysB41RkbIqZXu1at0QRBug4m+m41f/FY5eMhduNrcerEH93yu +d/+0huECgYEA8euOKfEhi/dkL2GneTGamVlURr4uk4bjeaEI6mdTZ+//Jm8/Wdjr +y+VJ7D/5E5v9zaYxKzOoPTFScueIaLVeDsOuPGhTEPgCJZmyATCHGWIzrQXbBksx +ZKa1TDWpJbZ2N3NHJWyDLdVIZbCch+4P1Yj7bkxk70g4t1VGkvpwnmkCgYEA7JGP +eDdyyWKEPqGCgTp7z5rtoTdiUOAc+MbbDkx91hwB/2hHjXd+DtBdHpB9XoxL4d+i +i7JO8ETgueNT9o8Hu5F2Y8fI1g5gyBGBrZUqEpw5PE/luHDUcvsZh2MwF9nHOzxW +Tw72nov9R6+TmGDEQDen3Cu7K67xsK99ID9U0K0CgYAEuKMioGkWMTLMVeyNyfEJ +cxvY8Zc5G9XOptzkrjWLfryNBHjJCRm49fWWXb0/q7itTcQB4tUytIa2ZVxdJsT6 +Jsl8tlCAsUZhc3ls2oSYczks9ENNASSqoTJClX2ClegCtwY5bb+1okbybRrw8C1w +7mZxxZ3mqZMpyMlCzw5pmQKBgCTovPqjpAwZi8p9xm/FISYN5P9fNb6qiLqjw++i +LYvnSMSBj5BZ0VgcWDr5jiXfO8Oc8a5b/obsKCe4eRQh0rIICvGcbRBApAby/EmJ +2UuYBjILwSqap/rchGokJo/CEZyDWG/zRLUN4FF76ko+5r+iL1VhmU1CeUD40Evk +taXtAoGAKuN8bdDe9GrTypcSZRb9YKvCu9yKODHPRhGYuqlGu6hlfUG9BVkmsccp +7ofzCXrJCf8RzUkEoKt0zlvzPUJizwJbWFfSLK9w0RR9bT2MxNz2aX+pR1OF3sk2 +ZgyxyQ5GO1wKvgUOFI96zwdatMAKuYX4d+EBc/JystHNURByVBs= +-----END RSA PRIVATE KEY----- diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/request-template.json b/functional-tests/hoverctl/sandbox-2112582993/testdata/request-template.json new file mode 100644 index 000000000..e3c1576a8 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/request-template.json @@ -0,0 +1 @@ +{"data":[{"requestTemplate": {"path": "/path1", "method": "GET", "destination": "www.virtual.com"}, "response": {"status": 201, "encodedBody": false, "body": "body1", "headers": {"Header": ["value1"]}}}, {"requestTemplate": {"path": "/path2", "method": "GET", "destination": "www.virtual.com", "headers": {"Header": ["value2"]}}, "response": {"status": 202, "body": "body2", "headers": {"Header": ["value2"]}}}]} diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/sim1.json b/functional-tests/hoverctl/sandbox-2112582993/testdata/sim1.json new file mode 100644 index 000000000..ddb8bc8d6 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/sim1.json @@ -0,0 +1,79 @@ +{ + "data": { + "pairs": [ + { + "request": { + "path": [ + { + "matcher": "exact", + "value": "/" + } + ], + "method": [ + { + "matcher": "exact", + "value": "GET" + } + ], + "destination": [ + { + "matcher": "exact", + "value": "time.jsontest.com" + } + ], + "scheme": [ + { + "matcher": "exact", + "value": "http" + } + ], + "body": [ + { + "matcher": "exact", + "value": "" + } + ], + "query": {} + }, + "response": { + "status": 200, + "body": "{\n \"date\": \"06-04-2019\",\n \"milliseconds_since_epoch\": 1559690466840,\n \"time\": \"11:21:06 PM\"\n}\n", + "encodedBody": false, + "headers": { + "Access-Control-Allow-Origin": [ + "*" + ], + "Content-Length": [ + "100" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Tue, 04 Jun 2019 23:21:06 GMT" + ], + "Hoverfly": [ + "Was-Here" + ], + "Server": [ + "Google Frontend" + ], + "X-Cloud-Trace-Context": [ + "be9cf5bf37b02f6397e03f6477e302b0" + ] + }, + "templated": false + } + } + ], + "globalActions": { + "delays": [], + "delaysLogNormal": [] + } + }, + "meta": { + "schemaVersion": "v5", + "hoverflyVersion": "v1.0.0", + "timeExported": "2019-06-05T00:21:35+01:00" + } +} diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/sim2.json b/functional-tests/hoverctl/sandbox-2112582993/testdata/sim2.json new file mode 100644 index 000000000..19a8daf7d --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/sim2.json @@ -0,0 +1,79 @@ +{ + "data": { + "pairs": [ + { + "request": { + "path": [ + { + "matcher": "exact", + "value": "/api" + } + ], + "method": [ + { + "matcher": "exact", + "value": "GET" + } + ], + "destination": [ + { + "matcher": "exact", + "value": "time.jsontest.com" + } + ], + "scheme": [ + { + "matcher": "exact", + "value": "http" + } + ], + "body": [ + { + "matcher": "exact", + "value": "" + } + ], + "query": {} + }, + "response": { + "status": 200, + "body": "{\n \"date\": \"06-04-2019\",\n \"milliseconds_since_epoch\": 1559690466840,\n \"time\": \"11:21:06 PM\"\n}\n", + "encodedBody": false, + "headers": { + "Access-Control-Allow-Origin": [ + "*" + ], + "Content-Length": [ + "100" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Tue, 04 Jun 2019 23:21:06 GMT" + ], + "Hoverfly": [ + "Was-Here" + ], + "Server": [ + "Google Frontend" + ], + "X-Cloud-Trace-Context": [ + "be9cf5bf37b02f6397e03f6477e302b0" + ] + }, + "templated": false + } + } + ], + "globalActions": { + "delays": [], + "delaysLogNormal": [] + } + }, + "meta": { + "schemaVersion": "v5", + "hoverflyVersion": "v1.0.0", + "timeExported": "2019-06-05T00:21:35+01:00" + } +} diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/test-student-data.csv b/functional-tests/hoverctl/sandbox-2112582993/testdata/test-student-data.csv new file mode 100644 index 000000000..fcf0aa580 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/test-student-data.csv @@ -0,0 +1,6 @@ +Id,Name,Marks +1,Test1,45 +2,Test2,55 +3,Test3,67 +4,Test4,89 +*,NA,ABSENT \ No newline at end of file diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/test-student-data1.csv b/functional-tests/hoverctl/sandbox-2112582993/testdata/test-student-data1.csv new file mode 100644 index 000000000..9aebcc94a --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/test-student-data1.csv @@ -0,0 +1,3 @@ +Id,Name,Marks +1,Test1,20 +*,NA,ABSENT \ No newline at end of file diff --git a/functional-tests/hoverctl/sandbox-2112582993/testdata/test.pac b/functional-tests/hoverctl/sandbox-2112582993/testdata/test.pac new file mode 100644 index 000000000..cfa607d25 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-2112582993/testdata/test.pac @@ -0,0 +1,18 @@ +function FindProxyForURL(url, host) { + // our local URLs from the domains below example.com don't need a proxy: + if (shExpMatch(host, "*.example.com")) + { + return "DIRECT"; + } + + // URLs within this network are accessed through + // port 8080 on fastproxy.example.com: + if (isInNet(host, "10.0.0.0", "255.255.248.0")) + { + return "PROXY fastproxy.example.com:8080"; + } + + // All other requests go through port 8080 of proxy.example.com. + // should that fail to respond, go directly to the WWW: + return "PROXY proxy.example.com:8080; DIRECT"; +} \ No newline at end of file diff --git a/functional-tests/hoverctl/sandbox-442953051/stdout b/functional-tests/hoverctl/sandbox-442953051/stdout new file mode 100644 index 000000000..e69de29bb diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/add_random_delay.py b/functional-tests/hoverctl/sandbox-442953051/testdata/add_random_delay.py new file mode 100755 index 000000000..be14d352c --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/add_random_delay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +import sys +import logging +import random +from time import sleep + +logging.basicConfig(filename='random_delay_middleware.log', level=logging.DEBUG) +logging.debug('Random delay middleware is called') + +# set delay to random value less than one second + +SLEEP_SECS = random.random() + +def main(): + + data = sys.stdin.readlines() + # this is a json string in one line so we are interested in that one line + payload = data[0] + logging.debug("sleeping for %s seconds" % SLEEP_SECS) + sleep(SLEEP_SECS) + + + # do not modifying payload, returning same one + print(payload) + +if __name__ == "__main__": + main() diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/cert.pem b/functional-tests/hoverctl/sandbox-442953051/testdata/cert.pem new file mode 100644 index 000000000..d04f58b21 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWDCCAkCgAwIBAgIVALg7EVVQrv7N34JFuuJg9LeW3R+QMA0GCSqGSIb3DQEB +CwUAMC8xGzAZBgNVBAoTEkhvdmVyZmx5IEF1dGhvcml0eTEQMA4GA1UEAxMHY2F0 +LnBlbTAeFw0xNTExMjIxNzI4MjRaFw0xNzExMjExNzI4MjRaMC8xGzAZBgNVBAoT +EkhvdmVyZmx5IEF1dGhvcml0eTEQMA4GA1UEAxMHY2F0LnBlbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAN+OtQ5fRqcieaPPKMhoi4MwEi2b8cakfeQI +3hqm+N5eJUIK5gC4bRydTUy4I4+GVxDuqG2+Ec+Ue7sYzWeXrArF1bCw/1KoNskE +HKUIcpu3BXfMzOZpYdcC5oKo3j+GimgkNOnZYmOdRzbPj+5jOSC9jWKH3Fb+Jn+Y +hL5ey67cIXZoXbqC0rJVOoKKRqbCR1z+YJ4Tn3z4jn5WktsdL2LiE62oxhAiQCr/ +GkSuM3swvZ+5C55ftrCyq6vbTLwz39pQI1RM5QfQLQXiZgMW1K+sbdW0MUVGbjTp +uzP6QyRquajvRGP3uEhMyHLtVa/21y3XzDK/NL0IRwgS3pBEXPUCAwEAAaNrMGkw +DgYDVR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFCK64Dm2Eadc3j1Moo9zqqQtLYzPMBIGA1UdEQQLMAmC +B2NhdC5wZW0wDQYJKoZIhvcNAQELBQADggEBAL1ft3tMwtbaOJvwi7SlJ1mtNgUt +fnvGkgUkzz44/ppe+Bq0LT0PaE2iLfvjaJbB6C/aYZPVooUjdzM86DUkGD/nNwzn +RrcUEKI40j2BtCVLbwvK0opAQgNlrAUptwz8jXbf04lpVuXCJ470+qQxMRDPvBZi +T80suZMmplH+mGnrGFOnW7tiq182BGZ9DCBynMfoJJdZfgQFD11trhk/2fs/ebIi +0/k95fP7E+c5PoWy9DbTcP7XpTf2yxHsPZujrUhDSaXYfyBOZ79yzj4W366tV9dj +md6kIskFx/FyADwu43z4TUaVpeHcK1IXrsrbWilgtSKsP8eLMmdSXvj2sqw= +-----END CERTIFICATE----- diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/delays.json b/functional-tests/hoverctl/sandbox-442953051/testdata/delays.json new file mode 100644 index 000000000..c270e6cef --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/delays.json @@ -0,0 +1,13 @@ +{ + "data": [ + { + "urlPattern": "host1", + "delay": 100 + }, + { + "httpMethod": "POST", + "urlPattern": "host2", + "delay": 110 + } + ] +} \ No newline at end of file diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/key.pem b/functional-tests/hoverctl/sandbox-442953051/testdata/key.pem new file mode 100644 index 000000000..28afbdbe6 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA3461Dl9GpyJ5o88oyGiLgzASLZvxxqR95AjeGqb43l4lQgrm +ALhtHJ1NTLgjj4ZXEO6obb4Rz5R7uxjNZ5esCsXVsLD/Uqg2yQQcpQhym7cFd8zM +5mlh1wLmgqjeP4aKaCQ06dliY51HNs+P7mM5IL2NYofcVv4mf5iEvl7Lrtwhdmhd +uoLSslU6gopGpsJHXP5gnhOffPiOflaS2x0vYuITrajGECJAKv8aRK4zezC9n7kL +nl+2sLKrq9tMvDPf2lAjVEzlB9AtBeJmAxbUr6xt1bQxRUZuNOm7M/pDJGq5qO9E +Y/e4SEzIcu1Vr/bXLdfMMr80vQhHCBLekERc9QIDAQABAoIBAGUORGgHx49btTLI +PT5Ci/Y0b7MwUB2kU8gV/hh8K/mRAzSUap4ewCv0K3IntuN1LbxYtchN6A02qKvN +rWRLmpiQD2W8zN3XblR1yGENrYkYNZ+O83ygXHruM7cSkMyUi9JBs62V97Th7sQn +FYAEWFmlddj5Yy/r2QlKr47CmT4kKaw5DQsNjJnBLLvTkwHemT4tHDnIRrvKmR/I +lEh5OtI0AS8PxnpR54Mg2HOHlJteGPC5yw9FKetIoNkiQ8QpY278WV6tytRoRwA+ +BFT7tELTXJjeOoMysB41RkbIqZXu1at0QRBug4m+m41f/FY5eMhduNrcerEH93yu +d/+0huECgYEA8euOKfEhi/dkL2GneTGamVlURr4uk4bjeaEI6mdTZ+//Jm8/Wdjr +y+VJ7D/5E5v9zaYxKzOoPTFScueIaLVeDsOuPGhTEPgCJZmyATCHGWIzrQXbBksx +ZKa1TDWpJbZ2N3NHJWyDLdVIZbCch+4P1Yj7bkxk70g4t1VGkvpwnmkCgYEA7JGP +eDdyyWKEPqGCgTp7z5rtoTdiUOAc+MbbDkx91hwB/2hHjXd+DtBdHpB9XoxL4d+i +i7JO8ETgueNT9o8Hu5F2Y8fI1g5gyBGBrZUqEpw5PE/luHDUcvsZh2MwF9nHOzxW +Tw72nov9R6+TmGDEQDen3Cu7K67xsK99ID9U0K0CgYAEuKMioGkWMTLMVeyNyfEJ +cxvY8Zc5G9XOptzkrjWLfryNBHjJCRm49fWWXb0/q7itTcQB4tUytIa2ZVxdJsT6 +Jsl8tlCAsUZhc3ls2oSYczks9ENNASSqoTJClX2ClegCtwY5bb+1okbybRrw8C1w +7mZxxZ3mqZMpyMlCzw5pmQKBgCTovPqjpAwZi8p9xm/FISYN5P9fNb6qiLqjw++i +LYvnSMSBj5BZ0VgcWDr5jiXfO8Oc8a5b/obsKCe4eRQh0rIICvGcbRBApAby/EmJ +2UuYBjILwSqap/rchGokJo/CEZyDWG/zRLUN4FF76ko+5r+iL1VhmU1CeUD40Evk +taXtAoGAKuN8bdDe9GrTypcSZRb9YKvCu9yKODHPRhGYuqlGu6hlfUG9BVkmsccp +7ofzCXrJCf8RzUkEoKt0zlvzPUJizwJbWFfSLK9w0RR9bT2MxNz2aX+pR1OF3sk2 +ZgyxyQ5GO1wKvgUOFI96zwdatMAKuYX4d+EBc/JystHNURByVBs= +-----END RSA PRIVATE KEY----- diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/request-template.json b/functional-tests/hoverctl/sandbox-442953051/testdata/request-template.json new file mode 100644 index 000000000..e3c1576a8 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/request-template.json @@ -0,0 +1 @@ +{"data":[{"requestTemplate": {"path": "/path1", "method": "GET", "destination": "www.virtual.com"}, "response": {"status": 201, "encodedBody": false, "body": "body1", "headers": {"Header": ["value1"]}}}, {"requestTemplate": {"path": "/path2", "method": "GET", "destination": "www.virtual.com", "headers": {"Header": ["value2"]}}, "response": {"status": 202, "body": "body2", "headers": {"Header": ["value2"]}}}]} diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/sim1.json b/functional-tests/hoverctl/sandbox-442953051/testdata/sim1.json new file mode 100644 index 000000000..ddb8bc8d6 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/sim1.json @@ -0,0 +1,79 @@ +{ + "data": { + "pairs": [ + { + "request": { + "path": [ + { + "matcher": "exact", + "value": "/" + } + ], + "method": [ + { + "matcher": "exact", + "value": "GET" + } + ], + "destination": [ + { + "matcher": "exact", + "value": "time.jsontest.com" + } + ], + "scheme": [ + { + "matcher": "exact", + "value": "http" + } + ], + "body": [ + { + "matcher": "exact", + "value": "" + } + ], + "query": {} + }, + "response": { + "status": 200, + "body": "{\n \"date\": \"06-04-2019\",\n \"milliseconds_since_epoch\": 1559690466840,\n \"time\": \"11:21:06 PM\"\n}\n", + "encodedBody": false, + "headers": { + "Access-Control-Allow-Origin": [ + "*" + ], + "Content-Length": [ + "100" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Tue, 04 Jun 2019 23:21:06 GMT" + ], + "Hoverfly": [ + "Was-Here" + ], + "Server": [ + "Google Frontend" + ], + "X-Cloud-Trace-Context": [ + "be9cf5bf37b02f6397e03f6477e302b0" + ] + }, + "templated": false + } + } + ], + "globalActions": { + "delays": [], + "delaysLogNormal": [] + } + }, + "meta": { + "schemaVersion": "v5", + "hoverflyVersion": "v1.0.0", + "timeExported": "2019-06-05T00:21:35+01:00" + } +} diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/sim2.json b/functional-tests/hoverctl/sandbox-442953051/testdata/sim2.json new file mode 100644 index 000000000..19a8daf7d --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/sim2.json @@ -0,0 +1,79 @@ +{ + "data": { + "pairs": [ + { + "request": { + "path": [ + { + "matcher": "exact", + "value": "/api" + } + ], + "method": [ + { + "matcher": "exact", + "value": "GET" + } + ], + "destination": [ + { + "matcher": "exact", + "value": "time.jsontest.com" + } + ], + "scheme": [ + { + "matcher": "exact", + "value": "http" + } + ], + "body": [ + { + "matcher": "exact", + "value": "" + } + ], + "query": {} + }, + "response": { + "status": 200, + "body": "{\n \"date\": \"06-04-2019\",\n \"milliseconds_since_epoch\": 1559690466840,\n \"time\": \"11:21:06 PM\"\n}\n", + "encodedBody": false, + "headers": { + "Access-Control-Allow-Origin": [ + "*" + ], + "Content-Length": [ + "100" + ], + "Content-Type": [ + "application/json" + ], + "Date": [ + "Tue, 04 Jun 2019 23:21:06 GMT" + ], + "Hoverfly": [ + "Was-Here" + ], + "Server": [ + "Google Frontend" + ], + "X-Cloud-Trace-Context": [ + "be9cf5bf37b02f6397e03f6477e302b0" + ] + }, + "templated": false + } + } + ], + "globalActions": { + "delays": [], + "delaysLogNormal": [] + } + }, + "meta": { + "schemaVersion": "v5", + "hoverflyVersion": "v1.0.0", + "timeExported": "2019-06-05T00:21:35+01:00" + } +} diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/test-student-data.csv b/functional-tests/hoverctl/sandbox-442953051/testdata/test-student-data.csv new file mode 100644 index 000000000..fcf0aa580 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/test-student-data.csv @@ -0,0 +1,6 @@ +Id,Name,Marks +1,Test1,45 +2,Test2,55 +3,Test3,67 +4,Test4,89 +*,NA,ABSENT \ No newline at end of file diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/test-student-data1.csv b/functional-tests/hoverctl/sandbox-442953051/testdata/test-student-data1.csv new file mode 100644 index 000000000..9aebcc94a --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/test-student-data1.csv @@ -0,0 +1,3 @@ +Id,Name,Marks +1,Test1,20 +*,NA,ABSENT \ No newline at end of file diff --git a/functional-tests/hoverctl/sandbox-442953051/testdata/test.pac b/functional-tests/hoverctl/sandbox-442953051/testdata/test.pac new file mode 100644 index 000000000..cfa607d25 --- /dev/null +++ b/functional-tests/hoverctl/sandbox-442953051/testdata/test.pac @@ -0,0 +1,18 @@ +function FindProxyForURL(url, host) { + // our local URLs from the domains below example.com don't need a proxy: + if (shExpMatch(host, "*.example.com")) + { + return "DIRECT"; + } + + // URLs within this network are accessed through + // port 8080 on fastproxy.example.com: + if (isInNet(host, "10.0.0.0", "255.255.248.0")) + { + return "PROXY fastproxy.example.com:8080"; + } + + // All other requests go through port 8080 of proxy.example.com. + // should that fail to respond, go directly to the WWW: + return "PROXY proxy.example.com:8080; DIRECT"; +} \ No newline at end of file