2

I have two spring boot applications. One running a ZuulProxy server on port 8081 and forwarding all requests made to /notification to another spring boot application (notification server) running on port 8086.

The zuul configuration looks like this

 zuul:  
  routes:
    notification:
          path: /notification/**
          url: http://localhost:8086

The notification server code looks like this

 @RestController
public class JmsController {

    private Logger log = Logger.getLogger(this.getClass());

    private final List<SseEmitter> emitters = new ArrayList<>();

    @Autowired
    private JmsService jmsService;

    @RequestMapping(value = "send", method = RequestMethod.POST, consumes= MediaType.APPLICATION_JSON_VALUE)
    public NotificationDTO sendMessage(@RequestBody NotificationDTO dto){
        jmsService.sendMessage(dto);
        return dto;
    }

    @RequestMapping(path = "/stream", method = RequestMethod.GET)    
    public SseEmitter stream() throws IOException {

        SseEmitter emitter = new SseEmitter();

        emitters.add(emitter);
        emitter.onCompletion(() -> emitters.remove(emitter));

        return emitter;
    }

    @JmsListener(destination = "${queue.name}", containerFactory = "myJmsContainerFactory")
    public void receiveMessage(NotificationDTO dto) throws Exception{

        log.info("Got message" + dto);

        if(emitters!=null && emitters.size() > 0) {
            emitters.forEach((SseEmitter emitter) -> {
                try {
                    emitter.send(dto, MediaType.APPLICATION_JSON);
                } catch (IOException e) {
                    emitter.complete();
                    emitters.remove(emitter);
                    log.info(e);
                }
            });
        }else{
            log.info("No emitters");
        }

    }


}

The html page is as follows:

    <script>
    var myName = null;
    var form = document.getElementById('form');
    var nameForm = null;
    var connected = false;

    var setMyName = function () {
        myName = $('#myname').val();
        $('#myModal').modal('hide');
        return false;
    };

    var connect = function () {
        var source = new EventSource('http://localhost:8081/notification/stream');

        source.addEventListener('open', function (e) {
            console.log('connected');
        });
        source.addEventListener('message', function (e) {
            console.log(e.data);
            var message = JSON.parse(e.data);
            var outMesage = $(
                    '<div class="row">' +
                    '  <div class="col-md-2"><strong>' + message.id + '</strong></div>' +
                    '  <div class="col-md-10"><p>' + message.label + '</p></div>' +
                    '</div>'
            );
            $('#notificationLog').append(outMesage);
        }, false);

        source.addEventListener('error', function (e) {
            if (e.readyState == EventSource.CLOSED) {
                connected = false;
                connect();
            }
        }, false);
    };


    $(function () {
        connect();
    });
</script>

When I run the html page directly against the notification server http://localhost:8086/index.html the application works fine.

When I run the html page against the zuul proxy server http://localhost:8081/notification/index2.html I get the following error after 10 seconds:

java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:170)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:160)
    at org.apache.http.impl.io.SocketInputBuffer.fillBuffer(SocketInputBuffer.java:84)
    at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:273)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:140)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:260)
    at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:283)
    at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:251)
    at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:223)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:271)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123)
    at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:685)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:487)
    at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:863)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:115)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
    at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.forwardRequest(SimpleHostRoutingFilter.java:259)
    at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.forward(SimpleHostRoutingFilter.java:222)
    at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.run(SimpleHostRoutingFilter.java:174)
    at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112)
    at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:197)
    at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:161)
    at com.netflix.zuul.FilterProcessor.route(FilterProcessor.java:120)
    at com.netflix.zuul.ZuulRunner.route(ZuulRunner.java:84)
    at com.netflix.zuul.http.ZuulServlet.route(ZuulServlet.java:111)
    at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:77)
    at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:158)
    at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequestInternal(ZuulController.java:43)
    at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:146)
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:295)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:102)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:68)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

I uploaded the code of the application to github in case you need to run the code or get more details about this implementation.

https://github.com/karimgarza/zuulnotifications

Sending a POST like this should automatically show a message on the browser. The webpage only receives messages for the first 10 seconds and it then times out.

curl 'http://localhost:8081/notification/send' -H 'Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.8,pt;q=0.6,pt-BR;q=0.4' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36' -H 'Content-Type: application/json' -H 'Accept: */*' -H 'Connection: keep-alive' --data-binary '{"id":"12345","label":"my test 6 curl","type":1,"url":"http://www.gmail.com"}' --compressed

Thank you in advanced

user1785917
  • 113
  • 2
  • 8
  • Zuul doesn't currently support websockets – spencergibb Aug 06 '15 at 15:29
  • I don't think server sent events use web sockets. SSEs are sent over traditional HTTP. That means they do not require a special protocol or server implementation to get working. http://stackoverflow.com/questions/5195452/websockets-vs-server-sent-events-eventsource – user1785917 Aug 06 '15 at 15:34
  • ok, thanks. I'll take a look. – spencergibb Aug 06 '15 at 15:40
  • I think I found where the problem is: https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java The time out is coded to 10000 milliseconds, so all connections are closed after 10 seconds. For server sent events this timeout should be configurable. – user1785917 Aug 12 '15 at 13:45
  • 1
    Looking at that code, it's not hardcoded, but the default to a configurable value you can set in `application.properties`: `zuul.host.socket-timeout-millis`. The other is `zuul.host.connect-timeout-millis`. – spencergibb Aug 12 '15 at 14:55
  • perfect! I added the following value as an example and it now works as expected. zuul.host.socket-timeout-millis=300000 – user1785917 Aug 12 '15 at 15:38

2 Answers2

8

Looking at that code, it's not hardcoded, but the default to a configurable value you can set in application.properties: zuul.host.socket-timeout-millis. The other is zuul.host.connect-timeout-millis.

spencergibb
  • 24,471
  • 6
  • 69
  • 75
0

For me the server sent events through the zuul are not working. I use spring boot 1.5.8 and cloud Dalston.SR4

Is SSE supported by zuul?

The XHR Request is opened to the enpdpoint but I got no response

matzeihnsein
  • 678
  • 7
  • 16