破损的管道将数据块发送到 chrome,仅通过范围请求流式传输 mp4
Broken pipe sending chunks of data to chrome only streaming mp4 via range requests
我有一个应用程序,我们在其中提供受自定义身份验证和授权框架保护的资源。这导致我们必须遵守范围 headers(特别是对于视频)。
我们必须更改此代码以支持 iOS 和 safari。在此过程中,我们开始从 Chrome 收到破损的管道异常,但我不明白为什么。视频按预期在 chrome、Safari 和 iOS 中播放,但我们想清除异常。
Chrome 在第一个请求中请求整个包,我们发送它。然后它回来并开始请求块(请参见下图中的 VID 请求。
什么会导致 chrome 终止此连接?是不是第一次响应返回整个资产有问题?
我也试过在第一个包裹发送所有东西时返回 200,但结果是一样的。
使用 Safari 时无异常。
最后,我们还有其他由 EAP 提供的不受保护的内容,没有报告异常。
管理响应的代码是:
protected void createResponse(HttpServletResponse resp, ResourceInterface resource,
ResourceInstance instance, String range) throws IOException, ServletException {
_logger.info("Range parameter {}", range);
int rangeStart = 0;
int rangeEnd = -1;
InputStream is = resource.getResourceStream(instance);
int lengthHeader = is.available();
if (range != null) {
String[] rangeSplit = range.split("=");
//String type = rangeSplit[0];
String[] rangeVals = rangeSplit[1].split("-");
rangeStart = Integer.parseInt(rangeVals[0]);
resp.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (rangeVals.length > 1 && !StringUtils.isBlank(rangeVals[1])) {
rangeEnd = Integer.parseInt(rangeVals[1]);
//bufferSize = rangeEnd - rangeStart + 1;
} else {
rangeEnd = lengthHeader-1;
}
_logger.info("Start Range {} - End Range {}", rangeStart, rangeEnd);
if (null != is) {
resp.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + rangeStart+"-"+rangeEnd + "/" + lengthHeader);
String lastModifiedPattern = "EEE, dd MMM yyyy HH:mm:ss zzz";
String dateString = DateConverter.getFormattedDate(new Date(), lastModifiedPattern);
resp.setHeader(HttpHeaders.LAST_MODIFIED, dateString);
_logger.info("serving resource {} with length {} bytes", instance.getFileName(), lengthHeader);
}
} else {
_logger.info("NULL RANGE");
}
resp.setHeader("Content-Disposition", "inline; filename=" + instance.getFileName());
resp.setContentType(instance.getMimeType());
//CDP_DW_PUB-279: modifica nome file originale risorse protected
String fileName = resource.getMasterFileName();
if ("Image".equals(resource.getType())) {
int lastIndexOf = instance.getFileName().lastIndexOf("_");
String suffix = instance.getFileName().substring(lastIndexOf + 1, lastIndexOf + 3);
StringBuilder sb = new StringBuilder(suffix);
sb.append("_");
sb.append(fileName);
fileName = sb.toString();
}
//CDP_DW_PUB-279
ServletOutputStream out = resp.getOutputStream();
try {
is.skip(rangeStart);
byte[] slice = new byte[rangeEnd-rangeStart+1];
is.read(slice, 0, rangeEnd-rangeStart+1);
resp.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(slice.length));
_logger.info("Slice size "+slice.length);
out.write(slice);
} catch (Throwable t) {
_logger.error("Error extracting protected resource", t);
throw new ServletException("Error extracting protected resource", t);
} finally {
out.close();
}
}
例外情况很一般:
Caused by: javax.servlet.ServletException: Error extracting protected resource
at org.entando.entando.plugins.jacms.aps.servlet.ProtectedResourceProvider.createResponse(ProtectedResourceProvider.java:192)
at org.entando.entando.plugins.jacms.aps.servlet.ProtectedResourceProvider.provideProtectedResource(ProtectedResourceProvider.java:112)
... 69 more
Caused by: org.eclipse.jetty.io.EofException
at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:292)
at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:429)
at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:384)
at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:139)
... 7 more
Caused by: java.io.IOException: Broken pipe
at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
at sun.nio.ch.IOUtil.write(IOUtil.java:65)
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471)
at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:270)
... 10 more
这是网络流量。 VID 调用是对 MP4
的调用
回复headers是:
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
Content-Type: video/mp4;charset=utf-8
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 2097152-2107841/2107842
Content-Length: 10690
Content-Type: video/mp4;charset=utf-8
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 0-2107841/2107842
Content-Length: 2107842
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
Content-Type: video/mp4;charset=utf-8
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 2097152-2107841/2107842
Content-Length: 10690
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
Content-Type: video/mp4;charset=utf-8
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 65536-2107841/2107842
Content-Length: 2042306
这最终解决了问题。
protected void createResponse(HttpServletResponse resp, ResourceInterface resource,
ResourceInstance instance) throws IOException, ServletException {
resp.setContentType(instance.getMimeType());
resp.setHeader("Content-Disposition", "inline; filename=" + instance.getFileName());
ServletOutputStream out = resp.getOutputStream();
try {
InputStream is = resource.getResourceStream(instance);
if (null != is) {
byte[] buffer = new byte[2048];
int length = -1;
// Transfer the data
while ((length = is.read(buffer)) != -1) {
out.write(buffer, 0, length);
out.flush();
}
is.close();
}
} catch (Throwable t) {
logger.error("Error extracting protected resource", t);
throw new ServletException("Error extracting protected resource", t);
} finally {
out.close();
}
}
我有一个应用程序,我们在其中提供受自定义身份验证和授权框架保护的资源。这导致我们必须遵守范围 headers(特别是对于视频)。
我们必须更改此代码以支持 iOS 和 safari。在此过程中,我们开始从 Chrome 收到破损的管道异常,但我不明白为什么。视频按预期在 chrome、Safari 和 iOS 中播放,但我们想清除异常。
Chrome 在第一个请求中请求整个包,我们发送它。然后它回来并开始请求块(请参见下图中的 VID 请求。
什么会导致 chrome 终止此连接?是不是第一次响应返回整个资产有问题?
我也试过在第一个包裹发送所有东西时返回 200,但结果是一样的。
使用 Safari 时无异常。
最后,我们还有其他由 EAP 提供的不受保护的内容,没有报告异常。
管理响应的代码是:
protected void createResponse(HttpServletResponse resp, ResourceInterface resource,
ResourceInstance instance, String range) throws IOException, ServletException {
_logger.info("Range parameter {}", range);
int rangeStart = 0;
int rangeEnd = -1;
InputStream is = resource.getResourceStream(instance);
int lengthHeader = is.available();
if (range != null) {
String[] rangeSplit = range.split("=");
//String type = rangeSplit[0];
String[] rangeVals = rangeSplit[1].split("-");
rangeStart = Integer.parseInt(rangeVals[0]);
resp.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (rangeVals.length > 1 && !StringUtils.isBlank(rangeVals[1])) {
rangeEnd = Integer.parseInt(rangeVals[1]);
//bufferSize = rangeEnd - rangeStart + 1;
} else {
rangeEnd = lengthHeader-1;
}
_logger.info("Start Range {} - End Range {}", rangeStart, rangeEnd);
if (null != is) {
resp.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + rangeStart+"-"+rangeEnd + "/" + lengthHeader);
String lastModifiedPattern = "EEE, dd MMM yyyy HH:mm:ss zzz";
String dateString = DateConverter.getFormattedDate(new Date(), lastModifiedPattern);
resp.setHeader(HttpHeaders.LAST_MODIFIED, dateString);
_logger.info("serving resource {} with length {} bytes", instance.getFileName(), lengthHeader);
}
} else {
_logger.info("NULL RANGE");
}
resp.setHeader("Content-Disposition", "inline; filename=" + instance.getFileName());
resp.setContentType(instance.getMimeType());
//CDP_DW_PUB-279: modifica nome file originale risorse protected
String fileName = resource.getMasterFileName();
if ("Image".equals(resource.getType())) {
int lastIndexOf = instance.getFileName().lastIndexOf("_");
String suffix = instance.getFileName().substring(lastIndexOf + 1, lastIndexOf + 3);
StringBuilder sb = new StringBuilder(suffix);
sb.append("_");
sb.append(fileName);
fileName = sb.toString();
}
//CDP_DW_PUB-279
ServletOutputStream out = resp.getOutputStream();
try {
is.skip(rangeStart);
byte[] slice = new byte[rangeEnd-rangeStart+1];
is.read(slice, 0, rangeEnd-rangeStart+1);
resp.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(slice.length));
_logger.info("Slice size "+slice.length);
out.write(slice);
} catch (Throwable t) {
_logger.error("Error extracting protected resource", t);
throw new ServletException("Error extracting protected resource", t);
} finally {
out.close();
}
}
例外情况很一般:
Caused by: javax.servlet.ServletException: Error extracting protected resource
at org.entando.entando.plugins.jacms.aps.servlet.ProtectedResourceProvider.createResponse(ProtectedResourceProvider.java:192)
at org.entando.entando.plugins.jacms.aps.servlet.ProtectedResourceProvider.provideProtectedResource(ProtectedResourceProvider.java:112)
... 69 more
Caused by: org.eclipse.jetty.io.EofException
at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:292)
at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:429)
at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:384)
at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:139)
... 7 more
Caused by: java.io.IOException: Broken pipe
at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
at sun.nio.ch.IOUtil.write(IOUtil.java:65)
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471)
at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:270)
... 10 more
这是网络流量。 VID 调用是对 MP4
的调用回复headers是:
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
Content-Type: video/mp4;charset=utf-8
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 2097152-2107841/2107842
Content-Length: 10690
Content-Type: video/mp4;charset=utf-8
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 0-2107841/2107842
Content-Length: 2107842
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
Content-Type: video/mp4;charset=utf-8
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 2097152-2107841/2107842
Content-Length: 10690
HTTP/1.1 206 Partial Content
Date: Thu, 08 Aug 2019 07:47:10 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Accept-Ranges: bytes
Last-Modified: Thu, 08 Aug 2019 09:47:10 CEST
Content-Disposition: inline; filename=38012166d7833c09c4b8632ea186634e.mp4
Content-Type: video/mp4;charset=utf-8
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Server: Jetty(9.4.8.v20180619)
Content-Range: bytes 65536-2107841/2107842
Content-Length: 2042306
这最终解决了问题。
protected void createResponse(HttpServletResponse resp, ResourceInterface resource,
ResourceInstance instance) throws IOException, ServletException {
resp.setContentType(instance.getMimeType());
resp.setHeader("Content-Disposition", "inline; filename=" + instance.getFileName());
ServletOutputStream out = resp.getOutputStream();
try {
InputStream is = resource.getResourceStream(instance);
if (null != is) {
byte[] buffer = new byte[2048];
int length = -1;
// Transfer the data
while ((length = is.read(buffer)) != -1) {
out.write(buffer, 0, length);
out.flush();
}
is.close();
}
} catch (Throwable t) {
logger.error("Error extracting protected resource", t);
throw new ServletException("Error extracting protected resource", t);
} finally {
out.close();
}
}