<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; HTTP</title>
	<atom:link href="https://blog.gmem.cc/tag/http/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Sun, 12 Apr 2026 02:07:19 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>AsyncHttpClient知识集锦</title>
		<link>https://blog.gmem.cc/async-http-client-faq</link>
		<comments>https://blog.gmem.cc/async-http-client-faq#comments</comments>
		<pubDate>Fri, 28 Jul 2017 04:01:18 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Network]]></category>
		<category><![CDATA[HTTP]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=15162</guid>
		<description><![CDATA[<p>简介 本文所指的AsyncHttpClient是指基于Netty的一个开源项目，该项目基于Java8编写，用于简化HTTP客户端的开发。该项目还支持WebSocket协议。 要使用该AsyncHttpClient，引入以下Maven依赖： [crayon-69db647228061037681062/] 代码示例 配置客户端 [crayon-69db647228066780797186/] 异步GET请求 [crayon-69db647228069239454963/] ListenableFuture是java.util.concurrent.Future的子类型，你可以使用Java8并发框架提供的任何特性，例如： [crayon-69db64722806b377435844/] 查询参数 [crayon-69db64722806d139792308/] 上传文件 [crayon-69db64722806f339573170/] 发送JSON请求  [crayon-69db647228071714911187/] 响应生命周期控制 [crayon-69db647228073904689686/] WebSocket [crayon-69db647228076019427514/] &#160; <a class="read-more" href="https://blog.gmem.cc/async-http-client-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/async-http-client-faq">AsyncHttpClient知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">简介</span></div>
<p>本文所指的AsyncHttpClient是指基于Netty的一个<a href="https://github.com/AsyncHttpClient/async-http-client">开源项目</a>，该项目基于Java8编写，用于简化HTTP客户端的开发。该项目还支持WebSocket协议。</p>
<p>要使用该AsyncHttpClient，引入以下Maven依赖：</p>
<pre class="crayon-plain-tag">&lt;dependency&gt;
   &lt;groupId&gt;org.asynchttpclient&lt;/groupId&gt;
   &lt;artifactId&gt;async-http-client&lt;/artifactId&gt;
   &lt;version&gt;LATEST_VERSION&lt;/version&gt;
&lt;/dependency&gt;</pre>
<div class="blog_h2"><span class="graybg">代码示例</span></div>
<div class="blog_h3"><span class="graybg">配置客户端</span></div>
<pre class="crayon-plain-tag">AsyncHttpClientConfig cf = new DefaultAsyncHttpClientConfig
    .Builder()
    // 设置代理服务器
    .setProxyServer(new ProxyServer.Builder("127.0.0.1", 8087))
    .build();

// 为客户端提供配置项
AsyncHttpClient http = new DefaultAsyncHttpClient(cf);</pre>
<div class="blog_h3"><span class="graybg">异步GET请求</span></div>
<pre class="crayon-plain-tag">ListenableFuture&lt;Response&gt; future =
http.prepareGet( "http://ip:port/path" ).execute( new AsyncCompletionHandler&lt;Response&gt;() {

    @Override
    public Response onCompleted( Response response ) throws Exception {
        String resp = response.getResponseBody();
        return response;
    }

    @Override
    public void onThrowable( Throwable t ) {
        // Something wrong happened.
    }
} );</pre>
<p>ListenableFuture是java.util.concurrent.Future的子类型，你可以使用Java8并发框架提供的任何特性，例如：</p>
<pre class="crayon-plain-tag">future.get();   // 阻塞等待处理完毕

// 转换为CompletableFuture
CompletableFuture&lt;Response&gt; promise = future.toCompletableFuture();
promise.exceptionally(t -&gt; { /* 当错误发生时 */  } )
       .thenApply(resp -&gt; { /*  处理请求 */ return resp; });</pre>
<div class="blog_h3"><span class="graybg">查询参数</span></div>
<pre class="crayon-plain-tag">http.preparePost( "http://ip:port/path" )
    // 添加请求参数
    .addQueryParam( "name", alex )
    .addQueryParam( "feature", "0" )
    .execute()</pre>
<div class="blog_h3"><span class="graybg">上传文件</span></div>
<pre class="crayon-plain-tag">http.preparePost( "http://ip:port/path" )
    // 上传多个文件
    .addBodyPart( new FilePart( "imageDatas", new ClassPathResource( "alex1.jpg" ).getFile() ) )
    .addBodyPart( new FilePart( "imageDatas", new ClassPathResource( "alex2.jpg" ).getFile() ) )
    .execute()</pre>
<div class="blog_h3"><span class="graybg">发送JSON请求</span> </div>
<pre class="crayon-plain-tag">http.preparePost( "http://192.168.0.89:9090/pems/stpush" )
    .addHeader( "Content-Type", "application/json; charset=utf-8" )
    .setBody( json.getBytes( "utf-8" ) )
    .execute().get();</pre>
<div class="blog_h3"><span class="graybg">响应生命周期控制</span></div>
<pre class="crayon-plain-tag">ByteArrayOutputStream bytes = new ByteArrayOutputStream();
String resp = http.prepareGet( "http://www.example.com/" ).execute( new AsyncHandler&lt;String&gt;() {

    public void onThrowable( Throwable t ) {

    }

    public State onBodyPartReceived( HttpResponseBodyPart bodyPart ) throws Exception {
        // 接收到一个上传文件后
        bytes.write( bodyPart.getBodyPartBytes() );
        return State.CONTINUE;
    }

    public State onStatusReceived( HttpResponseStatus responseStatus ) throws Exception {
        // 仅仅获得响应码
        int statusCode = responseStatus.getStatusCode();
        return State.ABORT;
    }

    public State onHeadersReceived( HttpHeaders headers ) throws Exception {
        // 仅仅接收响应头
        return State.ABORT;
    }

    public String onCompleted() throws Exception {
        // 给出此回调的返回值
        return bytes.toString( "UTF-8" );
    }
} ).get();</pre>
<div class="blog_h3"><span class="graybg">WebSocket</span></div>
<pre class="crayon-plain-tag">WebSocket websocket = http.prepareGet( "http://wsep" )
      .execute( new WebSocketUpgradeHandler.Builder().addWebSocketListener(
              new WebSocketTextListener() {
                  @Override
                  public void onMessage( String message ) {
                      // 接收到消息时的回调
                  }

                  @Override
                  public void onOpen( WebSocket websocket ) {
                      // WebSocket打开时的回调
                      websocket.sendTextMessage( "..." ).sendMessage( "..." );
                  }

                  @Override
                  public void onClose( WebSocket websocket ) {
                      // 关闭时的回调 
                  }

                  @Override
                  public void onError( Throwable t ) {
                  }
              } ).build() ).get();</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/async-http-client-faq">AsyncHttpClient知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/async-http-client-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTTP协议学习笔记</title>
		<link>https://blog.gmem.cc/http-study-note</link>
		<comments>https://blog.gmem.cc/http-study-note#comments</comments>
		<pubDate>Sun, 11 Aug 2013 04:09:52 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Network]]></category>
		<category><![CDATA[HTTP]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=6301</guid>
		<description><![CDATA[<p>基本知识 HTTP是一种请求/应答模式（Request–Response pattern）的应用层协议 HTTP基于TCP协议进行传输 URL与资源 URI是一类通用的资源标识符，由两个主要的子集URL和URN构成。 URL（统一资源定位符）用于表示浏览器寻找信息时所需的资源位置。 URL的语法 大多数URL方案（Scheme）的语法格式如下： [crayon-69db647228815787654999/] 各部分说明如下： 组件  说明  方案（scheme） 指定访问服务器以获取资源时要使用哪种协议 用户（user） 某些方案访问资源时需要的用户名 密码（password） 某些方案访问资源时需要的密码 主机（host） 资源宿主服务器的主机名或IP地址 端口（port） <a class="read-more" href="https://blog.gmem.cc/http-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/http-study-note">HTTP协议学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">基本知识</span></div>
<ol>
<li>HTTP是一种请求/应答模式（Request–Response pattern）的应用层协议</li>
<li>HTTP基于TCP协议进行传输</li>
</ol>
<div class="blog_h1"><span class="graybg">URL与资源</span></div>
<p>URI是一类通用的资源标识符，由两个主要的子集URL和URN构成。 URL（统一资源定位符）用于表示浏览器寻找信息时所需的资源位置。</p>
<div class="blog_h2"><span class="graybg">URL的语法</span></div>
<p>大多数URL方案（Scheme）的语法格式如下：</p>
<pre class="crayon-plain-tag">[scheme]://[user]:[password]@[host]:[port]/[path];]params]?[query]#[frag]</pre>
<p>各部分说明如下：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">组件 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>方案（scheme）</td>
<td>指定访问服务器以获取资源时要使用哪种协议</td>
</tr>
<tr>
<td>用户（user）</td>
<td>某些方案访问资源时需要的用户名</td>
</tr>
<tr>
<td>密码（password）</td>
<td>某些方案访问资源时需要的密码</td>
</tr>
<tr>
<td>主机（host）</td>
<td>资源宿主服务器的主机名或IP地址</td>
</tr>
<tr>
<td>端口（port）</td>
<td>资源宿主服务器正在监听的端口号，很多方案都有默认端口号</td>
</tr>
<tr>
<td>路径（path）</td>
<td>服务器上资源的本地名，由一个斜杠（/）将其与前面的URL组件分隔开来。路径组件的语法与服务器和方案有关</td>
</tr>
<tr>
<td>参数（params）</td>
<td>某些方案会用这个组件来指定输入参数。格式为<span style="background-color: #c0c0c0;">名/值对;</span>。URL中可以包含多个参数字段，每个参数字段尾部附加分号 </td>
</tr>
<tr>
<td>查询（query）</td>
<td>某些方案会用这个组件传递参数以激活应用程序。査询组件的内容没有通用格式。用字符<span style="background-color: #c0c0c0;">?</span>将其与URL的其它组件分隔开</td>
</tr>
<tr>
<td>片段（frag） </td>
<td>一小片或一部分资源的名字。该组件在客户端内部使用，不会传递给服务器，使用字符<span style="background-color: #c0c0c0;">#</span>与其它URL组件分隔</td>
</tr>
</tbody>
</table>
<p>下面是一些合法URL的例子：</p>
<pre class="crayon-plain-tag">https://blog.gmem.cc
https://blog.gmem.cc:442

ftp://alex:pswd@ftp.gmem.cc/public/readme.txt
ftp://anonymous@ftp.gmem.cc/public/readme.txt

mailto:me@gmem.cc

jdbc:oracle:thin:@//dc.gmem.cc:1521/gmem

file://D:/Programs/Scripts/autoproxy.pac</pre>
<div class="blog_h2"><span class="graybg">相对URL</span></div>
<p>URL有两种方式：绝对的和相对的URL。相对URL是不完整的，要获取其指向的资源，必须找到其<span style="background-color: #c0c0c0;">基础URL</span>：</p>
<ol>
<li>如果HTML文档包含 标签，那么使用该标签指定的URL</li>
<li>如果没有声明上述标签，该HTML文档本身所属的URL将用来分析基础URL</li>
</ol>
<div>如果基础URL指定为<pre class="crayon-plain-tag">http://gmem.cc/codes/</pre> 则相对URL<pre class="crayon-plain-tag">./index.html</pre> 对应绝对URL为：<pre class="crayon-plain-tag">http://gmem.cc/codes/index.html</pre> </div>
<div>如果基础URL指定为<pre class="crayon-plain-tag">http://gmem.cc/codes</pre> 则相对URL<pre class="crayon-plain-tag">./index.html</pre> 对应绝对URL为：<pre class="crayon-plain-tag">http://gmem.cc/index.html</pre> </div>
<div> </div>
<div>注意<span style="background-color: #c0c0c0;">点号.和双点号..</span>，分别代表当前目录和上级目录。<span style="background-color: #c0c0c0;">../..</span>则代表上级目录的上级目录。</div>
<div class="blog_h2"><span class="graybg">URL的字符编码</span></div>
<p>为安全的处理特殊字符，URL引入了转义语法，转义字符以<span style="background-color: #c0c0c0;">百分号%</span>开头，后面跟随两位十六进制的数字。常用特殊字符的编码如下：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">字符</td>
<td style="text-align: center;">ASCII编码 </td>
<td style="text-align: center;">示例 </td>
</tr>
</thead>
<tbody>
<tr>
<td>~</td>
<td>126(0x7E)</td>
<td rowspan="3"> http://gmem.cc/HTTP%20faq.html </td>
</tr>
<tr>
<td>空格</td>
<td>32(0x20)</td>
</tr>
<tr>
<td>%</td>
<td>37(0x25)</td>
</tr>
</tbody>
</table>
<p>对于非ASCII字符的URL编码，标准规范没有规定如何处理，因此不同浏览器的实现有所不同，这导致服务器端难以正确的解码URL中包含的字符。对于这些字符，可移植性最好的方法是使用JavaScript将其转换为Unicode编码，函数<span style="background-color: #c0c0c0;">escape/unescape、encodeURI/decodeURI</span>用于完成Unicode编码处理：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;"> Unicode字符</td>
<td style="text-align: center;">Unicode编码</td>
<td style="text-align: center;">URL编码</td>
</tr>
</thead>
<tbody>
<tr>
<td>绿</td>
<td>\u7EFF</td>
<td>%u7EFF</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">HTTP报文</span></div>
<div class="blog_h2"><span class="graybg">报文格式</span></div>
<p>HTTP报文由三个部分组成：</p>
<ol>
<li>起始行（Start line）：对报文进行简短的描述，分为请求行、响应行两种</li>
<li>首部（Header）：包含若干报文属性。以一个<span style="background-color: #c0c0c0;">空白行结尾</span></li>
<li>主体（Entity body）：可选的数据主体部分</li>
</ol>
<p>其中起始行、首部是<span style="background-color: #c0c0c0;">逐行</span>的ASCII文本，每行使用\r\n两个连续字符（CRLF）进行终止。主体部分则不同，可以是<span style="background-color: #c0c0c0;">文本数据，也可以是二进制</span>数据。</p>
<p>每个首部条目具有一定的格式：<pre class="crayon-plain-tag">[名称]: [空白符][值][CRLF]</pre> ，如果首部的值过长，需要跨行的，那么非首行以<span style="background-color: #c0c0c0;">至少一个空白字符</span>开头。值中包含多个项目的，通常使用<span style="background-color: #c0c0c0;">逗号分隔</span>。不同首部字段可以按任意<span style="background-color: #c0c0c0;">顺序排列</span>。有些首部<span style="background-color: #c0c0c0;">可以出现多次</span>。</p>
<p>HTTP报文分为请求报文、响应报文两类。</p>
<p>请求报文的格式为：</p>
<pre class="crayon-plain-tag">[method] [request-URL] [version]
[headers] 

[entity-body]
[last-content]</pre>
<p>响应报文的格式为：</p>
<pre class="crayon-plain-tag">[version] [status] [reason-phrase]
[headers]

[entity-body]
[last-content]</pre>
<p>上述格式中各组件的描述如下：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 250px; text-align: center;"> 组件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>方法（method）</td>
<td>客户端希望服务器对资源执行的动作。参考下面的常用HTTP方法小节</td>
</tr>
<tr>
<td>请求URL（request-URL）</td>
<td>客户端请求的资源的完整URL</td>
</tr>
<tr>
<td>版本（version）</td>
<td>报文所使用的HTTP版本，格式为：<pre class="crayon-plain-tag">HTTP/[major].[minor]</pre> 其中主、次版本号均为整数</td>
</tr>
<tr>
<td>状态码（status-code）</td>
<td>由3位数字组成，描述了请求过程中所发生的情况。每个状态码的第1位数字用于描述状态的一般类别</td>
</tr>
<tr>
<td>原因短语（reason-phrase）</td>
<td>数字状态码的可读版本</td>
</tr>
<tr>
<td>首部（header）</td>
<td>
<p>可以有零个或多个首部，<span style="background-color: #c0c0c0;">每个首部都包含一个名字，后面跟着一个冒号（:），然后是一个可选的空格，接着是一个值，最后是一个CRLF。</span>首部由一个空行 (CRLF）结束</p>
<p>有些HTTP版 本，比如HTTP/1.1，要求有效的请求或响应报文中必须包含特定的首部</p>
</td>
</tr>
<tr>
<td>实体主体部分（entity-body）</td>
<td>包含一个由任意数据组成的数据块（chunks of data）。该部分是可选的</td>
</tr>
<tr>
<td>尾部内容（last content）</td>
<td>实体部分的特殊内容，可以包含一些追加的头部（trailing headers）或者标注请求/响应的结束</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">常用HTTP方法</span></div>
<p>HTTP方法仅对请求报文有意义。并非所有Web服务器支持所有的方法，Web服务器也可以制定自己的扩展方法。</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">HTTP方法 </td>
<td style="text-align: center;">说明 </td>
<td style="text-align: center;">是否包含主体</td>
</tr>
</thead>
<tbody>
<tr>
<td>GET</td>
<td>从服务器获取一份文档</td>
<td>否</td>
</tr>
<tr>
<td>HEAD</td>
<td>只从服务器获取文档的首部</td>
<td>是</td>
</tr>
<tr>
<td>POST</td>
<td>向服务器发送需要处理的数据</td>
<td>是</td>
</tr>
<tr>
<td>PUT</td>
<td>将请求的主体部分存储在服务器上</td>
<td>否</td>
</tr>
<tr>
<td>TRACE</td>
<td>对可能经过代理服务器传送到服务器上去的报文进行追踪</td>
<td>否</td>
</tr>
<tr>
<td>OPTIONS</td>
<td>决定可以在服务器上执行哪些方法</td>
<td>否</td>
</tr>
<tr>
<td>DELETE</td>
<td>从服务器上删除一份文档</td>
<td>否</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">状态码</span></div>
<p>方法是用来告诉服务器做什么事情的，状态码则用来告诉客户端，发生了什么事情。状态码的分类如下：</p>
<p><img class="aligncenter size-full wp-image-6333" src="https://blog.gmem.cc/wp-content/uploads/2013/08/httpcode.jpg" alt="httpcode" width="90%" /></p>
<p>常见的状态码如下表：</p>
<p><img class="aligncenter size-full wp-image-6399" src="https://blog.gmem.cc/wp-content/uploads/2013/08/httpcodes.png" alt="httpcodes" width="90%" /></p>
<div class="blog_h2"><span class="graybg">报文首部</span></div>
<div class="blog_h3"><span class="graybg">首部分类</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 首部类型</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>通用首部 </td>
<td>请求、响应报文都可以出现的首部。提供了报文最基本的信息</td>
</tr>
<tr>
<td>请求首部 </td>
<td>提供关于请求的更多信息 </td>
</tr>
<tr>
<td>响应首部</td>
<td>提供关于响应的更多信息</td>
</tr>
<tr>
<td>实体首部</td>
<td>与HTTP报文的载荷（Payload）部分有关，可以描述报文体的尺寸、内容或者描述资源本身</td>
</tr>
<tr>
<td>扩展首部</td>
<td>非标准化的首部</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">通用首部</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">名称 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>Connection</td>
<td>允许客户端/服务器指定关于请求/响应连接的选项。举例：<pre class="crayon-plain-tag">Connection: Keep-Alive</pre> </td>
</tr>
<tr>
<td>Keep-Alive</td>
<td>调节Keep-Alive的行为。timeout、max属性在响应首部中发送，分别表示服务器希望连接持续的时间、还能为多少个事务保持连接活动状态。举例：<pre class="crayon-plain-tag">Keep-Alive: max=5, timeout=120</pre> </td>
</tr>
<tr>
<td>Date</td>
<td>提供报文创建的日期、时间戳。举例：<pre class="crayon-plain-tag">Date: Tue, 11 Jul 2000 18:23:51 GMT</pre> </td>
</tr>
<tr>
<td>MIME-Version</td>
<td>MIME类型的版本</td>
</tr>
<tr>
<td>Trailer</td>
<td>如果报文采用了分块传输编码（chunked transfer encoding）方式，可以用这个首部列出位于报文拖挂（trailer)部分的首部集合。举例：<pre class="crayon-plain-tag">Trailer: Date</pre> </td>
</tr>
<tr>
<td>Trasfer-Encoding</td>
<td>报文使用了何种编码方式进行传输。举例：<pre class="crayon-plain-tag">Transfer-Encoding: chunked</pre> </td>
</tr>
<tr>
<td>Upgrade</td>
<td>给出发送端想升级使用的新版本或协议。举例：<pre class="crayon-plain-tag">Upgrade: HTTP/2.0</pre> </td>
</tr>
<tr>
<td>Via</td>
<td>显示报文传输的中间节点（例如代理、网关）。举例：<pre class="crayon-plain-tag">Via: HTTP/1.1 Proxy1, HTTP/1.1 Proxy2</pre> </td>
</tr>
<tr>
<td>Cache-Control</td>
<td>用于随报文发出缓存指令。举例：<pre class="crayon-plain-tag">Cache-Control: max-age=10</pre> 。时间以秒为单位，除了指定max-age，还可以指定：no-store、no-cache、must-revalidate等</td>
</tr>
<tr>
<td>Pragma</td>
<td>另一种发送指令的方式，但不限于缓存指令。应当使用Cache-Control代替之。举例：<pre class="crayon-plain-tag">Pragma: no-cache</pre> 提示服务器应当返回一个刷新后的文档，即使服务器是代理并且已有缓存</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">请求首部</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">名称 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;" colspan="2">信息性请求首部：提供了客户端、请求本身的一些信息</td>
</tr>
<tr>
<td>Client-IP</td>
<td>提供客户端机器的IP地址 </td>
</tr>
<tr>
<td>From </td>
<td>提供客户端的电子邮件地址。举例：<pre class="crayon-plain-tag">From: me@gmem.cc</pre> </td>
</tr>
<tr>
<td>Host</td>
<td>请求发送往的服务器的主机名和端口。举例：<pre class="crayon-plain-tag">Host: blog.gmem.cc</pre> </td>
</tr>
<tr>
<td>Referer</td>
<td>从中发起了当前请求的文档的URL。举例：<pre class="crayon-plain-tag">Referer: www.google.com</pre> </td>
</tr>
<tr>
<td>UA-Color</td>
<td>提供与客户端显示器的显示顔色有关的信息</td>
</tr>
<tr>
<td>UA-CPU</td>
<td>给出客户端CPU的类型或制造商</td>
</tr>
<tr>
<td>UA-Disp</td>
<td>提供与客户端显示器能力有关的信息</td>
</tr>
<tr>
<td>UA-OS</td>
<td>给出运行在客户端机器上的操作系统名称及版本</td>
</tr>
<tr>
<td>UA-Pixels</td>
<td>提供了客户端显示器的像素信息</td>
</tr>
<tr>
<td>User-Agent</td>
<td>将发起请求的应用程序名称告知服务器。举例：<pre class="crayon-plain-tag">User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)</pre> </td>
</tr>
<tr>
<td colspan="2">Accept首部：将客户端的偏好、能力告知服务器，以便服务器生成定制化的信息</td>
</tr>
<tr>
<td>Accept </td>
<td>告诉服务器，客户端能够支持哪些媒体类型。举例：<pre class="crayon-plain-tag">Accept: text/html, image/*</pre> </td>
</tr>
<tr>
<td>Accept-Charset</td>
<td>告诉服务器，客户端能够支持哪些字符集 。举例：<pre class="crayon-plain-tag">Accept-Charset: iso-8859-1</pre> </td>
</tr>
<tr>
<td>Accept-Encoding</td>
<td>告诉服务器，客户端能够支持哪些编码方式。举例：<pre class="crayon-plain-tag">Accept-Encoding: gzip, compress</pre> </td>
</tr>
<tr>
<td>Accept-Language </td>
<td>告诉服务器，客户端能够支持哪些语言。举例：<pre class="crayon-plain-tag">Accept-Language: en, fr</pre> </td>
</tr>
<tr>
<td>TE</td>
<td>告诉服务器，客户端能够使用哪些扩展传输编码。举例：<pre class="crayon-plain-tag">TE: trailers</pre> </td>
</tr>
<tr>
<td colspan="2">条件请求首部：要求服务器在进行响应之前，确保条件为真</td>
</tr>
<tr>
<td>Expect</td>
<td>允许客户端列出所要求的服务器行为。举例：<pre class="crayon-plain-tag">Expect: 100-continue</pre> </td>
</tr>
<tr>
<td>If-Match</td>
<td>如果实体标记与文档当前的实体标记相匹配，就获取这份文档。举例：<pre class="crayon-plain-tag">If-Match: entity_tag001</pre> </td>
</tr>
<tr>
<td>If-Modified-Since</td>
<td>除非在某个指定的日期之后资源被修改过，否则就限制这个请求（服务器应当返回304应答）。举例：<pre class="crayon-plain-tag">If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT</pre> </td>
</tr>
<tr>
<td>If-None-Match</td>
<td>如果提供的实体标记与当前文档的实体标记不相符，就获取文档。举例：<pre class="crayon-plain-tag">If-None-Match: entity_tag001</pre> </td>
</tr>
<tr>
<td>If-Range</td>
<td>允许对文档的某个范围进行条件请求。举例：<pre class="crayon-plain-tag">If-Range: entity_tag001</pre></td>
</tr>
<tr>
<td>If-Unmodified-Since</td>
<td>除非在某个指定日期之后资源没有被修改过，否则就限制这个请求。举例：<pre class="crayon-plain-tag">If-Unmodified-Since: Tue, 11 Jul 2000 18:23:51 GMT</pre> </td>
</tr>
<tr>
<td>Range</td>
<td>如果服务器支持范围请求，就请求资源的指定范围。举例：<pre class="crayon-plain-tag">Range: bytes=100-599</pre> </td>
</tr>
<tr>
<td colspan="2">与安全相关的请求首部</td>
</tr>
<tr>
<td>Authorization</td>
<td>包含了客户端提供给服务器，以便对其自身进行认证的数据。举例：<pre class="crayon-plain-tag">Authorization: [credentials]</pre> </td>
</tr>
<tr>
<td>Cookie</td>
<td>客户端可以用它向服务器传送一个令牌（例如Tomcat的JSESSIONID）</td>
</tr>
<tr>
<td>Cookie2</td>
<td>用来说明请求端支持的Cookie版本</td>
</tr>
<tr>
<td colspan="2">与代理相关的请求首部</td>
</tr>
<tr>
<td>Max-Forwards</td>
<td>在通往服务器的路径上，将请求转发给其他代理或网关的最大次数（与TRACE方法一同使用）。举例：<pre class="crayon-plain-tag">Max-Forwards: 3</pre> </td>
</tr>
<tr>
<td>Proxy-Authorization</td>
<td>与Authorization首部功能相同，伹这个首部是在与代理进行认证时使用的</td>
</tr>
<tr>
<td>Proxy-Connection</td>
<td>与Connection首部功能相同，但这个首部是在与代理立连接时使用的</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">响应首部</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">名称 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">信息性响应首部</td>
</tr>
<tr>
<td>Age</td>
<td>响应的年龄，即响应是何时创建的，如果是从代理缓存获取的响应，可能Age早于请求时间。举例：<pre class="crayon-plain-tag">Age: 2147483648</pre> </td>
</tr>
<tr>
<td>Public</td>
<td>服务器为其资源支持的请求方法列表 </td>
</tr>
<tr>
<td>Retry-After</td>
<td>如果资源不可用的话，在此日期或时间重试。举例：<pre class="crayon-plain-tag">Retry-After: 60</pre> </td>
</tr>
<tr>
<td>Server</td>
<td>服务器应用程序软件的名称和版本。举例：<pre class="crayon-plain-tag">Server: Microsoft-IIS/5.0</pre> </td>
</tr>
<tr>
<td>Title</td>
<td>HTML文档的标题</td>
</tr>
<tr>
<td>Warning</td>
<td>比原因短语中E详细一些的警告报文</td>
</tr>
<tr>
<td colspan="2">协商首部：HTTP1.1可以允许服务器与客户端对资源进行协商</td>
</tr>
<tr>
<td>Accept -Ranges</td>
<td>对此资源来说，服务器可接受的范围类型。举例：<pre class="crayon-plain-tag">Accept-Ranges: none</pre> </td>
</tr>
<tr>
<td>Vary </td>
<td>服务器査看的其他首部的列表，可能会使响应发生变化，也就是说，这是 一个首部列表，服务器会根据这些首部的内容挑选出最适合的资源版本发送给客户端</td>
</tr>
<tr>
<td colspan="2">与安全相关的响应首部</td>
</tr>
<tr>
<td>Proxy-Authenticate</td>
<td>来自代理的对客户端的质询（challenges）列表。举例：<pre class="crayon-plain-tag">Proxy-Authenticate: Basic realm-admin</pre> </td>
</tr>
<tr>
<td>Set-Cookie</td>
<td>可以在客户端设置一个令牌，以便服务器对客户端进行标识。举例：<br />
<pre class="crayon-plain-tag">#该Cookie将发送给任何*.gmem.cc的站点
Set-cookie: JSESSIONID="2c85d5b9"; domain="gmem.cc"
#该Cookie将发送给任何*.gmem.cc下所有的/index/*页面
Set-cookie: JSESSIONID="2c85d5b9"; domain="gmem.cc"; path="/index/"</pre>
</td>
</tr>
<tr>
<td>Set-Cookie2</td>
<td>与 Set-Cookie 类似，来自RFC 2965</td>
</tr>
<tr>
<td>WWW-Authenticate</td>
<td>来自服务器的对客户端的质询列表</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">实体首部</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">名称 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>Allow</td>
<td>列出可以对此实体执行的请求方法。举例：<pre class="crayon-plain-tag">Allow: GET, HEAD</pre> </td>
</tr>
<tr>
<td>Location</td>
<td>对于一个已经移动的资源，用于重定向请求者至另一个位置。举例：<pre class="crayon-plain-tag">Location: http://gmem.cc/redirect.php</pre> </td>
</tr>
<tr>
<td colspan="2">内容相关的实体头部</td>
</tr>
<tr>
<td>Content-Base</td>
<td>解析主体（Body）中的相对URL时使用的基础URL     </td>
</tr>
<tr>
<td>Content-Encoding</td>
<td>主体的编码方式。举例：<pre class="crayon-plain-tag">Content-Encoding: gzip, compress</pre> </td>
</tr>
<tr>
<td>Content-Language</td>
<td>理解主体时最适宜使用的自然语言。举例：<pre class="crayon-plain-tag">Content-Language: en</pre> </td>
</tr>
<tr>
<td>Content-Length</td>
<td>主体的长度或尺寸。举例：<pre class="crayon-plain-tag">Content-Length: 9990</pre> 。该首部很重要，如果连接出现故障，主体被截断，该首部可以用来判断内容是否完整</td>
</tr>
<tr>
<td>Content-Location</td>
<td>资源的实际位置。举例：<pre class="crayon-plain-tag">Content-Location: http://gmem.cc/page.php</pre> </td>
</tr>
<tr>
<td>Content-MD5</td>
<td>主体的MD5校验和。举例：<pre class="crayon-plain-tag">Content-MD5: [md5-digest]</pre> </td>
</tr>
<tr>
<td>Content-Range</td>
<td>在整个资源中此实体所处的字节范围。举例：<pre class="crayon-plain-tag">Content-Range: 1001-2000/5000</pre> 最后的5000表示总长度</td>
</tr>
<tr>
<td>Content-Type</td>
<td>主体的内容类型。举例：<pre class="crayon-plain-tag">Content-Type: text/html</pre> </td>
</tr>
<tr>
<td colspan="2">实体缓存相关头部</td>
</tr>
<tr>
<td>ETag</td>
<td>与该实体关联的实体标记（Entity tag），对于可以使用多种URL请求的资源，ETag可以用于确定实际被发送的资源是否为同一资源。举例：<pre class="crayon-plain-tag">ETag: b38b9-17dd-367c5dcd</pre> </td>
</tr>
<tr>
<td>Expires</td>
<td>到什么时间（一个绝对时间）为止，实体将过期，应当从其原始出处重新获取。举例：<pre class="crayon-plain-tag">Expires: Tue, 11 Jul 2000 18:23:51 GMT</pre> </td>
</tr>
<tr>
<td>Last-Modified</td>
<td>实体最后一次修改的时间。举例：<pre class="crayon-plain-tag">Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT</pre> </td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">底层连接</span></div>
<div class="blog_h2"><span class="graybg">HTTP/HTTPS协议栈</span></div>
<p><img class="aligncenter size-full wp-image-6420" src="https://blog.gmem.cc/wp-content/uploads/2013/08/http-stack.png" alt="http-stack" width="80%" /></p>
<div class="blog_h2"><span class="graybg">性能影响因素</span></div>
<ol>
<li>DNS查找：如果URI对应的主机名在本地DNS缓存中不存在，则需要通过DNS服务器查找，该过程可能消耗几百毫秒——几十秒</li>
<li>TCP握手与连接建立：小的HTTP事物可能在TCP连接的建立上花费超过50%的时间</li>
<li>其它与TCP协议本身有关的性能因素</li>
</ol>
<div class="blog_h2"><span class="graybg">提升HTTP性能</span></div>
<ol>
<li>并行连接，如果同时开启多个TCP连接，可能提升HTML页面的加载性能。浏览器通常启用了并行连接，一般几个</li>
<li>持久化连接：在一个HTTP事物结束后，TCP连接不会关闭，直到服务器或者客户端显式的关闭。持久化连接包括：
<ol>
<li>HTTP/1.0+ keep-alive：实现该特性的客户端通过Connection: Keep-Alive首部请求一条持久化连接。如果服务器支持该请求，则其在响应报文里面也加上Connection: Keep-Alive，否则就表示不支持，客户端将在收到响应后关闭连接</li>
<li>HTTP/1.1 persistent：默认激活，因此除非特别说明，HTTP/1.1的连接默认是持久的。要在事务结束后关闭连接，必须在报文中显式的添加<pre class="crayon-plain-tag">Connection: close</pre> 首部。客户端和服务器可以随时关闭空闲连接</li>
</ol>
</li>
<li>管道化连接：HTTP/1.1允许在持久化连接可选的使用请求管道。在响应到达之前，即可将后续的多条请求放入管道，该机制在高延迟网络中，有利于减少环回时间。客户端不应当以管道化方式处理非幂等操作（例如POST），非幂等操作必须等待前一条的响应到达后再次发送</li>
</ol>
<div class="blog_h1"><span class="graybg">客户端身份识别与Cookie机制</span></div>
<p>以下几种方式可以用于HTTP的身份识别：</p>
<ol>
<li>承载用户身份信息的HTTP首部：多种HTTP首部可以用于承载用户身份相关信息。例如From、User-Agent、Referer、Authorization、Cookie等</li>
<li>客户端IP地址跟踪。该方式的缺点：
<ol>
<li>只能用于识别机器，而不是用户</li>
<li>对于动态分配的IP地址，无法使用</li>
<li>对于位于NAT背后的客户端主机，无法使用</li>
<li>服务器看到的可能是代理的IP地址，而不是真实客户端</li>
<li>IP欺骗（伪造）：在因特网上很容易发生，因此IP身份验证一般只适用于基于信任的内部网络</li>
</ol>
</li>
<li>用户登录，使用HTTP基本认证来识别用户：HTTP提供了一个原生的<span style="background-color: #c0c0c0;">质询/响应（challenge/response)框架</span>，服务器可以发出401响应和WWW-Authentication首部，浏览器将弹出登录框，用户输入身份信息后，通过Base64编码存放到Authorization首部，并重发先前的请求</li>
<li>胖URL，在URL中嵌入识别信息：为每个用户生成特点版本的URL</li>
<li>Cookie，一种功能强大、高效的持久身份识别技术</li>
</ol>
<p> Cookie的相关知识可以参考文章：<a href="/http-faq#cookie-faq">HTTP知识集锦</a></p>
<div class="blog_h1"><span class="graybg">安全HTTP</span></div>
<div class="blog_h2"><span class="graybg">HTTPS简介</span></div>
<p>HTTPS是最流行的HTTP安全形式，由网景首创，主流浏览器均支持。HTTPS方案的URL以“https”开头。</p>
<p>使用HTTPS时，所有数据在通过网络发送之前，都要经过加密。<span style="background-color: #c0c0c0;">HTTPS在HTTP（应用层）下面提供一个传输级的安全层，该安全层通过SSL或者其继任者TLS</span>实现，可以认为<span style="background-color: #c0c0c0;">HTTPS就是在SSL之上传输的HTTP</span>。SSL与TLS非常类似，以下不作区分。</p>
<p>大部分涉及安全处理的编码、解码工作由SSL库完成，HTTPS协议端点只需要将TCP调用转为SSL的I/O调用、再辅以一些配置性的调用即可。</p>
<div class="blog_h2"><span class="graybg">客户端用证书对服务器进行验证的过程</span></div>
<p>通过HTTPS建立安全Web事务后，现代浏览器会自动获得服务器的数字证书，如果没有数字证书，安全连接就会失败，服务器证书包含很多字段，包括：</p>
<ol>
<li>Web站点的名称和主机名</li>
<li>Web站点的公开密钥（公钥）</li>
<li>证书颁发结构的名称</li>
<li>来自证书颁发机构的、对上述公钥的签名</li>
</ol>
<p>浏览器会对颁发机构进行检查，对于权威的颁发机构，其公钥一般已经被浏览器预装。浏览器可以通过此公钥对签名的有效性进行核实。如果浏览器对颁发机构一无所知，则通常会提示用户，询问其是否信任此颁发机构。</p>
<div class="blog_h2"><span class="graybg">HTTPS技术细节</span></div>
<p>HTTPS将数据发送到TCP层之前，会通过SSL对数据进行加密。基于SSL的安全传输机制的建立过程如下：</p>
<ol>
<li>客户端连接到服务器的HTTPS端口（默认443），建立TCP连接</li>
<li>客户端和服务器通过SSL握手，初始化SSL层
<ol>
<li>交换协议版本号</li>
<li>选择一个两端都知晓的密码</li>
<li>对两端的身份进行验证</li>
<li>生成<span style="background-color: #c0c0c0;">临时会话密钥</span>，用于通道加密</li>
</ol>
</li>
<li>客户端将HTTP报文发送给SSL层，SSL层将其加密，然后传递给TCP层并通过网络发送出去</li>
</ol>
<div class="blog_h1"><span class="graybg"><a id="http2"></a>HTTP/2</span></div>
<p>HTTP/2 的目的是通过支持<span style="background-color: #c0c0c0;">完整的请求与响应复用</span>来减少延迟，通过有效<span style="background-color: #c0c0c0;">压缩 HTTP 标头字段</span>将协议开销降至最低，同时增加对<span style="background-color: #c0c0c0;">请求优先级</span>和<span style="background-color: #c0c0c0;">服务器推送</span>的支持。为达成这些目标，HTTP/2 还给我们带来了大量其他协议层面的辅助实现，例如<span style="background-color: #c0c0c0;">新的流控制、错误处理和升级机制</span>。</p>
<p>HTTP/2 没有改动 HTTP 的应用语义。HTTP 方法、状态代码、URI 和标头字段等核心概念一如往常。不过，<span style="background-color: #c0c0c0;">HTTP/2 修改了数据格式化（分帧）以及在客户端与服务器间传输的方式</span>。</p>
<p>各主流客户端对HTTP2的支持情况参考：<a href="https://caniuse.com/#feat=http2">https://caniuse.com/#feat=http2</a></p>
<div class="blog_h2"><span class="graybg">二进制分帧层</span></div>
<p>HTTP/2 所有性能增强的核心在于新的二进制分帧层，它定义了如何封装 HTTP 消息并在客户端与服务器之间传输：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2013/08/http2-frame.png"><img class="aligncenter size-full wp-image-18265" src="https://blog.gmem.cc/wp-content/uploads/2013/08/http2-frame.png" alt="http2-frame" width="843" height="431" /></a></p>
<p>&nbsp;</p>
<p><span style="color: #212121;">这里所谓的“层”，指的是位于套接字接口与应用可见的高级 HTTP API 之间一个经过优化的新编码机制，HTTP 的语义不受影响，不同的是传输期间的编码方式变了。HTTP/2 将所有传输的信息分割为更小的消息和帧，并<span style="background-color: #c0c0c0;">采用二进制格式对它们编码</span>。</span></p>
<div class="blog_h2"><span class="graybg">数据流、消息和帧</span></div>
<p>新的二进制分帧机制改变了客户端与服务器之间交换数据的方式。 为了说明这个过程，我们需要了解 HTTP/2 的三个概念：</p>
<ol>
<li>数据流：已建立的<span style="background-color: #c0c0c0;">连接内的双向字节流</span>，可以承载一条或多条消息</li>
<li>消息：与<span style="background-color: #c0c0c0;">逻辑请求或响应消息对应</span>的完整的一系列帧</li>
<li>帧：HTTP/2 通信的最小单位，每个帧都包含帧头，至少也会标识出当前帧所属的数据流</li>
</ol>
<p>注意：</p>
<ol>
<li>所有通信都在一个 TCP 连接上完成，此连接可以承载任意数量的双向数据流</li>
<li>每个数据流都有一个唯一的标识符和可选的优先级信息，用于承载双向消息</li>
<li>每条消息都是一条逻辑 HTTP 消息（例如请求或响应），包含一个或多个帧</li>
<li>帧是最小的通信单位，承载着特定类型的数据，例如 HTTP 头、消息负载</li>
<li><span style="background-color: #c0c0c0;">来自不同数据流的帧可以交错发送</span>，然后再根据每个帧头的数据流标识符重新组装</li>
</ol>
<div class="blog_h3"><span class="graybg">帧</span></div>
<p>帧是HTTP/2最小通信单元，在一个TCP连接上，同一时刻最多有一个帧在发送。</p>
<p>帧报文的格式（单位bit）：</p>
<pre class="crayon-plain-tag">+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                      ...
+---------------------------------------------------------------+</pre>
<p>字段说明：</p>
<ol>
<li>Length：帧载荷部分的长度</li>
<li>Type： 帧类型。0000 0000为DATA帧，0000 0001为HEADERS帧</li>
<li>Flags：不同类型的帧分别设置，特殊标记位。例如HEADERS帧会设置END_HEADERS标记位来提示头传送完毕</li>
<li>R：无用</li>
<li>Stream Identifier：当前帧所属的流的标识符</li>
<li>Frame Payload帧的载荷部分</li>
</ol>
<div class="blog_h3"><span class="graybg">流</span></div>
<p>HTTP/2支持多路复用，每一路就是一个流。流具有标识符，每个帧都指明它所属的流的标识符。</p>
<p>由于HTTP/2支持Server Push，也就是说服务器可以主动创建流。那么客户端和服务器的流ID如何防止冲突？协议规定，服务器使用偶数，客户端使用奇数。</p>
<p>流的状态转换示意图：</p>
<pre class="crayon-plain-tag">+--------+
                        send PP |        | recv PP
                       ,--------|  idle  |--------.
                      /         |        |         \
                     v          +--------+          v
              +----------+          |           +----------+
              |          |          | send H /  |          |
       ,------| reserved |          | recv H    | reserved |------.
       |      | (local)  |          |           | (remote) |      |
       |      +----------+          v           +----------+      |
       |          |             +--------+             |          |
       |          |     recv ES |        | send ES     |          |
       |   send H |     ,-------|  open  |-------.     | recv H   |
       |          |    /        |        |        \    |          |
       |          v   v         +--------+         v   v          |
       |      +----------+          |           +----------+      |
       |      |   half   |          |           |   half   |      |
       |      |  closed  |          | send R /  |  closed  |      |
       |      | (remote) |          | recv R    | (local)  |      |
       |      +----------+          |           +----------+      |
       |           |                |                 |           |
       |           | send ES /      |       recv ES / |           |
       |           | send R /       v        send R / |           |
       |           | recv R     +--------+   recv R   |           |
       | send R /  `-----------&gt;|        |&lt;-----------'  send R / |
       | recv R                 | closed |               recv R   |
       `-----------------------&gt;|        |&lt;----------------------'
                                +--------+

          send:   endpoint sends this frame
          recv:   endpoint receives this frame

          H:  HEADERS frame (with implied CONTINUATIONs)
          PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
          ES: END_STREAM flag
          R:  RST_STREAM frame</pre>
<p>其中：</p>
<ol>
<li>idle：流目前尚未启用</li>
<li>open：流目前正在使用</li>
<li>closed：流已经使用完成</li>
<li>reserved（local）：本地保留，即将要使用但是尚未使用</li>
<li>reserved（remote）：远端保留，即将要使用但是未使用</li>
<li>half closed（local）：本地半关闭，即将关闭但是尚未关闭</li>
<li>half closed（remote）：远端半关闭，即将关闭但是尚未关闭</li>
</ol>
<p>收到或者发送HEADERS这种类型的帧会使流进入open状态，也就是说，HEADERS一定会建立一个新的流</p>
<p>发送PUSH_PROMISE的那一方会把流保存为reserved（local）的状态，当发送完HEADERS之后会变成half closed（remote）状态</p>
<div class="blog_h2"><span class="graybg">帧类型</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">帧类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">0x0 DATA</td>
<td>关联到帧的任意长度的帧，为了传递一个HTTP请求/应答，可能需要多个DATA帧</td>
</tr>
<tr>
<td class="blog_h3">0x1 HEADERS</td>
<td>用于打开流，附带一些HTTP头片段，可以发送到idle、reserved(local)、open、half-closed(remote）类型的流</td>
</tr>
<tr>
<td class="blog_h3">0x2 PRIORITY</td>
<td>指定发送者建议的、流的优先级</td>
</tr>
<tr>
<td class="blog_h3">0x3 RST_STREAM</td>
<td>用于立即终止流</td>
</tr>
<tr>
<td class="blog_h3">0x4 SETTINGS</td>
<td>携带影响端点通信方式的参数</td>
</tr>
<tr>
<td class="blog_h3">0x5 PUSH_PROMISE</td>
<td>用于在准备发起流之前，通知对方。附带将要创建的流的ID以及一系列头数据</td>
</tr>
<tr>
<td class="blog_h3">0x6 PING</td>
<td>用于度量RTT，测试空闲连接是否还在正常工作</td>
</tr>
<tr>
<td class="blog_h3">0x7 GOAWAY</td>
<td>
<p>用于发起连接的shutdown，或者指示严重的错误状态</p>
<p>使用此帧，可以优雅的禁止建立新流，同时允许旧流正常完毕</p>
</td>
</tr>
<tr>
<td class="blog_h3">0x8 WINDOW_UPDATE</td>
<td>用于实现流控，流控可以在窗口级别、整个连接级别进行</td>
</tr>
<tr>
<td class="blog_h3">0x9 CONTINUATION</td>
<td>用于继续一系列的头部块片段。前置的帧必须是不设置END_HEADERS的HEADERS, PUSH_PROMISE, 或CONTINUATION </td>
</tr>
<tr>
<td class="blog_h3">0xa ALTSVC</td>
<td>允许源的资源在另外一个网络位置访问，可能用不同的协议访问</td>
</tr>
<tr>
<td class="blog_h3">0xc ORIGIN</td>
<td>在指定的连接山，什么源（Origin）是可用的</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">消息结构</span></div>
<p>一个典型的HTTP报文包含请求/响应，组成如下：</p>
<ol>
<li>零或多个HEADERS帧，一个HEADERS帧可能跟着0-N个CONTINUATION帧，以补充单个HEADERS容量不够的情况。大部分情况下一个HEADERS帧就包含了完整的报文头</li>
<li>0-N个DATA数据帧，包含了具体的消息载荷内容</li>
<li>一个HEADERS帧，后面跟随0-N个包含有<span style="background-color: #c0c0c0;">报尾（Trailer-part）</span>的CONTINUATION帧</li>
</ol>
<div class="blog_h2"><span class="graybg">服务器推送</span></div>
<p>HTTP/2服务器可以<span style="background-color: #c0c0c0;">对一个客户端请求发送多个响应</span>。换句话说，除了对最初请求的响应外，服务器还可以向客户端推送额外资源，而无需客户端明确地请求。</p>
<p>所有服务器推送数据流都由 PUSH_PROMISE 帧发起，表明了服务器向客户端推送所述资源的意图，并且需要先于请求推送资源的响应数据传输。</p>
<p>在客户端接收到 PUSH_PROMISE 帧后，它可以根据自身情况（例如有缓存）选择拒绝数据流（通过 RST_STREAM 帧）。</p>
<div class="blog_h2"><span class="graybg">协议协商</span></div>
<p>Google的SPDY协议包含名为NPN的TLS扩展，SPDY被HTTP/2取代后，NPN相应的成为ALPN（应用层协议协商）。</p>
<p>客户端在<span style="background-color: #c0c0c0;">建立 TLS 连接的 Client Hello 握手中，通过 ALPN 扩展列出了自己支持的各种应用层协议</span>，其中HTTP/2 协议名称是 h2。如果服务端支持 HTTP/2，在 Server Hello 中指定 ALPN 的结果为 h2 则协商完成，否则从客户端ALPN的列表中选择一个自己支持的协议。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/http-study-note">HTTP协议学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/http-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTTP知识集锦</title>
		<link>https://blog.gmem.cc/http-faq</link>
		<comments>https://blog.gmem.cc/http-faq#comments</comments>
		<pubDate>Mon, 27 May 2013 02:39:49 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Network]]></category>
		<category><![CDATA[CORS]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[JsonP]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=6288</guid>
		<description><![CDATA[<p>Cookies相关知识 Cookies是一种最常使用的客户端存储机制，Cookies由Netscape在1994年提出，用来为HTTP协议引入状态机制。 为了使基于Cookies的状态机制能够工作，Web服务器会在HTTP响应的Set-Cookie头中设置Cookie，保存用户身份、状态信息，而浏览器在下一次发送HTTP请求的时候，会把先前Web服务器设置的Cookie绑定在HTTP请求头中，发送回去。 由于每次HTTP请求都会重复的发送Cookies，所有Cookies不适合存放过大的数据，事实上，大部分浏览器对每个域名可设置Cookies的大小、都有严格限制，通常在数十个、几KB以内。 使用Cookies的注意点 Cookies的Domain可以设置为IP地址，但是不能使用通配符，例如.0.1这样的Domain是非法的 Domain：域名至少包含两个点号。域设置为.gmem.cc的Cookie可以让gmem.cc域名下所有网站使用，例如blog.gmem.cc Path：限制使用Cookie的网页目录，路径设置为/wp-admin/的Cookie可以让gmem.cc/wp-admin/下所有子URL对应的网页访问 Secure：可以限制Cookie只能通过加密连接（SSL）发送 Expires：可以限制Cookie的生存期，如果不设置，那么浏览器关闭后Cookie即消失 同源策略 所谓源，是指由三元组：协议 + 域名 + 端口确定的唯一网络实体。 修改源 通过JavaScript可以修改当前页面的源，但是只能域名为当前域名的超级域名： [crayon-69db6472293cb549711123/] 修改后，同源策略检查结果将受到影响。使用这种技术，可以让位于子域名中的多个网站方便的交互。 跨源资源访问 原则： <a class="read-more" href="https://blog.gmem.cc/http-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/http-faq">HTTP知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div id="cookie-faq" class="blog_h2"><span class="graybg">Cookies相关知识</span></div>
<p>Cookies是一种最常使用的客户端存储机制，Cookies由Netscape在1994年提出，用来为HTTP协议引入状态机制。</p>
<p>为了使基于Cookies的状态机制能够工作，Web服务器会在HTTP响应的Set-Cookie头中设置Cookie，保存用户身份、状态信息，而浏览器在下一次发送HTTP请求的时候，会把先前Web服务器设置的Cookie绑定在HTTP请求头中，发送回去。</p>
<p>由于每次HTTP请求都会重复的发送Cookies，所有Cookies不适合存放过大的数据，事实上，大部分浏览器对每个域名可设置Cookies的大小、都有严格限制，通常在数十个、几KB以内。</p>
<div class="blog_h3"><span class="graybg">使用Cookies的注意点</span></div>
<ol>
<li>Cookies的Domain可以设置为IP地址，但是不能使用通配符，例如.0.1这样的Domain是非法的</li>
<li>Domain：域名至少包含两个点号。域设置为.gmem.cc的Cookie可以让gmem.cc域名下所有网站使用，例如blog.gmem.cc</li>
<li>Path：限制使用Cookie的网页目录，路径设置为/wp-admin/的Cookie可以让gmem.cc/wp-admin/下所有子URL对应的网页访问</li>
<li>Secure：可以限制Cookie只能通过加密连接（SSL）发送</li>
<li>Expires：可以限制Cookie的生存期，如果不设置，那么浏览器关闭后Cookie即消失</li>
</ol>
<div class="blog_h2"><span class="graybg">同源策略</span></div>
<p>所谓源，是指由三元组：协议 + 域名 + 端口确定的唯一网络实体。</p>
<div class="blog_h3"><span class="graybg">修改源</span></div>
<p>通过JavaScript可以修改当前页面的源，但是只能域名为当前域名的超级域名：</p>
<pre class="crayon-plain-tag">document.domain = "gmem.cc";</pre>
<p>修改后，同源策略检查结果将受到影响。使用这种技术，可以让位于<span style="background-color: rgb(192, 192, 192);">子域名中的多个网站方便的交互</span>。</p>
<div class="blog_h3"><span class="graybg">跨源资源访问</span></div>
<p>原则：</p>
<ol>
<li>通常允许跨域执行写操作：例如链接、重定向、表单提交</li>
<li>通常允许跨域资源嵌入</li>
<li>通常不允许跨域读操作：例如Ajax请求</li>
</ol>
<p>其中第一条，为CSRF（跨站请求伪造，Cross-site request forgery）埋下隐患。攻击者可能引导用户访问一个恶意站点，而此恶意站点包含了修改敏感信息的表单，此表单自动跨源发送，伪造用户身份篡改信息。可以使用不可测的CSRF标记来防止跨域写。</p>
<p>第二条，可以嵌入的资源包括：</p>
<ol>
<li>script标签</li>
<li>link标签，嵌入css。CSS的跨域需要一个设置正确的Content-Type消息头</li>
<li>img标签</li>
<li>video、audio标签</li>
<li>object、embed、applet插件</li>
<li>@font-face引入的字体。某些浏览器要求同源字体</li>
<li>frame、iframe。站点可以使用<pre class="crayon-plain-tag">X-Frame-Options</pre> 头禁止这类资源嵌入</li>
</ol>
<p>你可以使用CORS、JsonP等技术允许来进行跨源的资源共享。</p>
<div class="blog_h2"><span class="graybg">JsonP相关知识</span></div>
<p>JsonP即JSON with Padding，其实与JSON没有直接关系，是一种跨站资源共享的方式。</p>
<p>由于浏览器的同源策略，blog.gmem.cc一般是无法与诸如soulspark.im之类的其它网站进行通信的，但是HTTP的<pre class="crayon-plain-tag">&lt;script&gt;</pre> 脚本是一个例外，例如此脚本，可以动态的从其它网站上获取信息。</p>
<p>假设blog.gmem.cc/postinfo/{postid}提供一个获取文章信息的RESTful服务，其返回值是text/json格式，示例如下：</p>
<pre class="crayon-plain-tag">{
    "post_author" : "alex",
    "post_title"  : "HTTP协议知识集锦"
}</pre>
<p> 那么soulspark.im的某个页面如何才能访问并获取某篇文章的信息呢？</p>
<p>如果直接使用Ajax请求访问，同源策略可能导致访问被禁止，那么如果使用<pre class="crayon-plain-tag">&lt;script&gt;</pre> 脚本，上述返回值并不能被soulspark.im的脚本提取并使用。</p>
<p>要支持JsonP，blog.gmem.cc必须改造postinfo服务，允许其传入一个“Padding”参数，该参数由调用者soulspark.im指定，通常是一个JavaScript函数的名称，改造后的postinfo服务的URL示例为：<pre class="crayon-plain-tag">blog.gmem.cc/postinfo/{postid}?jsonp=decodePost</pre> ，而返回值则是如下形式的脚本：</p>
<pre class="crayon-plain-tag">//decodePost即所谓Padding，由客户端提供的简短字符串，通常就是所谓“回调函数”
decodePost(
    {
        "post_author" : "alex",
        "post_title"  : "HTTP协议知识集锦"
    }
);</pre>
<p>这样，soulspark.im就可以调用decodePost()，从而获取文章的信息了。</p>
<p>需要注意的时，<pre class="crayon-plain-tag">&lt;script&gt;</pre> 脚本不一定非要硬编码在HTML文档中，在页面运行过程中，可以随时动态创建该元素以使用JsonP。</p>
<div class="blog_h3"><span class="graybg">JsonP的安全风险</span></div>
<p>JsonP允许远端Web服务注入任意JavaScript代码，因此具有很大的安全性风险，如果远端服务器具有安全缺陷，或者响应内容在网络上被篡改，将导致本地Web页面受到威胁。</p>
<div class="blog_h2"><span class="graybg"><a id="cors"></a>CORS相关知识</span></div>
<p>CORS，即Cross-origin resource sharing（跨来源资源共享）是一份浏览器技术规范。相比起JsonP，它有如下优势：</p>
<ol>
<li>支持除了GET以外的其它HTTP请求方法</li>
<li>支持使用一般的Ajax（XMLHttpRequest、Fetch）直接获取资源</li>
<li>支持跨源使用Webfont、WebGL贴图、样式表、脚本</li>
</ol>
<p>所谓跨源，指发起<span style="background-color: #c0c0c0;">请求的那个网页所在的源（协议 + 域名 + 端口）</span>，与<span style="background-color: #c0c0c0;">请求的目标网页的源不一致</span>。</p>
<p>大部分现代浏览器，包括IE 8+，均支持CORS。</p>
<div class="blog_h3"><span class="graybg">服务器</span></div>
<p>要支持CORS，服务器端必须设置<pre class="crayon-plain-tag">Access-Control-Allow-Origin</pre> 响应头，下面是某个Python Web服务的代码片段：</p>
<pre class="crayon-plain-tag">headers.setdefault('Access-Control-Allow-Origin', '*')
"""
上述配置允许任何其它的Domain向服务器发起请求。
把*换成http://soulspark.im，则仅仅允许soulspark.im发起请求
"""</pre>
<p>如果服务器不进行设置，客户端在尝试CORS访问时将会报错。 </p>
<p>与CORS有关的响应头包括：</p>
<table class=" fixed-word-wrap full-width">
<tbody>
<tr>
<td style="width: 30%;">Access-Control-Allow-Origin</td>
<td>必须字段。允许哪些源发起请求，源由客户端在请求头<pre class="crayon-plain-tag">Origin</pre> 中设置</td>
</tr>
<tr>
<td>Access-Control-Allow-Credentials</td>
<td>布尔值，可选字段。是否允许跨域的发送Cookie，默认情况下Cookie不包含在CORS请求中</td>
</tr>
<tr>
<td>Access-Control-Expose-Headers</td>
<td>默认的，CORS方式发送Ajax时，只能拿到Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma这几个响应头。如果允许获得其它响应头，需要在这里指定</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">客户端</span></div>
<p>在浏览器中，Origin头会在需要时，自动的被浏览器设置，JavaScript代码无法干预。因此<span style="background-color: rgb(192, 192, 192);">CORS依赖浏览器的正确行为</span>来工作。</p>
<p>发送CORS的Ajax请求时要想带着Cookie，不但需要服务器同意，客户端也要显式的指定：</p>
<pre class="crayon-plain-tag">var xhr = new XMLHttpRequest();
xhr.withCredentials = true;  // 附带Cookie</pre>
<p>启用上述设置时，Access-Control-Allow-Origin不允许指定为*，同时Cookie仍然受到同源策略的限制。</p>
<p>由于CORS依赖于浏览器的行为，因此服务器不能对客户端做假设。</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/http-faq">HTTP知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/http-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
