AH02418: HTTP Request Line; Unrecognized protocol 'HTTP/0.9' (perhaps whitespace was injected?) 解决办法

本文记录了在全志H3平台上使用Apache 2.4.37遇到500InternalServerError的问题排查及解决过程,主要原因是test_char.h文件的内容不正确。

最近使用openwrt 的 apache ,移植到全志H3 平台。
编译一切正常,实际运行时打开网页总是报错 500 Internal Server Error:
在这里插入图片描述
查看 /var/log/error_log 如下:(需要将 http.conf 的 LogLevel 设为 debug )

root@172:/# cat /var/log/error_log
[Tue Jul 23 07:01:50.739491 2019] [core:debug] [pid 2542] protocol.c(917): [client 172.16.0.158:25524] AH02418: HTTP Request Line; Unrecognized protocol ‘HTTP/0.9’ (perhaps whitespace was injected?)
[Tue Jul 23 07:01:50.740507 2019] [http:error] [pid 2542] [client 172.16.0.158:25524] AH02429: Response header name ‘Content-Length’ contains invalid characters, aborting request
[Tue Jul 23 07:01:50.992364 2019] [core:debug] [pid 2542] protocol.c(917): [client 172.16.0.158:25525] AH02418: HTTP Request Line; Unrecognized protocol ‘HTTP/0.9’ (perhaps whitespace was injected?)
[Tue Jul 23 07:01:50.992502 2019] [http:error] [pid 2542] [client 172.16.0.158:25525] AH02429: Response header name ‘Content-Length’ contains invalid characters, aborting request

AH02418: HTTP Request Line; Unrecognized protocol ‘HTTP/0.9’ (perhaps whitespace was injected?)

我用的apache 版本是 2.4.37 。

使用 gdb 调试跟踪代码:
在这里插入图片描述
分析代码含义,按正常流程的话到运行到 793 行,之后应该进入794行,但是从gdb 调试结果来看,并没有,问题就出在这。
在这里插入图片描述
重点分析 793 行的 ap_scan_vchar_obstext 函数。

//位于 httpd-2.4.37\server\util.c
 /* Scan a string for visible ASCII (0x21-0x7E) or obstext (0x80+)
 * and return a pointer to the first ctrl/space character encountered.
 */
AP_DECLARE(const char *) ap_scan_vchar_obstext(const char *ptr)
{
    for ( ; TEST_CHAR(*ptr, T_VCHAR_OBSTEXT); ++ptr) ;

    return ptr;
}

TEST_CHAR 是个宏定义

#include "test_char.h"

/* we assume the folks using this ensure 0 <= c < 256... which means
 * you need a cast to (unsigned char) first, you can't just plug a
 * char in here and get it to work, because if char is signed then it
 * will first be sign extended.
 */
#define TEST_CHAR(c, f)        (test_char_table[(unsigned char)(c)] & (f))

test_char_table 的定义位于 test_char.h 中
正常在x86 linux 中 test_char.h 是在编译过程中执行 gen_test_char > test_char.h 自动生成的。
在这里插入图片描述
而在opwert 中使用交叉编译 ,编出来的gen_test_char 是不能在 x86 linux 上运行的,所以openwrt 中使用了 打补丁的方式。它的补丁位于 openwrt\feeds\packages\net\apache\patches\002-test_char_h.patch

Index: httpd-2.4.25/server/test_char.h
===================================================================
--- /dev/null
+++ httpd-2.4.25/server/test_char.h
@@ -0,0 +1,23 @@
+/* this file is automatically generated by gen_test_char, do not edit */
+#define T_ESCAPE_SHELL_CMD     (1)
+#define T_ESCAPE_PATH_SEGMENT  (2)
+#define T_OS_ESCAPE_PATH       (4)
+#define T_HTTP_TOKEN_STOP      (8)
+#define T_ESCAPE_LOGITEM       (16)
+#define T_ESCAPE_FORENSIC      (32)
+
+static const unsigned char test_char_table[256] = {
+    32,62,62,62,62,62,62,62,62,62,63,62,62,62,62,62,62,62,62,62,
+    62,62,62,62,62,62,62,62,62,62,62,62,14,0,23,6,1,38,1,1,
+    9,9,1,0,8,0,0,10,0,0,0,0,0,0,0,0,0,0,40,15,
+    15,8,15,15,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,15,31,15,7,0,7,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,15,39,15,1,62,54,54,54,54,54,54,54,54,54,54,54,54,
+    54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,
+    54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,
+    54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,
+    54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,
+    54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,
+    54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54
+};

这跟使用 gen_test_char 生成的是否一致呢?
把 gen_test_char 拷贝到 H3 的板子的 /usr/sbin,运行看一下:

