Build optimization true breaks SSE proxy requests in ng serve
See original GitHub issueπ Bug report
Command (mark with an x
)
- serve (best guess)
Is this a regression?
No.
Description
Due to another issue #20864 I accidentally turned on the optimization property projects.___.architect.build.options.optimization
in angular.json. For reproduction it does not matter if the whole property is true
or any of the more fine-grained options scripts/styles.minify/fonts
is true
:
"optimization": {
"scripts": true,
"styles": {
"minify": true,
"inlineCritical": false
},
"fonts": true
}
Besides I use a proxy configuration to get data from the backend:
{
"/rest": {
"target": "https://localhost:9000",
"changeOrigin": true,
"secure": false,
"logLevel": "debug",
"timeout": 3600000
}
}
If above optimization is active the server in dev-mode starts with a big red banner:
****************************************************************************************
This is a simple server for use in testing or debugging Angular applications locally.
It hasn't been reviewed for security issues.
DON'T USE IT FOR PRODUCTION!
****************************************************************************************
The regular rest endpoints work normally with this.
One special endpoint that emit server sent events is failing though. The browser connects and holds the connection but the events are not received by the EventSource
in the browser anymore. The reason seems to be that activated compression does not work with SSE.
I can reproduce the behavior with curl
when I activate the --compressed
option:
works: curl 'http://localhost:4200/rest/events' -H 'Accept: text/event-stream' -H 'Connection: keep-alive' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
fails: curl 'http://localhost:4200/rest/events' -H 'Accept: text/event-stream' --compressed -H 'Connection: keep-alive' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
I do not know which component/library should handle the issue that compression does not work well with SSE. As a user of angular it took me some hours to pin the problem. Maybe the same issue from a different project: https://github.com/chimurai/http-proxy-middleware/issues/371
π¬ Minimal Reproduction
- in angular.json set
projects.mgmt-gui.architect.build.options.optimization
totrue
- start the app with
ng serve
- in the app you need to use βnew EventSource(url-to-backend)β
- activate a proxy-config to redirect request to your backend on another port
- have and start a backend that emit server sent events
- as example my java/spring SseController looks like:
package com.example;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import de.ehex.connector.basic.event.EventService;
import de.ehex.connector.mgmt.rest.RestConstants;
@Controller
@PreAuthorize(RestConstants.AUTH_ANY_ADMIN)
@RequestMapping(RestConstants.ANWENDUNGS_KONNEKTOR_BASEURL + "/ereignisse")
public class SseController {
private static final Logger LOG = LoggerFactory.getLogger(SseController.class);
private final CopyOnWriteArrayList<SseEmitter> emitters = new CopyOnWriteArrayList<>();
@Autowired
private EventService eventService;
@Autowired
private TaskScheduler scheduler;
@PostConstruct
public void init() {
this.scheduler.scheduleAtFixedRate(this::sendTime, Instant.now(), Duration.ofSeconds(1));
}
@PreDestroy
public void destroy() {
try {
this.emitters.forEach(e -> e.complete());
} catch (final RuntimeException e) {
this.emitters.clear();
}
}
@RequestMapping(method = RequestMethod.GET, produces = { MediaType.TEXT_EVENT_STREAM_VALUE })
public SseEmitter handle() {
final SseEmitter emitter = new SseEmitter(600_000l);
this.emitters.add(emitter);
emitter.onCompletion(() -> {
this.emitters.remove(emitter);
});
emitter.onTimeout(() -> {
this.emitters.remove(emitter);
});
return emitter;
}
public void sendTime() {
if (this.emitters.isEmpty()) {
return;
}
String msg = Instant.now().toString();
LOG.debug("sending msg to {} subscriber: {}", this.emitters.size(), msg);
final List<SseEmitter> deadEmitters = new ArrayList<>();
this.emitters.forEach(emitter -> {
try {
emitter.send(msg);
} catch (final IOException e) {
deadEmitters.add(emitter);
}
});
this.emitters.removeAll(deadEmitters);
}
}
π₯ Exception or Error
No exceptions, not even in debug log of the proxy-config.
π Your Environment
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ β³ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 12.2.4
Node: 14.16.0
Package Manager: npm 6.14.11
OS: linux x64
Angular: 12.2.4
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1202.4
@angular-devkit/build-angular 12.2.4
@angular-devkit/core 12.2.4
@angular-devkit/schematics 12.2.4
@schematics/angular 12.2.4
rxjs 6.6.3
typescript 4.3.5
Anything else relevant?
No.
Issue Analytics
- State:
- Created 2 years ago
- Comments:5
Top GitHub Comments
Compression isnβt particular useful during development, also in version 13 we disable this completely. Iβll open a PR to back-port this.
This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.