Why does Mountebank predicate cause 200 instead of 500 in response? - predicate

Teaching self mountebank. I want to create a mock endpoint such that by issuing POST command to http://localhost:2525/test?mock-response-code=500, the response code will be 500 and the message body will be some custom text.
When I create the imposter, however, and run the relevant curl command, I am getting a 200 response.
curl -i -X POST http://localhost:2525/imposters --data '#createImposter_r500.ejs'
info: [mb:2525] POST /imposters
info: [http:61161] Open for business...
HTTP/1.1 201 Created
Access-Control-Allow-Origin: *
Location: http://localhost:2525/imposters/61161
Content-Type: application/json; charset=utf-8
Content-Length: 767
Date: Sun, 18 Aug 2019 13:57:57 GMT
Connection: keep-alive
{
"protocol": "http",
"port": 61161,
"numberOfRequests": 0,
"recordRequests": false,
"requests": [],
"stubs": [
{
"responses": [
{
"is": {
"statusCode": 500
}
}
],
"predicates": [
{
"equals": {
"path": "/test",
"method": "POST",
"query": {
"mock-response-code": 500
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:2525/imposters/61161/stubs/0"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:2525/imposters/61161"
},
"stubs": {
"href": "http://localhost:2525/imposters/61161/stubs"
}
}
}
createImposter_r500.ejs:
{
"protocol": "http",
"stubs": [{
"responses": [
{ "is": { "statusCode": 500 }}
],
"predicates": [{
"equals": {
"path": "/test",
"method": "POST",
"query": {"mock-response-code": 500}
}
}]
}]
}
Here is what happens when I try using that URL:
curl -i -X POST http://localhost:61161/test?mock-response-code=500
info: [http:61161] ::1:61429 => POST /test?mock-response-code=500
HTTP/1.1 200 OK
Connection: close
Date: Sun, 18 Aug 2019 14:18:06 GMT
Transfer-Encoding: chunked
Here is some debug output:
debug: [http:62650] ::1:63139 ESTABLISHED
info: [http:62650] ::1:63139 => POST /test?mock-response-code=400
debug: [http:62650] ::1:63139 => {"requestFrom":"::1:63139","method":"POST","path":"/test","query":{"mock-response-code":"400"},"headers":{"Host":"localhost:62650","User-Agent":"curl/7.54.0","Accept":"*/*"},"body":"","ip":"::1"}
debug: [http:62650] no predicate match
debug: [http:62650] generating response from {"is":{}}
debug: [http:62650] ::1:63139 <= {"statusCode":200,"headers":{"Connection":"close"},"body":"","_mode":"text"}
debug: [http:62650] ::1:63139 CLOSED
(Port is different from above b/c it's from a different run.)

I found the answer myself with continued experimentation.
Quotation marks - that simple.
"query": {"mock-response-code": "400"}
rather than
"query": {"mock-response-code": 400}

Related

New Relic: Script always returns Status 400 but works in Postman

I'm trying to make a healthcheck for my service in New Relic. So I just want to call one of my APIs every x minutes and see if it returns 200.
In New Relic I created a new synthetic monitor and now I'm trying to write a script for that monitor.
The script is supposed to make a post request to our service and receive a token in the response with status 200. In Postman this post request works and returns the token + Status 200 (I replaced the sensitive strings with <...>):
curl --location --request POST <TOKEN_URL> \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=<CLIENT_ID_DEV>' \
--data-urlencode 'client_secret=<CLIENT_SECRET_DEV>'
But when I try to recreate that with the script, it always returns Status 400 Bad Request.
This is my script:
var assert = require('assert');
//Defining my authentication credentials.
var IDK_TOKEN_URL = $secure.TOKEN_URL;
var CLIENT_ID = $secure.CLIENT_ID_DEV;
var CLIENT_SECRET = $secure.CLIENT_SECRET_DEV;
var options = {
url: IDK_TOKEN_URL,
body: JSON.stringify({
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
//Define expected results using callback function.
function callback(error, response, body) {
console.log(response.statusCode + " status code")
assert.ok(response.statusCode == 200, 'Expected 200 OK response');
var info = JSON.parse(body);
assert.ok(info.success == true, 'Expected True results in Response Body, result was ' + info.success);
console.log("End reached");
}
//Make POST request, passing in options and callback.
$http.post(options, callback);
This is what I see in the console:
It seems to automatically append a port 443 to my url, so instead of <my url>.io/oidc/v1/token, the request seems to get fired to <my url>.io:443/oidc/v1/token?
And when I click on "View resource" in the image above, I see:
But I'm using a post method, why is it saying anything about get method is not allowed?
This is the HAR log that I can download in the new relic console:
"request": {
"cookies": [],
"headers": [
{
"name": "Content-Type",
"value": "application/x-www-form-urlencoded"
},
{
"name": "host",
"value": "<my url>.io"
},
{
"name": "content-length",
"value": "187"
},
{
"name": "X-Abuse-Info",
"value": "Request sent by a New Relic Synthetics Monitor (https://docs.newrelic.com/docs/synthetics/new-relic-synthetics/administration/identify-synthetics-requests-your-app) - monitor id: df1817f0-fac2-49f4-a0d5-479d254dfa1a | account id: 2807718"
},
{
"name": "X-NewRelic-Synthetics",
"value": "[1,2807718,\"fddc843c-8fe0-497f-bf5b-52c2805a265e\",\"b6da79b9-37ab-4a8a-a792-f3fa0f99f205\",\"df1817f0-fac2-49f4-a0d5-479d254dfa1a\"]"
}
],
"headersSize": 607,
"bodySize": 187,
"method": "POST",
"url": "<my url>.io:443/oidc/v1/token/",
"httpVersion": "HTTP/1.1",
"queryString": [],
"postData": {
"mimeType": "application/x-www-form-urlencoded",
"text": "{\"grant_type\":\"client_credentials\",\"client_id\":\"_SECURECREDENTIAL_\",\"client_secret\":\"_SECURECREDENTIAL_\"}",
"params": []
},
"_ajax": false,
"_mixedContentType": "unknown",
"_referrerPolicy": "",
"_isLinkPreload": false,
"_host": "<my url>.io",
"_port": 443,
"_path": "/oidc/v1/token/"
},
"response": {
"cookies": [],
"headers": [
{
"name": "Date",
"value": "Thu, 06 May 2021 10:21:05 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Content-Length",
"value": "67"
},
{
"name": "Connection",
"value": "close"
},
{
"name": "Cache-Control",
"value": "no-cache, no-store, max-age=0, must-revalidate"
},
{
"name": "Expires",
"value": "0"
},
{
"name": "Pragma",
"value": "no-cache"
},
{
"name": "Referrer-Policy",
"value": "origin"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=31536000 ; includeSubDomains"
},
{
"name": "Vary",
"value": "accept-encoding,origin,access-control-request-headers,access-control-request-method,accept-encoding"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "X-Frame-Options",
"value": "DENY"
},
{
"name": "X-Vcap-Request-Id",
"value": "e2006a3c-0c27-4194-6b81-d9f037158ca3"
},
{
"name": "X-Xss-Protection",
"value": "1; mode=block"
}
],
"headersSize": 544,
"bodySize": 67,
"status": 400,
"statusText": "Bad Request",
"httpVersion": "HTTP/1.1",
"content": {
"size": 639,
"compression": 572,
"mimeType": "application/json",
"text": ""
},
"redirectURL": "",
"_chromeStatusText": "Bad Request",
"_connectionReused": false,
"_fromServiceWorker": false,
"_fromDiskCache": false,
"_fromAppCache": false,
"_fromCache": false
},
I had to replace 'body' with 'form' like in this example.
I aslo added now the call to the API after the token was received. The final script was:
var assert = require('assert');
//Define your authentication credentials.
var TOKEN_URL = $secure.TOKEN_URL;
var MY_SERVICE_BASE_URL = $secure.MY_SERVICE_BASE_URL_DEV;
var CLIENT_ID = $secure.CLIENT_ID_DEV;
var CLIENT_SECRET = $secure.CLIENT_SECRET_DEV;
function new_relic_callback(err, response, body) {
assert.equal(response.statusCode, 200, 'Expected a 200 OK response');
};
function api_request_callback(err, response, body) {
var parsed_body = JSON.parse(body);
var api_request = {
url: CONSENT_BASE_URL + '/rest of URL...',
headers: {
'Authorization': 'Bearer ' + parsed_body["access_token"]
}
};
$http.get(api_request, new_relic_callback);
};
var token_request = {
url: TOKEN_URL,
form: {
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "client_credentials"
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
$http.post(token_request, api_request_callback);

403 Forbidden when accessing Kroger API from Azure-Hosted .Net Core 3.1 app

Problem: I need to determine the nature of this failure so I can know how to further troubleshoot. I have come up with a few hypothesis:
- could be a firewall/proxy configuration within Azure
- could be a misconfiguration with Kroger's API
- could be a public certificate rejection from Azure app
- could be something totally unrelated to any of above
Details: I’m attempting to connect to Kroger's developer API. The following code has been simplified for this post. (I have been using IHttpClientFactory previously to generate my HTTPClient). This works locally, but once it’s deployed to an Azure web service, I am presented with a 403 message (which appears to be coming from Azure and not the external API):
You don't have permission to access http://api.kroger.com on this server.
This same code works within Azure for other 3rd party APIs over HTTPS, so the suspicion I have had is that this error comes from not using correct client certificates, so Azure attempts to call over http?
I have tried many things to resolve this, including uploading a public certificate that I downloaded from https://api.kroger.com to my azure app service. It appears to pull the certificate correctly, but the request still fails with the same message.
Relevant code is below (Without using client certificate):
using(var client = _requestFactory.CreateClient()))
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("basic", _clientId);
client.DefaultRequestHeaders.Add("Accept", "application/json");
var newRequest = new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "scope", "product.compact" }
};
var response = await client.PostAsync($"https://api.kroger.com/v1/connect/oauth2/token", new FormUrlEncodedContent(newRequest));
return Ok(await response.Content.ReadAsStringAsync());
}
Below is the full response from the server.
{
"version": "1.1",
"content": {
"headers": [{
"key": "Content-Type",
"value": ["text/html"]
}, {
"key": "Content-Length",
"value": ["299"]
}, {
"key": "Expires",
"value": ["Sat, 08 Feb 2020 19:18:55 GMT"]
}]
},
"statusCode": 403,
"reasonPhrase": "Forbidden",
"headers": [{
"key": "Server",
"value": ["AkamaiGHost"]
}, {
"key": "Mime-Version",
"value": ["1.0"]
}, {
"key": "Date",
"value": ["Sat, 08 Feb 2020 19:18:55 GMT"]
}, {
"key": "Connection",
"value": ["close"]
}, {
"key": "Set-Cookie",
"value": ["akaalb_Digital_ALB_API=~op=KT_Digital_API_KCVG_F5:api-kcvg|~rv=47~m=api-kcvg:0|~os=75b4a9ec926d2a9e67035451773cec6c~id=63ba4b3e2a027e4d53b693e2fded5ac3; path=/; HttpOnly; Secure; SameSite=None"]
}],
"trailingHeaders": [],
"requestMessage": {
"version": "1.1",
"content": {
"headers": [{
"key": "Content-Type",
"value": ["application/x-www-form-urlencoded"]
}, {
"key": "Content-Length",
"value": ["51"]
}]
},
"method": {
"method": "POST"
},
"requestUri": "https://api.kroger.com/v1/connect/oauth2/token",
"headers": [{
"key": "Authorization",
"value": ["basic {removed}"]
}, {
"key": "Accept",
"value": ["application/json"]
}, {
"key": "Request-Context",
"value": ["appId={removed}"]
}, {
"key": "Request-Id",
"value": ["|{removed}"]
}, {
"key": "traceparent",
"value": ["{removed}"]
}],
"properties": {}
},
"isSuccessStatusCode": false
}
"headers": [{
"key": "Server",
"value": ["AkamaiGHost"]
}
Almost certainly Akamai issue. I have seen 403 errors due to header order or even user-Agent header that Akamai does not like.

How can I insert many documents at once?

When I want to insert a document using CouchDB, I send a request like this one:
POST http://localhost:5984/blogging/
Accept: application/json
Content-Type: application/json
{
"name": "Example",
"value": 5
}
And it Works. But, what if I want to insert two documents at once? I try:
POST http://localhost:5984/blogging/
Accept: application/json
Content-Type: application/json
[
{
"name": "Example 2",
"value": 6
},
{
"name": "Example 3",
"value": 7
}
]
And it answers me:
HTTP/1.1 400 Bad Request
Cache-Control: must-revalidate
Connection: close
Content-Length: 66
Content-Type: application/json
Date: Thu, 28 Feb 2019 08:03:52 GMT
Server: CouchDB/2.3.0 (Erlang OTP/19)
X-Couch-Request-ID: 9ed2f39fcf
X-CouchDB-Body-Time: 0
{
"error": "bad_request",
"reason": "Document must be a JSON object"
}
Well, first of all, I would suggest you to check this API from the documentation: http://docs.couchdb.org/en/1.6.1/api/database/bulk-api.html#inserting-documents-in-bulk
And, apart from that, you aren't sending a valid JSON object. I would change your request in this way:
POST http://localhost:5984/blogging/_bulk_docs
Accept: application/json
Content-Type: application/json
{
"docs": [
{
"name": "Example 2",
"value": 6
},
{
"name": "Example 3",
"value": 7
}
]
}

Parse multipart/related response and save attachment to file

I'm doing a simple POST that returns the below. I'm expecting a .txt.gz file. I need to know how to retrieve the attachment. I've tried creating a buffer, delimiting, and splitting before writing the file, but it always comes back as corrupt.
The request library explains how to consume a PUT request for an attachment, but not how to consume a response back.
Can anyone help? Been at this for a week now.
JSON.stringify(res)
{
"statusCode": 200,
"body": {
"type": "Buffer",
"data": [
13,
10,
45,
45(..truncated)
]
},
"headers": {
"set-cookie": [
"JSESSIONIDSSO=982EF51351B9497045DB6AB5A2D50DBF; Path=/; Secure; HttpOnly",
"JSESSIONID=995EF8A4A893860159CE463582E4CDDD; Path=/logcollectionservice; Secure; HttpOnly",
"SoapSession.id=-2851099334472488788"
],
"x-frame-options": "SAMEORIGIN",
"strict-transport-security": "max-age=31536000; includeSubdomains",
"content-type": "multipart/related; type=\"text/xml\"; start=\"<852C508C64251E692C9217DFA4EB7433>\"; boundary=\"----=_Part_109_17473961.1526352793275\"",
"transfer-encoding": "chunked",
"date": "Tue, 15 May 2018 02:53:13 GMT",
"connection": "close",
"server": ""
},
"request": {
"uri": {
"protocol": "https:",
"slashes": true,
"auth": null,
"host": "50.22.22.22:8443",
"port": "8443",
"hostname": "50.22.22.22",
"hash": null,
"search": null,
"query": null,
"pathname": "/logcollectionservice/services/DimeGetFileService",
"path": "/logcollectionservice/services/DimeGetFileService",
"href": "https://50.22.22.22:8443/logcollectionservice/services/DimeGetFileService"
},
"method": "POST",
"headers": {
"Authorization": "Basic YWRt123123123123123123",
"SOAPAction": "http://schemas.cisco.com/ast/soap/action/#LogCollectionPort#GetOneFile",
"content-length": 467
}
}
}
res.body
------=_Part_183_12814895.1526363698144
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: binary
Content-Id: <5DDAB109C3B14685FF6B0EA83D2CF8E4>
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body>
<ns1:GetOneFileResponse
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://schemas.cisco.com/ast/soap/"><DataHandler
href="cid:36F92AA211EE04B422D4B07AB07D841A" xsi:type="ns2:DataHandler"
xmlns:ns2="DimeGetFileService"/></ns1:GetOneFileResponse>
</soapenv:Body></soapenv:Envelope>
------=_Part_183_12814895.1526363698144
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-Id: <36F92AA211EE04B422D4B07AB07D841A>
����o�:���W��U����<��퐖.�I��8!h�0�l��?~���m�z��;'����>��oL���'Ϗ��3
'�tSV�EO�8zÂ���[a0����.��`�4wbO�q��FI���:�n܍>��e-
��!^#"�U5��;��^����U[V���s˽��q_��̣
����{gm����r��
���c��㬌Pf]Z�����cs���#ߵ� (truncated)
------=_Part_183_12814895.1526363698144--

Setting an Azure Scheduler Job Retry Policy

Using the Azure Management REST API, I'm trying to update (PATCH) an existing Scheduler job with a retry policy. The request is accepted but the json response object doesn't include the retry policy entries.
In the case of an update, other changes I make are reflected in the response.
Azure Scheduler job entity structure: http://msdn.microsoft.com/library/azure/dn528941.aspx
Azure Scheduler REST API ref: http://msdn.microsoft.com/en-us/library/azure/dn528946.aspx
I've be using Burp Suite to quickly iterate through different requests, but I never see anything to suggest the retry policy has been created.
What am I missing?
The following request will create a new job name SampleJob20 (with no retry policy)
PUT /<subid>/cloudservices/<cloudservicename>/resources/scheduler/~/JobCollections/<jobcollectionname>/jobs/SampleJob20?api-version=2014-04-01 HTTP/1.1
Content-Type: application/json
x-ms-version: 2012-03-01
Host: management.core.windows.net
Content-Length: 583
{
"startTime": "2013-01-30T12:08:00-08:00",
"action":
{
"type": "http",
"request":
{
"uri": "http://bing.com/",
"method": "GET",
"headers":
{
"Content-Type": "text/plain"
}
}
},
"recurrence":
{
"frequency": "minute",
"interval": 30,
"count": 1000
},
"state": "enabled"
}
The response for this request is:
HTTP/1.1 201 Created
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 364
Content-Type: application/json; charset=utf-8
Expires: -1
Server: 1.0.6198.148 (rd_rdfe_stable.141019-1428) Microsoft-HTTPAPI/2.0
x-ms-servedbyregion: ussouth2
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
x-ms-request-id: xxxxxxxxxxxxxxxxx
Date: Mon, 20 Oct 2014 19:13:40 GMT
{
"id":"SampleJob20",
"startTime":"2013-01-30T20:08:00Z",
"action":
{
"request":
{
"uri":"http:\/\/bing.com\/","method":"GET",
"headers":
{
"content-Type":"text\/plain"
}
},
"type":"http"
},
"recurrence":
{
"frequency":"minute",
"count":1000,
"interval":30
},
"state":"enabled",
"status":
{
"nextExecutionTime":"2014-10-20T19:38:00Z",
"executionCount":0,
"failureCount":0,
"faultedCount":0
}
}
When I try and update this job with a retry policy. The response is missing the retry policy detail.
PATCH /<subid>/cloudservices/<cloudservicename>/resources/scheduler/~/JobCollections/<jobcollectionname>/jobs/SampleJob20?api-version=2014-04-01 HTTP/1.1
Content-Type: application/json
x-ms-version: 2012-03-01
Host: management.core.windows.net
Content-Length: 451
{
"id": "SampleJob20",
"action":
{
"type": "http",
"request":
{
"uri": "http://bing.com",
"method": "GET",
"headers":
{
"Content-Type": "text/plain"
}
},
"retryPolicy":
{
"retryType":"fixed",
"retryInterval": "PT1M",
"retryCount": 3
}
}
}
If I create a new job with a retry policy the response contains the retry detail. However, the jobs retry policy cannot be updated either.
Can you try putting a new or the same job like this (It includes the retry policy in the Put request)
PUT
{
"startTime": "2013-01-30T12:08:00-08:00",
"action":
{
"type": "http",
"request":
{
"uri": "http://bing.com/",
"method": "GET",
"headers":
{
"Content-Type": "text/plain"
}
},
"retryPolicy":
{
"retryType":"fixed",
"retryInterval": "PT1M",
"retryCount": 3
}
},
"recurrence":
{
"frequency": "minute",
"interval": 30,
"count": 1000
},
"state": "enabled"
}

Resources