mock-server / mockserver

MockServer enables easy mocking of any system you integrate with via HTTP or HTTPS with clients written in Java, JavaScript and Ruby. MockServer also includes a proxy that introspects all proxied traffic including encrypted SSL traffic and supports Port Forwarding, Web Proxying (i.e. HTTP proxy), HTTPS Tunneling Proxying (using HTTP CONNECT) and SOCKS Proxying (i.e. dynamic port forwarding).
http://mock-server.com
Apache License 2.0
4.57k stars 1.07k forks source link

httpResponse headers 'Content-Type' appending rather than replacing #599

Closed tarrym closed 4 years ago

tarrym commented 5 years ago

I'm trying to set up an expectation, for an XML API. Sounds simple enough, but no matter what I try, I cannot get the mock server to return the correct content type.

This is my code to set up the expectation:

var mockServerClient = require('mockserver-client').mockServerClient("localhost", "8000");

mockServerClient.mockAnyResponse({
        "httpRequest": {
            "method": "POST",
            "headers": { "Content-Type": [ ".*/xml" ] },
            "body": {
                "type": "XML",
                "xml": "<RQ id=\"123456789\" />"
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": [
                { "name": "Content-Type", "values": [ "text/xml" ] }
            ],
            "body": {
                "type": "XML",
                "xml": "<RS state=\"HAPPY DAYS!\"/>"
            },
            "delay": { "timeUnit": "MILLISECONDS", "value": 250 }
        },
        "times": { "unlimited": true }
    }).then(
        function () {
            console.log("Mocked: mockAPI [" + response.statusCode + "] ✔");
        },
        function (error) {
            console.log(error);
        }
    );

Note: docker is mapping port 8000 (host) to 1080 (container).

After running the above code, the mock-server logs show:

mock-xml-api_1  | 2019-02-28 20:28:36,014 INFO o.m.m.HttpStateHandler creating expectation:
mock-xml-api_1  | 
mock-xml-api_1  |   {
mock-xml-api_1  |     "httpRequest" : {
mock-xml-api_1  |       "method" : "POST",
mock-xml-api_1  |       "headers" : {
mock-xml-api_1  |         "Content-Type" : [ ".*/xml" ]
mock-xml-api_1  |       },
mock-xml-api_1  |       "body" : {
mock-xml-api_1  |         "type" : "XML",
mock-xml-api_1  |         "xml" : "<RQ id=\"123456789\" />"
mock-xml-api_1  |       }
mock-xml-api_1  |     },
mock-xml-api_1  |     "times" : {
mock-xml-api_1  |       "unlimited" : true
mock-xml-api_1  |     },
mock-xml-api_1  |     "timeToLive" : {
mock-xml-api_1  |       "unlimited" : true
mock-xml-api_1  |     },
mock-xml-api_1  |     "httpResponse" : {
mock-xml-api_1  |       "statusCode" : 200,
mock-xml-api_1  |       "headers" : {
mock-xml-api_1  |         "Content-Type" : [ "text/xml", "application/json; charset=utf-8" ],
mock-xml-api_1  |         "Cache-Control" : [ "no-cache, no-store" ]
mock-xml-api_1  |       },
mock-xml-api_1  |       "body" : {
mock-xml-api_1  |         "type" : "XML",
mock-xml-api_1  |         "xml" : "<RS state=\"HAPPY DAYS!\"/>"
mock-xml-api_1  |       },
mock-xml-api_1  |       "delay" : {
mock-xml-api_1  |         "timeUnit" : "MILLISECONDS",
mock-xml-api_1  |         "value" : 250
mock-xml-api_1  |       }
mock-xml-api_1  |     }
mock-xml-api_1  |   }
mock-xml-api_1  | 
mock-xml-api_1  | Mocked: mockAPI [200] ✔

Note: "Content-Type" : [ "text/xml", "application/json; charset=utf-8" ], when the expectation specifies a response "Content-Type" of "text/xml" only.

I am able to invoke the mocked API, and get the expected response back, but the response has both content type headers:

$ curl -X POST   http://localhost:8000/mockAPI   -H 'Accept: text/xml'   -H 'Content-Type: application/xml'   -H 'cache-control: no-cache'   -d '<RQ id="123456789" />'   -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> POST /mockAPI HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: text/xml
> Content-Type: application/xml
> cache-control: no-cache
> Content-Length: 21
> 
* upload completely sent off: 21 out of 21 bytes
< HTTP/1.1 200 OK
< Content-Type: text/xml
< Content-Type: application/json; charset=utf-8
< Cache-Control: no-cache, no-store
< connection: keep-alive
< content-length: 25
< 
* Connection #0 to host localhost left intact
<RS state="HAPPY DAYS!"/>

The above was reproduced using:

tarrym commented 5 years ago

I've also just tried swapping the httpResponse for:

"httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["text/xml"]
            },
            "body": {
                "type": "XML",
                "xml": "<RS state=\"HAPPY DAYS!\"/>"
            },
            "delay": { "timeUnit": "MILLISECONDS", "value": 250 }
        }

And now the logs suggest that the response Content-Type is just ignored completely:

