feature: implemented the ngx.req.raw_header() function for returning the original raw HTTP protocol header string received by Nginx. thanks Matthieu Tourne for requesting this.

This commit is contained in:
agentzh (Yichun Zhang) 2013-03-06 17:14:12 -08:00
Родитель 4927e75b05
Коммит c95456c872
7 изменённых файлов: 650 добавлений и 4 удалений

34
README
Просмотреть файл

@ -2154,6 +2154,40 @@ Nginx API for Lua
This method was first introduced in the "v0.7.17" release.
ngx.req.raw_header
syntax: *str = ngx.req.raw_header(no_request_line?)*
context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*,
header_filter_by_lua**
Returns the original raw HTTP protocol header received by the Nginx
server.
By default, the request line and trailing "CR LF" terminator will also
be included. For example,
ngx.print(ngx.req.raw_header())
gives something like this:
GET /t HTTP/1.1
Host: localhost
Connection: close
Foo: bar
You can specify the optional "no_request_line" argument as a "true"
value to exclude the request line from the result. For example,
ngx.print(ngx.req.raw_header(true))
outputs something like this:
Host: localhost
Connection: close
Foo: bar
This method was first introduced in the "v0.7.17" release.
ngx.req.get_method
syntax: *method_name = ngx.req.get_method()*

Просмотреть файл

@ -1969,6 +1969,48 @@ Returns the HTTP version number for the current request as a Lua number.
Current possible values are 1.0, 1.1, and 0.9. Returns `nil` for unrecognized values.
This method was first introduced in the `v0.7.17` release.
ngx.req.raw_header
------------------
**syntax:** *str = ngx.req.raw_header(no_request_line?)*
**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua**
Returns the original raw HTTP protocol header received by the Nginx server.
By default, the request line and trailing `CR LF` terminator will also be included. For example,
ngx.print(ngx.req.raw_header())
gives something like this:
GET /t HTTP/1.1
Host: localhost
Connection: close
Foo: bar
You can specify the optional
`no_request_line` argument as a `true` value to exclude the request line from the result. For example,
ngx.print(ngx.req.raw_header(true))
outputs something like this:
Host: localhost
Connection: close
Foo: bar
This method was first introduced in the `v0.7.17` release.
ngx.req.get_method

Просмотреть файл

@ -1910,6 +1910,47 @@ Current possible values are 1.0, 1.1, and 0.9. Returns <code>nil</code> for unre
This method was first introduced in the <code>v0.7.17</code> release.
== ngx.req.raw_header ==
'''syntax:''' ''str = ngx.req.raw_header(no_request_line?)''
'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*''
Returns the original raw HTTP protocol header received by the Nginx server.
By default, the request line and trailing <code>CR LF</code> terminator will also be included. For example,
<geshi lang="lua">
ngx.print(ngx.req.raw_header())
</geshi>
gives something like this:
<geshi lang="text">
GET /t HTTP/1.1
Host: localhost
Connection: close
Foo: bar
</geshi>
You can specify the optional
<code>no_request_line</code> argument as a <code>true</code> value to exclude the request line from the result. For example,
<geshi lang="lua">
ngx.print(ngx.req.raw_header(true))
</geshi>
outputs something like this:
<geshi lang="text">
Host: localhost
Connection: close
Foo: bar
</geshi>
This method was first introduced in the <code>v0.7.17</code> release.
== ngx.req.get_method ==
'''syntax:''' ''method_name = ngx.req.get_method()''

Просмотреть файл

