Compare commits

...

3 Commits

Author SHA1 Message Date
Claude Bot
7e752b645a fix: restore RegExpPrototypeExec for validateLinkHeaderFormat
The validateLinkHeaderFormat function still needs RegExpPrototypeExec
to validate Link header values. This fixes test failures in:
- test-http-early-hints-invalid-argument.js
- test-http2-compat-write-early-hints.js

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 01:38:38 +00:00
autofix-ci[bot]
6edfdb6589 [autofix.ci] apply automated fixes 2025-09-14 14:41:34 +00:00
Claude Bot
66bce3d6da perf: rewrite HTTP validators in C++ for better performance
Migrates checkIsHttpToken and checkInvalidHeaderChar from JavaScript to C++
to improve performance of HTTP header validation.

- checkIsHttpToken now uses WebCore::isValidHTTPToken directly
- checkInvalidHeaderChar uses template specializations for 8-bit/16-bit strings
- Avoids regex overhead and per-character type checking
- Maintains full compatibility with existing Node.js behavior

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-14 14:38:25 +00:00
4 changed files with 70 additions and 27 deletions

View File

@@ -1017,4 +1017,64 @@ extern "C" int Bun__writeHTTPDate(char* buffer, size_t length, uint64_t timestam
tstruct.tm_sec % 99);
}
// JavaScript binding for checking HTTP tokens
JSC_DEFINE_HOST_FUNCTION(jsFunction_checkIsHttpToken, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (callFrame->argumentCount() < 1)
return JSC::JSValue::encode(JSC::jsBoolean(false));
auto* jsString = callFrame->argument(0).toString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
auto view = jsString->view(globalObject);
RETURN_IF_EXCEPTION(scope, {});
return JSC::JSValue::encode(JSC::jsBoolean(WebCore::isValidHTTPToken(view)));
}
// Helper template functions for checking invalid header characters
template<typename CharType>
static inline bool hasInvalidHeaderChar(const CharType* characters, size_t length)
{
// Check for invalid field-vchar
// field-value = *( field-content / obs-fold )
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
// field-vchar = VCHAR / obs-text
// Valid characters are: \t, \x20-\x7e, \x80-\xff
const CharType* end = characters + length;
for (const CharType* p = characters; p < end; ++p) {
CharType c = *p;
if (c != '\t' && (c < 0x20 || (c > 0x7e && c < 0x80) || c > 0xff)) {
return true; // Found invalid character
}
}
return false; // No invalid characters found
}
// JavaScript binding for checking invalid header characters
JSC_DEFINE_HOST_FUNCTION(jsFunction_checkInvalidHeaderChar, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (callFrame->argumentCount() < 1)
return JSC::JSValue::encode(JSC::jsBoolean(false));
auto* jsString = callFrame->argument(0).toString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
auto view = jsString->view(globalObject);
RETURN_IF_EXCEPTION(scope, {});
bool hasInvalid;
if (view->is8Bit()) {
hasInvalid = hasInvalidHeaderChar(view->span8().data(), view->length());
} else {
hasInvalid = hasInvalidHeaderChar(view->span16().data(), view->length());
}
return JSC::JSValue::encode(JSC::jsBoolean(hasInvalid));
}
}

View File

@@ -34,6 +34,7 @@
#include <wtf/HashSet.h>
#include <wtf/WallTime.h>
#include <wtf/text/StringHash.h>
#include <JavaScriptCore/JSCInlines.h>
namespace WebCore {
@@ -170,4 +171,8 @@ inline bool isHTTPSpace(char16_t character)
// return set;
// }
// JavaScript bindings for HTTP validators
JSC_DECLARE_HOST_FUNCTION(jsFunction_checkIsHttpToken);
JSC_DECLARE_HOST_FUNCTION(jsFunction_checkInvalidHeaderChar);
}

View File

@@ -4,15 +4,9 @@ const RegExpPrototypeExec = RegExp.prototype.exec;
const ArrayIsArray = Array.isArray;
const ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty;
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
/**
* Verifies that the given val is a valid HTTP token
* per the rules defined in RFC 7230
* See https://tools.ietf.org/html/rfc7230#section-3.2.6
*/
function checkIsHttpToken(val) {
return RegExpPrototypeExec.$call(tokenRegExp, val) !== null;
}
// Use native C++ implementations for better performance
const checkIsHttpToken = $newCppFunction("HTTPParsers.cpp", "jsFunction_checkIsHttpToken", 1);
const checkInvalidHeaderChar = $newCppFunction("HTTPParsers.cpp", "jsFunction_checkInvalidHeaderChar", 1);
/*
The rules for the Link header field are described here:
@@ -95,6 +89,7 @@ export default {
validateObject: $newCppFunction("NodeValidator.cpp", "jsFunction_validateObject", 2),
validateLinkHeaderValue: validateLinkHeaderValue,
checkIsHttpToken: checkIsHttpToken,
checkInvalidHeaderChar: checkInvalidHeaderChar,
/** `(value, name, min, max)` */
validateInteger: $newCppFunction("NodeValidator.cpp", "jsFunction_validateInteger", 0),
/** `(value, name, min, max)` */

View File

@@ -1,27 +1,10 @@
const { checkIsHttpToken } = require("internal/validators");
const { checkIsHttpToken, checkInvalidHeaderChar } = require("internal/validators");
const FreeList = require("internal/freelist");
const { methods, allMethods, HTTPParser } = process.binding("http_parser");
const incoming = require("node:_http_incoming");
const { IncomingMessage, readStart, readStop } = incoming;
const RegExpPrototypeExec = RegExp.prototype.exec;
let headerCharRegex;
/**
* True if val contains an invalid field-vchar
* field-value = *( field-content / obs-fold )
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
* field-vchar = VCHAR / obs-text
*/
function checkInvalidHeaderChar(val: string) {
if (!headerCharRegex) {
headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
}
return RegExpPrototypeExec.$call(headerCharRegex, val) !== null;
}
const validateHeaderName = (name, label?) => {
if (typeof name !== "string" || !name || !checkIsHttpToken(name)) {
throw $ERR_INVALID_HTTP_TOKEN(label || "Header name", name);