cjstehno / ersatz

🤖 A simulated HTTP server for testing client code with configurable responses.
https://cjstehno.github.io/ersatz
Apache License 2.0
47 stars 5 forks source link

Support for 'Expect: 100-continue' missing #122

Closed MichaelSp closed 4 years ago

MichaelSp commented 4 years ago

I just discovered, that it's currently not possible to mock an webdav file upload because support for the request header "Expect: 100-continue" is missing.

Here is the documentation how it should work: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100

To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request and receive a 100 Continue status code in response before sending the body.

cjstehno commented 4 years ago

Can you provide code for a failing example of this? My guess is that the underlying server used by Ersatz handles that status in a special way since the library itself allows you to configure any request/response header you want.

MichaelSp commented 4 years ago

Let me describe it like this. Expected behaviour is:

  1. client sends PUT with header "Expect: 100-continue"
  2. server reads header and checks if the header is OK
  3. server sends HTTP response 100 to the client
  4. client sends the body of the PUT request.
  5. server sends 200 code to the client

Actual behaviour is:

  1. client sends PUT with header "Expect: 100-continue"
  2. server reads header AND body
  3. server is waiting for the body
  4. client is waiting for the code 100 doesn't send the body
  5. connection closed by timeout.

But if you want I can try to come up with a minimal test.

MichaelSp commented 4 years ago

I see what you want to say, but at least the option to set 2 return codes is missing

cjstehno commented 4 years ago

Ersatz does support multiple responses for the same request, so something like this should work:

    void 'Multiple responses for request'(){
        setup:
        ersatzServer.expectations {
            put('/storage'){
                decoder TEXT_PLAIN, Decoders.utf8String
                header('Expect', '100-continue')
                // first request
                responder {
                    code(100)
                }
                // second request
                responder {
                    code(200)
                }
                called 2
            }
        }

        def payload = create(parse('text/plain'), 'some stuff')

        when:
        def response = http.put(
            ersatzServer.httpUrl('/storage'),
            payload,
            'Expect': '100-continue'
        )

        then:
        response.code() == 100

        when:
        response = http.put(
            ersatzServer.httpUrl('/storage'),
            payload,
            'Expect': '100-continue'
        )

        then:
        response.code() == 200
    }

Unfortunately, it appears that somewhere along the like I broke this functionality, and there appear to be no tests for it (shame on me). When this works again, does the test above accurately depict what you are trying to do?

cjstehno commented 4 years ago

After a bit more testing, it appears that the multiple response functionality works in general but there is an issue when a 200 status is returned. I am thinking that the underlying server (or client) is handling this in a different manner. I will have to dig in and figure out where 100 is being mishandled - the Expect: 100-continue seems to also come into play.

I am trying to get a release out by the end of the year. I will try to look at this issue if I have time.

MichaelSp commented 4 years ago

When this works again, does the test above accurately depict what you are trying to do?

close, but not it. It's actually just one request. If I understand correctly, the flow is like this

> request-header
< response 100
> request-body (same request as above)
< response 200
MichaelSp commented 4 years ago

Somehow I'm not able to reproduce it in a minimal POC. Here is my attempt: https://github.com/MichaelSp/ersatz-poc-122/blob/master/src/test/groovy/SimpleTest.groovy and it actually runs: https://github.com/MichaelSp/ersatz-poc-122/commit/ae42d417d6d13277048f8a2d71965a810d36d7b0/checks?check_suite_id=304163216

I'm gonna re-open if I'm able to reproduce