首先,您可以使用我为此创建的GitHub存储库自己尝试此代码。只需克隆存储库并运行
nodeheader。
(扰流器,如果您正在阅读本文,并且在时间上压力很大,需要工作,而不愿意学习(:(),最后有一个更简单的解决方案)
总体思路
这是一个很好的问题。您所要求的是 很有可能的 , 不需要客户端
,只需深入了解HTTP协议的工作原理,同时展示node.js的运行方式即可:)
如果我们更深入地了解底层TCP协议并针对这种特定情况自行处理HTTP请求,这将变得很容易。Node.js使您可以使用内置的net模块轻松地执行此操作。
HTTP协议
首先,让我们看一下HTTP请求的工作方式。
HTTP请求由以CRLF(
rn)分隔的key:value对的常规格式的标头部分组成。我们知道,当到达双倍CRLF(即
rnrn)时,标头部分就结束了。
典型的HTTP GET请求可能如下所示:
GET /resource HTTP/1.1 Cache-Control: no-cache User-Agent: Mozilla/5.0Hello=World&stuff=other
“空行”之前的顶部是标头部分,底部是请求的正文。您的请求在主体部分中看起来有所不同,因为它是用编码的,
multipart/form-data但标题仍保持相似。让我们来探索一下这对我们的适用方式。
Node.js中的TCP
我们可以侦听TCP中的原始请求,并读取获得的数据包,直到我们读取了所讨论的double
crlf。然后,我们将检查我们已经拥有的短标头部分,以进行所需的任何验证。之后,如果验证没有通过(例如,仅通过结束TCP连接),我们可以结束请求,也可以通过。这使我们不接收或读取请求主体,而仅接收或读取较小的标头。
将其嵌入到现有应用程序中的一种简单方法是针对特定用例将请求从代理请求发送到实际的HTTP服务器。
实施细节
该解决方案是 裸露的骨头 ,因为它得到。这只是一个建议。
这是工作流程:
我们需要
net
node.js中的模块,该模块允许我们在node.js中创建tcp服务器使用
net
将监听数据的模块 创建一个TCP服务器var tcpServer = net.createServer(function (socket) {...。不要忘记告诉它收听正确的端口- 在该回调内部,侦听数据事件
socket.on("data",function(data){,该事件将在数据包到达时触发。 - 从’data’事件中读取传递的缓冲区的数据,并将其存储在变量中
- 检查是否有双CRLF,以确保根据HTTP协议,请求HEADER部分已结束
- 假设验证是标头(用您的话来说是令牌),请在 仅 分析 标头 之后检查它(即,我们得到了双CRLF)。这在检查content-length标头时也有效。
- 如果您注意到标头没有签出,请致电
socket.end()
,这将关闭连接。
- 在该回调内部,侦听数据事件
这是我们将要使用的一些东西
读取标头的方法:
function readHeaders(headers) { var parsedHeaders = {}; var previous = ""; headers.forEach(function (val) { // check if the next line is actually continuing a header from previous line if (isContinuation(val)) { if (previous !== "") { parsedHeaders[previous] += depreURIComponent(val.trimLeft()); return; } else { throw new Exception("continuation, but no previous header"); } } // parse a header that looks like : "name: SP value". var index = val.indexOf(":"); if (index === -1) { throw new Exception("bad header structure: "); } var head = val.substr(0, index).toLowerCase(); var value = val.substr(index + 1).trimLeft(); previous = head; if (value !== "") { parsedHeaders[head] = depreURIComponent(value); } else { parsedHeaders[head] = null; } }); return parsedHeaders;};一种检查数据事件中缓冲区中双精度CRLF的方法,如果对象中存在它,则返回其位置:
function checkForCRLF(data) { if (!Buffer.isBuffer(data)) { data = new Buffer(data,"utf-8"); } for (var i = 0; i < data.length - 1; i++) { if (data[i] === 13) { //r if (data[i + 1] === 10) { //n if (i + 3 < data.length && data[i + 2] === 13 && data[i + 3] === 10) { return { loc: i, after: i + 4 }; } } } else if (data[i] === 10) { //n if (data[i + 1] === 10) { //n return { loc: i, after: i + 2 }; } } } return { loc: -1, after: -1337 };};而这个小的实用方法:
function isContinuation(str) { return str.charAt(0) === " " || str.charAt(0) === "t";}实作
var net = require("net"); // To use the node net module for TCP server. Node has equivalent modules for secure communication if you'd like to use HTTPS//Create the servervar server = net.createServer(function(socket){ // Create a TCP server var req = []; //buffers so far, to save the data in case the headers don't arrive in a single packet socket.on("data",function(data){ req.push(data); // add the new buffer var check = checkForCRLF(data); if(check.loc !== -1){ // This means we got to the end of the headers! var dataUpToHeaders= req.map(function(x){ return x.toString();//get buffer strings }).join(""); //get data up to /r/n dataUpToHeaders = dataUpToHeaders.substring(0,check.after); //split by line var headerList = dataUpToHeaders.trim().split("rn"); headerList.shift() ;// remove the request line itself, eg GET / HTTP1.1 console.log("Got headers!"); //Read the headers var headerObject = readHeaders(headerList); //Get the header with your token console.log(headerObject["your-header-name"]); // Now perform all checks you need for it } });}).listen(8080); // listen to port 8080 for the sake of the example如果您有任何问题随时问 :)
好的,我撒谎了,有一种更简单的方法!
但是那有什么乐趣呢?如果您最初跳过此处,则不会了解HTTP的工作原理:)
Node.js具有内置
http模块。由于请求本质上是在node.js中分块的,尤其是长请求,因此您可以在不更深入地了解协议的情况下实现相同的事情。
这次,让我们使用该
http模块创建一个http服务器
server = http.createServer( function(req, res) { //create an HTTP server // The parameters are request/response objects // check if method is post, and the headers contain your value. // The connection was established but the body wasn't sent yet, // More information on how this works is in the above solution var specialRequest = (req.method == "POST") && req.headers["YourHeader"] === "YourTokenValue"; if(specialRequest ){ // detect requests for special treatment // same as TCP direct solution add chunks req.on('data',function(chunkOfBody){ //handle a chunk of the message body }); }else{ res.end(); // abort the underlying TCP connection, since the request and response use the same TCP connection this will work //req.destroy() // destroy the request in a non-clean matter, probably not what you want. }}).listen(8080);这是基于以下事实:默认情况下,在发送标题(但未执行其他操作)之后,
requestnodejs
http模块中的句柄实际上已挂接到钩子上。(这在服务器模块中,在解析器模块中)
igorw用户提出了使用
100Continue标头的更干净的解决方案,假设您要定位的浏览器支持该标头。100Continue是一种状态代码,旨在完全执行您要执行的操作:
100(继续)状态(请参阅第10.1.1节)的目的是允许正在发送带有请求正文的请求消息的客户端确定源服务器是否愿意接受请求(基于请求标头)在客户端发送请求正文之前。在某些情况下,如果服务器在不查看正文的情况下拒绝邮件,则客户端发送正文可能是不合适的,或者效率很低。
这里是 :
var http = require('http');function handle(req, rep) { req.pipe(process.stdout); // pipe the request to the output stream for further handling req.on('end', function () { rep.end(); console.log(''); });}var server = new http.Server();server.on('checkContinue', function (req, rep) { if (!req.headers['x-foo']) { console.log('did not have foo'); rep.writeHead(400); rep.end(); return; } rep.writeContinue(); handle(req, rep);});server.listen(8080);您可以在此处查看示例输入/输出。这将要求您使用适当的
Expect:标头触发请求。



