diff --git a/README.markdown b/README.markdown
index 3df44953e0..d9521fefca 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3185,6 +3185,7 @@ Nginx API for Lua
* [ngx.shared.DICT.lpop](#ngxshareddictlpop)
* [ngx.shared.DICT.rpop](#ngxshareddictrpop)
* [ngx.shared.DICT.llen](#ngxshareddictllen)
+* [ngx.shared.DICT.lindex](#ngxshareddictlindex)
* [ngx.shared.DICT.ttl](#ngxshareddictttl)
* [ngx.shared.DICT.expire](#ngxshareddictexpire)
* [ngx.shared.DICT.flush_all](#ngxshareddictflush_all)
@@ -6235,6 +6236,7 @@ The resulting object `dict` has the following methods:
* [lpop](#ngxshareddictlpop)
* [rpop](#ngxshareddictrpop)
* [llen](#ngxshareddictllen)
+* [lindex](#ngxshareddictlindex)
* [ttl](#ngxshareddictttl)
* [expire](#ngxshareddictexpire)
* [flush_all](#ngxshareddictflush_all)
@@ -6607,6 +6609,25 @@ See also [ngx.shared.DICT](#ngxshareddict).
[Back to TOC](#nginx-api-for-lua)
+ngx.shared.DICT.lindex
+----------------------
+
+**syntax:** *val, err = ngx.shared.DICT:lindex(key, index)*
+
+**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua**
+
+Returns the element which at the index `index` of the list named `key` in the shm-base dictionary [ngx.shared.DICT](#ngxshareddict).
+
+The index is zero-based, so `0` means the first element, `1` for the second element and so forth. Also, negative index can be used to designate elements starting at the tail of the list, here `-1` means the last element and `-2` means the penultimate and so on.
+
+If `key` does not exist or `index` is out of the list bound, it will return `nil`. When the `key` already takes a value that is not a list, it will return `nil` and `"value not a list"`.
+
+**Note:** This method requires the `resty.core.shdict` or `resty.core` modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.
+
+See also [ngx.shared.DICT](#ngxshareddict).
+
+[Back to TOC](#nginx-api-for-lua)
+
ngx.shared.DICT.ttl
-------------------
**syntax:** *ttl, err = ngx.shared.DICT:ttl(key)*
diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki
index 9222811251..ca565f20cb 100644
--- a/doc/HttpLuaModule.wiki
+++ b/doc/HttpLuaModule.wiki
@@ -5232,6 +5232,7 @@ The resulting object dict
has the following methods:
* [[#ngx.shared.DICT.lpop|lpop]]
* [[#ngx.shared.DICT.rpop|rpop]]
* [[#ngx.shared.DICT.llen|llen]]
+* [[#ngx.shared.DICT.lindex|lindex]]
* [[#ngx.shared.DICT.ttl|ttl]]
* [[#ngx.shared.DICT.expire|expire]]
* [[#ngx.shared.DICT.flush_all|flush_all]]
@@ -5553,6 +5554,22 @@ This feature was first introduced in the v0.10.6
release.
See also [[#ngx.shared.DICT|ngx.shared.DICT]].
+== ngx.shared.DICT.lindex ==
+
+'''syntax:''' ''val, err = ngx.shared.DICT:lindex(key, index)''
+
+'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*''
+
+Returns the element which at the index index
of the list named key
in the shm-base dictionary [[#ngx.shared.DICT|ngx.shared.DICT]].
+
+The index is zero-based, so 0
means the first element, 1
for the second element and so forth. Also, negative index can be used to designate elements starting at the tail of the list, here -1
means the last element and -2
means the penultimate and so on.
+
+If key
does not exist or index
is out of the list bound, it will return nil
. When the key
already takes a value that is not a list, it will return nil
and "value not a list"
.
+
+'''Note:''' This method requires the resty.core.shdict
or resty.core
modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library.
+
+See also [[#ngx.shared.DICT|ngx.shared.DICT]].
+
== ngx.shared.DICT.ttl ==
'''syntax:''' ''ttl, err = ngx.shared.DICT:ttl(key)''
diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c
index b017bea658..9b41044973 100644
--- a/src/ngx_http_lua_shdict.c
+++ b/src/ngx_http_lua_shdict.c
@@ -3021,6 +3021,139 @@ ngx_http_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone)
# endif /* nginx_version >= 1011007 */
+int
+ngx_http_lua_ffi_shdict_lindex(ngx_shm_zone_t *zone, u_char *key,
+ size_t key_len, int *value_type, u_char **str_value_buf,
+ size_t *str_value_len, double *num_value, int index, char **err)
+{
+ int i;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_str_t name, value;
+ ngx_queue_t *queue;
+ ngx_http_lua_shdict_ctx_t *ctx;
+ ngx_http_lua_shdict_node_t *sd;
+ ngx_http_lua_shdict_list_node_t *lnode;
+
+ ctx = zone->data;
+ name = ctx->name;
+
+ hash = ngx_crc32_short(key, key_len);
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+#if 1
+ ngx_http_lua_shdict_expire(ctx, 1);
+#endif
+
+ rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
+
+ dd("shdict lookup returned %d", (int) rc);
+
+ if (rc == NGX_DECLINED || rc == NGX_DONE) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *value_type = LUA_TNIL;
+ return NGX_OK;
+ }
+
+ /* rc == NGX_OK */
+
+ if (sd->value_type != SHDICT_TLIST) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *err = "value not a list";
+ return NGX_ERROR;
+ }
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ if (index >= (int) sd->value_len || -index > (int) sd->value_len) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *value_type = LUA_TNIL;
+ return NGX_OK;
+ }
+
+ queue = ngx_http_lua_shdict_get_list_head(sd, key_len);
+
+ if (index >= 0) { /* forward */
+
+ for (i = 0; i <= index; i++) {
+ queue = ngx_queue_next(queue);
+ }
+
+ } else { /* backward */
+
+ for (i = -1; i >= index; i--) {
+ queue = ngx_queue_prev(queue);
+ }
+ }
+
+ lnode = ngx_queue_data(queue, ngx_http_lua_shdict_list_node_t, queue);
+
+ *value_type = lnode->value_type;
+
+ dd("data: %p", lnode->data);
+ dd("value len: %d", (int) lnode->value_len);
+
+ value.data = lnode->data;
+ value.len = (size_t) lnode->value_len;
+
+ if (*str_value_len < (size_t) value.len) {
+ if (*value_type == SHDICT_TSTRING) {
+ *str_value_buf = malloc(value.len);
+ if (*str_value_buf == NULL) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ switch (*value_type) {
+
+ case SHDICT_TSTRING:
+ *str_value_len = value.len;
+ ngx_memcpy(*str_value_buf, lnode->data, value.len);
+ break;
+
+ case SHDICT_TNUMBER:
+
+ if (value.len != sizeof(double)) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "bad lua list node number value size found "
+ "for key %*s in shared_dict %V: %z",
+ key_len, key, &name, value.len);
+
+ return NGX_ERROR;
+ }
+
+ *str_value_len = value.len;
+ ngx_memcpy(num_value, value.data, sizeof(double));
+
+ break;
+
+ default:
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "bad list node value type found "
+ "for key %*s in shared_dict %V: %d",
+ key_len, key, &name, *value_type);
+
+ return NGX_ERROR;
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return NGX_OK;
+}
+
+
#endif /* NGX_LUA_NO_FFI_API */