root@172:/# gen_test_char 
/* this file is automatically generated by gen_test_char, do not edit */
#define T_ESCAPE_SHELL_CMD     (1)
#define T_ESCAPE_PATH_SEGMENT  (2)
#define T_OS_ESCAPE_PATH       (4)
#define T_HTTP_TOKEN_STOP      (8)
#define T_ESCAPE_LOGITEM       (16)
#define T_ESCAPE_FORENSIC      (32)
#define T_ESCAPE_URLENCODED    (64)
#define T_HTTP_CTRLS           (128)
#define T_VCHAR_OBSTEXT        (256)

static const unsigned short test_char_table[256] = {
    0x0a8,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
    0x0fe,0x07e,0x0ff,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
    0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
    0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
    0x00e,0x140,0x15f,0x146,0x141,0x166,0x141,0x141,
    0x149,0x149,0x101,0x140,0x148,0x100,0x100,0x14a,
    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
    0x100,0x100,0x168,0x14b,0x14f,0x148,0x14f,0x14f,
    0x148,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
    0x100,0x100,0x100,0x14f,0x15f,0x14f,0x147,0x100,
    0x147,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
    0x100,0x100,0x100,0x14f,0x167,0x14f,0x141,0x0fe,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e 
};

结果果然不一样!!!!问题就出在这!!!!

所以需要修改openwrt\feeds\packages\net\apache\patches\002-test_char_h.patch 文件,openwrt 可以使用quilt 进行补丁修改,这里不具体介绍修改补丁的方法,直接附上我改好的 002-test_char_h.patch

--- /dev/null
+++ b/server/test_char.h
@@ -0,0 +1,45 @@
+/* this file is automatically generated by gen_test_char, do not edit */
+#define T_ESCAPE_SHELL_CMD     (1)
+#define T_ESCAPE_PATH_SEGMENT  (2)
+#define T_OS_ESCAPE_PATH       (4)
+#define T_HTTP_TOKEN_STOP      (8)
+#define T_ESCAPE_LOGITEM       (16)
+#define T_ESCAPE_FORENSIC      (32)
+#define T_ESCAPE_URLENCODED    (64)
+#define T_HTTP_CTRLS           (128)
+#define T_VCHAR_OBSTEXT        (256)
+
+static const unsigned short test_char_table[256] = {
+    0x0a8,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
+    0x0fe,0x07e,0x0ff,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
+    0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
+    0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,0x0fe,
+    0x00e,0x140,0x15f,0x146,0x141,0x166,0x141,0x141,
+    0x149,0x149,0x101,0x140,0x148,0x100,0x100,0x14a,
+    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
+    0x100,0x100,0x168,0x14b,0x14f,0x148,0x14f,0x14f,
+    0x148,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
+    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
+    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
+    0x100,0x100,0x100,0x14f,0x15f,0x14f,0x147,0x100,
+    0x147,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
+    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
+    0x100,0x100,0x100,0x100,0x100,0x100,0x100,0x100,
+    0x100,0x100,0x100,0x14f,0x167,0x14f,0x141,0x0fe,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,
+    0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e,0x17e 
+};
--- a/server/util.c
+++ b/server/util.c
@@ -96,6 +96,16 @@
 #undef APLOG_MODULE_INDEX
 #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
 
