HTTP窥视#
---
创建日期: 2015-09-04
---
一篇介绍HTTP的文章,一篇知道的人早就知道,不知道的看了还是不知道的文章。
重回HTTP#
早前有个朋友希望我做一个应用,通过获取指定网络地址的文件。 文件内容是一种时刻表,解析完成后根据时刻进行提醒。
应用本身不谈,说说获取这个文件的事。 时刻表文件是有可能变化的,不定期更新。 我猜测文件地址并不会变化,这是件好事,否则我哪知道去哪里获取文件,但要如何保持文件比较新? (不一定是最新,但是也不能差太远)如果每次进入应用都去下载,虽然文件不大,肯定不是个好办法。等等,我想起一个“304”。
“304”是HTTP协议中的一个返回状态代码, 浏览器获取文件后会有缓存,尤其是静态文件。 在下次还需要该文件时,如果服务器返回“304”表示这个文件没有改变, 那浏览器就可以从自己的缓存中读取文件不需要再下载。
这不就是我想要的? 初次下载时刻表并缓存,之后再进入应用时,询问服务器是否更新了文件,若是未更新则继续使用之前的,而若是更新了,再提示用户下载。
那么几个问题: 文件是否更新的凭据及如何取得该信息; 如何询问服务器该文件是否更新,并根据状态分别处理?
历史“经验”#
使用PHP的时候,脚本运行之初,
我们就可以访问相关信息,$_POST
、$_GET
等等。
而如果用户还上传了文件,访问$_FILES
也可以知道上传的文件被放到了哪
($_FILES
中包含其临时路径信息,如有需要再进一步操作此文件)。
而用JavaScript进行Ajax请求时,通常在请求完毕函数中判断,请求是否成功,使用返回的数据。
这些数据到底如何传送的呢?
我有自己的猜测(注意,以下内容是错误的,不能相信): 是不是服务端先把数据全生成了,然后直接扔给系统,系统再一点点把数据传输给客户端(因为我还是知道每次传输都是一小部分,到达目的地后重组),客户端的系统接收完数据后交给其处理程序。
这里的重点是发送的粒度是所有数据,整个文件整个文件的传输(重申这是错的,是我的误解)。 但是有些问题,如果传输到一半,其中一方放弃了,剩余部分还继续? 比如下载一个文件中途取消了,服务端系统还在往客户端系统传输?(因为我以为是系统在传输数据,程序并没有参与)
另外,为什么断点传输的信息头总是从需求位置到最后位置,而不是一小段一小段。 (我的误解是:这样服务端系统就不需要生成那么多数据,并且当客户端又暂停时,服务端系统不会继续发送数据给客户端系统,至少不会非常多) (我感觉可能没说明白,毕竟是个非常奇怪的误解)
我产生这种误解正是在使用PHP和JavaScript时都是只处理了传输完成后的数据。
混沌时间#
以前学Linux编程的时候,感觉网络而言,一切都是socket,但是把部分东西给忘记了。
我知道HTTP协议有信息头,可是我操作的都是内容本体; 为什么post没有字节限制(还是上述的误解导致无法理解); 为什么post有两次连接,比get安全(这个也不知道是哪里看到的,还是我自己看错了,反正现在看来是不准确的)。
我要在应用中通过HTTP的方式获取时刻表,可是搜索到的全部都是直接取得文件内容,我要的信息头呢?
“上古”记忆#
我做了一个非常简单的 端口监听(可以监听80端口,知道浏览器请求的内容,可以在访问中途修改hosts文件,让请求指向本机) 以及端口发送(向其它服务器80端口发送数据,并获得响应内容) 程序。
其实在Chrome的开发工具中可以看到这些,只是没那么原始。
在做这个程序的过程中,我想起了一些事。 网络的数据发送由程序控制(非常正常的想法就应该是这样的,我都不知道之前为什么脑抽会认为是系统一齐发送), 连接成功后,再不断收发数据,而且每次发送都是阻塞的,如果发送失败(比如对方关闭了),就可以从返回结果知道了,所以整个粒度比我误解的小。
其实HTTP的交互结果就是字符串,由信息头和消息本体组成(这些介绍很多网站都有),只是我们通常都是使用处理过的数据。
一缕光明#
请求静态文件后,服务器会在返回信息头上添加一个字段“Last-Modified”,值为文件最后修改时间。
保存这个信息,等到下次请求时,在请求的信息头中加入“If-Modified-Since”字段与时间。 如果文件没有修改,就会返回“304”的信息头; 如果修改了,则是“200”(成功的情况)的信息头和作为消息本体的文件内容。
但是我想让用户选择是否下载更新后的时刻表,普通的get请求很可能直接把新文件也传输回来了。 此时,需要一个head请求,也就是,只返回预期结果的信息头,就可以选择了。 head请求应该经常用于下载中,比如在没有下载文件的情况下先知道其名称与大小。
我以前一直困惑,为什么jQuery说它压缩后只有20+K,可是不管我怎么下载都是90+K。 其实是通过压缩算法将文件压缩后再作为消息本体传输,只要告诉对方用的什么算法,同时对方知道如何解析。 浏览器正常使用仍然压缩前/解压后的内容,只是传输过程减少了不少字节,纯文字的压缩率很高的。 (那么如果作为一个静态文件服务器,文件不更新情况下,而且多数浏览器都支持的压缩方法,只要偶尔压缩一次,之后直接传输压缩后的内容,每次的压缩过程不是都节省了?这个应该就是CDN的一部分,我猜的。)
初学nodejs,使用express,发现我什么也没做居然会返回“304”,它是怎么判断是否修改过? 难道没有执行脚本就把缓存返回了? 后来发现有个“ETag”字段,浏览器后续请求会将之前的“ETag”字段改成“If-None-Match”,服务端脚本仍然执行了,执行完成进行计算,如果“ETag”相同,就返回“304”,所以对于服务端并没有减轻负担,但减少了传输数据。
后会有期#
HTTP我想说的就结束了,感觉什么都没说。
使用HTTP,发现居然都用的明文传输,我的账号密码全都放在post请求消息本体上! (前面说post连接两次估计是指浏览器会将信息头和消息本体分开send,然而如果一起send也没什么关系,服务端receive没有区别)我们需要加密!
不对称密钥中,同时产生公钥、私钥,公钥加密由私钥解开,可是又不能从公钥推算出私钥是什么。 但是这种方法非常慢,内容加密还是使用对称加密。 SSL先通过不对称加密传输对称密钥,再通过对称密钥加密内容发送,接收并解析。
所以原则上说,可以让JavaScript和服务端脚步利用这种方式,对消息本体进行加密解密,或许效率不高(毕竟是脚本),而难度不低。还是乖乖用HTTPS吧。
原本不清楚HTTPS是只加密消息本体(不含信息头),还是一起加密,用那个程序监听了下,是整体的,而且还看到了SPDY字样,所以提速是在这?
但我还不清楚的是,HTTPS的密钥是一次获取,持续使用,还是每次连接都要交换密钥。
我们还是使用和HTTP相同的方式写脚本,HTTPS的加密解密由浏览器和服务器负责,我们收到的都是处理过的数据。
后话,我看着有些大网站登入居然不是用的HTTPS,感觉不该吧? 当然,它们可以通过一些时间节点加哈希的方式阻止明文显示(发送Time和Hash(Time+Password)获得的令牌,服务端进行比较,并且Time不能与当前时间偏差太大), 可感觉很不严肃……