@ -18,6 +18,7 @@
static int ngx_http_lua_ngx_req_http_version(lua_State *L);
static int ngx_http_lua_ngx_req_raw_header(lua_State *L);
static int ngx_http_lua_ngx_req_header_set_helper(lua_State *L);
static int ngx_http_lua_ngx_header_get(lua_State *L);
static int ngx_http_lua_ngx_header_set(lua_State *L);
@ -62,6 +63,172 @@ ngx_http_lua_ngx_req_http_version(lua_State *L)
}
static int
ngx_http_lua_ngx_req_raw_header(lua_State *L)
{
int n;
u_char *data, *p, *last;
unsigned no_req_line = 0, found;
size_t size;
ngx_buf_t *b, *first = NULL;
ngx_int_t i;
ngx_http_request_t *r;
ngx_http_connection_t *hc;
n = lua_gettop(L);
if (n > 0) {
no_req_line = lua_toboolean(L, 1);
}
dd("no req line: %d", (int) no_req_line);
lua_pushlightuserdata(L, &ngx_http_lua_request_key);
lua_rawget(L, LUA_GLOBALSINDEX);
r = lua_touserdata(L, -1);
lua_pop(L, 1);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
hc = r->http_connection;
if (hc->nbusy) {
b = NULL; /* to suppress a gcc warning */
size = 0;
for (i = 0; i < hc->nbusy; i++) {
b = hc->busy[i];
if (first == NULL) {
if (r->request_line.data >= b->pos
|| r->request_line.data + r->request_line.len + 2
<= b->start)
{
continue;
}
dd("found first at %d", (int) i);
first = b;
}
size += b->pos - b->start;
if (b == r->header_in) {
break;
}
}
} else {
if (r != r->main) {
b = r->main->header_in;
} else {
b = r->header_in;
}
if (b == NULL) {
lua_pushnil(L);
return 1;
}
size = b->pos - r->request_line.data;
}
data = lua_newuserdata(L, size);
if (hc->nbusy) {
last = data;
found = 0;
for (i = 0; i < hc->nbusy; i++) {
b = hc->busy[i];
if (!found) {
if (b != first) {
continue;
}
dd("found first");
found = 1;
}
p = last;
if (b == first) {
dd("request line: %.*s", (int) r->request_line.len,
r->request_line.data);
if (no_req_line) {
last = ngx_copy(last,
r->request_line.data + r->request_line.len
+ 2,
b->pos - r->request_line.data
- r->request_line.len - 2);
} else {
last = ngx_copy(last,
r->request_line.data,
b->pos - r->request_line.data);
}
} else {
last = ngx_copy(last, b->start, b->pos - b->start);
}
#if 1
/* skip truncated header entries (if any) */
while (last > p && last[-1] != LF) {
last--;
}
#endif
for (; p != last; p++) {
if (*p == '\0') {
if (p + 1 == last) {
/* XXX this should not happen */
dd("found string end!!");
} else if (*(p + 1) == LF) {
*p = CR;
} else {
*p = ':';
}
}
}
if (b == r->header_in) {
break;
}
}
} else {
if (no_req_line) {
last = ngx_copy(data,
r->request_line.data + r->request_line.len + 2,
size - r->request_line.len - 2);
} else {
last = ngx_copy(data, r->request_line.data, size);
}
for (p = data; p != last; p++) {
if (*p == '\0') {
if (p + 1 != last && *(p + 1) == LF) {
*p = CR;
} else {
*p = ':';
}
}
}
}
lua_pushlstring(L, (char *) data, last - data);
return 1;
}
static int
ngx_http_lua_ngx_req_get_headers(lua_State *L) {
ngx_list_part_t *part;
@ -506,6 +673,9 @@ ngx_http_lua_inject_req_header_api(ngx_log_t *log, lua_State *L)
lua_pushcfunction(L, ngx_http_lua_ngx_req_http_version);
lua_setfield(L, -2, "http_version");
lua_pushcfunction(L, ngx_http_lua_ngx_req_raw_header);
lua_setfield(L, -2, "raw_header");
lua_pushcfunction(L, ngx_http_lua_ngx_req_header_clear);
lua_setfield(L, -2, "clear_header");

Просмотреть файл

@ -2005,7 +2005,7 @@ ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L)
{
/* ngx.req table */
lua_createtable(L, 0 /* narr */, 22 /* nrec */); /* .req */
lua_createtable(L, 0 /* narr */, 23 /* nrec */); /* .req */
ngx_http_lua_inject_req_header_api(log, L);
ngx_http_lua_inject_req_uri_api(log, L);

Просмотреть файл

@ -124,7 +124,7 @@ n = 1
--- request
GET /test
--- response_body
n = 22
n = 23
--- no_error_log
[error]
@ -146,7 +146,7 @@ n = 22
--- request
GET /test
--- response_body
n = 22
n = 23
--- no_error_log
[error]
@ -173,7 +173,7 @@ n = 22
--- request
GET /test
--- response_body
n = 22
n = 23
--- no_error_log
[error]

359
t/104-req-raw-header.t Normal file
Просмотреть файл

@ -0,0 +1,359 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib 'lib';
use Test::Nginx::Socket;
#worker_connections(1014);
#master_on();
#workers(2);
#log_level('warn');
repeat_each(2);
plan tests => repeat_each() * (blocks() * 3 + 6);
#no_diff();
no_long_string();
run_tests();
__DATA__
=== TEST 1: small header
--- config
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- request
GET /t
--- response_body eval
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: Close\r
\r
}
--- no_error_log
[error]
=== TEST 2: large header
--- config
client_header_buffer_size 10;
large_client_header_buffers 30 561;
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- request
GET /t
--- more_headers eval
CORE::join "\n", map { "Header$_: value-$_" } 1..512
--- response_body eval
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: Close\r
}
.(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- no_error_log
[error]
=== TEST 3: large header (no request line)
--- config
client_header_buffer_size 10;
large_client_header_buffers 30 561;
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header(true))
';
}
--- request
GET /t
--- more_headers eval
CORE::join "\n", map { "Header$_: value-$_" } 1..512
--- response_body eval
qq{Host: localhost\r
Connection: Close\r
}
.(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- no_error_log
[error]
=== TEST 4: small header (no request line)
--- config
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header(true))
';
}
--- request
GET /t
--- response_body eval
qq{Host: localhost\r
Connection: Close\r
\r
}
--- no_error_log
[error]
=== TEST 5: small header (no request line, with leading CRLF)
--- config
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header(true))
';
}
--- raw_request eval
"\r\nGET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
\r
"
--- response_body eval
qq{Host: localhost\r
Connection: close\r
\r
}
--- no_error_log
[error]
=== TEST 6: small header, with leading CRLF
--- config
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- raw_request eval
"\r\nGET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
\r
"
--- response_body eval
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
\r
}
--- no_error_log
[error]
=== TEST 7: large header, with leading CRLF
--- config
client_header_buffer_size 10;
large_client_header_buffers 30 561;
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- raw_request eval
"\r\nGET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
".
(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- response_body eval
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
}
.(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- no_error_log
[error]
=== TEST 8: large header, with leading CRLF, excluding request line
--- config
client_header_buffer_size 10;
large_client_header_buffers 30 561;
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header(true))
';
}
--- raw_request eval
"\r\nGET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
".
(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- response_body eval
qq{Host: localhost\r
Connection: close\r
}
.(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- no_error_log
[error]
=== TEST 9: large header, with lots of leading CRLF, excluding request line
--- config
client_header_buffer_size 10;
large_client_header_buffers 30 561;
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header(true))
';
}
--- raw_request eval
("\r\n" x 534) . "GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
".
(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- response_body eval
qq{Host: localhost\r
Connection: close\r
}
.(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"
--- no_error_log
[error]
=== TEST 10: small header, pipelined
--- config
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- pipelined_requests eval
["GET /t", "GET /th"]
--- more_headers
Foo: bar
--- response_body eval
[qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: keep-alive\r
Foo: bar\r
\r
}, qq{GET /th HTTP/1.1\r
Host: localhost\r
Connection: close\r
Foo: bar\r
\r
}]
--- no_error_log
[error]
=== TEST 11: large header, pipelined
--- config
client_header_buffer_size 10;
large_client_header_buffers 30 561;
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- pipelined_requests eval
["GET /t", "GET /t"]
--- more_headers eval
CORE::join "\n", map { "Header$_: value-$_" } 1..512
--- response_body eval
my $headers = (CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n";
[qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: keep-alive\r
$headers},
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
$headers}]
--- no_error_log
[error]
=== TEST 12: small header, multi-line header
--- config
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- raw_request eval
"GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
Foo: bar baz\r
blah
\r
"
--- response_body eval
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
Foo: bar baz\r
blah
\r
}
--- no_error_log
[error]
=== TEST 13: large header, multi-line header
--- config
client_header_buffer_size 10;
large_client_header_buffers 50 567;
location /t {
content_by_lua '
ngx.print(ngx.req.raw_header())
';
}
--- raw_request eval
my $headers = (CORE::join "\r\n", map { "Header$_: value-$_\r\n hello $_ world blah blah" } 1..512) . "\r\n\r\n";
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
$headers}
--- response_body eval
qq{GET /t HTTP/1.1\r
Host: localhost\r
Connection: close\r
}
.(CORE::join "\r\n", map { "Header$_: value-$_\r\n hello $_ world blah blah" } 1..512) . "\r\n\r\n"
--- no_error_log
[error]