+#define T_ESCAPE_SHELL_CMD    (0x01)
+#define T_ESCAPE_PATH_SEGMENT (0x02)
+#define T_OS_ESCAPE_PATH      (0x04)
+#define T_HTTP_TOKEN_STOP     (0x08)
+#define T_ESCAPE_LOGITEM      (0x10)
+#define T_ESCAPE_FORENSIC     (0x20)
+#define T_ESCAPE_URLENCODED   (0x40)
+#define T_HTTP_CTRLS          (0x80)
+#define T_VCHAR_OBSTEXT      (0x100)
+
 /*
  * Examine a field value (such as a media-/content-type) string and return
  * it sans any parameters; e.g., strip off any ';charset=foo' and the like.

替换补丁文件后,从新编译,测试结果:
在这里插入图片描述
耶!终于OK了!

总结:
AH02418: HTTP Request Line; Unrecognized protocol ‘HTTP/0.9’ (perhaps whitespace was injected?)
问题原因:server/test_char.h 内容不对
解决办法:生成正确的 test_char.h 文件,从新编译

生成正确的 test_char.h 文件方法,拷贝编译目录 httpd-2.x.x/server/gen_test_char 到板子上运行,
其输出结果就是正确的 test_char.h 。

你遇到的这些错误: ``` lexer.l:8: unrecognized %option: / lexer.l:9: unrecognized %option: / lexer.l:10: unrecognized %option: / lexer.l:13: unrecognized rule ... ``` 说明 **Flex 无法识别你的 `%option` 指令和词法规则**,根本原因是:**你在 Flex 文件中使用了错误的注释语法!** --- ## ❌ 错误原因 你在 `lexer.l` 中写了: ```c %option bison-bridge // 启用与 Bison 的 YYSTYPE 兼容模式 %option noyywrap // 避免链接时报 yywrap 未定义 %option yylineno // 支持行号追踪 ``` 但 **Flex 不支持 C++ 风格的 `//` 注释!** 👉 Flex 在扫描 `.l` 文件时,会把 `//` 当成两个斜杠字符,而不是注释。 于是它试图将 `" 启用与 Bison..."` 解析为规则或选项,导致: - `unrecognized %option: /` → 把 `/` 当成了非法字符 - `unrecognized rule` → 把后面的字符串当成了无效的正则表达式 --- ## ✅ 正确做法:使用 C 风格注释 `/* ... */` ### ✅ 修正后的 `lexer.l`(关键修复) ```c %{ #include <stdio.h> #include <string.h> #include "parser.tab.h" /* 必须包含 Bison 生成的头文件 */ %} /* ============== 关键选项 ============== */ %option bison-bridge /* 启用与 Bison 的 YYSTYPE 兼容模式 */ %option noyywrap /* 避免链接时报 yywrap 未定义 */ %option yylineno /* 支持行号追踪 */ %% /* ============== 词法规则 ============== */ [ \t\n] ; /* 忽略空白字符 */ int { return INT_TYPE; } float { return FLOAT_TYPE; } void { return VOID; } main { return MAIN; } if { return IF; } else { return ELSE; } while { return WHILE; } for { return FOR; } "+" { return PLUS; } "-" { return MINUS; } "*" { return TIMES; } "=" { return ASSIGN; } ";" { return SEMI; } "," { return COMMA; } "(" { return LPAREN; } ")" { return RPAREN; } "{" { return LBRACE; } "}" { return RBRACE; } "<" { return LT; } ">" { return GT; } "<=" { return LE; } ">=" { return GE; } "==" { return EQ; } "!=" { return NE; } "||" { return OR; } "&&" { return AND; } "!" { return NOT; } [a-zA-Z_][a-zA-Z0-9_]* { strcpy(yylval.str, yytext); /* 使用 str 字段保存标识符名 */ return ID; } [0-9]+ { yylval.num = atoi(yytext); /* 将数字转为整数并赋值给 num 字段 */ return DIGIT; } . { printf("⚠️ 未知字符: %s\n", yytext); } %% /* 不需要写 yywrap(),因为 %option noyywrap */ ``` --- ## 🔧 重点说明 | 内容 | 正确写法 | |------|----------| | 注释 | 只能用 `/* ... */`,不能用 `//` | | `%option` 位置 | 必须在第一段(`%{...%}` 之后,`%%` 之前) | | 头文件包含 | `#include "parser.tab.h"` 必须存在且路径正确 | | 编译顺序 | 先运行 `bison -d parser.y` 生成 `parser.tab.h` | --- ## 🛠️ 完整编译流程(再次强调) ```bash # 1. 先用 Bison 生成 .tab.c 和 .tab.h bison -d parser.y -o parser.tab.c # 2. 再用 Flex 生成 lex.yy.c(此时 parser.tab.h 已存在) flex lexer.l -o lex.yy.c # 3. 最后编译链接 gcc lex.yy.c parser.tab.c -o tpl_parser ``` --- ## ✅ 测试运行 创建测试文件: ```bash cat > test.tpl << &#39;EOF&#39; int main() { int a; a = 1; while (a < 10) { a = a + 1; } } EOF ``` 运行程序: ```bash ./tpl_parser test.tpl ``` 输出应为: ``` 开始语法分析... ✅ 语法分析成功!生成语法分析树: Program → int main() { VarDecl: Declare int a; Assign: a = 1; while (a < 10) { Assign: a = (a + 1); }; } ``` --- ## ✅ 总结:Flex 文件书写规范 | 规则 | 要求 | |------|------| | 注释 | 必须使用 `/* ... */`,禁止使用 `//` | | `%option` | 写在 `%{...%}` 之后、`%%` 之前 | | 包含头文件 | `#include "parser.tab.h"` 必须放在 `%{...%}` 中 | | 编译顺序 | 先 `bison` 后 `flex` | | 调试技巧 | 可加 `--debug` 参数查看 flex 扫描过程 | --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值