无法将 Content-Type Header 添加到 SSE 端点
Unable to add Content-Type Header to SSE endpoint
我有一个 Java 服务器和一个 Java 脚本客户端,它们已成功使用服务器 Sent-Events 发送更新。最近,我们被迫在使用 kubernetes 内置的 NGINX 作为服务器和客户端之间的代理与使用基于 kong 的代理之间切换,该代理使用不同的配置扩展 NGINX。新代理缓冲 SSE 通过它打破协议,并且因为其他应用程序正在使用新代理我不允许关闭所有缓冲,这是以前代理解决问题的方式。作为替代方案,我一直在尝试向 SSE 响应添加 3 个 http headers 以触发 NGINX 关闭此特定端点的缓冲:
"Content-Type" : "text/event-stream"
"Cache-Control", "no-cache"
"X-Accel-Buffering", "no"
我已经能够相对轻松地添加 "Cache-Control" 和 "X-Accel-Buffering" header,但是我尝试了几种不同的方法来添加 "Content-Type" header 没有任何效果。
下面是第一次尝试,注意 "Content-Type" header 与 RestServiceImpl 中的其他两个 header 设置相同,但从我的日志来看,其他两个 [=69=添加了 ] 而没有添加 "Content-Type"。
@Api
@Path("/service")
public interface RestService {
@GET
@Path("/sseconnect")
@Produces(SseFeature.SERVER_SENT_EVENTS)
EventOutput listenToBroadcast(@Context HttpServletResponse response);
}
@Component
public class RestServiceImpl implements RestService {
@Autowired
private Broadcaster broadcaster;
public RestServiceImpl() {
}
@Override
public EventOutput listenToBroadcast(HttpServletResponse response) {
response.addHeader("Content-Type", "text/event-stream");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("X-Accel-Buffering", "no");
return broadcaster.add();
}
}
public class Broadcaster extends SseBroadcaster {
private final OutboundEvent.Builder eventBuilder =
new OutboundEvent.Builder();
public EventOutput add() {
final EventOutput eventOutput = new EventOutput();
super.add(eventOutput);
return eventOutput;
}
public void sendEvents(Events events, String name) {
final OutboundEvent outboundEvent = eventBuilder.name(name)
.mediaType(MediaType.APPLICATION_JSON_TYPE)
.data(Events.class, events).build();
super.broadcast(outboundEvent);
}
}
我第二次尝试修改 "Content-Type" header 的添加方式,如下所示:
@Override
public EventOutput listenToBroadcast(HttpServletResponse response) {
response.setContentType("text/event-stream");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("X-Accell-Buffering", no);
return broadcaster.add();
}
这具有相同的效果,添加了 "Cache-Control" 和 "X-Accell-Bufferig",但没有添加 "Content-Type"。
对于第三次尝试,我将 HttpServletResponse 替换为 ContainerResponse
@Override
public EventOutput listenToBroadcast(ContainerResponse containerResponse)
{
final MultivaluedMap<String, Object> headers =
containerResponse.getHeaders();
headers.add("Content-Type", "text/event-stream");
headers.add("Cache-Control", "no-cache");
headers.add("X-Accel-Buffering", "no");
return broadcaster.add();
}
这个解决方案破坏了端点并最终给我一个 406 错误,所以我不能明确地说它不会起作用。可能是一个糟糕的实现,但我没有看到任何东西。
第四次尝试,我尝试添加带有 ContainerResponseFilter 的 header。
@Provider
@PreMatching
public class SseResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
final MultivaluedMap<String, Object> headers =
responseContext.getHeaders();
final List<Object> contentTypeValues = new ArrayList<Object>();
contentTypeValues.add("text/event-stream");
headers.put("Content-Type", contentTypeValues);
final List<Object> cacheControlValues = new ArrayList<Object>();
cacheControlValues.add("no-cache");
headers.put("Cache-Control", cacheControlValues);
final List<Object> xAccelBufferingValues =
new ArrayList<Object>();
xAccelBufferingValues.add("no");
headers.put("X-Accel-Buffering", xAccelBufferingValues);
}
}
此尝试将 "Cache-Control" 和 "X-Accel-Buffering" header 添加到应用程序中的每个端点,但没有在任何地方添加 "Content-Type"。请注意,其他一些 api 正在返回允许设置 "Content-Type" 的响应 object,并且这些 REST 端点正在获得 "Content-Type" header为每个特定端点正确设置。不幸的是,SSE 库 returns EventOutput 不允许我像 Response 那样设置 Content-Type header。
对于第五次尝试,我用 WriterInterceptor 替换了 ContainerResponseFilter
@Provider
public class SseWriterInterceptor implements WriterInterceptor {
@Override
public void arroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
final MultivaluedMap<String, Object> headers =
context.getHeaders();
final List<Object> contentTypeValues = new ArrayList<Object>();
contentTypeValues.add("text/event-stream");
headers.put("Content-Type", contentTypeValues);
final List<Object> cacheControlValues = new ArrayList<Object>();
cacheControlValues.add("no-cache");
headers.put("Cache-Control", cacheControlValues);
final List<Object> xAccelBufferingValues =
new ArrayList<Object>();
xAccelBufferingValues.add("no");
headers.put("X-Accel-Buffering", xAccelBufferingValues);
}
}
此解决方案与之前的解决方案一样工作,它向应用程序中的所有端点添加了 "Cache-Control" 和 "X-Accel-Buffering",但再次停止了 "Content-Type"。
总而言之,我似乎无法弄清楚是什么阻止我将 "Content-Type" header 成功添加到我的 SSE 端点。
这个问题类似于:
Why does Jersey swallow my "Content-Encoding" header
但是,将 "Content-Type" 添加到 Response object 的给定解决方案不起作用,因为我使用的 SSE returns 一个 EventOutput object 没有办法添加 "Content-Type" header.
事实证明,泽西岛并不是 "Content-Type" header 未设置的罪魁祸首。该应用程序是作为持续部署环境的一部分构建的,该环境为诸如日志记录和安全扫描之类的事情添加了许多过滤器。其中一个过滤器阻止 "Content-Type" header 在使用 SSE 库时被填充。
我能够确定这是 运行 我本地机器上的应用程序与 运行 它在基于 kubernetes 的开发环境中的应用程序并看到 "Content-Type" header 使用本地的第一个解决方案正确填充。
我有一个 Java 服务器和一个 Java 脚本客户端,它们已成功使用服务器 Sent-Events 发送更新。最近,我们被迫在使用 kubernetes 内置的 NGINX 作为服务器和客户端之间的代理与使用基于 kong 的代理之间切换,该代理使用不同的配置扩展 NGINX。新代理缓冲 SSE 通过它打破协议,并且因为其他应用程序正在使用新代理我不允许关闭所有缓冲,这是以前代理解决问题的方式。作为替代方案,我一直在尝试向 SSE 响应添加 3 个 http headers 以触发 NGINX 关闭此特定端点的缓冲:
"Content-Type" : "text/event-stream"
"Cache-Control", "no-cache"
"X-Accel-Buffering", "no"
我已经能够相对轻松地添加 "Cache-Control" 和 "X-Accel-Buffering" header,但是我尝试了几种不同的方法来添加 "Content-Type" header 没有任何效果。
下面是第一次尝试,注意 "Content-Type" header 与 RestServiceImpl 中的其他两个 header 设置相同,但从我的日志来看,其他两个 [=69=添加了 ] 而没有添加 "Content-Type"。
@Api
@Path("/service")
public interface RestService {
@GET
@Path("/sseconnect")
@Produces(SseFeature.SERVER_SENT_EVENTS)
EventOutput listenToBroadcast(@Context HttpServletResponse response);
}
@Component
public class RestServiceImpl implements RestService {
@Autowired
private Broadcaster broadcaster;
public RestServiceImpl() {
}
@Override
public EventOutput listenToBroadcast(HttpServletResponse response) {
response.addHeader("Content-Type", "text/event-stream");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("X-Accel-Buffering", "no");
return broadcaster.add();
}
}
public class Broadcaster extends SseBroadcaster {
private final OutboundEvent.Builder eventBuilder =
new OutboundEvent.Builder();
public EventOutput add() {
final EventOutput eventOutput = new EventOutput();
super.add(eventOutput);
return eventOutput;
}
public void sendEvents(Events events, String name) {
final OutboundEvent outboundEvent = eventBuilder.name(name)
.mediaType(MediaType.APPLICATION_JSON_TYPE)
.data(Events.class, events).build();
super.broadcast(outboundEvent);
}
}
我第二次尝试修改 "Content-Type" header 的添加方式,如下所示:
@Override
public EventOutput listenToBroadcast(HttpServletResponse response) {
response.setContentType("text/event-stream");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("X-Accell-Buffering", no);
return broadcaster.add();
}
这具有相同的效果,添加了 "Cache-Control" 和 "X-Accell-Bufferig",但没有添加 "Content-Type"。
对于第三次尝试,我将 HttpServletResponse 替换为 ContainerResponse
@Override
public EventOutput listenToBroadcast(ContainerResponse containerResponse)
{
final MultivaluedMap<String, Object> headers =
containerResponse.getHeaders();
headers.add("Content-Type", "text/event-stream");
headers.add("Cache-Control", "no-cache");
headers.add("X-Accel-Buffering", "no");
return broadcaster.add();
}
这个解决方案破坏了端点并最终给我一个 406 错误,所以我不能明确地说它不会起作用。可能是一个糟糕的实现,但我没有看到任何东西。
第四次尝试,我尝试添加带有 ContainerResponseFilter 的 header。
@Provider
@PreMatching
public class SseResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
final MultivaluedMap<String, Object> headers =
responseContext.getHeaders();
final List<Object> contentTypeValues = new ArrayList<Object>();
contentTypeValues.add("text/event-stream");
headers.put("Content-Type", contentTypeValues);
final List<Object> cacheControlValues = new ArrayList<Object>();
cacheControlValues.add("no-cache");
headers.put("Cache-Control", cacheControlValues);
final List<Object> xAccelBufferingValues =
new ArrayList<Object>();
xAccelBufferingValues.add("no");
headers.put("X-Accel-Buffering", xAccelBufferingValues);
}
}
此尝试将 "Cache-Control" 和 "X-Accel-Buffering" header 添加到应用程序中的每个端点,但没有在任何地方添加 "Content-Type"。请注意,其他一些 api 正在返回允许设置 "Content-Type" 的响应 object,并且这些 REST 端点正在获得 "Content-Type" header为每个特定端点正确设置。不幸的是,SSE 库 returns EventOutput 不允许我像 Response 那样设置 Content-Type header。
对于第五次尝试,我用 WriterInterceptor 替换了 ContainerResponseFilter
@Provider
public class SseWriterInterceptor implements WriterInterceptor {
@Override
public void arroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
final MultivaluedMap<String, Object> headers =
context.getHeaders();
final List<Object> contentTypeValues = new ArrayList<Object>();
contentTypeValues.add("text/event-stream");
headers.put("Content-Type", contentTypeValues);
final List<Object> cacheControlValues = new ArrayList<Object>();
cacheControlValues.add("no-cache");
headers.put("Cache-Control", cacheControlValues);
final List<Object> xAccelBufferingValues =
new ArrayList<Object>();
xAccelBufferingValues.add("no");
headers.put("X-Accel-Buffering", xAccelBufferingValues);
}
}
此解决方案与之前的解决方案一样工作,它向应用程序中的所有端点添加了 "Cache-Control" 和 "X-Accel-Buffering",但再次停止了 "Content-Type"。
总而言之,我似乎无法弄清楚是什么阻止我将 "Content-Type" header 成功添加到我的 SSE 端点。
这个问题类似于: Why does Jersey swallow my "Content-Encoding" header 但是,将 "Content-Type" 添加到 Response object 的给定解决方案不起作用,因为我使用的 SSE returns 一个 EventOutput object 没有办法添加 "Content-Type" header.
事实证明,泽西岛并不是 "Content-Type" header 未设置的罪魁祸首。该应用程序是作为持续部署环境的一部分构建的,该环境为诸如日志记录和安全扫描之类的事情添加了许多过滤器。其中一个过滤器阻止 "Content-Type" header 在使用 SSE 库时被填充。
我能够确定这是 运行 我本地机器上的应用程序与 运行 它在基于 kubernetes 的开发环境中的应用程序并看到 "Content-Type" header 使用本地的第一个解决方案正确填充。