502无法解析服务器标头_编写下载服务器。 第二部分:标头:Last-Modified,ETag和If-None-Match...

502无法解析服务器标头

客户端缓存是万维网的基础之一。 服务器应通知客户端资源的有效性,客户端应尽可能快地对其进行缓存。 如我们所见,如果不缓存Web,它将非常缓慢。 只需在任何网站上Ctrl + F5并将其与普通F5进行比较-后者就会更快,因为它使用了已缓存的资源。 缓存对于下载也很重要。 如果我们已经获取了几兆字节的数据并且它们没有改变,那么通过网络推送它们是非常浪费的。

使用

HTTP ETag标头可用于避免重复下载客户端已有的资源。 服务器与第一响应服务器一起返回ETag标头,该标头通常是文件内容的哈希值。 客户端可以保留ETag并在以后请求相同资源时将其发送(在If-None-Match请求标头中)。 如果在此期间未更改,则服务器可以简单地返回304 Not Modified响应。 让我们从对ETag支持的集成测试开始:

def 'should send file if ETag not present'() {expect:mockMvc.perform(get('/download/' + FileExamples.TXT_FILE_UUID)).andExpect(status().isOk())}def 'should send file if ETag present but not matching'() {expect:mockMvc.perform(get('/download/' + FileExamples.TXT_FILE_UUID).header(IF_NONE_MATCH, '"WHATEVER"')).andExpect(status().isOk())
}def 'should not send file if ETag matches content'() {given:String etag = FileExamples.TXT_FILE.getEtag()expect:mockMvc.perform(get('/download/' + FileExamples.TXT_FILE_UUID).header(IF_NONE_MATCH, etag)).andExpect(status().isNotModified()).andExpect(header().string(ETAG, etag))
}

有趣的是,Spring框架中内置了ShallowEtagHeaderFilter 。 安装它会使所有测试通过,包括最后一个测试:

