Export to GitHub

mockwebserver - issue #7

support lazy-evaluated responses


Posted on Mar 25, 2013 by Swift Cat

HATEOAS apis send back link headers or links embedded in xml or json. It is sometimes the case that a client expects to resolve these as absolute. It would be nice to have a way to either defer expansion of a MockWebServerResponse or somehow else allow templating.

lacking means to do this, I hacked a little and do a lazy replace of the word "URL" in a QueueDispatcher.

/** * there's no built-in way to defer evaluation of a response body, hence this * method, which allows us to send back links to the mock server. */ private AtomicReference<URL> setURLReplacingDispatcher(MockWebServer server) { final AtomicReference<URL> url = new AtomicReference<URL>();

  final QueueDispatcher dispatcher = new QueueDispatcher() {
     protected final BlockingQueue&lt;MockResponse&gt; responseQueue = new LinkedBlockingQueue&lt;MockResponse&gt;();

     @Override
     public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
        MockResponse response = responseQueue.take();
        if (response.getBody() != null) {
           String newBody = new String(response.getBody()).replace(&quot;URL&quot;, url.get().toString());
           response = response.setBody(newBody);
        }
        return response;
     }

     @Override
     public void enqueueResponse(MockResponse response) {
        responseQueue.add(response);
     }
  };
  server.setDispatcher(dispatcher);
  return url;

}

Comment #1

Posted on Apr 12, 2013 by Massive Bear

Could you write a dispatcher that isn't a queue? You could just inspect the request and construct a new response.

Comment #2

Posted on Apr 12, 2013 by Swift Cat

Hehe yeah that would be much easier. I'll give it a go.

Comment #3

Posted on Apr 13, 2013 by Massive Bear

(No comment was entered for this change.)

Comment #4

Posted on Apr 13, 2013 by Happy Rhino

Actually, I can't do this, as the host, port aren't available in RecordedRequest. I could offer a patch to add this..

Comment #5

Posted on Apr 13, 2013 by Swift Cat

basically, MockWebServer.readRequest could add to RecordedRequest a URI field (possibly named href) which contains the host and port from Socket, appending the existing path.

Comment #6

Posted on Apr 13, 2013 by Massive Bear

adrian: can't you get that information from the MockWebServer instance?

Comment #7

Posted on Apr 13, 2013 by Swift Cat

well it seems chicken-egg. afaict, the port isn't assigned until after play() is called, and you can't set the request dispatcher after play() is called. Hence, the dance above. Am I missing something?

Comment #8

Posted on Apr 15, 2013 by Massive Bear

and you can't set the request dispatcher after play() is called

and you can't set the request dispatcher after play() is called

The following seems to work for me (version 20130403):

MockWebServer server = new MockWebServer(); server.enqueue(new MockResponse().addHeader("ETag", "ABCDEF")); server.play(); final int port = server.getPort(); server.setDispatcher(new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { throw new UnsupportedOperationException(Integer.toString(port)); } }); ... Running MwsIssueTest Apr 15, 2013 6:15:46 PM com.google.mockwebserver.MockWebServer$2 run WARNING: MockWebServer connection failed java.lang.UnsupportedOperationException: 52604 ...

It feels pretty nasty, admittedly, but doesn't break. @adrian: were you getting an exception?

Comment #9

Posted on Apr 23, 2013 by Swift Cat

to save folks at home hours lost troubleshooting, above advice doesn't address the concern, as it doesn't inspect the request. It just ends up hanging.

Enqueuing requests uses whatever dispatcher was assigned. If you change the dispatcher after enqueueing requests, then these requests are lost. This is due to the following logic in MockWebServer.

public void enqueue(MockResponse response) {
    ((QueueDispatcher) dispatcher).enqueueResponse(response.clone());
}

The correct workaround is similar to above but ordered differently.

MockWebServer server = new MockWebServer(); server.play(); server.setDispatcher(dispatcherFor(server.getPort()); // note after play, and enqueue follows vs precedes this server.enqueue(new MockResponse().addHeader("ETag", "ABCDEF"));

Bottom line: If you are switching out the dispatcher, you must enqueue requests after, not before the call to MWS.setDispatcher.

Comment #10

Posted on Apr 23, 2013 by Swift Cat

^^ s/request/response/g

Comment #11

Posted on Apr 23, 2013 by Massive Bear

to save folks at home hours lost troubleshooting, above advice doesn't address the concern, as it doesn't inspect the request. It just ends up hanging.

Egads :-( Sorry for sending anyone up a semi-blind alley. Thanks for fixing, Adrian!

Status: WontFix

Labels:
Type-Defect Priority-Medium