Node.js从原始HTTP请求字符串创建对象

20

我有一个原始的HTTP请求字符串,需要从中创建一个对象表示。

为了避免重复发明轮子,我考虑使用内部http解析器来获取一个http.IncomingMessage的实例。

这可行吗?

我认为是可行的,因为字符串与完整流并没有太大的不同。

如何做到这一点?

我查看了源代码,并按以下方式获取了请求解析器

var HTTPParser = process.binding('http_parser').HTTPParser;
var parser = new HTTPParser(HTTPParser.REQUEST)

编辑

来自node.js测试的一些进展。

var request = Buffer(raw);
var parser = new HTTPParser(HTTPParser.REQUEST);

parser.execute(request, 0, request.length);

编辑2

一些事件处理程序丢失了(全部丢失)

parser.onHeadersComplete = function(res) {
    console.log('onHeadersComplete');
    console.log(res);
};

parser.onBody = function(body) {
    console.log('body done');
    console.log(body.toString());
}

parser.onMessageComplete = function(res) {
    console.log('done');
};

谢谢


我更新了我的问题。我有一个字符串 "GET / HTTP/1.1\nHost: localhost\n\n",我需要解析它以创建一个对象。生成的对象必须是 http.IncomingMessage 的实例。 - PauloASilva
2
那么问题具体是什么? - OrangeDog
1
为什么不使用 http 模块?require('http').createServer(function(req,res){ ... })每次有请求时都会调用该函数。req 已经是 http.IncomingMessage 的实例。 - stewe
@CoDEmanX你没有理解重点。我不需要HTTP服务器,也不使用网络。我正在寻找像http-string-parser这样的解析器用于Node.js,但是既然Node.js有内置解析器,为什么要使用它呢? - PauloASilva
@major-mann:你说得对! - PauloASilva
显示剩余7条评论
3个回答

2

显然,http_parser模块是一个基于回调的低级解析器。它将通过这些回调向您发送其可以解析的字符串的任何部分,并由您从中创建IncomingMessage或其他所需内容。

我认为类似这样的东西可能是您正在寻找的:

var HTTPParser = process.binding('http_parser').HTTPParser;

function parseMessage(request) {
    var _message = {};
    var _parser = new HTTPParser(HTTPParser.REQUEST);

    _parser.onHeadersComplete = function(headers) { 
        _message = headers; 
    }

    _parser.onBody = function(body, start, len) {
        _message.data = body.slice(start, start+len);
    }

    var _result = _parser.execute(request, 0, request.length);

    if (_result != request.length) { 
        _message.error = _result; 
    }
    else {
        _message.error = false;
    }
    return _message;
}

var request = Buffer("GET / HTTP/1.1\nHost: localhost\nContent-Length: 2\n\nHi\n\n");
result = parseMessage(request);

请注意,特定的IncomingMessage类是使用socket参数化的,并且通常围绕在服务器内部使用的想法构建而成。解析它的代码有些混乱,不太适合直接重用(依据我的口味)。

2
最近的Node版本更改了接口,现在回调函数的分配方式是像parser[HTTPParser.kOnHeaders] = function() {...}这样的形式。 - Fr0sT

1

虽然这是一个老话题,但我还是想说一些。

将HTTPParser导出到自己的模块或应用程序中并不简单,因为http库使用了许多内部本地函数和构造函数。此外,HTTPParser本身是从C库绑定而来的,还有一些辅助函数。

据我所知,Node > 4中已经删除了http.parsers,所以唯一的方法是从http库中导入所有必要的内容。

对于node 0.x,有一个简单的导入方式:

var parser = require("http").parsers.alloc();

parser.onIncoming = function(response) {
  console.log(response);
};

function parse(data) {
    var buffer = new Buffer(data);
    parser.execute(buffer, 0, buffer.length);
}
/**
 * tests
 */
parse("DELETE / HTTP/1.1\r\n");
parse("user-agent: curl\r\n");
parse("x-pingback:");
parse("12023\r\n");
parse("\r\n");

//response
{ _readableState: 
   { highWaterMark: 16384,
     buffer: [],
     length: 0,
     pipes: null,
     pipesCount: 0,
     flowing: false,
     ended: false,
     endEmitted: false,
     reading: false,
     calledRead: false,
     sync: true,
     needReadable: false,
     emittedReadable: false,
     readableListening: false,
     objectMode: false,
     defaultEncoding: 'utf8',
     ranOut: false,
     awaitDrain: 0,
     readingMore: false,
     decoder: null,
     encoding: null },
  readable: true,
  domain: null,
  _events: {},
  _maxListeners: 10,
  socket: undefined,
  connection: undefined,
  httpVersion: '1.1',
  complete: false,
  headers: { 'user-agent': 'curl', 'x-pingback': '12023' },
  trailers: {},
  _pendings: [],
  _pendingIndex: 0,
  url: '/',
  method: 'DELETE',
  statusCode: null,
  client: undefined,
  _consuming: false,
  _dumped: false,
  httpVersionMajor: 1,
  httpVersionMinor: 1,
  upgrade: false }

更多信息请点击此处

同时,感谢@KT提供的优雅解决方案


有没有针对较新的Node版本(如当前的LTS v16)的解决方案?require("http").parsers未定义。 - Marc

0

手动解析怎么样?下面是从缓冲区解析HTTP请求的示例。首先需要将字符串转换为缓冲区。

const data = Buffer.from([80, 79, 83, 84, 32, 104, 116, 116, 112, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 49, 50, 51, 52, 47, 104, 101, 108, 108, 111, 32, 72, 84, 84, 80, 47, 49, 46, 49, 13, 10, 72, 111, 115, 116, 58, 32, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 49, 50, 51, 52, 13, 10, 85, 115, 101, 114, 45, 65, 103, 101, 110, 116, 58, 32, 99, 117, 114, 108, 47, 55, 46, 55, 56, 46, 48, 13, 10, 65, 99, 99, 101, 112, 116, 58, 32, 42, 47, 42, 13, 10, 80, 114, 111, 120, 121, 45, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 58, 32, 75, 101, 101, 112, 45, 65, 108, 105, 118, 101, 13, 10, 67, 111, 110, 116, 101, 110, 116, 45, 76, 101, 110, 103, 116, 104, 58, 32, 55, 13, 10, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 45, 119, 119, 119, 45, 102, 111, 114, 109, 45, 117, 114, 108, 101, 110, 99, 111, 100, 101, 100, 13,10, 13, 10, 97, 61, 49, 38, 98, 61, 50]);


const delimIndex = data.indexOf('\r\n\r\n');
const lines = data.slice(0, delimIndex).toString('utf8').split('\r\n');
const startLine = lines[0];
const headers = lines.slice(1);
const body=data.slice(delimIndex + 4);

console.log(`${startLine}\n\n${headers.join('\n')}\n\n${body}`);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接