@WebAppConfiguration
@ContextConfiguration(classes = [MainApplication])
@ActiveProfiles("test")
class DownloadControllerSpec extends Specification {private MockMvc mockMvc@Autowiredpublic void setWebApplicationContext(WebApplicationContext wac) {mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilter(new Sha512ShallowEtagHeaderFilter(), "/download/*").build()}//tests...}

我实际上插入了使用SHA-512而不是默认MD5的自己的Sha512ShallowEtagHeaderFilter 。 同样由于某种原因,默认实现在哈希值前面加上0

public class ShallowEtagHeaderFilter {protected String generateETagHeaderValue(byte[] bytes) {StringBuilder builder = new StringBuilder("\"0");DigestUtils.appendMd5DigestAsHex(bytes, builder);builder.append('"');return builder.toString();}//...
}

与:

public class Sha512ShallowEtagHeaderFilter extends ShallowEtagHeaderFilter {@Overrideprotected String generateETagHeaderValue(byte[] bytes) {final HashCode hash = Hashing.sha512().hashBytes(bytes);return "\"" + hash + "\"";}
}

不幸的是,我们无法使用内置过滤器,因为它们必须首先完全读取响应主体才能计算ETag 。 这基本上关闭了上一篇文章中介绍的主体流传输–整个响应都存储在内存中。 我们必须自己实现ETag功能。 从技术上讲, If-None-Match可以包含多个ETag值。 但是,谷歌浏览器和ShallowEtagHeaderFilter支持它,因此我们也将跳过它。 为了控制响应头,我们现在返回ResponseEntity<Resource>

@RequestMapping(method = GET, value = "/{uuid}")
public ResponseEntity<Resource> download(@PathVariable UUID uuid,@RequestHeader(IF_NONE_MATCH) Optional<String> requestEtagOpt) {return storage.findFile(uuid).map(pointer -> prepareResponse(pointer, requestEtagOpt)).orElseGet(() -> new ResponseEntity<>(NOT_FOUND));
}private ResponseEntity<Resource> prepareResponse(FilePointer filePointer, Optional<String> requestEtagOpt) {return requestEtagOpt.filter(filePointer::matchesEtag).map(this::notModified).orElseGet(() -> serveDownload(filePointer));
}private ResponseEntity<Resource> notModified(String etag) {log.trace("Cached on client side {}, returning 304", etag);return ResponseEntity.status(NOT_MODIFIED).eTag(etag).body(null);
}private ResponseEntity<Resource> serveDownload(FilePointer filePointer) {log.debug("Serving '{}'", filePointer);final InputStream inputStream = filePointer.open();final InputStreamResource resource = new InputStreamResource(inputStream);return ResponseEntity.status(OK).eTag(filePointer.getEtag()).body(resource);
}

该过程由可选的requestEtagOpt控制。 如果存在并且与客户端发送的内容匹配,则返回304。否则照常发送200 OK。 本示例中引入的FilePointer新方法如下所示:

import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;public class FileSystemPointer implements FilePointer {private final File target;private final HashCode tag;public FileSystemPointer(File target) {try {this.target = target;this.tag = Files.hash(target, Hashing.sha512());} catch (IOException e) {throw new IllegalArgumentException(e);}}@Overridepublic InputStream open() {try {return new BufferedInputStream(new FileInputStream(target));} catch (FileNotFoundException e) {throw new IllegalArgumentException(e);}}@Overridepublic String getEtag() {return "\"" + tag + "\"";}@Overridepublic boolean matchesEtag(String requestEtag) {return getEtag().equals(requestEtag);}
}

在这里,您将看到FileSystemPointer实现,该实现直接从文件系统读取文件。 关键部分是缓存标记,而不是在每次请求时都重新计算标记。 上面的实现的行为符合预期,例如,Web浏览器不会再次下载资源。

3.使用

ETagIf-None-Match标头类似,还有Last-ModifiedIf-Modified-Since 。 我想它们很容易解释:第一台服务器返回Last-Modified响应标头,指示给定资源的最后修改时间( duh! )。 客户端缓存此时间戳,并将其与后续请求一起传递给If-Modified-Since请求标头中的相同资源。 如果同时未更改资源,则服务器将响应304,从而节省带宽。 这是一个后备机制,同时实现ETagLast-Modified是一个好习惯。 让我们从集成测试开始:

def 'should not return file if wasn\'t modified recently'() {given:Instant lastModified = FileExamples.TXT_FILE.getLastModified()String dateHeader = toDateHeader(lastModified)expect:mockMvc.perform(get('/download/' + FileExamples.TXT_FILE_UUID).header(IF_MODIFIED_SINCE, dateHeader)).andExpect(status().isNotModified())
}def 'should not return file if server has older version than the client'() {given:Instant lastModifiedLaterThanServer = FileExamples.TXT_FILE.getLastModified().plusSeconds(60)String dateHeader = toDateHeader(lastModifiedLaterThanServer)expect:mockMvc.perform(get('/download/' + FileExamples.TXT_FILE_UUID).header(IF_MODIFIED_SINCE, dateHeader)).andExpect(status().isNotModified())
}def 'should return file if was modified after last retrieval'() {given:Instant lastModifiedRecently = FileExamples.TXT_FILE.getLastModified().minusSeconds(60)String dateHeader = toDateHeader(lastModifiedRecently)expect:mockMvc.perform(get('/download/' + FileExamples.TXT_FILE_UUID).header(IF_MODIFIED_SINCE, dateHeader)).andExpect(status().isOk())
}private static String toDateHeader(Instant lastModified) {ZonedDateTime dateTime = ZonedDateTime.ofInstant(lastModified, ZoneOffset.UTC)DateTimeFormatter.RFC_1123_DATE_TIME.format(dateTime)
}

并执行:

@RequestMapping(method = GET, value = "/{uuid}")
public ResponseEntity<Resource> download(@PathVariable UUID uuid,@RequestHeader(IF_NONE_MATCH) Optional<String> requestEtagOpt,@RequestHeader(IF_MODIFIED_SINCE) Optional<Date> ifModifiedSinceOpt) {return storage.findFile(uuid).map(pointer -> prepareResponse(pointer,requestEtagOpt,ifModifiedSinceOpt.map(Date::toInstant))).orElseGet(() -> new ResponseEntity<>(NOT_FOUND));
}private ResponseEntity<Resource> prepareResponse(FilePointer filePointer, Optional<String> requestEtagOpt, Optional<Instant> ifModifiedSinceOpt) {if (requestEtagOpt.isPresent()) {final String requestEtag = requestEtagOpt.get();if (filePointer.matchesEtag(requestEtag)) {return notModified(filePointer);}}if (ifModifiedSinceOpt.isPresent()) {final Instant isModifiedSince = ifModifiedSinceOpt.get();if (filePointer.modifiedAfter(isModifiedSince)) {return notModified(filePointer);}}return serveDownload(filePointer);
}private ResponseEntity<Resource> serveDownload(FilePointer filePointer) {log.debug("Serving '{}'", filePointer);final InputStream inputStream = filePointer.open();final InputStreamResource resource = new InputStreamResource(inputStream);return response(filePointer, OK, resource);
}private ResponseEntity<Resource> notModified(FilePointer filePointer) {log.trace("Cached on client side {}, returning 304", filePointer);return response(filePointer, NOT_MODIFIED, null);
}private ResponseEntity<Resource> response(FilePointer filePointer, HttpStatus status, Resource body) {return ResponseEntity.status(status).eTag(filePointer.getEtag()).lastModified(filePointer.getLastModified().toEpochMilli()).body(body);
}

可悲的是,习惯上使用Optional不再看起来不错,所以我坚持使用isPresent() 。 我们同时检查If-Modified-SinceIf-None-Match 。 如果两者都不匹配,我们将照常提供文件。 只是为了让您了解这些标头的工作方式,让我们执行一些端到端测试。 第一个要求:

> GET /download/4a8883b6-ead6-4b9e-8979-85f9846cab4b HTTP/1.1
> ...
> 
< HTTP/1.1 200 OK
< ETag: "8b97c678a7f1d2e0af...921228d8e"
< Last-Modified: Sun, 17 May 2015 15:45:26 GMT
< ...

带有ETag后续请求(已缩短):

> GET /download/4a8883b6-ead6-4b9e-8979-85f9846cab4b HTTP/1.1
> If-None-Match: "8b97c678a7f1d2e0af...921228d8e"
> ...
> 
< HTTP/1.1 304 Not Modified
< ETag: "8b97c678a7f1d2e0af...921228d8e"
< Last-Modified: Sun, 17 May 2015 15:45:26 GMT
< ...

如果我们的客户仅支持Last-Modified

> GET /download/4a8883b6-ead6-4b9e-8979-85f9846cab4b HTTP/1.1
> If-Modified-Since: Tue, 19 May 2015 06:59:55 GMT
> ...
> 
< HTTP/1.1 304 Not Modified
< ETag: "8b97c678a7f1d2e0af9cda473b36c21f1b68e35b93fec2eb5c38d182c7e8f43a069885ec56e127c2588f9495011fd8ce032825b6d3136df7adbaa1f921228d8e"
< Last-Modified: Sun, 17 May 2015 15:45:26 GMT

有许多内置工具,例如过滤器,可以为您处理缓存。 但是,如果需要确保在服务器端流传输文件而不是对其进行预缓冲,则需要格外小心。

编写下载服务器

