Index: tests/mod-redirect.t =================================================================== --- tests/mod-redirect.t (revision 1121) +++ tests/mod-redirect.t (working copy) @@ -8,7 +8,7 @@ use strict; use IO::Socket; -use Test::More tests => 6; +use Test::More tests => 8; use LightyTest; my $tf = LightyTest->new(); @@ -21,19 +21,37 @@ Host: vvv.example.org EOF ); -$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:'.$tf->{PORT}.'/' } ]; +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:'.$tf->{PORT}.'/$' } ]; ok($tf->handle_http($t) == 0, 'external redirect'); +$t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:'.$tf->{PORT}.'/$vvv%2f' } ]; +ok($tf->handle_http($t) == 0, 'external redirect with urlencoded'); + $t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:'.$tf->{PORT}.'/zzz' } ]; +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:'.$tf->{PORT}.'/%zzz' } ]; ok($tf->handle_http($t) == 0, 'external redirect with cond regsub'); $t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => 'http://localhost:'.$tf->{PORT}.'/%zzz%2f' } ]; +ok($tf->handle_http($t) == 0, 'external redirect with cond regsub and urlencoded'); + +$t->{REQUEST} = ( < "http://localhost:2048/" ) + url.redirect = ( "^/redirect/(.*)?$" => "http://localhost:2048/$$$!1" ) } -$HTTP["host"] =~ "(zzz).example.org" { - url.redirect = ( "^/redirect/$" => "http://localhost:2048/%1" ) +$HTTP["host"] =~ "zzz.example.org" { + url.redirect = ( "^/redirect/$" => "http://localhost:2048/" ) + $HTTP["referer"] =~ "(.+)" { + url.redirect = ( "^/redirect/$" => "http://localhost:2048/%%%!1" ) + } } $HTTP["host"] =~ "(remoteip)\.example\.org" { Index: src/buffer.c =================================================================== --- src/buffer.c (revision 1121) +++ src/buffer.c (working copy) @@ -760,7 +760,7 @@ map = encoded_chars_hex; break; case ENCODING_UNSET: - break; + return buffer_append_string_len(b, s, s_len); } assert(map != NULL); Index: src/plugin.h =================================================================== --- src/plugin.h (revision 1121) +++ src/plugin.h (working copy) @@ -93,7 +93,7 @@ int config_setup_connection(server *srv, connection *con); int config_patch_connection(server *srv, connection *con, comp_key_t comp); int config_check_cond(server *srv, connection *con, data_config *dc); -int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n); +int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n, buffer_encoding_t encoding); int config_exec_pcre_keyvalue_buffer(connection *con, pcre_keyvalue_buffer *kvb, data_config *context, buffer *match_buf, buffer *result); #endif Index: src/keyvalue.h =================================================================== --- src/keyvalue.h (revision 1121) +++ src/keyvalue.h (working copy) @@ -49,13 +49,30 @@ char *value; } s_keyvalue; +typedef enum { + PCRE_PIECE_STRING, + PCRE_PIECE_SUB_KEY, + PCRE_PIECE_SUB_KEY_ENCODE, + PCRE_PIECE_SUB_COND, + PCRE_PIECE_SUB_COND_ENCODE +} pcre_piece_type_t; typedef struct { + pcre_piece_type_t type; + union { + buffer *buf; + size_t num; + } u; +} pcre_piece_t; + +typedef struct { #ifdef HAVE_PCRE_H pcre *key; pcre_extra *key_extra; #endif buffer *value; + pcre_piece_t *pieces; + size_t pieces_count; } pcre_keyvalue; typedef enum { HTTP_AUTH_BASIC, HTTP_AUTH_DIGEST } httpauth_type; Index: src/keyvalue.c =================================================================== --- src/keyvalue.c (revision 1121) +++ src/keyvalue.c (working copy) @@ -1,9 +1,11 @@ #include #include #include +#include #include "server.h" #include "keyvalue.h" +#include "buffer.h" static keyvalue http_versions[] = { { HTTP_VERSION_1_1, "HTTP/1.1" }, @@ -313,6 +315,85 @@ return kvb; } +static size_t pcre_keyvalue_parse_value(pcre_piece_t *result, buffer *value) +{ + size_t start; + size_t i; + size_t count = 0; + size_t pattern_len = value->used - 1; + const char *pattern = value->ptr; + int lastisstring = 0; + +#define ADD_PIECE_SUB(_type, _num) \ + if (result) { \ + result[count].type = _type; \ + result[count].u.num = _num; \ + } \ + count ++; \ + lastisstring = 0; + +#define ADD_PIECE_STRING(end) \ + if ((end) != start) { \ + if (lastisstring) { \ + if (result) { \ + buffer_append_string_len(result[count - 1].u.buf, pattern + start, (end) - start); \ + } \ + } \ + else { \ + if (result) { \ + result[count].type = PCRE_PIECE_STRING; \ + result[count].u.buf = buffer_init(); \ + buffer_append_string_len(result[count].u.buf, pattern + start, (end) - start); \ + } \ + count ++; \ + } \ + lastisstring = 1; \ + } + + start = 0; + + for (i = 0; i < pattern_len; i++) { + if (pattern[i] == '$' || pattern[i] == '%') { + if (isdigit((unsigned char)pattern[i + 1])) { + size_t num = pattern[i + 1] - '0'; + ADD_PIECE_STRING(i); + + if (pattern[i] == '$') { + ADD_PIECE_SUB(PCRE_PIECE_SUB_KEY, num); + } else { + ADD_PIECE_SUB(PCRE_PIECE_SUB_COND, num); + } + + i++; + start = i + 1; + } else if (pattern[i + 1] == '!' && isdigit((unsigned char)pattern[i + 2])) { + size_t num = pattern[i + 2] - '0'; + ADD_PIECE_STRING(i); + + if (pattern[i] == '$') { + ADD_PIECE_SUB(PCRE_PIECE_SUB_KEY_ENCODE, num); + } else { + ADD_PIECE_SUB(PCRE_PIECE_SUB_COND_ENCODE, num); + } + + i += 2; + start = i + 1; + } else if (pattern[i + 1] == pattern[i]) { + ADD_PIECE_STRING(i); + start = i; + /* add the % or $ */ + ADD_PIECE_STRING(i + 1); + i ++; + start = i + 1; + } + } + } + ADD_PIECE_STRING(pattern_len); +#undef ADD_PIECE_STRING + + return count; +} + int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, const char *value) { #ifdef HAVE_PCRE_H size_t i; @@ -357,6 +438,28 @@ } kv->value = buffer_init_string(value); + kv->pieces_count = pcre_keyvalue_parse_value(NULL, kv->value); + kv->pieces = calloc(kv->pieces_count, sizeof(*kv->pieces)); + pcre_keyvalue_parse_value(kv->pieces, kv->value); +#if 0 + fprintf(stderr, "pattern %s is parsed into:\n", kv->value->ptr); + for (i = 0; i < kv->pieces_count; i ++) { + if (kv->pieces[i].type == PCRE_PIECE_STRING) { + fprintf(stderr, "#%d PCRE_PIECE_STRING: %s\n", i, kv->pieces[i].u.buf->ptr); + } else { + const char *type; + switch (kv->pieces[i].type) { + case PCRE_PIECE_STRING: type = "PCRE_PIECE_STRING" ; break; + case PCRE_PIECE_SUB_KEY: type = "PCRE_PIECE_SUB_KEY" ; break; + case PCRE_PIECE_SUB_KEY_ENCODE: type = "PCRE_PIECE_SUB_KEY_ENCODE" ; break; + case PCRE_PIECE_SUB_COND: type = "PCRE_PIECE_SUB_COND" ; break; + case PCRE_PIECE_SUB_COND_ENCODE: type = "PCRE_PIECE_SUB_COND_ENCODE"; break; + default: SEGFAULT(); + } + fprintf(stderr, "#%d %s: %d\n", i, type, kv->pieces[i].u.num); + } + } +#endif kvb->used++; Index: src/configfile-glue.c =================================================================== --- src/configfile-glue.c (revision 1121) +++ src/configfile-glue.c (working copy) @@ -1,5 +1,4 @@ #include -#include #include "base.h" #include "buffer.h" @@ -446,7 +445,7 @@ return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE); } -int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n) +int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *result, int n, buffer_encoding_t encoding) { cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; if (n > cache->patterncount) { @@ -454,9 +453,10 @@ } n <<= 1; /* n *= 2 */ - buffer_append_string_len(buf, + buffer_append_string_encoded(result, cache->comp_value->ptr + cache->matches[n], - cache->matches[n + 1] - cache->matches[n]); + cache->matches[n + 1] - cache->matches[n], + encoding); return 1; } @@ -468,9 +468,7 @@ #ifdef HAVE_PCRE_H pcre *match; pcre_extra *extra; - const char *pattern; - size_t pattern_len; - int n; + int n, num; size_t i; pcre_keyvalue *kv; # define N 10 @@ -481,55 +479,54 @@ match = kv->key; extra = kv->key_extra; - pattern = kv->value->ptr; - pattern_len = kv->value->used - 1; if ((n = pcre_exec(match, extra, match_buf->ptr, match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { return n; } } else { - const char **list; - size_t start, end; + pcre_piece_t *pieces = kv->pieces; + size_t pieces_count = kv->pieces_count; size_t k; - /* it matched */ - pcre_get_substring_list(match_buf->ptr, ovec, n, &list); - /* search for $[0-9] */ buffer_reset(result); - start = 0; end = pattern_len; - for (k = 0; k < pattern_len; k++) { - if ((pattern[k] == '$' || pattern[k] == '%') && - isdigit((unsigned char)pattern[k + 1])) { - /* got one */ + for (k = 0; k < pieces_count; k ++) { + if (pieces[k].type == PCRE_PIECE_STRING) { + buffer_append_string_buffer(result, pieces[k].u.buf); + } + else { + num = pieces[k].u.num; + switch (pieces[k].type) { + case PCRE_PIECE_SUB_KEY: + if (num <= n) { + num <<= 1; /* num *= 2 */ + buffer_append_string_len(result, match_buf->ptr + ovec[num], ovec[num + 1] - ovec[num]); + } + break; - size_t num = pattern[k + 1] - '0'; + case PCRE_PIECE_SUB_KEY_ENCODE: + if (num <= n) { + num <<= 1; /* num *= 2 */ + buffer_append_string_encoded(result, match_buf->ptr + ovec[num], ovec[num + 1] - ovec[num], ENCODING_REL_URI_PART); + } + break; - end = k; + case PCRE_PIECE_SUB_COND: + config_append_cond_match_buffer(con, context, result, num, ENCODING_UNSET); + break; - buffer_append_string_len(result, pattern + start, end - start); - - if (pattern[k] == '$') { - /* n is always > 0 */ - if (num < (size_t)n) { - buffer_append_string(result, list[num]); - } - } else { - config_append_cond_match_buffer(con, context, result, num); + case PCRE_PIECE_SUB_COND_ENCODE: + config_append_cond_match_buffer(con, context, result, num, ENCODING_REL_URI_PART); + break; + case PCRE_PIECE_STRING: + SEGFAULT(); } - - k++; - start = k + 1; } } - buffer_append_string_len(result, pattern + start, pattern_len - start); - - pcre_free(list); - return i; } }