From 321d9cbb2f6e9cfa45dc82528fa5d29fa0a2a45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=9B=E4=BC=9F?= Date: Wed, 23 Aug 2017 14:14:52 +0800 Subject: [PATCH 01/12] ngx ssl add client addr function --- src/ngx_http_lua_ssl_certby.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 95be47f6b5..9331b9c3e4 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -883,6 +883,30 @@ ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name, #endif } +int +ngx_http_lua_ffi_ssl_client_addr(ngx_http_request_t *r, char **addr, + size_t *addrlen, char **err) +{ + ngx_ssl_conn_t *ssl_conn; + ngx_str_t *addr_text; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + addr_text = &r->connection->addr_text; + *addr = (char *)addr_text->data; + *addrlen = addr_text->len; + + return NGX_OK; +} int ngx_http_lua_ffi_cert_pem_to_der(const u_char *pem, size_t pem_len, u_char *der, From 0fca791c0099cec78c70795f9d62d7b4f2ad4b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=9B=E4=BC=9F?= Date: Thu, 24 Aug 2017 10:39:22 +0800 Subject: [PATCH 02/12] fix code style --- src/ngx_http_lua_ssl_certby.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 9331b9c3e4..6534a7a496 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -902,7 +902,7 @@ ngx_http_lua_ffi_ssl_client_addr(ngx_http_request_t *r, char **addr, } addr_text = &r->connection->addr_text; - *addr = (char *)addr_text->data; + *addr = (char *) addr_text->data; *addrlen = addr_text->len; return NGX_OK; From 80009dc713dfa9eaf47372adcf8ab0be7c416112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=9B=E4=BC=9F?= Date: Thu, 24 Aug 2017 21:39:53 +0800 Subject: [PATCH 03/12] returning empty string if using unix domain socket --- src/ngx_http_lua_ssl_certby.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 6534a7a496..d461e60194 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -901,6 +901,12 @@ ngx_http_lua_ffi_ssl_client_addr(ngx_http_request_t *r, char **addr, return NGX_ERROR; } +#if (NGX_HAVE_UNIX_DOMAIN) + *addr = ""; + *addrlen = 0; + return NGX_OK; +#endif + addr_text = &r->connection->addr_text; *addr = (char *) addr_text->data; *addrlen = addr_text->len; From dfcdb6aa5fb17166f180f253c350c44c719ed6a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=9B=E4=BC=9F?= Date: Thu, 24 Aug 2017 21:43:42 +0800 Subject: [PATCH 04/12] add test cases for client_addr --- t/139-ssl-cert-by.t | 195 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/t/139-ssl-cert-by.t b/t/139-ssl-cert-by.t index b521e9e88b..2c1d942ac6 100644 --- a/t/139-ssl-cert-by.t +++ b/t/139-ssl-cert-by.t @@ -1872,3 +1872,198 @@ failed to do SSL handshake: handshake failed qr/\[alert\] .*? no ssl_certificate_by_lua\* defined in server ~test2\\\.com\b/, qr/\[crit\] .*? SSL_do_handshake\(\) failed\b/, ] + + + +=== TEST 22: get client_addr - IPv4 +--- http_config + server { + listen 127.0.0.1:12345 ssl; + server_name test.com; + + ssl_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local addr, err = ssl.client_addr() + print("client ip: " .. addr) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +client ip: 127.0.0.1 + +--- no_error_log +[error] +[alert] + + +=== TEST 23: get client_addr - unix domain socket +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local addr, err = ssl.client_addr() + print("client ip: " .. addr) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +client ip: + +--- no_error_log +[error] +[alert] From 3b95604d96f7c51a052670bacf97d291a7ca9b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=9B=E4=BC=9F?= Date: Fri, 25 Aug 2017 11:11:12 +0800 Subject: [PATCH 05/12] rename index and change ip to addr --- t/139-ssl-cert-by.t | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/t/139-ssl-cert-by.t b/t/139-ssl-cert-by.t index 2c1d942ac6..9d78e3e052 100644 --- a/t/139-ssl-cert-by.t +++ b/t/139-ssl-cert-by.t @@ -1884,7 +1884,7 @@ qr/\[crit\] .*? SSL_do_handshake\(\) failed\b/, ssl_certificate_by_lua_block { local ssl = require "ngx.ssl" local addr, err = ssl.client_addr() - print("client ip: " .. addr) + print("client addr: " .. addr) } ssl_certificate ../../cert/test.crt; ssl_certificate_key ../../cert/test.key; @@ -1965,13 +1965,14 @@ received: foo close: 1 nil --- error_log -client ip: 127.0.0.1 +client addr: 127.0.0.1 --- no_error_log [error] [alert] + === TEST 23: get client_addr - unix domain socket --- http_config server { @@ -1981,7 +1982,7 @@ client ip: 127.0.0.1 ssl_certificate_by_lua_block { local ssl = require "ngx.ssl" local addr, err = ssl.client_addr() - print("client ip: " .. addr) + print("client addr: " .. addr) } ssl_certificate ../../cert/test.crt; ssl_certificate_key ../../cert/test.key; @@ -2062,7 +2063,7 @@ received: foo close: 1 nil --- error_log -client ip: +client addr: --- no_error_log [error] From 4e144b1df30da9d1bae828940efdc4950925df3e Mon Sep 17 00:00:00 2001 From: wangjunwei Date: Fri, 6 Oct 2017 22:19:31 +0800 Subject: [PATCH 06/12] rename client_addr to raw_client_addr --- src/ngx_http_lua_ssl_certby.c | 56 ++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index d461e60194..8094610c0b 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -884,11 +884,18 @@ ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name, } int -ngx_http_lua_ffi_ssl_client_addr(ngx_http_request_t *r, char **addr, - size_t *addrlen, char **err) +ngx_http_lua_ffi_ssl_raw_client_addr(ngx_http_request_t *r, char **addr, + size_t *addrlen, int *addrtype, char **err) { +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un *saun; +#endif ngx_ssl_conn_t *ssl_conn; - ngx_str_t *addr_text; + ngx_connection_t *c; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif if (r->connection == NULL || r->connection->ssl == NULL) { *err = "bad request"; @@ -901,15 +908,44 @@ ngx_http_lua_ffi_ssl_client_addr(ngx_http_request_t *r, char **addr, return NGX_ERROR; } -#if (NGX_HAVE_UNIX_DOMAIN) - *addr = ""; - *addrlen = 0; - return NGX_OK; + c = ngx_ssl_get_connection(ssl_conn); + + switch (c->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->sockaddr; + *addrlen = 16; + *addr = (char *) &sin6->sin6_addr.s6_addr; + *addrtype = NGX_HTTP_LUA_ADDR_TYPE_INET6; + + break; +#endif + +# if(NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + saun = (struct sockaddr_un *)c->sockaddr; + /* on Linux sockaddr might not include sun_path at all */ + if(c->socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) + { + *addr = ""; + *addrlen = 0; + } else { + *addr = saun->sun_path; + *addrlen = ngx_strlen(saun->sun_path); + } + + *addrtype = NGX_HTTP_LUA_ADDR_TYPE_UNIX; + break; #endif - addr_text = &r->connection->addr_text; - *addr = (char *) addr_text->data; - *addrlen = addr_text->len; + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->sockaddr; + *addr = (char *) &sin->sin_addr.s_addr; + *addrlen = 4; + *addrtype = NGX_HTTP_LUA_ADDR_TYPE_INET; + break; + } return NGX_OK; } From e8705ddb9daa2db384270bbe4b98d41b1f2e324d Mon Sep 17 00:00:00 2001 From: wangjunwei Date: Sat, 7 Oct 2017 13:14:26 +0800 Subject: [PATCH 07/12] fix code style problems --- src/ngx_http_lua_ssl_certby.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 8094610c0b..020b341873 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -883,16 +883,17 @@ ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name, #endif } + int ngx_http_lua_ffi_ssl_raw_client_addr(ngx_http_request_t *r, char **addr, size_t *addrlen, int *addrtype, char **err) { #if (NGX_HAVE_UNIX_DOMAIN) - struct sockaddr_un *saun; + struct sockaddr_un *saun; #endif - ngx_ssl_conn_t *ssl_conn; - ngx_connection_t *c; - struct sockaddr_in *sin; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif @@ -922,14 +923,14 @@ ngx_http_lua_ffi_ssl_raw_client_addr(ngx_http_request_t *r, char **addr, break; #endif -# if(NGX_HAVE_UNIX_DOMAIN) +# if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: saun = (struct sockaddr_un *)c->sockaddr; - /* on Linux sockaddr might not include sun_path at all */ - if(c->socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) - { + /* on Linux sockaddr might not include sun_path at all */ + if (c->socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) { *addr = ""; *addrlen = 0; + } else { *addr = saun->sun_path; *addrlen = ngx_strlen(saun->sun_path); @@ -950,6 +951,7 @@ ngx_http_lua_ffi_ssl_raw_client_addr(ngx_http_request_t *r, char **addr, return NGX_OK; } + int ngx_http_lua_ffi_cert_pem_to_der(const u_char *pem, size_t pem_len, u_char *der, char **err) From e9ba2388ea444f7e1dc83465a25b681535aca808 Mon Sep 17 00:00:00 2001 From: wangjunwei Date: Sat, 7 Oct 2017 13:32:57 +0800 Subject: [PATCH 08/12] point lua-resty-core to my own branch temporarily to make CI pass --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5e2359724c..ad921d8ec5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,6 +84,7 @@ install: - git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module - git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core + - git clone -b ngx-ssl-add-client-addr-function https://github.com/smartwjw/lua-nginx-module.git ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module From 748a824a0d6b6ec610faaed1bb8cfd4effd58e28 Mon Sep 17 00:00:00 2001 From: wangjunwei Date: Sat, 7 Oct 2017 13:39:04 +0800 Subject: [PATCH 09/12] fix wrong repo url --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad921d8ec5..9d3c28f506 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,8 +83,7 @@ install: - git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module - git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module - git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module - - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core - - git clone -b ngx-ssl-add-client-addr-function https://github.com/smartwjw/lua-nginx-module.git ../lua-resty-core + - git clone -b ngx-ssl-add-client-addr-function https://github.com/smartwjw/lua-resty-core.git ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module From dcc5cd7616769c7ee825a23f112e6242b74192a2 Mon Sep 17 00:00:00 2001 From: wangjunwei Date: Sat, 7 Oct 2017 21:32:46 +0800 Subject: [PATCH 10/12] fix test problems --- t/139-ssl-cert-by.t | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/t/139-ssl-cert-by.t b/t/139-ssl-cert-by.t index 9d78e3e052..7038f027ad 100644 --- a/t/139-ssl-cert-by.t +++ b/t/139-ssl-cert-by.t @@ -1875,7 +1875,7 @@ qr/\[crit\] .*? SSL_do_handshake\(\) failed\b/, -=== TEST 22: get client_addr - IPv4 +=== TEST 22: get raw_client_addr - IPv4 --- http_config server { listen 127.0.0.1:12345 ssl; @@ -1883,8 +1883,11 @@ qr/\[crit\] .*? SSL_do_handshake\(\) failed\b/, ssl_certificate_by_lua_block { local ssl = require "ngx.ssl" - local addr, err = ssl.client_addr() - print("client addr: " .. addr) + local byte = string.byte + local addr, addrtype, err = ssl.raw_client_addr() + local ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2), + byte(addr, 3), byte(addr, 4) + print("client ip: " .. ip) } ssl_certificate ../../cert/test.crt; ssl_certificate_key ../../cert/test.key; @@ -1965,7 +1968,7 @@ received: foo close: 1 nil --- error_log -client addr: 127.0.0.1 +client ip: 127.0.0.1 --- no_error_log [error] @@ -1973,7 +1976,7 @@ client addr: 127.0.0.1 -=== TEST 23: get client_addr - unix domain socket +=== TEST 23: get raw_client_addr - unix domain socket --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -1981,8 +1984,8 @@ client addr: 127.0.0.1 ssl_certificate_by_lua_block { local ssl = require "ngx.ssl" - local addr, err = ssl.client_addr() - print("client addr: " .. addr) + local addr, addrtyp, err = ssl.raw_client_addr() + print("client socket file: " .. addr) } ssl_certificate ../../cert/test.crt; ssl_certificate_key ../../cert/test.key; @@ -2063,7 +2066,7 @@ received: foo close: 1 nil --- error_log -client addr: +client socket file: --- no_error_log [error] From c90d70b232df0971da7107ff7f65a451f460ddee Mon Sep 17 00:00:00 2001 From: wangjunwei Date: Sun, 8 Oct 2017 06:16:16 +0000 Subject: [PATCH 11/12] fix test problems --- t/139-ssl-cert-by.t | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/t/139-ssl-cert-by.t b/t/139-ssl-cert-by.t index 7038f027ad..77a986c83e 100644 --- a/t/139-ssl-cert-by.t +++ b/t/139-ssl-cert-by.t @@ -1877,6 +1877,8 @@ qr/\[crit\] .*? SSL_do_handshake\(\) failed\b/, === TEST 22: get raw_client_addr - IPv4 --- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + server { listen 127.0.0.1:12345 ssl; server_name test.com; @@ -1886,8 +1888,8 @@ qr/\[crit\] .*? SSL_do_handshake\(\) failed\b/, local byte = string.byte local addr, addrtype, err = ssl.raw_client_addr() local ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2), - byte(addr, 3), byte(addr, 4) - print("client ip: " .. ip) + byte(addr, 3), byte(addr, 4)) + print("client ip: ", ip) } ssl_certificate ../../cert/test.crt; ssl_certificate_key ../../cert/test.key; @@ -1978,6 +1980,8 @@ client ip: 127.0.0.1 === TEST 23: get raw_client_addr - unix domain socket --- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; server_name test.com; @@ -1985,7 +1989,7 @@ client ip: 127.0.0.1 ssl_certificate_by_lua_block { local ssl = require "ngx.ssl" local addr, addrtyp, err = ssl.raw_client_addr() - print("client socket file: " .. addr) + print("client socket file: ", addr) } ssl_certificate ../../cert/test.crt; ssl_certificate_key ../../cert/test.key; From 76937f55458f1bdc94dd9d550cd5920f65045764 Mon Sep 17 00:00:00 2001 From: WenMing Date: Thu, 16 Nov 2017 22:40:14 +0800 Subject: [PATCH 12/12] fixed test case. --- t/139-ssl-cert-by.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/139-ssl-cert-by.t b/t/139-ssl-cert-by.t index 77a986c83e..24810bf347 100644 --- a/t/139-ssl-cert-by.t +++ b/t/139-ssl-cert-by.t @@ -11,7 +11,7 @@ my $openssl_version = eval { `$NginxBinary -V 2>&1` }; if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) { plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1"); } else { - plan tests => repeat_each() * (blocks() * 6 + 6); + plan tests => repeat_each() * (blocks() * 6 + 4); } $ENV{TEST_NGINX_HTML_DIR} ||= html_dir();