  • 第一部分:始终流式传输,永远不要完全保留在内存中
  • 第二部分:标头:Last-Modified,ETag和If-None-Match
  • 第三部分:标头:内容长度和范围
  • 第四部分:有效地执行HEAD操作
  • 第五部分:油门下载速度
  • 第六部分:描述您发送的内容(内容类型等)
  • 这些文章中开发的示例应用程序可在GitHub上找到。

翻译自: https://www.javacodegeeks.com/2015/06/writing-a-download-server-part-ii-headers-last-modified-etag-and-if-none-match.html

502无法解析服务器标头

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337846.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言strcat_s函数的原理

点击上方蓝字关注我&#xff0c;了解更多咨询1、dst 内存空间大小目标字符串长度原始字符串场地‘\0’。2、使用sizeof函数获取内存空间大小&#xff0c;strlen函数获取字符串长度。即获取内存空间大小和查字符串长度。实例#include "stdafx.h" #include<stdlib.h…

java开发用怎么软件开发_Java 9中的5个功能将改变您开发软件的方式(还有2个不会)...

java开发用怎么软件开发有望在Java 9中发布的最令人兴奋的功能是什么&#xff1f; 近期不要对Java 9相对沉默而分心。JDK提交者正在努力准备下一个版本&#xff0c;预计在2015年12月才完成功能的下一个版本。此后&#xff0c;它将通过严格的测试和错误修复了将其准备于一般可用…

php redis并发读写,PHP使用Redis实现防止大并发下二次写入的方法

本文实例讲述了PHP使用Redis实现防止大并发下二次写入的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;PHP调用redis进行读写操作&#xff0c;大并发下会出现&#xff1a;读取key1&#xff0c;没有内容则写入内容&#xff0c;但是大并发下会出现同时多个php进程写…

理解C语言指针概念只需几分钟

点击上方蓝字关注我&#xff0c;了解更多咨询当我们声明一个变量或常量时&#xff0c;计算机系统会为这个变量或常量分配存储单元&#xff0c;变量的数据存储到被分配的存储单元内&#xff0c;对变量的赋值和取值操作都是针对存储单元的操作。C编译器是如何通过变量找到与其对应…

php做一个网页的源代码,用HTML5做一个个人网站此文仅展示个人主页界面。内附源代码下载地址...

下载说明&#xff1a; 1.再好的作品都不如将来要做的作品。在每一次的设计当中都能有所收获&#xff0c;才是设计师在web开发中最得益的。 2.本站所有作品均是杨青个人设计。如果发现模板有错&#xff0c;请尽情谅解。 3.如果遇到什么问htmlcss编写的个人主页&#xff0c;适合初…

jax-rs jax-ws_JAX-RS 2.x与Spring MVC:返回对象列表的XML表示

jax-rs jax-wsJSON是所有REST * API的王者&#xff0c;但是您仍然可能需要公开多种表示形式&#xff0c;包括XML。 使用JAX-RS和Spring MVC都非常简单。 实际上&#xff0c;唯一要做的就是用JAXB注释对从API调用返回的POJO进行注释。 但是在我看来&#xff0c;序列化对象列表时…

C语言中经典的程序设计结构:顺序、条件、循环

点击上方蓝字关注我&#xff0c;了解更多咨询无论使用何种编程语言&#xff0c;都含有程序设计的三大经典结构。即&#xff1a;顺序结构、条件结构和循环结构&#xff0c;C语言也是如此。综述顺序结构&#xff0c;就是一条大路走到底&#xff0c;没有岔路口&#xff0c;一步步从…

php protected const,关于const:PHP类常量 – 公共,私有还是受保护?

假设常量属性是自动公开的&#xff0c;对吗&#xff1f;有没有办法让它们成为私有的或受保护的&#xff1f;事先谢谢。隐藏它们的原因是什么&#xff1f;即使它们是公开的——它们是只读的。常量应该是公共的&#xff0c;因为它们描述的是关于类的不可变事实&#xff0c;而不是…

7-8垃圾箱分布_您认为有关垃圾收集的7件事-完全错了

7-8垃圾箱分布关于Java Garbage Collection的最大误解是什么&#xff1f;它的真实情况如何&#xff1f; 小时候&#xff0c;我的父母曾经告诉我&#xff0c;如果我学习不好&#xff0c;我将成为垃圾收集者。 他们几乎不知道&#xff0c;垃圾回收实际上是很棒的。 也许这就是为…

c语言中abort函数的使用

点击上方蓝字关注我&#xff0c;了解更多咨询1、abort函数的作用是异常终止一个进程&#xff0c;意味着abort后面的代码将不再执行。2、当调用abort函数时&#xff0c;会导致程序异常终止&#xff0c;而不会进行一些常规的清除工作。实例#include <stdio.h> #include <…

php 异常值检测,PHP中的错误处理、异常处理机制分析

例&#xff1a;$a fopen(test.txt,r);//这里并没有对文件进行判断就打开了&#xff0c;如果文件不存在就会报错?>那么正确的写法应该如下&#xff1a;if(file_exists(test.txt)){$ffopen(test.txt,r);//使用完后关闭fclose($f);}?>一、PHP错误处理的三种方式A、简单的…

c语言中如何防止数组下标越界

点击上方蓝字关注我&#xff0c;了解更多咨询1、若数组长度和下标访问值出现错误&#xff0c;则会导致数组下标越界。数组下标从0开始&#xff0c;访问值为-1。2、在使用循环遍历数组元素时&#xff0c;注意防范off-by-one的错误。对于作为函数参数传入的数组下标&#xff0c;要…

java oauth2.0_教程:如何实现Java OAuth 2.0以使用GitHub和Google登录

java oauth2.0将Google和GitHub OAuth登录添加到Java应用程序的指南 我们添加到Takipi的最新功能之一是3rd party登录。 如果您像我一样懒惰&#xff0c;那么我想您也希望跳过填写表单和输入新密码的操作 。 只要有权限&#xff0c;许多人都希望使用第三方登录&#xff0c;只要…

c语言中数组访问越界如何理解

点击上方蓝字关注我&#xff0c;了解更多咨询1、可以通过数组下标直接访问数组中的元素。2、如果一个数组被定义为n个元素&#xff0c;那么访问n个元素是合法的。如果访问n个元素以外&#xff0c;则是非法的&#xff0c;称为访问越界。实例int a[5] {0}; //等价 int a[5] {0,…

php全部公开课,PHP公开课|这篇PHP的each()函数教学数,只为了帮你的PHP会学的更好...

【摘要】PHP作为一种超文本预处理器&#xff0c;已经成为了我们常用的网站编程语言&#xff0c;并且结合了C语言&#xff0c;Java等我们常见的编程语言&#xff0c;所以&#xff0c;有很多web开发领域的新人都看中了他的使用广泛性&#xff0c;有很多人都想了解php的内容&#…

ogm session_带有Hibernate OGM的NoSQL –第三部分:在WildFly上构建REST应用程序

ogm session欢迎回到我们的教程系列“带有Hibernate OGM的NoSQL”&#xff01; 感谢Gunnar Morling&#xff08; gunnarmorling &#xff09;创建了本教程。 在这一部分中&#xff0c;您将学习如何在WildFly服务器上运行的Java EE应用程序中使用Hibernate OGM。 使用本教程前面…

java的define,关于预处理器:Java中的#define

我开始用Java编程&#xff0c;我想知道是否相当于C EDCOX1(0)的存在。对谷歌的快速搜索表明它没有&#xff0c;但有人能告诉我是否存在类似的东西吗&#xff1f;在Java中&#xff1f;我正在努力使我的代码更可读。例如&#xff0c;我希望能够编写myArray[PROTEINS]&#xff0c;…

c语言中函数是怎么理解?

点击上方蓝字关注我&#xff0c;了解更多咨询1、C语言中的函数是一个可以重复使用的代码&#xff0c;用于独立完成某个功能。2、它可以接收用户传输的参数&#xff0c;也可以不接收&#xff0c;将代码段封装成函数的过程称为函数定义。如果有返回值&#xff0c;使用return语句返…

java g1 收集调优_Java性能调优:充分利用垃圾收集器

java g1 收集调优JVM背后发生了什么&#xff0c;垃圾回收如何影响Java性能&#xff1f; 性能调优世界是一个危险的地方&#xff0c;一个JVM标志失衡&#xff0c;事情很快就会变得繁琐。 因此 &#xff0c;我们决定求助于Java性能调优专家&#xff0c; 单调 JVM探查器mjprof的创…

java的构造函数详解,Java构造函数与普通函数用法详解

函数也被称为方法&#xff01;函数的作用及特点&#xff1a;1、用于定义功能&#xff0c;将功能封装。2、可以提高代码的复用性。函数注意事项&#xff1a;1、不能进行函数套用(不可以在函数内定义函数)。2、函数只有被调用才能被执行。3、基本数据类型(String、int、….)修饰的…