前端学HTTP之内容协商
前面的话
一个URL常常需要代表若干不同的资源。例如那种需要以多种语言提供其内容的网站站点。如果某个站点有说法语的和说英语的两种用户,它可能想用这两种语言提供网站站点信息。理想情况下,服务器应当向英语用户发送英文版,向法语用户发送法文版——用户只要访问网站主页就可以得到相应语言的内容
HTTP提供了内容协商方法,允许客户端和服务器作这样的决定。通过这些方法,单一的URL就可以代表不同的资源(比如,同一个网站页面的法语版和英语版),这些不同的版本称为变体。本文将详细介绍内容协商
总括
对于特定的URL来说,服务器可以根据一些原则来决定发送什么内容给客户端最合适。在有些场合下,服务器甚至可以自动生成定制的页面。比如,服务器可以为手持设备把HTML页面转换成WML页面。这类动态内容变换被称为转码。这些变换动作是HTTP客户端和服务器之间进行内容协商的结果
共有3种不同的方法可以决定服务器上哪个页面最适合客户端:让客户端来选择、服务器自动判定,或让中间代理来选。这3种技术分别称为客户端驱动的协商、服务器驱动的协商以及透明协商
客户端驱动
对于服务器来说,收到客户端请求时只是发回响应,在其中列出可用的页面,让客户端决定要看哪个,这是最容易的事情。很显然,这是服务器最容易实现的方式,而且客户端很可能选择到最佳的版本(只要列表中有让客户端选择的足够信息)。不利之处是每个页面都需要两次请求:第一次获取列表,第二次获取选择的副本。这种技术速度很慢且过程枯燥乏味,让用户厌烦
从实现原理上来说,服务器实际上有两种方法为客户端提供选项:一是发送回一个HTML文档,里面有到该页面的各种版本的链接和每个版本的描述信息,另一种方法是发送回HTTP/1.1响应时,使用300 Multiple Choices响应代码。客户端浏览器收到这种响应时,在前一种情况下,会显示一个带有链接的页面,在后一种情况下,可能会弹出对话窗口,让用户做选择。不管怎么样,决定是由客户端的浏览器用户作出的
除了增加时延并且对每个页面都要进行繁琐的多次请求之外,这种方法还有一个缺点:它需要多个URL:公共页面要一个,其他每种特殊页面也都要一个。因此,比如说原始的请求地址是www.joes-hardware.com,Joe的服务器可能会回复某个页面,该页面里面有到www.joes-hardware.com/english和www.joes-hardware.com/french的链接。如果客户端想加书签的话,是要加在原始的公共页面上呢,还是加在选中的页面上呢?如果用户想把这个网站推荐给他的朋友,是告知www.joes-hardware.com这个地址好呢,还是只告诉他们讲英语的朋友www.joes-hardware.com/english这个地址?
服务器驱动
减少额外通信量的一种方法是让服务器来决定发送哪个页面回去,但为了做到这一点,客户端必须发送有关客户偏好的足够信息,以便服务器能够作出准确的决策。服务器通过客户端请求的首部集来获得这方面的信息
有以下两种机制可供HTTP服务器评估发送什么响应给客户端比较合适
1、检査内容协商首部集。服务器察看客户端发送的Accept首部集,设法用相应的响应首部与之匹配
2、根据其他(非内容协商)首部进行变通。例如,服务器可以根据客户端发送的User-Agent首部来发送响应
【内容协商首部集】
客户端可以用下面列出的HTTP首部集发送用户的偏好信息
首部 描述
Accept 告知服务器发送何种媒体类型
Accept-Language 告知服务器发送何种语言
Accept-Charset 告知服务器发送何种字符集
Accept-Encoding 告知服务器采用何种编码
[注意]这些首部与实体首部非常类似。不过,这两种首部的用途截然不同。实体首部集像运输标签,它们描述了把报文从服务器传输给客户端的过程中必须的各种报文主体属性。而内容协商首部集是由客户端发送给服务器用于交换偏好信息的,以便服务器可以从文档的不同版本中选择出最符合客户端偏好的那个来提供服务
服务器用下面列出的实体首部集来匹配客户端的Accept首部集
Accept首部 实体首部
Accept Content-Type
Accept-Language Content-Language
Accept-Charset Content-Type
Accept-Encoding Content-Encoding
由于HTTP是无状态的协议,表示服务器不会在不同的请求之间追踪客户端的偏好,所以客户端必须在每个请求中都发送其偏好信息
如果两个客户端都发送了Accept-Language首部,描述它们感兴趣的语言信息,服务器就能够决定发送www.joes-hardware.com的何种版本给哪个客户端了。让服务器自动选择发送回去的文档,减少了往返通信的时延,这种时延是客户端驱动模型中无法避免的
然而,假设某个客户端偏好西班牙文,那服务器应当回送哪个版本的页面呢?英语还是法语?服务器只有两种选择:猜测或回退到客户端驱动模型,问客户端选择哪个。假如这个西班牙人碰巧懂一点英语,他可能会选择英文页面,这不是最理想的,但它能解决问题。在这种情况下,这个西班牙人需要有办法传达更多与其偏好有关的信息,也就是他的确对英语略知一二,在没有西班牙语的时候,英语也行
幸运的是,HTTP提供了一种机制,可以让与这个西班牙人情况类似的客户端更详细地描述其偏好。这种机制就是质量值(简称q值)
HTTP协议中定义了质量值,允许客户端为每种偏好类别列出多种选项,并为每种偏好选项关联一个优先次序。例如,客户端可以发送下列形式的Accept-Language首部:
Accept-Language: en; q=0.5, fr; q=0.0 , nl; q=1.0, tr; q=0.0
其中q值的范围从0.0-1.0(0.0是优先级最低的,而1.0是优先级最高的)。上面列出的那个首部,说明该客户端最愿意接收荷兰语(缩写为nl)文档,但英语(缩写为en)文档也行;无论如何,这个客户端都不愿意收到法语(缩写为fr)或土耳 其语(缩写为tr)的版本
[注意]偏好的排列顺序并不重要,只有与偏好相关的q值才是重要的
服务器偶尔也会碰到找不到文档可以匹配客户端的任何偏好的情况。对于这种情况,服务器可以修改文档,也就是对文档进行转码,以匹配客户端的偏好
【其他首部集】
服务器也可以根据其他客户端请求首部集来匹配响应,比如User-Agent首部。例如,服务器知道老版本的浏览器不支持JavaScript语言,这样就可以向其发送不含有JavaScript的页面版本
在这种情况下,没有q值机制可供査找“最近似”的匹配。服务器或者去找完全匹配,或者简单地有什么就给什么,这取决于服务器的实现
由于缓存需要尽力提供所缓存文档中正确的“最佳”版本,HTTP协议定义了服务器在响应中发送的Vary首部。这个首部告知缓存,还有客户端和所有下游的代理,服务器根据哪些首部来决定发送响应的最佳版本
【Apache】
下面概括了著名的Web服务器Apache是如何支持内容协商的。网站的内容提供者,比如说Joe要负责为Joe的索引页面提供不同的版本。Joe还必须把这些索引页面文件放在和站点相关的Apache服务器的适当目录下。用以下两种方式可以启用内容协商
1、在网站目录中,为网站中每个有变体的URI创建一个type-map(类型映射)文件。这个type-map文件列出了每个变体和其相关的内容协商首部集
2、启用MultiViews指令,这样会使Apache自动为目录创建type-map文件
【使用type-map文件】
Apache服务器需要知道type-map文件的命名规则。可以在服务器的配置文件中设置handler来说明type-map文件的后缀名。例如:
AddHandler type-map .var
这行就说明了后缀是.var的文件就是type-map文件
下面给出一个type-map文件示例
根据这个type-map文件,Apache服务器就知道要发送joes-hardware.en.html给请求英语版的客户端,发送joes-hardware.fr.de.html给请求法语版的客户端。Apache服务器也支持质量值
【使用MultiView】
为了使用MultiView,必须在网站目录下的access.conf文件中的适当小节(<Directory>、<Location>,或<Files>)使用OPTION指令来启用它
如果启用了MultiView,而浏览器又请求了名为joes-hardware的资源,服务器就会査找所有名字中含有joes-hardware的文件,并为它们创建type-map文件。服务器会根据名字猜测其对应的内容协商首部集。例如,法语版的joes-hardware应当含有.fr
另一种在服务器端实现内容协商的方法是使用服务器端扩展,比如微软的动态服务器页面(Microsoft’s Active Server Pages, ASP)
透明协商
透明协商机制试图从服务器上去除服务器驱动协商所需的负载,并用中间代理来代表客户端以使与客户端的报文交换最小化。假定代理了解客户端的预期,这样就可以代表客户端与服务器协商,在客户端请求内容的时候,代理已经收到了客户端的预期
为了支持透明内容协商,服务器必须有能力告知代理,服务器需要检査哪些请求首部,以便对客户端的请求进行最佳匹配。HTTP/1.1规范中没有定义任何透明协商机制,但定义了Vary首部。服务器在响应中发送了Vary首部,以告知中间节点需要使用哪些请求首部进行内容协商
代理缓存可以为通过单个URL访问的文档保存不同的副本。如果服务器把它们的决策过程传给缓存,这些代理就能代表服务器与客户端进行协商。缓存同时也是进行内容转码的好地方,因为部署在缓存里的通用转码器能对任意服务器,而不仅仅是一台服务器传来的内容进行转码
【缓存与备用候选】
对内容进行缓存的时候是假设内容以后还可以重用。然而,为了确保对客户端请求回送的是正确的已缓存响应,缓存必须应用服务器在回送响应时所用到的大部分决策逻辑
上面描述了客户端发送的Accept首部集,以及为了给每条请求选择最佳的响应,服务器使用的与这些首部集匹配的相应实体首部集。缓存也必须使用相同的首部集来决定回送哪个已缓存的响应
下图展示了涉及缓存的正确及错误的操作序列。缓存把第一个请求转发给服务器,并存储其响应。对于第二个请求,缓存根据URL査找到了匹配的文档。但是,这份文档是法语版的,而请求者想要的是西班牙语版的。如果缓存只是把文档的法语版本发给请求者的话,它就犯了错误
因此,缓存也应该把第二条请求转发给服务器,并保存该URL的响应与“备用候选”响应。缓存现在就保存了同一个URL的两份不同的文档,与服务器上一样。这些不同的版本称为变体(variant)或备用候选(alternate)。内容协商可看成是为客户端请求选择最合适变体的过程
【Vary 首部】
这里是浏览器和服务器发送的一些典型的请求及响应首部
然而,如果服务器的决策不是依据Accept首部集,而是比如User-Agent首部的话,情况会如何?这不像听起来这么极端。例如,服务器可能知道老版本的浏览器不支持JavaScript语言,因此可能会回送不包含JavaScript的页面版本。如果服务器是根据其他首部来决定发送哪个页面的话,缓存必须知道这些首部是什么,这样才能在选择回送的页面时做出同样的逻辑判断
HTTP的Vary响应首部中列出了所有客户端请求首部,服务器可用这些首部来选择文档或产生定制的内容(在常规的内容协商首部集之外的内容)。例如,若所提供的文档取决于User-Agent首部,Vary首部就必须包含User-Agent
当新的请求到达时,缓存会根据内容协商首部集来寻找最佳匹配。但在把文档提供给客户端之前,它必须检査服务器有没有在已缓存响应中发送Vary首部。如果有Vary首部,那么新请求中那些首部的值必须与旧的已缓存请求里相应的首部相同。因为服务器可能会根据客户端请求的首部来改变响应,为了实现透明协商,缓存必须为每个已缓存变体保存客户端请求首部和相应的服务器响应首部,参见下图
如果某服务器的Vary首部看起来像下面这样,大量不同的User-Agent和Cookie值将会产生非常多的变体:
Vary: User-Agent, Cookie
缓存必须为每个变体保存其相应的文档版本。当缓存执行査找时,首先会对内容协商首部集进行内容匹配,然后比较请求的变体与缓存的变体。如果无法匹配,缓存就从原始服务器获取文档
转码
我们已经讨论了一个机制,该机制可以让客户端和服务器从某个URL的一系列文档中挑选出最适合客户端的文档。实现这些机制的前提是,存在一些满足客户端需求的文档——不管是完全满足还是在一定程度上满足
然而,如果服务器没有能满足客户端需求的文档会怎么样呢?服务器可以给出一个错误响应。但理论上,服务器可以把现存的文档转换成某种客户端可用的文档。这种选项称为转码
下面列出了一些假设的转码
转换之前 转换之后
HTML文档 WML文档
高分辨率图像 低分辨率图像
彩色图像 黑白图像
有多个框架的复杂页面 没有很多框架或图像的简单文本页面
有Java小应用程序的HTML页面 没有Java小应用程序的HTML页面
有广告的页面 去除广告的页面
有3种类别的转码:格式转换、信息综合以及内容注入
【格式转换】
格式转换是指将数据从一种格式转换成另一种格式,使之可以被客户端査看。通过HTML到WML的转换,无线设备就可以访问通常供桌面客户端査看的文档了。通过慢速连接访问Web页面的客户端并不需要接收高分辨率图像,如果通过格式转换降低图像分辨率和颜色来减小图像文件大小的话,这类客户端就能更容易地査看图像比较丰富的页面了
格式转换可以由内容协商首部集来驱动,但也能由User-Agent首部来驱动。注意,内容转换或转码与内容编码或传输编码是不同的,后两者一般用于更高效或安全地传输内容,而前两者则可使访问设备能够査看内容
【信息综合】
从文档中提取关键的信息片段称为信息综合(information synthesis),这是一种有用的转码操作。这种操作的例子包括根据小节标题生成文档的大纲,或者从页面中删除广告和商标
根据内容中的关键字对页面分类是更精细的技术,有助于总结文档的精髓。这种技术常用于Web页面分类系统中,比如门户网站的Web页面目录
【内容注入】
前面描述的两类转码通常会减少Web文档的内容,但还有另一类转换会增加文档的内容,即内容注入转码。内容注入转码的例子有自动广告生成器和用户追踪系统
设想一下,一个能往途经的每个HTML页面中自动添加广告的广告植入转码器是多么的诱人,当然也很烦人。这类转码操作只能动态进行——它必须即时添加与当前的特定用户有关,或针对特定用户的广告。也可以构建用户追踪系统,在页面中动态增加内容,用于收集用户査看页面和客户端浏览方式的统计信息
【转码与静态预生成的对比】
转码的替代做法是在Web服务器上建立Web页面的不同副本,例如一个是HTML,一个是WML;一个图像分辨率高,一个图像分辨率低;一个有多媒体内容,一个没有。但是,这种方法不是很切合实际,原因很多:某个页面中的任何小改动都会牵扯很多页面,需要很多空间来存储各页面的不同版本,而且使页面编目和Web服务器编程(以提供正确的版本)变得更加困难。有些转码操作,比如广告插入(尤其是定向广告插入),就不能静态实现——因为插入什么广告和请求页面的用户有关
对单一的根页面进行即时转换,是比静态的预生成更容易的解决方案。但这样会在提供内容时增加时延。不过有时候其中一些计算可以由第三方进行,这样就减少了Web服务器上的计算负荷——比如可以由代理或缓存中的外部Agent完成转换
下图显示了在代理缓存中进行的转码
前端学HTTP之内容协商的更多相关文章
- 前端学做 PPT
前端学做 PPT 公司做技术分享.年终总结都需要用到ppt. 要快速.省事的做出高质量的 ppt,一方面需要熟练使用制作 ppt 的工具,另一方面得知道用工具做成什么样子才是好作品.前者比较简单,后者 ...
- 前端学HTTP之网络基础
× 目录 [1]网络 [2]OSI [3]TCP/IP 前面的话 HTTP协议对于前端工程师是非常重要的.我们在浏览网站时,访问的每一个WEB页面都需要使用HTTP协议实现.如果不了解HTTP协议,就 ...
- 前端学HTTP之报文首部
前面的话 首部和方法配合工作,共同决定了客户端和服务器能做什么事情.在请求和响应报文中都可以用首部来提供信息,有些首部是某种报文专用的,有些首部则更通用一些.本文将详细介绍HTTP报文中的首部 结构 ...
- 前端学HTTP之数据传输
× 目录 [1]客户机处理 [2]集线器处理 [3]路由器1处理[4]路由器2处理[5]交换机处理[6]服务器处理[7]反向传输 前面的话 上一篇中,介绍了网络基础.本文将详细介绍客户机在浏览网页ab ...
- 前端学PHP之自定义模板引擎
前面的话 在大多数的项目组中,开发一个Web程序都会出现这样的流程:计划文档提交之后,前端工程师制作了网站的外观模型,然后把它交给后端工程师,它们使用后端代码实现程序逻辑,同时使用外观模型做成基本架构 ...
- 前端学PHP之Smarty模板引擎
前面的话 对PHP来说,有很多模板引擎可供选择,但Smarty是一个使用PHP编写出来的,是业界最著名.功能最强大的一种PHP模板引擎.Smarty像PHP一样拥有丰富的函数库,从统计字数到自动缩进. ...
- 前端学Markdown
前面的话 我个人理解,Markdown就是一个富文本编辑器语言,类似于sass对于css的功能,Markdown也可以叫做HTML预处理器,只不过它是一门轻量级的标记语言,可以更简单的实现HTML ...
- 前端学HTTP之web攻击技术
前面的话 简单的HTTP协议本身并不存在安全性问题,因此协议本身几乎不会成为攻击的对象.应用HTTP协议的服务器和客户端,以及运行在服务器上的Web应用等资源才是攻击目标.本文将详细介绍攻击web站点 ...
- 前端学HTTP之日志记录
前面的话 几乎所有的服务器和代理都会记录下它们所处理的HTTP事务摘要.这么做出于一系列的原因:跟踪使用情况.安全性.计费.错误检测等等.本文将谥介绍日志记录 记录内容 大多数情况下,日志的记录出于两 ...
随机推荐
- 记一次debug记录:Uncaught SyntaxError: Unexpected token ILLEGAL
在使用FIS3搭建项目的时候,遇到了一些问题,这里记录下. 这里是发布搭建代码: // 代码发布时 fis.media('qa') .match('*.{js,css,png}', { useHash ...
- 故障重现, JAVA进程内存不够时突然挂掉模拟
背景,服务器上的一个JAVA服务进程突然挂掉,查看产生了崩溃日志,如下: # Set larger code cache with -XX:ReservedCodeCacheSize= # This ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- TODO:Laravel 内置简单登录
TODO:Laravel 内置简单登录 1. 激活Laravel的Auth系统Laravel 利用 PHP 的新特性 trait 内置了非常完善好用的简单用户登录注册功能,适合一些不需要复杂用户权限管 ...
- ExtJS 4.2 Date组件扩展:添加清除按钮
ExtJS中除了提供丰富的组件外,我们还可以扩展他的组件. 在这里,我们将在Date日期组件上添加一个[清除]按钮,用于此组件已选中值的清除. 目录 1. Date组件介绍 2. 主要代码说明 3. ...
- ASP.NET Core框架揭秘(持续更新中…)
之前写了一系列关于.NET Core/ASP.NET Core的文章,但是大都是针对RC版本.到了正式的RTM,很多地方都发生了改变,所以我会将之前发布的文章针对正式版本的.NET Core 1.0进 ...
- 使用cmake自动构建工程
公司引擎是用cmake根据目标平台来构建工程的,刚接触的时候深深体会到cmake的方便:如果目标平台是windows,它可以帮你自动构建出vs工程:如果是安卓,自动构建出eclipse工程,如果是IO ...
- AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking
我们应该看到过很多类似这样的例子:某个控件拥有加载网络图片的能力.但这究竟是怎么做到的呢?看完这篇文章就明白了. 前言 这篇我们会介绍 AFNetworking 中的3个UIKit中的分类.UIAct ...
- 开发者最爱的Firebug停止更新和维护
近日,Firebug团队在其官网上宣布,Firebug将不再继续开发和维护,并邀请大家使用Firefox的内置开发工具. Firebug最初是2006年1月由Joe Hewitt编写, ...
- 反应器(Reactor)和主动器(Proactor)
网络方面用的比较多的库是libevent和boost.asio,两者都是跨平台的.其中libevent是基于Reactor实现的,而boost.asio是基于Proactor实现的.Reactor和P ...