mock-xml-api_1  | 2019-02-28 20:40:56,914 INFO o.m.m.HttpStateHandler creating expectation:
mock-xml-api_1  | 
mock-xml-api_1  |   {
mock-xml-api_1  |     "httpRequest" : {
mock-xml-api_1  |       "method" : "POST",
mock-xml-api_1  |       "headers" : {
mock-xml-api_1  |         "Content-Type" : [ ".*/xml" ]
mock-xml-api_1  |       },
mock-xml-api_1  |       "body" : {
mock-xml-api_1  |         "type" : "XML",
mock-xml-api_1  |         "xml" : "<RQ id=\"123456789\" />"
mock-xml-api_1  |       }
mock-xml-api_1  |     },
mock-xml-api_1  |     "times" : {
mock-xml-api_1  |       "unlimited" : true
mock-xml-api_1  |     },
mock-xml-api_1  |     "timeToLive" : {
mock-xml-api_1  |       "unlimited" : true
mock-xml-api_1  |     },
mock-xml-api_1  |     "httpResponse" : {
mock-xml-api_1  |       "statusCode" : 200,
mock-xml-api_1  |       "headers" : {
mock-xml-api_1  |         "Content-Type" : [ "application/json; charset=utf-8" ],
mock-xml-api_1  |         "Cache-Control" : [ "no-cache, no-store" ]
mock-xml-api_1  |       },
mock-xml-api_1  |       "body" : {
mock-xml-api_1  |         "type" : "XML",
mock-xml-api_1  |         "xml" : "<RS state=\"HAPPY DAYS!\"/>"
mock-xml-api_1  |       },
mock-xml-api_1  |       "delay" : {
mock-xml-api_1  |         "timeUnit" : "MILLISECONDS",
mock-xml-api_1  |         "value" : 250
mock-xml-api_1  |       }
mock-xml-api_1  |     }
mock-xml-api_1  |   }
mock-xml-api_1  | 
mock-xml-api_1  | Mocked: mockAPI [200] ✔
Julio-CT commented 5 years ago

same issue here, the content type is just ignored and is returned "application/json"

mockServerClient("localhost", 2080) .mockAnyResponse({ "httpRequest": { "method": "POST", "path": "/1ASIWNXGAML", "headers": { "SOAPAction": ["http://webservices.amadeus.com/PNRRET_11_3_1A"] } }, "httpResponse": { "statusCode": 200, "reasonPhrase": "Ok", "headers": { "content-type" : [ "text/xml" ], "connection": ["keep-alive"] }, "body": { "type": "STRING", "string": "qwe<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:awsse=\"http://xml.amadeus.com/2010/06/Session_v3\" xmlns:wsa=\"http://www.w3.org/2005/08/addressing\"><soapenv:Header><wsa:To>http://www.w3.org/2005/08/addressing/anonymous</wsa:To><wsa:From><wsa:Address>https://nodeD1.test.webservices.amadeus.com/1ASIWNXGAML</wsa:Address></wsa:From><wsa:Action>http://webservices.amadeus.com/PNRRET_11_3_1A</wsa:Action><wsa:MessageID>urn:uuid:1fdb4aa5-a2ee-f4c4-7d2f-98948b2b3553</wsa:MessageID><wsa:RelatesTo RelationshipType=\"http://www.w3.org/2005/08/addressing/reply\">uuid:13d4f129-a7fd-47a8-8013-9efc8a696406</wsa:RelatesTo><awsse:Session TransactionStatusCode=\"End\"><awsse:SessionId>002QDVZ25G</awsse:SessionId><awsse:SequenceNumber>1</awsse:SequenceNumber><awsse:SecurityToken>30KPMKWGZZKY6M7IK2OEL05J1</awsse:SecurityToken></awsse:Session></soapenv:Header><soapenv:Body>RetrieveResponse></RetrieveResponse></soapenv:Body></soapenv:Envelope>" } } }) .then( function () { console.log("expectation created"); }, function (error) { console.log(error); } );

GlynnBrown commented 5 years ago

@Julio-CT, This is happening to me too, when I set up an expectation and post a test request with a Content-Type set to Xml, I am getting appliation/json back in the reply.

@jamesdbloom can please look into this issue, as MockServer is a brilliant piece of code and this little annoyance is tainting the experience unfortunately :.(

Julio-CT commented 5 years ago

need to correct my comment.

in fact MockServer is acting as expected, is the Mock Server JS client (https://rawgit.com/jamesdbloom/mockserver-client-node/mockserver-5.5.1/mockServerClient.js) that is sending the content type always as "application/json"

jamesdbloom commented 4 years ago

This was caused by a "feature" in the javascript client that had a default set of headers for "simple" expectation creation. However, I think that feature is more confusing than beneficial so I have made the default set of headers empty initially. The client still supports users providing a default header set that is re-used on all future expectations, except now this is initially empty.

See: https://github.com/jamesdbloom/mockserver-client-node/commit/e7bcf66bab4e0d3b66b2057f2223dede9c6087de

This is now resolved and will be in the next released version in the next few weeks.