- Web 与 HTTP
- HTTP 报文
- HTTP 方法
- HTTP 状态码
- HTTP 首部字段
- HTTP 持久连接
- 通信数据转发程序
- HTTP 1.x 报文传输的瓶颈
- HTTP/2
- WebSocket
- HTTPS
面试小抄本之网络协议篇
Web 与 HTTP
HTTP 协议的出现主要是为了解决文本传输的难题,目前已经超出 Web 应用场景;被广泛应用的主流版本是 HTTP/1.1,参考 RFC 2616
URI(Uniform Resource Identifier)
统一资源标识符,即由某个协议方案表示的资源的定位标识符
http://user:pass@www.example.com:80/dir/index.html?uid=1#part1
- 协议方案名:
http
- 认证信息:
user:pass
(可选) - 服务器地址:
www.example.com
(域名或IP) - 服务器端口号:
80
(可选) - 带层次的文件路径:
/dir/index.html
- 查询字符串:
uid=1
(可选) - 锚点:
part1
(可选)
HTTP 报文
报文(message) 是 HTTP 通信中的基本单位,由8位组字节流(octet sequence)组成
message = start-line
*(message-header CRLF)
CRLF
[message-body]
其中
start-line = Request-Line | Response-Line
message-header = field-name ":" [ field-value ]
报文主体(message-body) 用于传输请求或响应的 实体主体(entity-body),通常两者是相等的,仅当传输时进行编码操作时、实体主体内容变化
message-body = entity-body
| <entity-body encoded as per Transfer-Encoding>
HTTP 请求报文
由请求行(包含请求方法、请求 URI、协议版本),可选的首部字段和内容实体构成
POST /form/submit.cgi HTTP/1.1 请求行
Host: example.com } ### 实体(Entity)开始行
Connection: keep-alive }
Content-Type: application/x-www-form-urlencoded } 首部字段
Content-Length: 16 }
... }
空行(CR+LF)
name=a&age=20 内容实体
HTTP 响应报文
由响应行(协议版本、状态码及原因短语),可选的首部字段和内容实体构成
HTTP/1.1 200 OK 响应行
Date: Tue, 10 Jul 2012 06:50:15 GMT } ### 实体(Entity)开始行
Content-Length: 362 }
Content-Type: text/html } 首部字段
... }
空行(CR+LF)
<html> } 内容实体
... }
HTTP 报文传输
发送邮件时,可以在邮件中写入文字并添加多份附件,这是因为采用了 MIME(Multipurpose Internet Mail Extension, 多用途因特网邮件扩展)机制,允许邮件处理文本、图片、视频等多个类型的数据。例如,图片等二进制数据以 ASCII 码字符串编码的方式指明,就是利用 MIME 来描述标记数据类型。而在 MIME 扩展中会使用一种称为多部分对象集合(Multipart)的方法,来容纳多份不同类型的数据。
HTTP 协议中也采纳了 Multipart,发送的一份报文主体内可能含有多类型实体;通常在图片或文本文件等上传时使用。
使用 Multipart 时,首部字段需定义 Content-Type,字段值为 multipart/form-data
(表单文件上传)或 multipart/byteranges
(状态码 206 响应报文);同时要定义 boundary 字符串以划分各类实体。注:Multipart 中每个部分类型也可以含有首部字段。
multipart/form-data
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="field1"
Joe Blow
--AaB03x
Content-Disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
...(file1.txt的数据)...
--AaB03x--
multipart/byteranges
HTTP/1.1 206 Partial Content
Date: Fri, 13 Jul 2012 02:45:26 GMT
Last-Modified: Fri, 31 Aug 2007 02:02:20 GMT
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 500-999/8000
...(范围指定的数据)...
--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 7000-7999/8000
...(范围指定的数据)...
--THIS_STRING_SEPARATES--
HTTP 方法
- GET:获取资源
- POST:传输实体主体
- PUT:传输文件
- DELETE:删除文件
- HEAD:仅获取报文首部,
curl -I example.com
- OPTIONS:查询支持的方法,
curl -X OPTIONS example.com
- TRACE:回显服务器收到的请求,主要用于测试
HTTP 状态码
- 1XX 请求已接收,继续处理
- 2XX 成功:200 OK / 204 No Content / 206 Partial Content
- 3XX 重定向:301 Moved Permanently / 302 Not Found / 303 See Other / 304 Not Modified / 307 Temporary Redirect
- 4XX 客户端错误:400 Bad Request / 401 Unauthorized / 403 Forbidden / 404 Not Found
- 5XX 服务端错误:500 Internal Server Error / 503 Service Unavailable
206 Partial Content: 表示客户端进行了范围请求,而服务器成功执行了这部分的 GET 请求;响应报文中包含由 Content-Range 指定范围的实体内容
301 Moved Permanently: 表示请求的资源已经分配了新的 URI(Location 首部字段)
$ curl -I google.com
HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
302 Not Found: 表示资源临时移动,下例中 Google 会重定向至 com.hk 域(根据请求IP的地理位置返回相应 ccTLD 的 URI)。访问 http://www.google.com/ncr 避免基于地理位置的重定向,ncr 即 No Country Redirection
$ curl -I www.google.com
HTTP/1.1 302 Found
Location: http://www.google.com.hk/url?sa=p&hl=zh-CN&...
304 Not Modified: 表示客户端发送了附带条件请求(If-XXX);304 状态码返回时,不包含响应的主体部分
401 Unauthorized: 表示发送的请求需带有 HTTP 认证信息(基本认证、摘要认证);若之前已进行过1次请求,则表示认证失败
HTTP 首部字段
请求和响应都会使用首部字段,主要传递如报文主体大小、认证信息等内容。首部字段由冒号分割的字段名和字段值构成,如:Content-Type: text/html
一个字段可能包含复合指令,如:Keep-Alive: timeout=15, max=100
1. 通用首部
- Cache-Control: 缓存指令
- Connection: 连接管理指令
- Date: 创建报文的时间
- Trailer: 报文末端首部一览
- Transfer-Encoding: 报文主体的传输编码方式
- Upgrade: 升级为其他协议
- Via: 代理服务器响应信息,记录了报文传输路径(类似 traceroute)
- Warning: 错误通知
- Pragma: 旧版本遗留字段
Connection
-
控制不再转发给代理的首部字段
-
管理持久连接,即服务端想明确断开连接时,指定
Connection: close
Transfer-Encoding
分块传输编码(Chunked Transfer Coding)将实体主体分成多个块,每一块用十六进制标记块大小。Transfer-Encoding: chunked
即指定使用分块传输。
HTTP/1.1 200 OK
Transfer-Encoding: chunked
...
cf0 ←16进制(10进制为3312)
...3312字节分块数据...
392 ←16进制(10进制为914)
...914字节分块数据...
0
2. 请求首部
- Authorization: 认证信息
- Host: 被请求资源的主机和端口号(必须)
- Max-Forwards: 最大传输逐跳数
- Proxy-Authorization: 代理服务器要求客户端的认证信息
- Range: 实体的字节范围请求。如
Range: bytes=5001-10000
,服务端返回 206 Partial Content,当无法处理范围请求时、返回 200 OK 及全部资源。通过范围请求可以实现断续下载和多线程下载 - Referer: 请求的 URI 是从哪里发起的,出于安全考虑、客户端可以选择不发送该字段(一些工业级防火墙会把出口包的 referer 清除,以保护局域网隐私)
- User-Agent: 客户端程序信息
同缓存控制相关的条件请求字段:
- If-Match / If-None-Match: 比较 ETag
- If-Modified-Since / If-Unmodified-Since: 比较资源更新时间
内容协商机制(Content Negotiation),即客户端和服务端就响应的资源内容进行协商、以返回最合适的资源,比如 Web 站点的语言版本等。Accept-XXX 首部字段即用来实现协商:
- Accept: 可处理的媒体类型及权重
- Accept-Charset: 优先的字符集
- Accept-Encoding: 优先的内容编码,gzip / compress / deflate / identity
- Accept-Language: 优先的自然语言
3. 响应首部
- Accept-Ranges: 告知客户端能否处理范围请求,bytes / none
- Age: 推算资源创建了多久,单位为秒;代理创建响应时必须加上该字段
- ETag: 资源信息
- Location: 指示客户端重定向 URI,一般配合 3XX Redirection 使用
- Retry-After: 对再次发起请求的时间要求
- Server: 服务端程序信息
- Vary: 对缓存进行控制,如
Vary: Accept-Language
指示缓存代理服务器只能返回符合 Accept-Language 字段值的资源,如果没有需重新请求源服务器
4. 实体首部
主要是对请求头部的应答,比如 Content-XXX 对应 Accept-XXX 请求
- Allow: 资源支持的 HTTP 方法
- Content-Encoding: 实体主体适用的编码方式,gzip / compress / deflate / identity
- Content-Language: 实体主体的自然语言
- Content-Length: 实体主体大小(Bytes),如果使用分块传输时、不能使用 Content-Length 字段
- Content-Location: 替代对应资源的 URI
- Content-MD5: 对报文主体执行 MD5 算法获得128位二进制数,通过 Base64 编码后写入该字段值;主要用于检查报文主体在传输过程中是否完整
- Content-Range: 对范围请求的响应,如
Content-Range: bytes 5001-10000/10000
,表示当前发送部分及整个实体大小 - Content-Type: 实体主体的媒体类型,如
Content-Type: text/html; charset=UTF-8
- Expires: 实体主体的过期日期
- Last-Modified: 资源的最后修改时间
5. 非 RFC2616 定义的首部字段
在 HTTP 等多种协议中,通过给非标准参数加前缀 X- 来区别,但在 RFC 6648 - Depracating the “X-“ Perfix and Similar Constructs in Application Protocals 中提议停止该做法
X-Frame-Options
用于控制网站内容在其他站点的 Frame 标签内的显示问题,主要防止点击劫持(clickjacking);字段值为 DENY(拒接加载)、SAMEORIGIN(同源页面匹配时许可)
X-XSS-Protection
控制浏览器 XSS 防护机制的开关,字段值为 0(将 XSS 过滤设为无效)、1(有效)
X-Csrf-Token / X-CSRFToken
跨站请求伪造(CSRF)防护
X-Requested-With
用于识别 Ajax 请求,多数 JavaScript 框架定义字段值为 XMLHttpRequest
X-Forwarded-For
是一个事实标准(de facto standard)的扩展字段,用于标识经过了代理(Proxy)及负载均衡(Load Balancer)的客户端IP。通过 Proxy 的连接只会表现出最后一跳代理的IP,XFF 的有效也依赖于代理服务器是否可信。
XFF 格式:X-Forwarded-For: client1, proxy1, proxy2
Content-Security-Policy
内容安全策略,限定浏览器能够加载的内容来源(Content Origins),包括 JS / CSS / 字体 / 图片 / 音视频 / Web Workers / HTML Frames,以及嵌入式对象如 Java Applets / ActiveX 等。
CSP 指令如 script-src / style-src / img-src
即定义相应资源类型的加载策略;指令值如 none(不允许任何内容)、self(同源内容,相同协议、域名和端口)、a.com / *.a.com(限定域名 / 子域名)等。
同 X-Content-Security-Policy, X-Webkit-CSP (Deprecated)
详细阅读:
- Content Security Policy
- 浏览器安全策略说之内容安全策略CSP
- Content Security Policy 介绍
- Content Security Policy Level 2 介绍
- What is JSONP all about
突破 SOP 的限制,有几种策略:
- 跨域资源共享(Corss-Origin Resource Sharing, CORS):要求被请求域在响应报头添加
Access-Control-Allow-Origin
标签以允许指定域的站点、访问当前域的资源;CROS 实现主要依赖服务端 - JSONP:客户端无法访问基于
XMLHttpRequest
跨域请求得到的数据(注:SOP 不限制发送请求,仅限制访问得到的数据),但具有src
属性的标签(如 script, img, iframe)不受 SOP 限制,浏览器可以执行请求得到的 JavaScript 代码。很多访问统计的插件是通过 JSONP 实现;另对于 JSONP 的请求调用是基于对被请求域的信任,因此也有一定的安全隐患 - HTML5 Web Messaging:当开发者可以修改请求域及被请求域的页面时,可以考虑基于 Web Messaging 实现页面间跨域通信
- WebSocket:允许跨域通信
Cookie / Set-Cookie
HTTP 是无状态协议(stateless),协议本身不对请求和响应做持久化处理(不保留之前一切的报文信息);因此对于服务器而言,无法区分每一个请求是来自同一个客户端还是不同客户端。状态保存通过 Cookie 或者 QueryString(不安全)实现。
- name: 指定 Cookie 名 / 值
- expires: 指定 Cookie 有效期,若不指定则截止到浏览器会话关闭
- path: 指定服务器上的路径作为 Cookie 适用对象
- domain: 指定域名作为 Cookie 适用对象,若不指定则为创建 Cookie 的服务器域名
- secure: 仅在 HTTPS 安全通信时才发送
- HttpOnly: 不能被 javaScript 脚本访问,如设定后
document.cookie
无法读取
DNT
拒接信息被收集(Do Not Track),字段值为 0(同意被追踪)、1(拒接被追踪);需要 Web 服务器提供支持
6. 端到端首部 / 逐跳首部
HTTP 首部字段将定义成缓存代理和非缓存代理的行为,分成端到端首部(End-to-end Header)及逐跳首部(Hop-by-hop header)
代理必须转发端到端首部;逐跳首部只对单次转发有效,仅如下字段:
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- Trailer
- TE
- Transfer-Encoding
- Upgrade
HTTP 持久连接
为了避免每次 HTTP 请求造成无谓的 TCP 连接建立和断开,HTTP/1.1 的 Keep-alive 规定了只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。HTTP/1.1 所有的连接默认为持久连接。
Client Server
| === SYN ==> |
| <== SYN/ACK ==> | } 建立 TCP 连接
| === ACK ==> |
| === HTTP Req ==> |
| <== HTTP Res === | } HTTP 持久连接
| === HTTP Req ==> |
| <== HTTP Res === |
| <== FIN ==> |
| === ACK ==> | } 断开 TCP 连接
| === FIN ==> |
| <== ACK === |
基于持久连接,管线化(pipelining)允许不用等待响应即可直接发送下一个请求,即可以同时并行发送多个请求,不需要依次等待响应。
Client Server
| 建立 TCP 连接 |
| === HTTP Req (1) ==> |
| === HTTP Req (2) ==> |
| <== HTTP Res (1) === |
| <== HTTP Res (2) === |
| 断开 TCP 连接 |
Pipelining 本质上也仅是一种补丁优化方案,存在线头阻塞问题(Head of line blocking):即客户端还是按照发送请求的顺序来接受响应,如果某个请求阻塞、会导致后续请求都受到影响。
而且 RFC2616 中定义一个域最多2个连接,现代浏览器如 Chrome 允许同时打开 6个HTTP 和 6个HTTPS 连接(chrome://net-internals#sockets)
Chrome DevTools Network 面板中同一个 Connection-Id 即表示连接被复用:… a TCP connection was reused instead of handshaking and establishing a new one. RTT cost + TCP slow-start means that established connections send data faster.
通信数据转发程序
1. 代理服务器
代理不改变请求 URI,接收客户端发送的请求,转发送给前方持有资源的目标服务器(源服务器);转发时附加 Via 首部字段以标记出经过的主机信息
代理的作用:
- 利用缓存技术减少流量(缓存代理,Caching Proxy)
- 实现访问控制(ACL)
根据转发请求/响应时是否对报文进行修改,可以区分为透明代理或非透明代理
2. 网关
网关的工作机制和代理十分相似,而网关能使通信线路上的服务器提供非 HTTP 协议服务
3. 隧道
隧道通常作为加密通信线路使用(使用 SSL 等),隧道本身不会去解析 HTTP 请求
HTTP 1.x 报文传输的瓶颈
- 不能在同一个连接上进行并发请求;客户端通常需要开启多个连接来加速请求资源的过程
- 请求只能从客户端开始;客户端不可接收除响应外的指令
- 请求 / 响应首部未经压缩;每次互相发送相同的首部造成的浪费较多
基于 HTTP 1.x 的长连接解决方案:
-
Ajax 解决方案
Ajax 的核心技术是名为 XMLHTTPRequest 的 API,通过 JavaScript 的调用就能和服务器进行 HTTP 通信;因此可以从已加载完的 Web 页面上发起请求,只更新局部页面。
基于 Ajax 可以实现轮询以建立实时通讯。
-
Comet 解决方案
Comet 模拟实现了 Server Push 功能,即服务端收到请求后首先将响应置于挂起状态,当服务端有内容更新时、再返回响应。
Comet 面临的问题:长连接通信的不确定性,即客户端不知道何时服务端能返回数据;服务端需要维持一个连接、且不知道何时能释放该连接(不知道客户端是否已关闭)
优化手段通常是在 C/S 间保持心跳信息,Server 端同时要建立超时机制。
-
Flash 解决方案
Flash 有自己的 Socket 实现,为实时通讯提供了可能,通过 JavaScript 调用 Flash 的 API 与服务端通信;但 Flash 对移动设备支持不佳。
参考阅读:Comet:基于 HTTP 长连接的“服务器推”技术
HTTP/2
2015.5 HTTP/2 以 RFC 7540 正式发布,在与 HTTP 1.x 语义兼容的基础上,优化传输效率、减少网络延迟。
HTTP/2 enables a more efficient use of network resources and a reduced perception of latency by introducing header field compression and allowing multiple concurrent exchanges on the same connection. It also introduces unsolicited push of representations from servers to clients.
协议介绍
- 帧(Frame):HTTP/2 通信的最小单元,是一个二进制结构
- 消息(Message):代表一次请求 / 响应,包含多个帧组成
- 连接(Connection):对应的 TCP 连接,可以有多个流
- 流(Stream):连接上的双向字节流,具备多路复用(Multiplexed)和优先级设定(Prioritized)特性
同域名下的通信都在单个 Connection 上完成,该连接可以承载多个 Stream,每个流以 Message 形式发送,消息由多个 Frame 组成,帧可以乱序发送(根据 HEADERS 的流标识重组)
带来的优势:
- 二进制传输协议:相对文本协议、解析起来更高效(对帧的边界识别更容易)且传输安全
- 多路复用:通过单一的 HTTP/2 连接发起多重请求
- 赋予请求优先级
- 头部压缩
- 服务端推送
1. 多路复用
参考 HTTP 1.1 持久连接,HTTP/2 的多路复用体现在:当页面向同一个域发起多个请求时,会自动合并到一个连接中
因此以下 HTTP 1.x 的 Web 工程化方案可以不再使用:
- 资源合并:如 CSS Sprite, JS / CSS 文件拼接,资源合并的目的在于减少 HTTP 请求数,但这会造成冗余加载(耗时增加、且流量浪费)及缓存失效(微小模块的改动都会引起整体资源的更新)
- 多域分发:由于对同一个域发起的请求数有限,采用多域部署来加快浏览器的资源加载,同时也将 Cookie 分域管理
2. 头部压缩
HTTP/2 通过在客户端 / 服务端使用 首部表 存储及更新头部字段(键值对形式),且对于新的请求 / 响应中的头部信息、只做增量传递。
压缩算法简述:用静态表(Static Table,0~61共62个条目)将 HTTP/2 头部常用字串替换为索引,用动态表(Dynamic Table)保存需要动态修改的内容(如 cookie);使用霍夫曼编码对文本字串进行压缩。
详细阅读:HTTP/2 头部压缩技术介绍
3. 服务器推送
浏览器请求得到的页面 HTML 往往依赖一些 CSS / JS 等资源才能渲染,因此服务器在浏览器发现外链并发起请求前主动推送资源
对比各个请求模型下的时延:
4. HTTP/2 协商
客户端协商 HTTP/2 通过发送带 upgrade 头部的请求:
GET /page HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: HTTP/2.0
HTTP2-Settings: (SETTINGS payload)
如果服务器支持则接受升级,切换到新分帧;否则通过 HTTP/1.1 返回响应
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: HTTP/2.0
(... HTTP 2.0 response ...)
参考阅读:
WebSocket
WebSocket 是基于 TCP 连接进行双向通讯的机制,客户端/服务器可以同时发送请求并响应请求。
WebSocket 握手利用了 HTTP 的 Upgrade,一旦握手完成、后续数据传输直接在 TCP 上完成。
参考阅读:浏览器中常见网络协议介绍
HTTPS
HTTP 协议的安全问题
- 通信使用明文,内容可能会被窃听
- 不验证通信方的身份,可能遭遇伪装;无意义的请求也会处理,海量请求导致 DoS 攻击
- 无法验证报文的完整性,有可能已遭篡改
窃听:互联网上的任何角落都可能存在;普遍使用加密技术以应对,加密的对象主要有:
- 通信加密:SSL(Secure Socket Layer,安全套接层)或 TLS(Transport Layer Security,传输层安全协议),加密 HTTP 的通信内容
- 内容加密:对报文主体加密,这要求 C/S 两端同时具备加密和解密机制;仍存在保存篡改风险
身份伪装:HTTP 无法确定通信方;SSL 提供证书可以确定服务器和客户端是实际存在
篡改:请求 / 响应在传输途中,是否遭遇拦截并篡改(MITM,中间人攻击);应对手段主要有:
- 首部字段的 Content-MD5 等验证方法,但该首部字段也可能遭篡改
- 文件下载站点会提供以 PGP 创建的数字签名及文件 MD5,但需要用户额外下载并检查;同样该校验文件本身也可能遭篡改
- 基于 SSL 提供认证和加密处理及摘要功能
加密技术
- 对称密钥加密:即加密、解密使用同一个密钥;如果密钥被窃取、则密文会被破解
- 非对称密钥加密:通信两端均有一对公钥 / 私钥,发送方使用对方的公钥加密、用自己私钥对收到的密文解密;参考阅读 RSA 加密算法
HTTPS 使用混合加密,即使用非对称加密交换共享密钥,后续基于该共享密钥进行对称加密通信。
获取对方的公钥的环节,同样存在篡改风险。因此不使用通信方式获取证书,而是使用浏览器预先植入的数字证书认证机构(CA, Certificate Authority)颁发的公钥证书来解决。
如果服务端向验证客户端的身份(比如网银交易),也会采用验证客户端证书的方式,但代价价高。
服务端也可以使用自签发证书,但这会被浏览器告警。
SSL / TLS
Transport Layer Security (TLS) and its predecessor, Secure Sockets Layer (SSL), both of which are frequently referred to as ‘SSL’.
SSL 主要解决上述三大问题:内容加密、身份认证、数据完整性。SSL 的优势在于它与应用层协议独立无关,HTTP / FTP / SMTP 等可以透明地创建于 SSL 协议之上,SSL 在应用层协议通信前即完成了加密算法、通信密钥的协商以及服务器认证工作。
SSL Handshake:
$ curl -v -I https://github.com
Rebuilt URL to: https://github.com/
* Trying 192.30.252.122...
* Connected to github.com (192.30.252.122) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ...
* successfully set certificate verify locations:
* CAfile: /usr/local/etc/openssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: businessCategory=Private Organization; jurisdictionC=US; jurisdictionST=Delaware; serialNumber=5157550; street=88 Colin P Kelly, Jr Street; postalCode=94107; C=US; ST=California; L=San Francisco; O=GitHub, Inc.; CN=github.com
* start date: Mar 10 00:00:00 2016 GMT
* expire date: May 17 12:00:00 2018 GMT
* subjectAltName: host "github.com" matched cert's "github.com"
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 Extended Validation Server CA
* SSL certificate verify ok.
> HEAD / HTTP/1.1
> Host: github.com
...