├── .gitattributes ├── .gitignore ├── .travis.yml ├── Makefile ├── README.markdown ├── dist.ini ├── lib └── resty │ └── memcached.lua ├── t ├── cert │ ├── ca.crt │ ├── ca.key │ ├── server.crt │ └── server.key ├── mock.t ├── sanity.t ├── tableset.t ├── tls.t ├── touch.t └── version.t └── valgrind.suppress /.gitattributes: -------------------------------------------------------------------------------- 1 | *.t linguist-language=Text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *~ 4 | go 5 | t/servroot/ 6 | reindex 7 | *.t_ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: focal 3 | 4 | branches: 5 | only: 6 | - "master" 7 | 8 | os: linux 9 | 10 | language: c 11 | 12 | compiler: 13 | - gcc 14 | 15 | addons: 16 | apt: 17 | packages: 18 | - memcached 19 | - time 20 | - psmisc 21 | - net-tools 22 | - libevent-dev 23 | - libssl1.1 24 | - libssl-dev 25 | 26 | cache: 27 | directories: 28 | - download-cache 29 | 30 | env: 31 | global: 32 | - JOBS=3 33 | - NGX_BUILD_JOBS=$JOBS 34 | - LUAJIT_PREFIX=/opt/luajit21 35 | - LUAJIT_LIB=$LUAJIT_PREFIX/lib 36 | - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 37 | - LUA_INCLUDE_DIR=$LUAJIT_INC 38 | - LUA_CMODULE_DIR=/lib 39 | - OPENSSL_PREFIX=/opt/ssl 40 | - OPENSSL_LIB=$OPENSSL_PREFIX/lib 41 | - OPENSSL_INC=$OPENSSL_PREFIX/include 42 | - OPENSSL_VER=1.1.1l 43 | - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH 44 | - TEST_NGINX_SLEEP=0.006 45 | - MEMCACHED_VERSION=1.6.10 46 | matrix: 47 | - NGINX_VERSION=1.27.1 48 | 49 | install: 50 | - if [ ! -d download-cache ]; then mkdir download-cache; fi 51 | - if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -O download-cache/openssl-$OPENSSL_VER.tar.gz https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz; fi 52 | - wget https://memcached.org/files/memcached-${MEMCACHED_VERSION}.tar.gz 53 | - sudo apt-get install -qq -y axel 54 | - cpanm --sudo --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1) 55 | - wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz 56 | - git clone https://github.com/openresty/openresty.git ../openresty 57 | - git clone https://github.com/openresty/nginx-devel-utils.git 58 | - git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module 59 | - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module 60 | - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core 61 | - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache 62 | - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module 63 | - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx 64 | - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git 65 | - git clone https://github.com/openresty/mockeagain.git 66 | 67 | script: 68 | - tar -xf memcached-${MEMCACHED_VERSION}.tar.gz 69 | - cd memcached-${MEMCACHED_VERSION} && ./configure --enable-tls && make && sudo make install && cd .. 70 | - sudo /usr/local/bin/memcached -u nobody -d -o track_sizes -U 11211 -p 11211 -l 127.0.0.1 71 | - sudo /usr/local/bin/memcached -u nobody -d -Z -o track_sizes -o ssl_chain_cert=./t/cert/server.crt -o ssl_key=./t/cert/server.key -p 11212 -l 127.0.0.1 72 | - cd luajit2/ 73 | - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1) 74 | - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1) 75 | - cd .. 76 | - tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz 77 | - cd openssl-$OPENSSL_VER/ 78 | - ./config shared --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1) 79 | - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1) 80 | - sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1) 81 | - cd ../mockeagain/ && make CC=$CC -j$JOBS && cd .. 82 | - export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:/usr/local/bin:$PATH 83 | - export LD_PRELOAD=$PWD/mockeagain/mockeagain.so 84 | - export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH 85 | - export TEST_NGINX_RESOLVER=8.8.4.4 86 | - export NGX_BUILD_CC=$CC 87 | - ngx-build $NGINX_VERSION --without-pcre2 --with-ipv6 --with-http_realip_module --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --add-module=../echo-nginx-module --with-debug > build.log 2>&1 || (cat build.log && exit 1) 88 | - nginx -V; memcached -V; 89 | - ldd `which nginx`|grep -E 'luajit|ssl|pcre' 90 | - prove -r t 91 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OPENRESTY_PREFIX=/usr/local/openresty-debug 2 | 3 | PREFIX ?= /usr/local 4 | LUA_INCLUDE_DIR ?= $(PREFIX)/include 5 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) 6 | INSTALL ?= install 7 | 8 | .PHONY: all test install 9 | 10 | all: ; 11 | 12 | install: all 13 | $(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty 14 | $(INSTALL) lib/resty/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty 15 | 16 | test: all 17 | PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t 18 | 19 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | lua-resty-memcached - Lua memcached client driver for the ngx_lua based on the cosocket API 5 | 6 | Table of Contents 7 | ================= 8 | 9 | * [Name](#name) 10 | * [Status](#status) 11 | * [Description](#description) 12 | * [Synopsis](#synopsis) 13 | * [Methods](#methods) 14 | * [new](#new) 15 | * [connect](#connect) 16 | * [sslhandshake](#sslhandshake) 17 | * [set](#set) 18 | * [set_timeout](#set_timeout) 19 | * [set_timeouts](#set_timeouts) 20 | * [set_keepalive](#set_keepalive) 21 | * [get_reused_times](#get_reused_times) 22 | * [close](#close) 23 | * [add](#add) 24 | * [replace](#replace) 25 | * [append](#append) 26 | * [prepend](#prepend) 27 | * [get](#get) 28 | * [gets](#gets) 29 | * [cas](#cas) 30 | * [touch](#touch) 31 | * [flush_all](#flush_all) 32 | * [delete](#delete) 33 | * [incr](#incr) 34 | * [decr](#decr) 35 | * [stats](#stats) 36 | * [version](#version) 37 | * [quit](#quit) 38 | * [verbosity](#verbosity) 39 | * [init_pipeline](#init_pipeline) 40 | * [commit_pipeline](#commit_pipeline) 41 | * [cancel_pipeline](#cancel_pipeline) 42 | * [Automatic Error Logging](#automatic-error-logging) 43 | * [Limitations](#limitations) 44 | * [TODO](#todo) 45 | * [Author](#author) 46 | * [Copyright and License](#copyright-and-license) 47 | * [See Also](#see-also) 48 | 49 | Status 50 | ====== 51 | 52 | This library is considered production ready. 53 | 54 | Description 55 | =========== 56 | 57 | This Lua library is a memcached client driver for the ngx_lua nginx module: 58 | 59 | http://wiki.nginx.org/HttpLuaModule 60 | 61 | This Lua library takes advantage of ngx_lua's cosocket API, which ensures 62 | 100% nonblocking behavior. 63 | 64 | Note that at least [ngx_lua 0.5.0rc29](https://github.com/chaoslawful/lua-nginx-module/tags) or [OpenResty 1.0.15.7](http://openresty.org/#Download) is required. 65 | 66 | Synopsis 67 | ======== 68 | 69 | ```lua 70 | lua_package_path "/path/to/lua-resty-memcached/lib/?.lua;;"; 71 | 72 | server { 73 | location /test { 74 | content_by_lua ' 75 | local memcached = require "resty.memcached" 76 | local memc, err = memcached:new() 77 | if not memc then 78 | ngx.say("failed to instantiate memc: ", err) 79 | return 80 | end 81 | 82 | memc:set_timeout(1000) -- 1 sec 83 | 84 | -- or connect to a unix domain socket file listened 85 | -- by a memcached server: 86 | -- local ok, err = memc:connect("unix:/path/to/memc.sock") 87 | 88 | local ok, err = memc:connect("127.0.0.1", 11211) 89 | if not ok then 90 | ngx.say("failed to connect: ", err) 91 | return 92 | end 93 | 94 | local ok, err = memc:flush_all() 95 | if not ok then 96 | ngx.say("failed to flush all: ", err) 97 | return 98 | end 99 | 100 | local ok, err = memc:set("dog", 32) 101 | if not ok then 102 | ngx.say("failed to set dog: ", err) 103 | return 104 | end 105 | 106 | local res, flags, err = memc:get("dog") 107 | if err then 108 | ngx.say("failed to get dog: ", err) 109 | return 110 | end 111 | 112 | if not res then 113 | ngx.say("dog not found") 114 | return 115 | end 116 | 117 | ngx.say("dog: ", res) 118 | 119 | -- put it into the connection pool of size 100, 120 | -- with 10 seconds max idle timeout 121 | local ok, err = memc:set_keepalive(10000, 100) 122 | if not ok then 123 | ngx.say("cannot set keepalive: ", err) 124 | return 125 | end 126 | 127 | -- or just close the connection right away: 128 | -- local ok, err = memc:close() 129 | -- if not ok then 130 | -- ngx.say("failed to close: ", err) 131 | -- return 132 | -- end 133 | '; 134 | } 135 | } 136 | ``` 137 | 138 | [Back to TOC](#table-of-contents) 139 | 140 | Methods 141 | ======= 142 | 143 | The `key` argument provided in the following methods will be automatically escaped according to the URI escaping rules before sending to the memcached server. 144 | 145 | [Back to TOC](#table-of-contents) 146 | 147 | new 148 | --- 149 | `syntax: memc, err = memcached:new(opts?)` 150 | 151 | Creates a memcached object. In case of failures, returns `nil` and a string describing the error. 152 | 153 | It accepts an optional `opts` table argument. The following options are supported: 154 | 155 | * `key_transform` 156 | 157 | an array table containing two functions for escaping and unescaping the 158 | memcached keys, respectively. By default, 159 | the memcached keys will be escaped and unescaped as URI components, that is 160 | 161 | ```lua 162 | memached:new{ 163 | key_transform = { ngx.escape_uri, ngx.unescape_uri } 164 | } 165 | ``` 166 | 167 | [Back to TOC](#table-of-contents) 168 | 169 | connect 170 | ------- 171 | `syntax: ok, err = memc:connect(host, port)` 172 | 173 | `syntax: ok, err = memc:connect("unix:/path/to/unix.sock")` 174 | 175 | Attempts to connect to the remote host and port that the memcached server is listening to or a local unix domain socket file listened by the memcached server. 176 | 177 | Before actually resolving the host name and connecting to the remote backend, this method will always look up the connection pool for matched idle connections created by previous calls of this method. 178 | 179 | [Back to TOC](#table-of-contents) 180 | 181 | sslhandshake 182 | ------------ 183 | 184 | **syntax:** *session, err = memc:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)* 185 | 186 | Does SSL/TLS handshake on the currently established connection. See the 187 | [tcpsock.sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake) 188 | API from OpenResty for more details. 189 | 190 | [Back to TOC](#table-of-contents) 191 | 192 | set 193 | --- 194 | `syntax: ok, err = memc:set(key, value, exptime, flags)` 195 | 196 | Inserts an entry into memcached unconditionally. If the key already exists, overrides it. 197 | 198 | The `value` argument could also be a Lua table holding multiple Lua 199 | strings that are supposed to be concatenated as a whole 200 | (without any delimiters). For example, 201 | 202 | ```lua 203 | memc:set("dog", {"a ", {"kind of"}, " animal"}) 204 | ``` 205 | 206 | is functionally equivalent to 207 | 208 | ```lua 209 | memc:set("dog", "a kind of animal") 210 | ``` 211 | 212 | The `exptime` parameter is optional and defaults to `0` (meaning never expires). The expiration time is in seconds. 213 | 214 | The `flags` parameter is optional and defaults to `0`. 215 | 216 | [Back to TOC](#table-of-contents) 217 | 218 | set_timeout 219 | ---------- 220 | `syntax: ok, err = memc:set_timeout(timeout)` 221 | 222 | Sets the timeout (in ms) protection for subsequent operations, including the `connect` method. 223 | 224 | Returns 1 when successful and nil plus a string describing the error otherwise. 225 | 226 | [Back to TOC](#table-of-contents) 227 | 228 | set_timeouts 229 | ---------- 230 | `syntax: ok, err = memc:set_timeouts(connect_timeout, send_timeout, read_timeout)` 231 | 232 | Sets the timeouts (in ms) for connect, send and read operations respectively. 233 | 234 | Returns 1 when successful and nil plus a string describing the error otherwise. 235 | 236 | set_keepalive 237 | ------------ 238 | `syntax: ok, err = memc:set_keepalive(max_idle_timeout, pool_size)` 239 | 240 | Puts the current memcached connection immediately into the ngx_lua cosocket connection pool. 241 | 242 | You can specify the max idle timeout (in ms) when the connection is in the pool and the maximal size of the pool every nginx worker process. 243 | 244 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 245 | 246 | Only call this method in the place you would have called the `close` method instead. Calling this method will immediately turn the current memcached object into the `closed` state. Any subsequent operations other than `connect()` on the current object will return the `closed` error. 247 | 248 | [Back to TOC](#table-of-contents) 249 | 250 | get_reused_times 251 | ---------------- 252 | `syntax: times, err = memc:get_reused_times()` 253 | 254 | This method returns the (successfully) reused times for the current connection. In case of error, it returns `nil` and a string describing the error. 255 | 256 | If the current connection does not come from the built-in connection pool, then this method always returns `0`, that is, the connection has never been reused (yet). If the connection comes from the connection pool, then the return value is always non-zero. So this method can also be used to determine if the current connection comes from the pool. 257 | 258 | [Back to TOC](#table-of-contents) 259 | 260 | close 261 | ----- 262 | `syntax: ok, err = memc:close()` 263 | 264 | Closes the current memcached connection and returns the status. 265 | 266 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 267 | 268 | 269 | [Back to TOC](#table-of-contents) 270 | 271 | add 272 | --- 273 | `syntax: ok, err = memc:add(key, value, exptime, flags)` 274 | 275 | Inserts an entry into memcached if and only if the key does not exist. 276 | 277 | The `value` argument could also be a Lua table holding multiple Lua 278 | strings that are supposed to be concatenated as a whole 279 | (without any delimiters). For example, 280 | 281 | ```lua 282 | memc:add("dog", {"a ", {"kind of"}, " animal"}) 283 | ``` 284 | 285 | is functionally equivalent to 286 | 287 | ```lua 288 | memc:add("dog", "a kind of animal") 289 | ``` 290 | 291 | The `exptime` parameter is optional and defaults to `0` (meaning never expires). The expiration time is in seconds. 292 | 293 | The `flags` parameter is optional, defaults to `0`. 294 | 295 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 296 | 297 | [Back to TOC](#table-of-contents) 298 | 299 | replace 300 | ------- 301 | `syntax: ok, err = memc:replace(key, value, exptime, flags)` 302 | 303 | Inserts an entry into memcached if and only if the key does exist. 304 | 305 | The `value` argument could also be a Lua table holding multiple Lua 306 | strings that are supposed to be concatenated as a whole 307 | (without any delimiters). For example, 308 | 309 | ```lua 310 | memc:replace("dog", {"a ", {"kind of"}, " animal"}) 311 | ``` 312 | 313 | is functionally equivalent to 314 | 315 | ```lua 316 | memc:replace("dog", "a kind of animal") 317 | ``` 318 | 319 | The `exptime` parameter is optional and defaults to `0` (meaning never expires). The expiration time is in seconds. 320 | 321 | The `flags` parameter is optional, defaults to `0`. 322 | 323 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 324 | 325 | [Back to TOC](#table-of-contents) 326 | 327 | append 328 | ------ 329 | `syntax: ok, err = memc:append(key, value, exptime, flags)` 330 | 331 | Appends the value to an entry with the same key that already exists in memcached. 332 | 333 | The `value` argument could also be a Lua table holding multiple Lua 334 | strings that are supposed to be concatenated as a whole 335 | (without any delimiters). For example, 336 | 337 | ```lua 338 | memc:append("dog", {"a ", {"kind of"}, " animal"}) 339 | ``` 340 | 341 | is functionally equivalent to 342 | 343 | ```lua 344 | memc:append("dog", "a kind of animal") 345 | ``` 346 | 347 | The `exptime` parameter is optional and defaults to `0` (meaning never expires). The expiration time is in seconds. 348 | 349 | The `flags` parameter is optional, defaults to `0`. 350 | 351 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 352 | 353 | [Back to TOC](#table-of-contents) 354 | 355 | prepend 356 | ------- 357 | `syntax: ok, err = memc:prepend(key, value, exptime, flags)` 358 | 359 | Prepends the value to an entry with the same key that already exists in memcached. 360 | 361 | The `value` argument could also be a Lua table holding multiple Lua 362 | strings that are supposed to be concatenated as a whole 363 | (without any delimiters). For example, 364 | 365 | ```lua 366 | memc:prepend("dog", {"a ", {"kind of"}, " animal"}) 367 | ``` 368 | 369 | is functionally equivalent to 370 | 371 | ```lua 372 | memc:prepend("dog", "a kind of animal") 373 | ``` 374 | 375 | The `exptime` parameter is optional and defaults to `0` (meaning never expires). The expiration time is in seconds. 376 | 377 | The `flags` parameter is optional and defaults to `0`. 378 | 379 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 380 | 381 | [Back to TOC](#table-of-contents) 382 | 383 | get 384 | --- 385 | `syntax: value, flags, err = memc:get(key)` 386 | `syntax: results, err = memc:get(keys)` 387 | 388 | Get a single entry or multiple entries in the memcached server via a single key or a table of keys. 389 | 390 | Let us first discuss the case When the key is a single string. 391 | 392 | The key's value and associated flags value will be returned if the entry is found and no error happens. 393 | 394 | In case of errors, `nil` values will be turned for `value` and `flags` and a 3rd (string) value will also be returned for describing the error. 395 | 396 | If the entry is not found, then three `nil` values will be returned. 397 | 398 | Then let us discuss the case when the a Lua table of multiple keys are provided. 399 | 400 | In this case, a Lua table holding the key-result pairs will be always returned in case of success. Each value corresponding each key in the table is also a table holding two values, the key's value and the key's flags. If a key does not exist, then there is no responding entries in the `results` table. 401 | 402 | In case of errors, `nil` will be returned, and the second return value will be a string describing the error. 403 | 404 | [Back to TOC](#table-of-contents) 405 | 406 | gets 407 | ---- 408 | `syntax: value, flags, cas_unique, err = memc:gets(key)` 409 | 410 | `syntax: results, err = memc:gets(keys)` 411 | 412 | Just like the `get` method, but will also return the CAS unique value associated with the entry in addition to the key's value and flags. 413 | 414 | This method is usually used together with the `cas` method. 415 | 416 | [Back to TOC](#table-of-contents) 417 | 418 | cas 419 | --- 420 | `syntax: ok, err = memc:cas(key, value, cas_unique, exptime?, flags?)` 421 | 422 | Just like the `set` method but does a check and set operation, which means "store this data but 423 | only if no one else has updated since I last fetched it." 424 | 425 | The `cas_unique` argument can be obtained from the `gets` method. 426 | 427 | [Back to TOC](#table-of-contents) 428 | 429 | touch 430 | --- 431 | `syntax: ok, err = memc:touch(key, exptime)` 432 | 433 | Update the expiration time of an existing key. 434 | 435 | Returns `1` for success or `nil` with a string describing the error otherwise. 436 | 437 | This method was first introduced in the `v0.11` release. 438 | 439 | [Back to TOC](#table-of-contents) 440 | 441 | flush_all 442 | --------- 443 | `syntax: ok, err = memc:flush_all(time?)` 444 | 445 | Flushes (or invalidates) all the existing entries in the memcached server immediately (by default) or after the expiration 446 | specified by the `time` argument (in seconds). 447 | 448 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 449 | 450 | [Back to TOC](#table-of-contents) 451 | 452 | delete 453 | ------ 454 | `syntax: ok, err = memc:delete(key)` 455 | 456 | Deletes the key from memcached immediately. 457 | 458 | The key to be deleted must already exist in memcached. 459 | 460 | In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error. 461 | 462 | [Back to TOC](#table-of-contents) 463 | 464 | incr 465 | ---- 466 | `syntax: new_value, err = memc:incr(key, delta)` 467 | 468 | Increments the value of the specified key by the integer value specified in the `delta` argument. 469 | 470 | Returns the new value after incrementation in success, and `nil` with a string describing the error in case of failures. 471 | 472 | [Back to TOC](#table-of-contents) 473 | 474 | decr 475 | ---- 476 | `syntax: new_value, err = memc:decr(key, value)` 477 | 478 | Decrements the value of the specified key by the integer value specified in the `delta` argument. 479 | 480 | Returns the new value after decrementation in success, and `nil` with a string describing the error in case of failures. 481 | 482 | [Back to TOC](#table-of-contents) 483 | 484 | stats 485 | ----- 486 | `syntax: lines, err = memc:stats(args?)` 487 | 488 | Returns memcached server statistics information with an optional `args` argument. 489 | 490 | In case of success, this method returns a lua table holding all of the lines of the output; in case of failures, it returns `nil` with a string describing the error. 491 | 492 | If the `args` argument is omitted, general server statistics is returned. Possible `args` argument values are `items`, `sizes`, `slabs`, among others. 493 | 494 | [Back to TOC](#table-of-contents) 495 | 496 | version 497 | ------- 498 | `syntax: version, err = memc:version(args?)` 499 | 500 | Returns the server version number, like `1.2.8`. 501 | 502 | In case of error, it returns `nil` with a string describing the error. 503 | 504 | [Back to TOC](#table-of-contents) 505 | 506 | quit 507 | ---- 508 | `syntax: ok, err = memc:quit()` 509 | 510 | Tells the server to close the current memcached connection. 511 | 512 | Returns `1` in case of success and `nil` other wise. In case of failures, another string value will also be returned to describe the error. 513 | 514 | Generally you can just directly call the `close` method to achieve the same effect. 515 | 516 | [Back to TOC](#table-of-contents) 517 | 518 | verbosity 519 | --------- 520 | `syntax: ok, err = memc:verbosity(level)` 521 | 522 | Sets the verbosity level used by the memcached server. The `level` argument should be given integers only. 523 | 524 | Returns `1` in case of success and `nil` other wise. In case of failures, another string value will also be returned to describe the error. 525 | 526 | [Back to TOC](#table-of-contents) 527 | 528 | init_pipeline 529 | --------- 530 | `syntax: err = memc:init_pipeline(n?)` 531 | 532 | Enable the Memcache pipelining mode. All subsequent calls to Memcache command methods will automatically get buffer and will send to the server in one run when the commit_pipeline method is called or get cancelled by calling the cancel_pipeline method. 533 | 534 | The optional params `n` is buffer tables size. default value 4 535 | 536 | [Back to TOC](#table-of-contents) 537 | 538 | commit_pipeline 539 | --------- 540 | `syntax: results, err = memc:commit_pipeline()` 541 | 542 | Quits the pipelining mode by committing all the cached Memcache queries to the remote server in a single run. All the replies for these queries will be collected automatically and are returned as if a big multi-bulk reply at the highest level. 543 | 544 | This method success return a lua table. failed return a lua string describing the error upon failures. 545 | 546 | [Back to TOC](#table-of-contents) 547 | 548 | cancel_pipeline 549 | --------- 550 | `syntax: memc:cancel_pipeline()` 551 | 552 | Quits the pipelining mode by discarding all existing buffer Memcache commands since the last call to the init_pipeline method. 553 | 554 | the method no return. always succeeds. 555 | 556 | [Back to TOC](#table-of-contents) 557 | 558 | Automatic Error Logging 559 | ======================= 560 | 561 | By default the underlying [ngx_lua](http://wiki.nginx.org/HttpLuaModule) module 562 | does error logging when socket errors happen. If you are already doing proper error 563 | handling in your own Lua code, then you are recommended to disable this automatic error logging by turning off [ngx_lua](http://wiki.nginx.org/HttpLuaModule)'s [lua_socket_log_errors](http://wiki.nginx.org/HttpLuaModule#lua_socket_log_errors) directive, that is, 564 | 565 | ```nginx 566 | lua_socket_log_errors off; 567 | ``` 568 | 569 | [Back to TOC](#table-of-contents) 570 | 571 | Limitations 572 | =========== 573 | 574 | * This library cannot be used in code contexts like `set_by_lua*`, `log_by_lua*`, and 575 | `header_filter_by_lua*` where the ngx\_lua cosocket API is not available. 576 | * The `resty.memcached` object instance cannot be stored in a Lua variable at the Lua module level, 577 | because it will then be shared by all the concurrent requests handled by the same nginx 578 | worker process (see 579 | http://wiki.nginx.org/HttpLuaModule#Data_Sharing_within_an_Nginx_Worker ) and 580 | result in bad race conditions when concurrent requests are trying to use the same `resty.memcached` instance. 581 | You should always initiate `resty.memcached` objects in function local 582 | variables or in the `ngx.ctx` table. These places all have their own data copies for 583 | each request. 584 | 585 | [Back to TOC](#table-of-contents) 586 | 587 | TODO 588 | ==== 589 | 590 | * implement the memcached pipelining API. 591 | * implement the UDP part of the memcached ascii protocol. 592 | 593 | [Back to TOC](#table-of-contents) 594 | 595 | Author 596 | ====== 597 | 598 | Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. 599 | 600 | [Back to TOC](#table-of-contents) 601 | 602 | Copyright and License 603 | ===================== 604 | 605 | This module is licensed under the BSD license. 606 | 607 | Copyright (C) 2012-2017, by Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. 608 | 609 | All rights reserved. 610 | 611 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 612 | 613 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 614 | 615 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 616 | 617 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 618 | 619 | [Back to TOC](#table-of-contents) 620 | 621 | See Also 622 | ======== 623 | * the ngx_lua module: http://wiki.nginx.org/HttpLuaModule 624 | * the memcached wired protocol specification: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt 625 | * the [lua-resty-redis](https://github.com/agentzh/lua-resty-redis) library. 626 | * the [lua-resty-mysql](https://github.com/agentzh/lua-resty-mysql) library. 627 | 628 | [Back to TOC](#table-of-contents) 629 | 630 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name=lua-resty-memcached 2 | abstract=Lua memcached client driver for the ngx_lua based on the cosocket API 3 | author=Yichun "agentzh" Zhang (agentzh) 4 | is_original=yes 5 | license=2bsd 6 | lib_dir=lib 7 | doc_dir=lib 8 | repo_link=https://github.com/openresty/lua-resty-memcached 9 | main_module=lib/resty/memcached.lua 10 | -------------------------------------------------------------------------------- /lib/resty/memcached.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) Yichun Zhang (agentzh), CloudFlare Inc. 2 | 3 | 4 | local escape_uri = ngx.escape_uri 5 | local unescape_uri = ngx.unescape_uri 6 | local match = string.match 7 | local tcp = ngx.socket.tcp 8 | local strlen = string.len 9 | local concat = table.concat 10 | local tab_insert = table.insert 11 | local setmetatable = setmetatable 12 | local type = type 13 | local tab_clear = require "table.clear" 14 | 15 | local cmd_tab = {} 16 | 17 | local _M = { 18 | _VERSION = '0.17' 19 | } 20 | 21 | 22 | local mt = { __index = _M } 23 | 24 | local ok, new_tab = pcall(require, "table.new") 25 | if not ok or type(new_tab) ~= "function" then 26 | new_tab = function (narr, nrec) return {} end 27 | end 28 | 29 | local function _read_reply(sock, len) 30 | local line, err 31 | if len == nil then 32 | line, err = sock:receive() 33 | else 34 | line, err = sock:receive(len) 35 | end 36 | if not line then 37 | if err == "timeout" then 38 | sock:close() 39 | end 40 | return nil, err 41 | end 42 | return line, nil 43 | end 44 | 45 | function _M.new(self, opts) 46 | local sock, err = tcp() 47 | if not sock then 48 | return nil, err 49 | end 50 | 51 | local escape_key = escape_uri 52 | local unescape_key = unescape_uri 53 | 54 | if opts then 55 | local key_transform = opts.key_transform 56 | 57 | if key_transform then 58 | escape_key = key_transform[1] 59 | unescape_key = key_transform[2] 60 | if not escape_key or not unescape_key then 61 | return nil, "expecting key_transform = { escape, unescape } table" 62 | end 63 | end 64 | end 65 | 66 | local memc = setmetatable({ 67 | sock = sock, 68 | escape_key = escape_key, 69 | unescape_key = unescape_key, 70 | }, mt) 71 | 72 | return memc 73 | end 74 | 75 | 76 | local function set_timeouts(self, connect, send, read) 77 | local sock = self.sock 78 | if not sock then 79 | return nil, "not initialized" 80 | end 81 | 82 | sock:settimeouts(connect, send, read) 83 | return 1 84 | end 85 | _M.set_timeouts = set_timeouts 86 | 87 | function _M.set_timeout(self, timeout) 88 | return set_timeouts(self, timeout, timeout, timeout) 89 | end 90 | 91 | 92 | function _M.connect(self, ...) 93 | local sock = self.sock 94 | if not sock then 95 | return nil, "not initialized" 96 | end 97 | 98 | return sock:connect(...) 99 | end 100 | 101 | 102 | function _M.sslhandshake(self, ...) 103 | local sock = self.sock 104 | if not sock then 105 | return nil, "not initialized" 106 | end 107 | 108 | return sock:sslhandshake(...) 109 | end 110 | 111 | 112 | local function _multi_get(self, keys) 113 | local sock = self.sock 114 | if not sock then 115 | return nil, "not initialized" 116 | end 117 | 118 | local nkeys = #keys 119 | 120 | if nkeys == 0 then 121 | return {}, nil 122 | end 123 | 124 | local escape_key = self.escape_key 125 | tab_clear(cmd_tab) 126 | tab_insert(cmd_tab, "get") 127 | 128 | for i = 1, nkeys do 129 | tab_insert(cmd_tab, " ") 130 | tab_insert(cmd_tab, escape_key(keys[i])) 131 | end 132 | tab_insert(cmd_tab, "\r\n") 133 | 134 | -- print("multi get cmd: ", cmd_tab) 135 | 136 | local bytes, err = sock:send(cmd_tab) 137 | if not bytes then 138 | return nil, err 139 | end 140 | 141 | local unescape_key = self.unescape_key 142 | local results = {} 143 | 144 | while true do 145 | local line, err = sock:receive() 146 | if not line then 147 | if err == "timeout" then 148 | sock:close() 149 | end 150 | return nil, err 151 | end 152 | 153 | if line == 'END' then 154 | break 155 | end 156 | 157 | local key, flags, len = match(line, '^VALUE (%S+) (%d+) (%d+)$') 158 | -- print("key: ", key, "len: ", len, ", flags: ", flags) 159 | 160 | if not key then 161 | return nil, line 162 | end 163 | 164 | local data, err = sock:receive(len) 165 | if not data then 166 | if err == "timeout" then 167 | sock:close() 168 | end 169 | return nil, err 170 | end 171 | 172 | results[unescape_key(key)] = {data, flags} 173 | 174 | data, err = sock:receive(2) -- discard the trailing CRLF 175 | if not data then 176 | if err == "timeout" then 177 | sock:close() 178 | end 179 | return nil, err 180 | end 181 | end 182 | 183 | return results 184 | end 185 | 186 | local function _get_reply(sock) 187 | local line, err = _read_reply(sock) 188 | if err then 189 | return nil, nil, err 190 | end 191 | 192 | if line == 'END' then 193 | return nil, nil, nil 194 | end 195 | 196 | local flags, len = match(line, '^VALUE %S+ (%d+) (%d+)$') 197 | if not flags then 198 | return nil, nil, line 199 | end 200 | 201 | -- print("len: ", len, ", flags: ", flags) 202 | local data, err = _read_reply(sock, len) 203 | if err then 204 | return nil, nil, err 205 | end 206 | 207 | local _, err = _read_reply(sock, 7) -- discard the trailing "\r\nEND\r\n" 208 | if err then 209 | return nil, nil, err 210 | end 211 | 212 | return data, flags 213 | end 214 | 215 | function _M.get(self, key) 216 | if type(key) == "table" then 217 | return _multi_get(self, key) 218 | end 219 | 220 | local sock = self.sock 221 | if not sock then 222 | return nil, nil, "not initialized" 223 | end 224 | 225 | local reqs = rawget(self, "_reqs") 226 | if reqs then 227 | local readers = rawget(self, "_readers") 228 | tab_insert(reqs, "get ") 229 | tab_insert(reqs, self.escape_key(key)) 230 | tab_insert(reqs, "\r\n") 231 | tab_insert(readers, _get_reply) 232 | return 1 233 | end 234 | 235 | tab_clear(cmd_tab) 236 | tab_insert(cmd_tab, "get ") 237 | tab_insert(cmd_tab, self.escape_key(key)) 238 | tab_insert(cmd_tab, "\r\n") 239 | local bytes, err = sock:send(cmd_tab) 240 | if not bytes then 241 | return nil, nil, err 242 | end 243 | 244 | return _get_reply(sock) 245 | end 246 | 247 | 248 | local function _multi_gets(self, keys) 249 | local sock = self.sock 250 | if not sock then 251 | return nil, "not initialized" 252 | end 253 | 254 | local nkeys = #keys 255 | 256 | if nkeys == 0 then 257 | return {}, nil 258 | end 259 | 260 | local escape_key = self.escape_key 261 | tab_clear(cmd_tab) 262 | tab_insert(cmd_tab, "gets") 263 | for i = 1, nkeys do 264 | tab_insert(cmd_tab, " ") 265 | tab_insert(cmd_tab, escape_key(keys[i])) 266 | end 267 | tab_insert(cmd_tab, "\r\n") 268 | 269 | -- print("multi get cmd: ", cmd_tab) 270 | 271 | local bytes, err = sock:send(cmd_tab) 272 | if not bytes then 273 | return nil, err 274 | end 275 | 276 | local unescape_key = self.unescape_key 277 | local results = {} 278 | 279 | while true do 280 | local line, err = _read_reply(sock) 281 | if err then 282 | return nil, err 283 | end 284 | 285 | if line == 'END' then 286 | break 287 | end 288 | 289 | local key, flags, len, cas_uniq = 290 | match(line, '^VALUE (%S+) (%d+) (%d+) (%d+)$') 291 | 292 | -- print("key: ", key, "len: ", len, ", flags: ", flags) 293 | 294 | if not key then 295 | return nil, line 296 | end 297 | 298 | local data, err = _read_reply(sock, len) 299 | if err then 300 | return nil, err 301 | end 302 | 303 | results[unescape_key(key)] = {data, flags, cas_uniq} 304 | data, err = _read_reply(sock, 2) -- discard the trailing CRLF 305 | if err then 306 | return nil, err 307 | end 308 | end 309 | 310 | return results 311 | end 312 | 313 | 314 | function _M.gets(self, key) 315 | if type(key) == "table" then 316 | return _multi_gets(self, key) 317 | end 318 | 319 | local sock = self.sock 320 | if not sock then 321 | return nil, nil, nil, "not initialized" 322 | end 323 | 324 | tab_clear(cmd_tab) 325 | tab_insert(cmd_tab, "gets ") 326 | tab_insert(cmd_tab, self.escape_key(key)) 327 | tab_insert(cmd_tab, "\r\n") 328 | local bytes, err = sock:send(cmd_tab) 329 | if not bytes then 330 | return nil, nil, nil, err 331 | end 332 | 333 | local line, err = _read_reply(sock) 334 | if err then 335 | return nil, nil, nil, err 336 | end 337 | 338 | if line == 'END' then 339 | return nil, nil, nil, nil 340 | end 341 | 342 | local flags, len, cas_uniq = match(line, '^VALUE %S+ (%d+) (%d+) (%d+)$') 343 | if not flags then 344 | return nil, nil, nil, line 345 | end 346 | 347 | -- print("len: ", len, ", flags: ", flags) 348 | 349 | local data, err = _read_reply(sock, len) 350 | if not data then 351 | return nil, nil, nil, err 352 | end 353 | 354 | line, err = _read_reply(sock, 7) -- discard the trailing "\r\nEND\r\n" 355 | if not line then 356 | return nil, nil, nil, err 357 | end 358 | 359 | return data, flags, cas_uniq 360 | end 361 | 362 | 363 | local function _expand_table(value) 364 | local segs = {} 365 | local nelems = #value 366 | local nsegs = 0 367 | for i = 1, nelems do 368 | local seg = value[i] 369 | nsegs = nsegs + 1 370 | if type(seg) == "table" then 371 | segs[nsegs] = _expand_table(seg) 372 | else 373 | segs[nsegs] = seg 374 | end 375 | end 376 | return concat(segs) 377 | end 378 | 379 | local function _store_reply(sock) 380 | local data, err = _read_reply(sock) 381 | if err then 382 | return nil, err 383 | end 384 | 385 | if data == "STORED" then 386 | return 1 387 | end 388 | 389 | return nil, data 390 | end 391 | 392 | local function _store(self, cmd, key, value, exptime, flags) 393 | if not exptime then 394 | exptime = 0 395 | end 396 | 397 | if not flags then 398 | flags = 0 399 | end 400 | 401 | local sock = self.sock 402 | if not sock then 403 | return nil, "not initialized" 404 | end 405 | 406 | if type(value) == "table" then 407 | value = _expand_table(value) 408 | end 409 | 410 | local reqs = rawget(self, "_reqs") 411 | if reqs then 412 | local readers = rawget(self, "_readers") 413 | tab_insert(reqs, cmd) 414 | tab_insert(reqs, " ") 415 | tab_insert(reqs, self.escape_key(key)) 416 | tab_insert(reqs, " ") 417 | tab_insert(reqs, flags) 418 | tab_insert(reqs, " ") 419 | tab_insert(reqs, exptime) 420 | tab_insert(reqs, " ") 421 | tab_insert(reqs, strlen(value)) 422 | tab_insert(reqs, "\r\n") 423 | tab_insert(reqs, value) 424 | tab_insert(reqs, "\r\n") 425 | 426 | tab_insert(readers, _store_reply) 427 | return 1 428 | end 429 | 430 | tab_clear(cmd_tab) 431 | tab_insert(cmd_tab, cmd) 432 | tab_insert(cmd_tab, " ") 433 | tab_insert(cmd_tab, self.escape_key(key)) 434 | tab_insert(cmd_tab, " ") 435 | tab_insert(cmd_tab, flags) 436 | tab_insert(cmd_tab, " ") 437 | tab_insert(cmd_tab, exptime) 438 | tab_insert(cmd_tab, " ") 439 | tab_insert(cmd_tab, strlen(value)) 440 | tab_insert(cmd_tab, "\r\n") 441 | tab_insert(cmd_tab, value) 442 | tab_insert(cmd_tab, "\r\n") 443 | 444 | local bytes, err = sock:send(cmd_tab) 445 | if not bytes then 446 | return nil, err 447 | end 448 | 449 | return _store_reply(sock) 450 | end 451 | 452 | 453 | function _M.set(self, ...) 454 | return _store(self, "set", ...) 455 | end 456 | 457 | 458 | function _M.add(self, ...) 459 | return _store(self, "add", ...) 460 | end 461 | 462 | 463 | function _M.replace(self, ...) 464 | return _store(self, "replace", ...) 465 | end 466 | 467 | 468 | function _M.append(self, ...) 469 | return _store(self, "append", ...) 470 | end 471 | 472 | 473 | function _M.prepend(self, ...) 474 | return _store(self, "prepend", ...) 475 | end 476 | 477 | 478 | function _M.cas(self, key, value, cas_uniq, exptime, flags) 479 | if not exptime then 480 | exptime = 0 481 | end 482 | 483 | if not flags then 484 | flags = 0 485 | end 486 | 487 | local sock = self.sock 488 | if not sock then 489 | return nil, "not initialized" 490 | end 491 | 492 | tab_clear(cmd_tab) 493 | tab_insert(cmd_tab, "cas ") 494 | tab_insert(cmd_tab, self.escape_key(key)) 495 | tab_insert(cmd_tab, " ") 496 | tab_insert(cmd_tab, flags) 497 | tab_insert(cmd_tab, " ") 498 | tab_insert(cmd_tab, exptime) 499 | tab_insert(cmd_tab, " ") 500 | tab_insert(cmd_tab, strlen(value)) 501 | tab_insert(cmd_tab, " ") 502 | tab_insert(cmd_tab, cas_uniq) 503 | tab_insert(cmd_tab, "\r\n") 504 | tab_insert(cmd_tab, value) 505 | tab_insert(cmd_tab, "\r\n") 506 | 507 | -- local cjson = require "cjson" 508 | -- print("request: ", cjson.encode(cmd_tab)) 509 | 510 | local bytes, err = sock:send(cmd_tab) 511 | if not bytes then 512 | return nil, err 513 | end 514 | 515 | local line, err = _read_reply(sock) 516 | if err then 517 | return nil, err 518 | end 519 | 520 | -- print("response: [", line, "]") 521 | 522 | if line == "STORED" then 523 | return 1 524 | end 525 | 526 | return nil, line 527 | end 528 | 529 | local function _delete_reply(sock) 530 | local res, err = _read_reply(sock) 531 | if err then 532 | return nil, err 533 | end 534 | 535 | if res ~= 'DELETED' then 536 | return nil, res 537 | end 538 | 539 | return 1 540 | end 541 | 542 | function _M.delete(self, key) 543 | local sock = self.sock 544 | if not sock then 545 | return nil, "not initialized" 546 | end 547 | 548 | key = self.escape_key(key) 549 | 550 | local reqs = rawget(self, "_reqs") 551 | if reqs then 552 | local readers = rawget(self, "_readers") 553 | tab_insert(reqs, "delete ") 554 | tab_insert(reqs, key) 555 | tab_insert(reqs, "\r\n") 556 | tab_insert(readers, _delete_reply) 557 | return 1 558 | end 559 | 560 | tab_clear(cmd_tab) 561 | tab_insert(cmd_tab, "delete ") 562 | tab_insert(cmd_tab, key) 563 | tab_insert(cmd_tab, "\r\n") 564 | local bytes, err = sock:send(cmd_tab) 565 | if not bytes then 566 | return nil, err 567 | end 568 | 569 | return _delete_reply(sock) 570 | end 571 | 572 | 573 | function _M.set_keepalive(self, ...) 574 | local sock = self.sock 575 | if not sock then 576 | return nil, "not initialized" 577 | end 578 | 579 | return sock:setkeepalive(...) 580 | end 581 | 582 | 583 | function _M.get_reused_times(self) 584 | local sock = self.sock 585 | if not sock then 586 | return nil, "not initialized" 587 | end 588 | 589 | return sock:getreusedtimes() 590 | end 591 | 592 | 593 | function _M.flush_all(self, time) 594 | local sock = self.sock 595 | if not sock then 596 | return nil, "not initialized" 597 | end 598 | 599 | tab_clear(cmd_tab) 600 | if time then 601 | tab_insert(cmd_tab, "flush_all ") 602 | tab_insert(cmd_tab, time) 603 | tab_insert(cmd_tab, "\r\n") 604 | else 605 | tab_clear(cmd_tab) 606 | tab_insert(cmd_tab, "flush_all\r\n") 607 | end 608 | 609 | local bytes, err = sock:send(cmd_tab) 610 | if not bytes then 611 | return nil, err 612 | end 613 | 614 | local res, err = _read_reply(sock) 615 | if err then 616 | return nil, err 617 | end 618 | 619 | if res ~= 'OK' then 620 | return nil, res 621 | end 622 | 623 | return 1 624 | end 625 | 626 | local function _incr_decr_reply(sock) 627 | local line, err = _read_reply(sock) 628 | if err then 629 | return nil, err 630 | end 631 | 632 | if not match(line, '^%d+$') then 633 | return nil, line 634 | end 635 | 636 | return line 637 | end 638 | 639 | local function _incr_decr(self, cmd, key, value) 640 | local sock = self.sock 641 | if not sock then 642 | return nil, "not initialized" 643 | end 644 | 645 | local reqs = rawget(self, "_reqs") 646 | local readers = rawget(self, "_readers") 647 | if reqs then 648 | tab_insert(reqs, cmd) 649 | tab_insert(reqs, " ") 650 | tab_insert(reqs, self.escape_key(key)) 651 | tab_insert(reqs, " ") 652 | tab_insert(reqs, value) 653 | tab_insert(reqs, "\r\n") 654 | tab_insert(readers, _incr_decr_reply) 655 | return 1 656 | end 657 | 658 | tab_clear(cmd_tab) 659 | tab_insert(cmd_tab, cmd) 660 | tab_insert(cmd_tab, " ") 661 | tab_insert(cmd_tab, self.escape_key(key)) 662 | tab_insert(cmd_tab, " ") 663 | tab_insert(cmd_tab, value) 664 | tab_insert(cmd_tab, "\r\n") 665 | 666 | local bytes, err = sock:send(cmd_tab) 667 | if not bytes then 668 | return nil, err 669 | end 670 | 671 | return _incr_decr_reply(sock) 672 | end 673 | 674 | 675 | function _M.incr(self, key, value) 676 | return _incr_decr(self, "incr", key, value) 677 | end 678 | 679 | 680 | function _M.decr(self, key, value) 681 | return _incr_decr(self, "decr", key, value) 682 | end 683 | 684 | 685 | local function _stats_reply(sock) 686 | local lines = {} 687 | local n = 0 688 | while true do 689 | local line, err = _read_reply(sock) 690 | if err then 691 | return nil, err 692 | end 693 | 694 | if line == 'END' then 695 | return lines, nil 696 | end 697 | 698 | if not match(line, "ERROR") then 699 | n = n + 1 700 | lines[n] = line 701 | else 702 | return nil, line 703 | end 704 | end 705 | 706 | -- cannot reach here... 707 | return lines 708 | end 709 | 710 | function _M.stats(self, args) 711 | local sock = self.sock 712 | if not sock then 713 | return nil, "not initialized" 714 | end 715 | 716 | local reqs = rawget(self, "_reqs") 717 | local readers = rawget(self, "_readers") 718 | if reqs then 719 | if args then 720 | tab_insert(reqs, "stats ") 721 | tab_insert(reqs, args) 722 | tab_insert(reqs, "\r\n") 723 | else 724 | tab_insert(reqs, "stats\r\n") 725 | end 726 | tab_insert(readers, _stats_reply) 727 | return 1 728 | end 729 | 730 | local bytes, err 731 | if args then 732 | tab_clear(cmd_tab) 733 | tab_insert(cmd_tab, "stats ") 734 | tab_insert(cmd_tab, args) 735 | tab_insert(cmd_tab, "\r\n") 736 | bytes, err = sock:send(cmd_tab) 737 | else 738 | bytes, err = sock:send("stats\r\n") 739 | end 740 | if not bytes then 741 | return nil, err 742 | end 743 | 744 | return _stats_reply(sock) 745 | end 746 | 747 | local function _version_reply(sock) 748 | local line, err = _read_reply(sock) 749 | if err then 750 | return nil, err 751 | end 752 | 753 | local ver = match(line, "^VERSION (.+)$") 754 | if not ver then 755 | return nil, ver 756 | end 757 | 758 | return ver 759 | end 760 | 761 | function _M.version(self) 762 | local sock = self.sock 763 | if not sock then 764 | return nil, "not initialized" 765 | end 766 | 767 | local bytes, err = sock:send("version\r\n") 768 | if not bytes then 769 | return nil, err 770 | end 771 | 772 | return _version_reply(sock) 773 | end 774 | 775 | function _M.quit(self) 776 | local sock = self.sock 777 | if not sock then 778 | return nil, "not initialized" 779 | end 780 | 781 | local bytes, err = sock:send("quit\r\n") 782 | if not bytes then 783 | return nil, err 784 | end 785 | 786 | return 1 787 | end 788 | 789 | local function _verbosity_reply(sock) 790 | local line, err = _read_reply(sock) 791 | if err then 792 | return nil, err 793 | end 794 | 795 | if line ~= 'OK' then 796 | return nil, line 797 | end 798 | 799 | return 1 800 | end 801 | 802 | function _M.verbosity(self, level) 803 | local sock = self.sock 804 | if not sock then 805 | return nil, "not initialized" 806 | end 807 | 808 | local reqs = rawget(self, "_reqs") 809 | if reqs then 810 | local readers = rawget(self, "_readers") 811 | tab_insert(reqs, "verbosity ") 812 | tab_insert(reqs, level) 813 | tab_insert(reqs, "\r\n") 814 | tab_insert(readers, _verbosity_reply) 815 | return 1 816 | end 817 | 818 | tab_clear(cmd_tab) 819 | tab_insert(cmd_tab, "verbosity ") 820 | tab_insert(cmd_tab, level) 821 | tab_insert(cmd_tab, "\r\n") 822 | local bytes, err = sock:send(cmd_tab) 823 | if not bytes then 824 | return nil, err 825 | end 826 | 827 | return _verbosity_reply(sock) 828 | end 829 | 830 | local function _touch_reply(sock) 831 | local line, err = _read_reply(sock) 832 | if err then 833 | return nil, err 834 | end 835 | 836 | -- moxi server from couchbase returned stored after touching 837 | if line == "TOUCHED" or line =="STORED" then 838 | return 1 839 | end 840 | return nil, line 841 | end 842 | 843 | function _M.touch(self, key, exptime) 844 | local sock = self.sock 845 | if not sock then 846 | return nil, "not initialized" 847 | end 848 | 849 | local reqs = rawget(self, "_reqs") 850 | local readers = rawget(self, "_readers") 851 | if reqs then 852 | tab_insert(reqs, "touch ") 853 | tab_insert(reqs, self.escape_key(key)) 854 | tab_insert(reqs, " ") 855 | tab_insert(reqs, exptime) 856 | tab_insert(reqs, "\r\n") 857 | tab_insert(readers, _touch_reply) 858 | return 1 859 | end 860 | 861 | tab_clear(cmd_tab) 862 | tab_insert(cmd_tab, "touch ") 863 | tab_insert(cmd_tab, self.escape_key(key)) 864 | tab_insert(cmd_tab, " ") 865 | tab_insert(cmd_tab, exptime) 866 | tab_insert(cmd_tab, "\r\n") 867 | local bytes, err = sock:send(cmd_tab) 868 | if not bytes then 869 | return nil, err 870 | end 871 | 872 | return _touch_reply(sock) 873 | end 874 | 875 | function _M.close(self) 876 | local sock = self.sock 877 | if not sock then 878 | return nil, "not initialized" 879 | end 880 | 881 | return sock:close() 882 | end 883 | 884 | 885 | function _M.init_pipeline(self, n) 886 | if self._reqs then 887 | return "already init pipeline" 888 | end 889 | 890 | if n and type(n) ~= 'number' then 891 | return "bad n arg: number expected, but got " .. type(n) 892 | end 893 | self._reqs = new_tab(n or 20, 0) 894 | self._readers = new_tab(n or 4, 0) 895 | return nil 896 | end 897 | 898 | 899 | function _M.cancel_pipeline(self) 900 | self._reqs = nil 901 | self._readers = nil 902 | end 903 | 904 | 905 | function _M.commit_pipeline(self) 906 | local reqs = rawget(self, "_reqs") 907 | local readers = rawget(self, "_readers") 908 | self._reqs = nil 909 | self._readers = nil 910 | if not reqs or not readers then 911 | return nil, "no pipeline" 912 | end 913 | local sock = self.sock 914 | if not sock then 915 | return nil, "not initialized" 916 | end 917 | 918 | if #readers == 0 then 919 | return nil, "no more cmds" 920 | end 921 | local bytes, err = sock:send(reqs) 922 | if not bytes then 923 | return nil, err 924 | end 925 | 926 | local results = {} 927 | for i, reader in ipairs(readers) do 928 | results[i] = { reader(sock) } 929 | end 930 | 931 | return results, nil 932 | end 933 | 934 | return _M 935 | -------------------------------------------------------------------------------- /t/cert/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkTCCAnmgAwIBAgIUJPrw/2C2SybyKqm5jyLpJjyF7F0wDQYJKoZIhvcNAQEL 3 | BQAwWDELMAkGA1UEBhMCY24xCzAJBgNVBAgMAmZqMQswCQYDVQQHDAJ4bTEOMAwG 4 | A1UECgwFb3JpbmMxDDAKBgNVBAsMA2RldjERMA8GA1UEAwwIcm9vdC5jb20wHhcN 5 | MjEwOTE3MDQwNzM3WhcNMzEwOTE1MDQwNzM3WjBYMQswCQYDVQQGEwJjbjELMAkG 6 | A1UECAwCZmoxCzAJBgNVBAcMAnhtMQ4wDAYDVQQKDAVvcmluYzEMMAoGA1UECwwD 7 | ZGV2MREwDwYDVQQDDAhyb290LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 8 | AQoCggEBALsqT2jdc7siXvzjoRELrXEBpsArb9yVv/5UQxMTfahl1D85LItFXM/y 9 | WHEBurB5G0ih8lfWiTkP5FKE3HIRXJ6FX0YViCUrz1Xt4mSzkiwpvvc0afcoYyF4 10 | pSazn1B4uFdHonUdYOWUbvTnfg2vMYof0xXmGRuyFX+wEnVgUa13uwl2ksGFpg9K 11 | IHfnOgk6YeIlsqoC0eeEL3FO/AAQe0KQWBrvWgiCRXU7MHpLvc1qD60EsUpKuVNI 12 | U21JNMDUjFugzsDFuUX8+sHY0qgFx3/BTuzymcrInN3ZHZL3+ZEODmxllDH2piVf 13 | 01KOp8o7IaFtljBj2ukttVpJ7RsbmwMCAwEAAaNTMFEwHQYDVR0OBBYEFFhJHTu7 14 | uPa5dRBN8TeKeiS0lvtQMB8GA1UdIwQYMBaAFFhJHTu7uPa5dRBN8TeKeiS0lvtQ 15 | MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEfN5I+hnduA2G5D 16 | rv9oNldOtifEw8+i3kcvfm4d6HkrKpMwsVXTlg5uosUXeQE9oGpOc6/H90GfYLc5 17 | Xae74DQ/xfJ7mIPi/XPEjD7c47tAA3qx5mpmyzQHgMUmWONCAdFsSaWDtmWyUv/7 18 | 0VG8E8ThuNHvMFDwvIqcJmM5Mjq8kEZy3DOIp02N9wOlY6ZH+KxUZBZ+1Nmf9HO3 19 | TW0lb/atay5EJwnqMer4wD3vRy1vHLg4RQ0oo9I0qT1uVcrQoEqM3u8BX+IAqv6g 20 | zb1P/v8atSo5BFL9NUVyl9BGldIuvGpokrsOGixeasUybborAllkYxZVV/1oGcmk 21 | Z7WEpGg= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /t/cert/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,596A1E93036923B5 4 | 5 | Z/gvrmUeqYIQnuB4qwm1oUKjFnO+DPSNMUsmc4FmleX5cHlMqfHNgt6tawqKKtS0 6 | 5vKrzBNAyjICEsjfWl01I8dB2wVxRuqC9hPF5Cbx//XPCcQXFeMBxMWPCZdyG9qV 7 | 8KXwvIsP/9J6b7amFeO7wPgISB9b4kOpvM3HrsrCfyVfQsTA7M526yZp/KH3JDt/ 8 | lTJipbVernZlo74rbjmjR2ake9dY3kX7UNXlsyx9+4cqpVd6U2PRrZ9GRxaCRkMr 9 | hu52COze7Kx7GXECU/f4Gn9vuZ4GvYYMyjm/lNWLMdZQuTSU4Uta59YxBsUS/uWU 10 | hHTwATCALvaqixrb/vErje7QkSjN2pJPSLHi5j13fqS4sTYMp3rRyy58CsJnvcJf 11 | rsFFQ6LansaAwO33yZzYCJoXH5TG/dvqGq19tx2N1WcAueCVywAcvn4sLWMvzRR2 12 | R9xbeD1wUoKE1RX5xIPmEg6TR/CCgozysLrwPRGtmIX3mi49Wh9wl0vYN39Y8xkA 13 | RFdM/EGs1p9TnbGtkZcun5EDsvbTUaFkWOmYhxfvx/qVNCAIsPMT5UAEmNDlJt0R 14 | 5Vv3B7DY+tgsbqfo/PozWC8cKJZSdkeopoFFAB0i/CMmZFyadIsgluC+0UhiwWHN 15 | alujA5Do5osm8tHNevPvSrOa16cFTpxo0XlRNE1QbMU0jYLbDPCnaYUOKkfwF0lV 16 | jMV/R0ChoysAcuDmCBSCsgT/LN9CF4sYB2ak8mryotlYXzt+85GCMazAJGKLqTJM 17 | Q1ES3bXyfNTCb+xMeab4utlEOczH/GxqO12wPFwqdKkodVTtyNHjigyCK2uEBqqD 18 | js2X5Wau+kLqdhkNfoKI8uyr++u4A2KtzkZ0GqgcKtlD170+fXZ5vH3QueT9btik 19 | 11BCzNrCMjWEfv2uXLI1kxOhLxrS3cM+IKU44WWS0rGbsx3zT+snOisusu9gFujR 20 | pNGb7yOOgPkDnCDREtsbB/4q+Gbkm/4oFa9jHGmadyGE95ohczZm2ek9fjuJQdPM 21 | SN8kvSXbiSmdjR5WAugj1AGwlqHEmWfuCRDOvMEsWsVrbQzHPz1d7lQ0n+4GZMD5 22 | DE1HyC4hhkXWFbrR18/hEzlj8minsad56sOsZnRukfqgC+UwmzLCQ0FeRxZ6YPLO 23 | G8QUEsV0Hkye24hTYDe1koXGCuGk+rzc3PiBFlxcCxHrP1LWCgDG43FI6pCFSaja 24 | +lZb3joEJI0bWBD3Qvu3VZa7bv/cmalXc3jlRLKIl0w8vktXC7sXfNAsg5b5/b/s 25 | ouad+mWHgCAyEk/3sQAiU7iH6CEY2uSJ+4zFRwJrf2FuMZFY1fqemjnSUNHs1TSk 26 | TmDCnMMd7HT04d2gBTCfStot5Ea9mLxrBuTERTDElg9wicqmjgPdIVStkeyfuroI 27 | vKCo22KVQKOyxuX+F9N0PjSoyLSXn/1b1Qd5fsWPQKemsO0T2RN2ylU/ESJXOWOF 28 | 68C+NYJ0PDzQvjRKa+BIdCS2S2upRDcZWkCPmqrKGS3A1tp2RwSJsa5ekIhKQDl+ 29 | qdtexk2csT4gUQ/M0A5xg0+L1HuT9wDnHUA26JcwZEOUBKUp4KYUwHrtacRPbJAp 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /t/cert/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDNzCCAh8CFBm+gdvmlf3JRMOp/BEuhJ4mrPjcMA0GCSqGSIb3DQEBCwUAMFgx 3 | CzAJBgNVBAYTAmNuMQswCQYDVQQIDAJmajELMAkGA1UEBwwCeG0xDjAMBgNVBAoM 4 | BW9yaW5jMQwwCgYDVQQLDANkZXYxETAPBgNVBAMMCHJvb3QuY29tMB4XDTIxMDkx 5 | NzA0MDkzM1oXDTMxMDkxNTA0MDkzM1owWDELMAkGA1UEBhMCY24xCzAJBgNVBAgM 6 | AmZqMQswCQYDVQQHDAJ4bTEOMAwGA1UECgwFb3JpbmMxDDAKBgNVBAsMA2RldjER 7 | MA8GA1UEAwwIdGVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB 8 | AQDNb1PwTcCe9tM2KJ/oFj72sQqGjyhPCGYRrEf3K2b7ZJ523EaYBcVpU3va/lqu 9 | QvbNoxEjssFNtXmlxL3wkptOLoNQ6ArsZs/J7HCWqqOwgNkGBQo53geTdVYyqdL/ 10 | Tb3DJAoH1n0qoo0qTjmgSjR6Hmj7HNf+9pkMLLUMtNUUO1mMeBXubJ+A0Kx9wjEw 11 | hlC8jhr0TlAJwXM9acTOeEbVQPPMTq7xETmqRGXnFTxBWcKawbJ78pmLUc0uv9Py 12 | UyuPYDjRtpPxwJSX63hk9gfuMVCmXl6JOfS2CtBZ2dRbbd72Wn8ld37wWZEX/M7M 13 | z09fOdyft0pTrXTf2f8Ne+FVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEMOO5c9 14 | kkSVe+vwPEr3JnGm+aQSgX8APa/CoXt+BETuecgjFLSH1Sb2nRWosUBMeQJzY4zx 15 | AbuBzyFsOUbFIJuUsKJjghDlOkrtUvq675vJ7/m5xzKxzl3I5TMHUmJ3+Zg7aOFu 16 | lJNLqGN3z536xKUPyEJoM9y6yvQuiDxzNr8Nb4iEg475Ro4OfOsNEIU888A3aWJY 17 | 5BsU3rnY1kkWPs7smr57O7bw0k0XRyEteeiS0aJ750J6aLDQBd+aVenUvWUoUq2L 18 | 5PK+/xSN6f8vLW6P0DW2wgtAjAcX73BISeBNUy6xbNDMy5n6poswsJ+ZxtodC/45 19 | Dv4MkBCZBv5HHog= 20 | -----END CERTIFICATE----- 21 | -----BEGIN CERTIFICATE----- 22 | MIIDkTCCAnmgAwIBAgIUJPrw/2C2SybyKqm5jyLpJjyF7F0wDQYJKoZIhvcNAQEL 23 | BQAwWDELMAkGA1UEBhMCY24xCzAJBgNVBAgMAmZqMQswCQYDVQQHDAJ4bTEOMAwG 24 | A1UECgwFb3JpbmMxDDAKBgNVBAsMA2RldjERMA8GA1UEAwwIcm9vdC5jb20wHhcN 25 | MjEwOTE3MDQwNzM3WhcNMzEwOTE1MDQwNzM3WjBYMQswCQYDVQQGEwJjbjELMAkG 26 | A1UECAwCZmoxCzAJBgNVBAcMAnhtMQ4wDAYDVQQKDAVvcmluYzEMMAoGA1UECwwD 27 | ZGV2MREwDwYDVQQDDAhyb290LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 28 | AQoCggEBALsqT2jdc7siXvzjoRELrXEBpsArb9yVv/5UQxMTfahl1D85LItFXM/y 29 | WHEBurB5G0ih8lfWiTkP5FKE3HIRXJ6FX0YViCUrz1Xt4mSzkiwpvvc0afcoYyF4 30 | pSazn1B4uFdHonUdYOWUbvTnfg2vMYof0xXmGRuyFX+wEnVgUa13uwl2ksGFpg9K 31 | IHfnOgk6YeIlsqoC0eeEL3FO/AAQe0KQWBrvWgiCRXU7MHpLvc1qD60EsUpKuVNI 32 | U21JNMDUjFugzsDFuUX8+sHY0qgFx3/BTuzymcrInN3ZHZL3+ZEODmxllDH2piVf 33 | 01KOp8o7IaFtljBj2ukttVpJ7RsbmwMCAwEAAaNTMFEwHQYDVR0OBBYEFFhJHTu7 34 | uPa5dRBN8TeKeiS0lvtQMB8GA1UdIwQYMBaAFFhJHTu7uPa5dRBN8TeKeiS0lvtQ 35 | MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEfN5I+hnduA2G5D 36 | rv9oNldOtifEw8+i3kcvfm4d6HkrKpMwsVXTlg5uosUXeQE9oGpOc6/H90GfYLc5 37 | Xae74DQ/xfJ7mIPi/XPEjD7c47tAA3qx5mpmyzQHgMUmWONCAdFsSaWDtmWyUv/7 38 | 0VG8E8ThuNHvMFDwvIqcJmM5Mjq8kEZy3DOIp02N9wOlY6ZH+KxUZBZ+1Nmf9HO3 39 | TW0lb/atay5EJwnqMer4wD3vRy1vHLg4RQ0oo9I0qT1uVcrQoEqM3u8BX+IAqv6g 40 | zb1P/v8atSo5BFL9NUVyl9BGldIuvGpokrsOGixeasUybborAllkYxZVV/1oGcmk 41 | Z7WEpGg= 42 | -----END CERTIFICATE----- 43 | -------------------------------------------------------------------------------- /t/cert/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAzW9T8E3AnvbTNiif6BY+9rEKho8oTwhmEaxH9ytm+2SedtxG 3 | mAXFaVN72v5arkL2zaMRI7LBTbV5pcS98JKbTi6DUOgK7GbPyexwlqqjsIDZBgUK 4 | Od4Hk3VWMqnS/029wyQKB9Z9KqKNKk45oEo0eh5o+xzX/vaZDCy1DLTVFDtZjHgV 5 | 7myfgNCsfcIxMIZQvI4a9E5QCcFzPWnEznhG1UDzzE6u8RE5qkRl5xU8QVnCmsGy 6 | e/KZi1HNLr/T8lMrj2A40baT8cCUl+t4ZPYH7jFQpl5eiTn0tgrQWdnUW23e9lp/ 7 | JXd+8FmRF/zOzM9PXzncn7dKU61039n/DXvhVQIDAQABAoIBAAIttw8qU+ubpHvD 8 | jIaSjdJh6EGrDPthlGuFjzHQ1QO/emq6RUlhbkmCqCKXZfDJJwLhQtJFGAWx8ftP 9 | DdCaTWlT1vKps0v0fymeB0ehwaCijSqiLQyWDTCEIwjggitQ8L08CZsxrBwioyqa 10 | r8PCZbIR1n0rPiMnDFJzuuvKkyPmxFu9OA5u99Njmp5sj1O4ZcLwNdIFz8PiWOBX 11 | BBtxoIACU+Gq26qIAoBZFzEa5CS+hJd/xkolx9PMz7wyvf9fHd6mQg+U/tdi5VMj 12 | YAGw3x58U+vwgaCNkXVgUHBmFJ8rsTS0kCl96JkzFbAQOUUWS1b0f4WB2yacSqPi 13 | 2f8LDjUCgYEA62Z8U6tHSMTK7SLwF3LG1NH7A2CmgxDHhb83vNYvyM+4muBun3qa 14 | 40tPEOnuVlXDV3pZWGr4evPnjJxlEXK4qDC5EE/N1i265pIg4SVNSGg4wdoFeuep 15 | w9CUPPPv959c9xAakwC7/xhVgHpXN1C/s/ZWDnlPceTmWcZezn9wVbsCgYEA32mL 16 | 23ATMcS0VxpLYLqIyp5WZ2wCfePwcF7s/GG6VOansm5dixVWpc0+bxG/q3zXH5wJ 17 | ZYOBrghDAq+ldjWGKlD6JIO66Rb/J+6yOghzxsI3BsfE+Vd0MUkACV/UH1AGdBex 18 | a/BmvyjM2shoUtufeQ/PXuhicjqtmQIYJelgLC8CgYEAzCXBRL6Rv6YLZsSZ+3vq 19 | u76vNiqnvZrbrj47FGWcY0biDk79IQmvMdep5j06GV2fX0oYGfoLLHZCJUusywj2 20 | 48X9eQv8svZN1JB+O0OiVl0se9GcVw55x/wn0wSq49EtSoaIxVwprf1oiVeQM8Sv 21 | vF6yGzNRlrCwpMLIjcUvoukCgYAqs1Ol11/zzTLiywXJMR3gDC9biQtY4G4eLCaD 22 | fnJMs+Ete2appKRxAC+ErxHBRxUe14X8PwxUJQQZyVSIqWZBRETrIbphvqF6EmdC 23 | LEdkn85An4IIXiy5FsjJAeXbLkJtgfHJPaOFudidoFxAKfXq/5iU/TmDt69iZUIU 24 | TOyyxQKBgQCZQX3Q0Z2XuRVeaH7BHWOYhUR55a2BJad8rjl/X+sThGvDbGF5nFcl 25 | h5vYHfLIWNG2MsVP/6CM5E2VovKenxqkszV6GJ+RsuI2BWUSNi1kitA2yjJaqh/f 26 | XcQlq+x4flLzGooXO5YUiXj9mtcpBFOEOW/O4CMnTMlNUYyx/bLJ+w== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /t/mock.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket::Lua; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(2); 7 | 8 | plan tests => repeat_each() * (4 * blocks() + 4); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; 18 | 19 | no_long_string(); 20 | 21 | run_tests(); 22 | 23 | __DATA__ 24 | 25 | === TEST 1: fail to flush 26 | --- http_config eval: $::HttpConfig 27 | --- config 28 | location /t { 29 | content_by_lua ' 30 | local memcached = require "resty.memcached" 31 | local memc = memcached:new() 32 | 33 | memc:set_timeout(1000) -- 1 sec 34 | 35 | local ok, err = memc:connect("127.0.0.1", 1921); 36 | if not ok then 37 | ngx.say("failed to connect: ", err) 38 | return 39 | end 40 | 41 | local ok, err = memc:flush_all() 42 | if not ok then 43 | ngx.say("failed to flush all: ", err) 44 | return 45 | end 46 | 47 | ngx.say("flush: ", ok); 48 | 49 | memc:close() 50 | '; 51 | } 52 | --- request 53 | GET /t 54 | --- tcp_listen: 1921 55 | --- tcp_query_len: 11 56 | --- tcp_query eval 57 | "flush_all\r\n" 58 | --- tcp_reply eval 59 | "SOME ERROR\r\n" 60 | --- response_body 61 | failed to flush all: SOME ERROR 62 | --- no_error_log 63 | [error] 64 | 65 | 66 | 67 | === TEST 2: continue using the obj when read timeout happens 68 | --- http_config eval: $::HttpConfig 69 | --- config 70 | location /t { 71 | content_by_lua ' 72 | local memcached = require "resty.memcached" 73 | local memc = memcached:new() 74 | 75 | local ok, err = memc:connect("127.0.0.1", 1921); 76 | if not ok then 77 | ngx.say("failed to connect: ", err) 78 | return 79 | end 80 | 81 | memc:set_timeout(100) -- 0.1 sec 82 | 83 | for i = 1, 2 do 84 | local data, flags, err = memc:get("foo") 85 | if not data and err then 86 | ngx.say("failed to get: ", err) 87 | else 88 | ngx.say("get: ", data); 89 | end 90 | ngx.sleep(0.1) 91 | end 92 | 93 | memc:close() 94 | '; 95 | } 96 | --- request 97 | GET /t 98 | --- tcp_listen: 1921 99 | --- tcp_query_len: 9 100 | --- tcp_query eval 101 | "get foo\r\n" 102 | --- tcp_reply eval 103 | "VALUE foo 0 5\r\nhello\r\nEND\r\n" 104 | --- tcp_reply_delay: 150ms 105 | --- response_body 106 | failed to get: timeout 107 | failed to get: closed 108 | --- error_log 109 | lua tcp socket read timed out 110 | 111 | 112 | 113 | === TEST 3: gets multi getting error responses 114 | --- http_config eval: $::HttpConfig 115 | --- config 116 | location /t { 117 | content_by_lua ' 118 | local memcached = require "resty.memcached" 119 | local memc = memcached:new() 120 | 121 | memc:set_timeout(1000) -- 1 sec 122 | 123 | local ok, err = memc:connect("127.0.0.1", 1921); 124 | if not ok then 125 | ngx.say("failed to connect: ", err) 126 | return 127 | end 128 | 129 | local res, err = memc:gets({"dog", "cat"}) 130 | if not res then 131 | ngx.say("failed to gets: ", err) 132 | return 133 | end 134 | 135 | ngx.say("gets: ", table.concat(res, ", ")); 136 | 137 | memc:close() 138 | '; 139 | } 140 | --- request 141 | GET /t 142 | --- tcp_listen: 1921 143 | --- tcp_query_len: 14 144 | --- tcp_query eval 145 | "gets dog cat\r\n" 146 | --- tcp_reply eval 147 | "SERVER_ERROR\r\n" 148 | --- response_body 149 | failed to gets: SERVER_ERROR 150 | --- no_error_log 151 | [error] 152 | 153 | 154 | 155 | === TEST 4: get multi getting error responses 156 | --- http_config eval: $::HttpConfig 157 | --- config 158 | location /t { 159 | content_by_lua ' 160 | local memcached = require "resty.memcached" 161 | local memc = memcached:new() 162 | 163 | memc:set_timeout(1000) -- 1 sec 164 | 165 | local ok, err = memc:connect("127.0.0.1", 1921); 166 | if not ok then 167 | ngx.say("failed to connect: ", err) 168 | return 169 | end 170 | 171 | local res, err = memc:get({"dog", "cat"}) 172 | if not res then 173 | ngx.say("failed to get: ", err) 174 | return 175 | end 176 | 177 | ngx.say("get: ", table.concat(res, ", ")); 178 | 179 | memc:close() 180 | '; 181 | } 182 | --- request 183 | GET /t 184 | --- tcp_listen: 1921 185 | --- tcp_query_len: 13 186 | --- tcp_query eval 187 | "get dog cat\r\n" 188 | --- tcp_reply eval 189 | "SERVER_ERROR\r\n" 190 | --- response_body 191 | failed to get: SERVER_ERROR 192 | --- no_error_log 193 | [error] 194 | -------------------------------------------------------------------------------- /t/sanity.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket::Lua; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(2); 7 | 8 | plan tests => repeat_each() * (3 * blocks() - 1); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; 18 | 19 | no_long_string(); 20 | 21 | run_tests(); 22 | 23 | __DATA__ 24 | 25 | === TEST 1: basic 26 | --- http_config eval: $::HttpConfig 27 | --- config 28 | location /t { 29 | content_by_lua ' 30 | local memcached = require "resty.memcached" 31 | local memc = memcached:new() 32 | 33 | assert(memc:set_timeout(1000)) -- 1 sec 34 | 35 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 36 | if not ok then 37 | ngx.say("failed to connect: ", err) 38 | return 39 | end 40 | 41 | local ok, err = memc:flush_all() 42 | if not ok then 43 | ngx.say("failed to flush all: ", err) 44 | return 45 | end 46 | 47 | local ok, err = memc:set("dog", 32) 48 | if not ok then 49 | ngx.say("failed to set dog: ", err) 50 | return 51 | end 52 | 53 | for i = 1, 2 do 54 | local res, flags, err = memc:get("dog") 55 | if err then 56 | ngx.say("failed to get dog: ", err) 57 | return 58 | end 59 | 60 | if not res then 61 | ngx.say("dog not found") 62 | return 63 | end 64 | 65 | ngx.say("dog: ", res, " (flags: ", flags, ")") 66 | end 67 | 68 | memc:close() 69 | '; 70 | } 71 | --- request 72 | GET /t 73 | --- response_body 74 | dog: 32 (flags: 0) 75 | dog: 32 (flags: 0) 76 | --- no_error_log 77 | [error] 78 | 79 | 80 | 81 | === TEST 2: add an exsitent key 82 | --- http_config eval: $::HttpConfig 83 | --- config 84 | location /t { 85 | content_by_lua ' 86 | local memcached = require "resty.memcached" 87 | local memc = memcached:new() 88 | 89 | memc:set_timeout(1000) -- 1 sec 90 | 91 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 92 | if not ok then 93 | ngx.say("failed to connect: ", err) 94 | return 95 | end 96 | 97 | local ok, err = memc:flush_all() 98 | if not ok then 99 | ngx.say("failed to flush all: ", err) 100 | return 101 | end 102 | 103 | local ok, err = memc:set("dog", 32) 104 | if not ok then 105 | ngx.say("failed to set dog: ", err) 106 | return 107 | end 108 | 109 | local ok, err = memc:add("dog", 56) 110 | if not ok then 111 | ngx.say("failed to add dog: ", err) 112 | end 113 | 114 | local res, flags, err = memc:get("dog") 115 | if err then 116 | ngx.say("failed to get dog: ", err) 117 | return 118 | end 119 | 120 | if not res then 121 | ngx.say("dog not found") 122 | return 123 | end 124 | 125 | ngx.say("dog: ", res) 126 | memc:close() 127 | '; 128 | } 129 | --- request 130 | GET /t 131 | --- response_body 132 | failed to add dog: NOT_STORED 133 | dog: 32 134 | --- no_error_log 135 | [error] 136 | 137 | 138 | 139 | === TEST 3: add a nonexistent key 140 | --- http_config eval: $::HttpConfig 141 | --- config 142 | location /t { 143 | content_by_lua ' 144 | local memcached = require "resty.memcached" 145 | local memc = memcached:new() 146 | 147 | memc:set_timeout(1000) -- 1 sec 148 | 149 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 150 | if not ok then 151 | ngx.say("failed to connect: ", err) 152 | return 153 | end 154 | 155 | local ok, err = memc:flush_all() 156 | if not ok then 157 | ngx.say("failed to flush all: ", err) 158 | return 159 | end 160 | 161 | local ok, err = memc:add("dog", 56) 162 | if not ok then 163 | ngx.say("failed to add dog: ", err) 164 | end 165 | 166 | local res, flags, err = memc:get("dog") 167 | if err then 168 | ngx.say("failed to get dog: ", err) 169 | return 170 | end 171 | 172 | if not res then 173 | ngx.say("dog not found") 174 | return 175 | end 176 | 177 | ngx.say("dog: ", res) 178 | memc:close() 179 | '; 180 | } 181 | --- request 182 | GET /t 183 | --- response_body 184 | dog: 56 185 | --- no_error_log 186 | [error] 187 | 188 | 189 | 190 | === TEST 4: set an existent key 191 | --- http_config eval: $::HttpConfig 192 | --- config 193 | location /t { 194 | content_by_lua ' 195 | local memcached = require "resty.memcached" 196 | local memc = memcached:new() 197 | 198 | memc:set_timeout(1000) -- 1 sec 199 | 200 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 201 | if not ok then 202 | ngx.say("failed to connect: ", err) 203 | return 204 | end 205 | 206 | local ok, err = memc:flush_all() 207 | if not ok then 208 | ngx.say("failed to flush all: ", err) 209 | return 210 | end 211 | 212 | local ok, err = memc:set("dog", 32) 213 | if not ok then 214 | ngx.say("failed to set dog: ", err) 215 | return 216 | end 217 | 218 | local ok, err = memc:set("dog", 56) 219 | if not ok then 220 | ngx.say("failed to set dog: ", err) 221 | return 222 | end 223 | 224 | local res, flags, err = memc:get("dog") 225 | if err then 226 | ngx.say("failed to get dog: ", err) 227 | return 228 | end 229 | 230 | if not res then 231 | ngx.say("dog not found") 232 | return 233 | end 234 | 235 | ngx.say("dog: ", res) 236 | memc:close() 237 | '; 238 | } 239 | --- request 240 | GET /t 241 | --- response_body 242 | dog: 56 243 | --- no_error_log 244 | [error] 245 | 246 | 247 | 248 | === TEST 5: replace an existent key 249 | --- http_config eval: $::HttpConfig 250 | --- config 251 | location /t { 252 | content_by_lua ' 253 | local memcached = require "resty.memcached" 254 | local memc = memcached:new() 255 | 256 | memc:set_timeout(1000) -- 1 sec 257 | 258 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 259 | if not ok then 260 | ngx.say("failed to connect: ", err) 261 | return 262 | end 263 | 264 | local ok, err = memc:flush_all() 265 | if not ok then 266 | ngx.say("failed to flush all: ", err) 267 | return 268 | end 269 | 270 | local ok, err = memc:set("dog", 32) 271 | if not ok then 272 | ngx.say("failed to set dog: ", err) 273 | return 274 | end 275 | 276 | local ok, err = memc:replace("dog", 56) 277 | if not ok then 278 | ngx.say("failed to replace dog: ", err) 279 | return 280 | end 281 | 282 | local res, flags, err = memc:get("dog") 283 | if err then 284 | ngx.say("failed to get dog: ", err) 285 | return 286 | end 287 | 288 | if not res then 289 | ngx.say("dog not found") 290 | return 291 | end 292 | 293 | ngx.say("dog: ", res) 294 | memc:close() 295 | '; 296 | } 297 | --- request 298 | GET /t 299 | --- response_body 300 | dog: 56 301 | --- no_error_log 302 | [error] 303 | 304 | 305 | 306 | === TEST 6: replace a nonexsistent key 307 | --- http_config eval: $::HttpConfig 308 | --- config 309 | location /t { 310 | content_by_lua ' 311 | local memcached = require "resty.memcached" 312 | local memc = memcached:new() 313 | 314 | memc:set_timeout(1000) -- 1 sec 315 | 316 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 317 | if not ok then 318 | ngx.say("failed to connect: ", err) 319 | return 320 | end 321 | 322 | local ok, err = memc:flush_all() 323 | if not ok then 324 | ngx.say("failed to flush all: ", err) 325 | return 326 | end 327 | 328 | local ok, err = memc:replace("dog", 56) 329 | if not ok then 330 | ngx.say("failed to replace dog: ", err) 331 | end 332 | 333 | local res, flags, err = memc:get("dog") 334 | if err then 335 | ngx.say("failed to get dog: ", err) 336 | return 337 | end 338 | 339 | if not res then 340 | ngx.say("dog not found") 341 | return 342 | end 343 | 344 | ngx.say("dog: ", res) 345 | memc:close() 346 | '; 347 | } 348 | --- request 349 | GET /t 350 | --- response_body 351 | failed to replace dog: NOT_STORED 352 | dog not found 353 | --- no_error_log 354 | [error] 355 | 356 | 357 | 358 | === TEST 7: prepend to a nonexsistent key 359 | --- http_config eval: $::HttpConfig 360 | --- config 361 | location /t { 362 | content_by_lua ' 363 | local memcached = require "resty.memcached" 364 | local memc = memcached:new() 365 | 366 | memc:set_timeout(1000) -- 1 sec 367 | 368 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 369 | if not ok then 370 | ngx.say("failed to connect: ", err) 371 | return 372 | end 373 | 374 | local ok, err = memc:flush_all() 375 | if not ok then 376 | ngx.say("failed to flush all: ", err) 377 | return 378 | end 379 | 380 | local ok, err = memc:prepend("dog", 56) 381 | if not ok then 382 | ngx.say("failed to prepend to dog: ", err) 383 | end 384 | 385 | local res, flags, err = memc:get("dog") 386 | if err then 387 | ngx.say("failed to get dog: ", err) 388 | return 389 | end 390 | 391 | if not res then 392 | ngx.say("dog not found") 393 | return 394 | end 395 | 396 | ngx.say("dog: ", res) 397 | memc:close() 398 | '; 399 | } 400 | --- request 401 | GET /t 402 | --- response_body 403 | failed to prepend to dog: NOT_STORED 404 | dog not found 405 | --- no_error_log 406 | [error] 407 | 408 | 409 | 410 | === TEST 8: prepend to an existent key 411 | --- http_config eval: $::HttpConfig 412 | --- config 413 | location /t { 414 | content_by_lua ' 415 | local memcached = require "resty.memcached" 416 | local memc = memcached:new() 417 | 418 | memc:set_timeout(1000) -- 1 sec 419 | 420 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 421 | if not ok then 422 | ngx.say("failed to connect: ", err) 423 | return 424 | end 425 | 426 | local ok, err = memc:flush_all() 427 | if not ok then 428 | ngx.say("failed to flush all: ", err) 429 | return 430 | end 431 | 432 | local ok, err = memc:set("dog", 32) 433 | if not ok then 434 | ngx.say("failed to set dog: ", err) 435 | end 436 | 437 | local ok, err = memc:prepend("dog", 56) 438 | if not ok then 439 | ngx.say("failed to prepend to dog: ", err) 440 | end 441 | 442 | local res, flags, err = memc:get("dog") 443 | if err then 444 | ngx.say("failed to get dog: ", err) 445 | return 446 | end 447 | 448 | if not res then 449 | ngx.say("dog not found") 450 | return 451 | end 452 | 453 | ngx.say("dog: ", res) 454 | memc:close() 455 | '; 456 | } 457 | --- request 458 | GET /t 459 | --- response_body 460 | dog: 5632 461 | --- no_error_log 462 | [error] 463 | 464 | 465 | 466 | === TEST 9: append to a nonexsistent key 467 | --- http_config eval: $::HttpConfig 468 | --- config 469 | location /t { 470 | content_by_lua ' 471 | local memcached = require "resty.memcached" 472 | local memc = memcached:new() 473 | 474 | memc:set_timeout(1000) -- 1 sec 475 | 476 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 477 | if not ok then 478 | ngx.say("failed to connect: ", err) 479 | return 480 | end 481 | 482 | local ok, err = memc:flush_all() 483 | if not ok then 484 | ngx.say("failed to flush all: ", err) 485 | return 486 | end 487 | 488 | local ok, err = memc:append("dog", 56) 489 | if not ok then 490 | ngx.say("failed to append to dog: ", err) 491 | end 492 | 493 | local res, flags, err = memc:get("dog") 494 | if err then 495 | ngx.say("failed to get dog: ", err) 496 | return 497 | end 498 | 499 | if not res then 500 | ngx.say("dog not found") 501 | return 502 | end 503 | 504 | ngx.say("dog: ", res) 505 | memc:close() 506 | '; 507 | } 508 | --- request 509 | GET /t 510 | --- response_body 511 | failed to append to dog: NOT_STORED 512 | dog not found 513 | --- no_error_log 514 | [error] 515 | 516 | 517 | 518 | === TEST 10: append to an existent key 519 | --- http_config eval: $::HttpConfig 520 | --- config 521 | location /t { 522 | content_by_lua ' 523 | local memcached = require "resty.memcached" 524 | local memc = memcached:new() 525 | 526 | memc:set_timeout(1000) -- 1 sec 527 | 528 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 529 | if not ok then 530 | ngx.say("failed to connect: ", err) 531 | return 532 | end 533 | 534 | local ok, err = memc:flush_all() 535 | if not ok then 536 | ngx.say("failed to flush all: ", err) 537 | return 538 | end 539 | 540 | local ok, err = memc:set("dog", 32) 541 | if not ok then 542 | ngx.say("failed to set dog: ", err) 543 | end 544 | 545 | local ok, err = memc:append("dog", 56) 546 | if not ok then 547 | ngx.say("failed to append to dog: ", err) 548 | end 549 | 550 | local res, flags, err = memc:get("dog") 551 | if err then 552 | ngx.say("failed to get dog: ", err) 553 | return 554 | end 555 | 556 | if not res then 557 | ngx.say("dog not found") 558 | return 559 | end 560 | 561 | ngx.say("dog: ", res) 562 | memc:close() 563 | '; 564 | } 565 | --- request 566 | GET /t 567 | --- response_body 568 | dog: 3256 569 | --- no_error_log 570 | [error] 571 | 572 | 573 | 574 | === TEST 11: delete an existent key 575 | --- http_config eval: $::HttpConfig 576 | --- config 577 | location /t { 578 | content_by_lua ' 579 | local memcached = require "resty.memcached" 580 | local memc = memcached:new() 581 | 582 | memc:set_timeout(1000) -- 1 sec 583 | 584 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 585 | if not ok then 586 | ngx.say("failed to connect: ", err) 587 | return 588 | end 589 | 590 | local ok, err = memc:flush_all() 591 | if not ok then 592 | ngx.say("failed to flush all: ", err) 593 | return 594 | end 595 | 596 | local ok, err = memc:set("dog", 32) 597 | if not ok then 598 | ngx.say("failed to set dog: ", err) 599 | end 600 | 601 | local ok, err = memc:delete("dog") 602 | if not ok then 603 | ngx.say("failed to delete dog: ", err) 604 | end 605 | 606 | local res, flags, err = memc:get("dog") 607 | if err then 608 | ngx.say("failed to get dog: ", err) 609 | return 610 | end 611 | 612 | if not res then 613 | ngx.say("dog not found") 614 | return 615 | end 616 | 617 | ngx.say("dog: ", res) 618 | 619 | local res, flags, err = memc:add("dog", 772) 620 | if err then 621 | ngx.say("failed to add dog: ", err) 622 | return 623 | end 624 | 625 | memc:close() 626 | '; 627 | } 628 | --- request 629 | GET /t 630 | --- response_body 631 | dog not found 632 | --- no_error_log 633 | [error] 634 | 635 | 636 | 637 | === TEST 12: delete a nonexsistent key 638 | --- http_config eval: $::HttpConfig 639 | --- config 640 | location /t { 641 | content_by_lua ' 642 | local memcached = require "resty.memcached" 643 | local memc = memcached:new() 644 | 645 | memc:set_timeout(1000) -- 1 sec 646 | 647 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 648 | if not ok then 649 | ngx.say("failed to connect: ", err) 650 | return 651 | end 652 | 653 | local ok, err = memc:flush_all() 654 | if not ok then 655 | ngx.say("failed to flush all: ", err) 656 | return 657 | end 658 | 659 | local ok, err = memc:delete("dog") 660 | if not ok then 661 | ngx.say("failed to delete dog: ", err) 662 | end 663 | 664 | local res, flags, err = memc:get("dog") 665 | if err then 666 | ngx.say("failed to get dog: ", err) 667 | return 668 | end 669 | 670 | if not res then 671 | ngx.say("dog not found") 672 | return 673 | end 674 | 675 | ngx.say("dog: ", res) 676 | memc:close() 677 | '; 678 | } 679 | --- request 680 | GET /t 681 | --- response_body 682 | failed to delete dog: NOT_FOUND 683 | dog not found 684 | --- no_error_log 685 | [error] 686 | 687 | 688 | 689 | === TEST 13: delete an existent key with delay 690 | --- SKIP 691 | --- http_config eval: $::HttpConfig 692 | --- config 693 | location /t { 694 | content_by_lua ' 695 | local memcached = require "resty.memcached" 696 | local memc = memcached:new() 697 | 698 | memc:set_timeout(1000) -- 1 sec 699 | 700 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 701 | if not ok then 702 | ngx.say("failed to connect: ", err) 703 | return 704 | end 705 | 706 | local ok, err = memc:flush_all() 707 | if not ok then 708 | ngx.say("failed to flush all: ", err) 709 | return 710 | end 711 | 712 | local ok, err = memc:set("dog", 32) 713 | if not ok then 714 | ngx.say("failed to set dog: ", err) 715 | return 716 | end 717 | 718 | local ok, err = memc:delete("dog", 1) 719 | if not ok then 720 | ngx.say("failed to delete dog: ", err) 721 | end 722 | 723 | local ok, err = memc:add("dog", 76) 724 | if not ok then 725 | ngx.say("failed to add dog: ", err) 726 | end 727 | 728 | local ok, err = memc:replace("dog", 53) 729 | if not ok then 730 | ngx.say("failed to replace dog: ", err) 731 | end 732 | 733 | local res, flags, err = memc:get("dog") 734 | if err then 735 | ngx.say("failed to get dog: ", err) 736 | return 737 | end 738 | 739 | if not res then 740 | ngx.say("dog not found") 741 | return 742 | end 743 | 744 | ngx.say("dog: ", res) 745 | memc:close() 746 | '; 747 | } 748 | --- request 749 | GET /t 750 | --- response_body 751 | failed to add dog: NOT_STORED 752 | failed to replace dog: NOT_STORED 753 | dog not found 754 | --- no_error_log 755 | [error] 756 | 757 | 758 | 759 | === TEST 14: flags 760 | --- http_config eval: $::HttpConfig 761 | --- config 762 | location /t { 763 | content_by_lua ' 764 | local memcached = require "resty.memcached" 765 | local memc = memcached:new() 766 | 767 | memc:set_timeout(1000) -- 1 sec 768 | 769 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 770 | if not ok then 771 | ngx.say("failed to connect: ", err) 772 | return 773 | end 774 | 775 | local ok, err = memc:flush_all() 776 | if not ok then 777 | ngx.say("failed to flush all: ", err) 778 | return 779 | end 780 | 781 | local ok, err = memc:set("dog", 32, 0, 526) 782 | if not ok then 783 | ngx.say("failed to set dog: ", err) 784 | return 785 | end 786 | 787 | local res, flags, err = memc:get("dog") 788 | if err then 789 | ngx.say("failed to get dog: ", err) 790 | return 791 | end 792 | 793 | if not res then 794 | ngx.say("dog not found") 795 | return 796 | end 797 | 798 | ngx.say("dog: ", res, " (flags: ", flags, ")") 799 | memc:close() 800 | '; 801 | } 802 | --- request 803 | GET /t 804 | --- response_body 805 | dog: 32 (flags: 526) 806 | --- no_error_log 807 | [error] 808 | 809 | 810 | 811 | === TEST 15: set with exptime 812 | --- http_config eval: $::HttpConfig 813 | --- config 814 | location /t { 815 | content_by_lua ' 816 | local memcached = require "resty.memcached" 817 | local memc = memcached:new() 818 | 819 | memc:set_timeout(1000) -- 1 sec 820 | 821 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 822 | if not ok then 823 | ngx.say("failed to connect: ", err) 824 | return 825 | end 826 | 827 | local ok, err = memc:flush_all() 828 | if not ok then 829 | ngx.say("failed to flush all: ", err) 830 | return 831 | end 832 | 833 | local res, flags, err = memc:get("dog") 834 | if err then 835 | ngx.say("failed to get dog: ", err) 836 | return 837 | end 838 | 839 | ngx.location.capture("/sleep"); 840 | 841 | local ok, err = memc:set("dog", 32, 1, 526) 842 | if not ok then 843 | ngx.say("failed to set dog: ", err) 844 | return 845 | end 846 | 847 | if not res then 848 | ngx.say("dog not found") 849 | return 850 | end 851 | 852 | ngx.say("dog: ", res, " (flags: ", flags, ")") 853 | memc:close() 854 | '; 855 | } 856 | 857 | location /sleep { 858 | echo_sleep 1.1; 859 | } 860 | --- request 861 | GET /t 862 | --- response_body 863 | dog not found 864 | --- no_error_log 865 | [error] 866 | 867 | 868 | 869 | === TEST 16: flush with a delay 870 | --- http_config eval: $::HttpConfig 871 | --- config 872 | location /t { 873 | content_by_lua ' 874 | local memcached = require "resty.memcached" 875 | local memc = memcached:new() 876 | 877 | memc:set_timeout(1000) -- 1 sec 878 | 879 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 880 | if not ok then 881 | ngx.say("failed to connect: ", err) 882 | return 883 | end 884 | 885 | local ok, err = memc:flush_all() 886 | if not ok then 887 | ngx.say("failed to flush all: ", err) 888 | return 889 | end 890 | 891 | local ok, err = memc:set("dog", 32) 892 | if not ok then 893 | ngx.say("failed to set dog: ", err) 894 | return 895 | end 896 | 897 | local ok, err = memc:flush_all(3) 898 | if not ok then 899 | ngx.say("failed to flush all: ", err) 900 | return 901 | end 902 | 903 | local res, flags, err = memc:get("dog") 904 | if err then 905 | ngx.say("failed to get dog: ", err) 906 | return 907 | end 908 | 909 | if not res then 910 | ngx.say("dog not found") 911 | return 912 | end 913 | 914 | ngx.say("dog: ", res, " (flags: ", flags, ")") 915 | memc:close() 916 | '; 917 | } 918 | --- request 919 | GET /t 920 | --- response_body 921 | dog: 32 (flags: 0) 922 | --- no_error_log 923 | [error] 924 | 925 | 926 | 927 | === TEST 17: incr an existent key 928 | --- http_config eval: $::HttpConfig 929 | --- config 930 | location /t { 931 | content_by_lua ' 932 | local memcached = require "resty.memcached" 933 | local memc = memcached:new() 934 | 935 | memc:set_timeout(1000) -- 1 sec 936 | 937 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 938 | if not ok then 939 | ngx.say("failed to connect: ", err) 940 | return 941 | end 942 | 943 | local ok, err = memc:flush_all() 944 | if not ok then 945 | ngx.say("failed to flush all: ", err) 946 | return 947 | end 948 | 949 | local ok, err = memc:set("dog", 32) 950 | if not ok then 951 | ngx.say("failed to set dog: ", err) 952 | return 953 | end 954 | 955 | local value, err = memc:incr("dog", 2) 956 | if not value then 957 | ngx.say("failed to incr dog: ", err) 958 | return 959 | end 960 | 961 | ngx.say("dog is now: ", value) 962 | 963 | local res, flags, err = memc:get("dog") 964 | if err then 965 | ngx.say("failed to get dog: ", err) 966 | return 967 | end 968 | 969 | if not res then 970 | ngx.say("dog not found") 971 | return 972 | end 973 | 974 | ngx.say("dog: ", res, " (flags: ", flags, ")") 975 | memc:close() 976 | '; 977 | } 978 | --- request 979 | GET /t 980 | --- response_body 981 | dog is now: 34 982 | dog: 34 (flags: 0) 983 | --- no_error_log 984 | [error] 985 | 986 | 987 | 988 | === TEST 18: incr a nonexistent key 989 | --- http_config eval: $::HttpConfig 990 | --- config 991 | location /t { 992 | content_by_lua ' 993 | local memcached = require "resty.memcached" 994 | local memc = memcached:new() 995 | 996 | memc:set_timeout(1000) -- 1 sec 997 | 998 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 999 | if not ok then 1000 | ngx.say("failed to connect: ", err) 1001 | return 1002 | end 1003 | 1004 | local ok, err = memc:flush_all() 1005 | if not ok then 1006 | ngx.say("failed to flush all: ", err) 1007 | return 1008 | end 1009 | 1010 | local value, err = memc:incr("dog", 2) 1011 | if not value then 1012 | ngx.say("failed to incr dog: ", err) 1013 | return 1014 | end 1015 | 1016 | ngx.say("dog is now: ", value) 1017 | 1018 | local res, flags, err = memc:get("dog") 1019 | if err then 1020 | ngx.say("failed to get dog: ", err) 1021 | return 1022 | end 1023 | 1024 | if not res then 1025 | ngx.say("dog not found") 1026 | return 1027 | end 1028 | 1029 | ngx.say("dog: ", res, " (flags: ", flags, ")") 1030 | memc:close() 1031 | '; 1032 | } 1033 | --- request 1034 | GET /t 1035 | --- response_body 1036 | failed to incr dog: NOT_FOUND 1037 | --- no_error_log 1038 | [error] 1039 | 1040 | 1041 | 1042 | === TEST 19: decr an existent key 1043 | --- http_config eval: $::HttpConfig 1044 | --- config 1045 | location /t { 1046 | content_by_lua ' 1047 | local memcached = require "resty.memcached" 1048 | local memc = memcached:new() 1049 | 1050 | memc:set_timeout(1000) -- 1 sec 1051 | 1052 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1053 | if not ok then 1054 | ngx.say("failed to connect: ", err) 1055 | return 1056 | end 1057 | 1058 | local ok, err = memc:flush_all() 1059 | if not ok then 1060 | ngx.say("failed to flush all: ", err) 1061 | return 1062 | end 1063 | 1064 | local ok, err = memc:set("dog", 32) 1065 | if not ok then 1066 | ngx.say("failed to set dog: ", err) 1067 | return 1068 | end 1069 | 1070 | local value, err = memc:decr("dog", 3) 1071 | if not value then 1072 | ngx.say("failed to decr dog: ", err) 1073 | return 1074 | end 1075 | 1076 | ngx.say("dog is now: ", value) 1077 | 1078 | local res, flags, err = memc:get("dog") 1079 | if err then 1080 | ngx.say("failed to get dog: ", err) 1081 | return 1082 | end 1083 | 1084 | if not res then 1085 | ngx.say("dog not found") 1086 | return 1087 | end 1088 | 1089 | ngx.say("dog: ", res, " (flags: ", flags, ")") 1090 | memc:close() 1091 | '; 1092 | } 1093 | --- request 1094 | GET /t 1095 | --- response_body 1096 | dog is now: 29 1097 | dog: 29 (flags: 0) 1098 | --- no_error_log 1099 | [error] 1100 | 1101 | 1102 | 1103 | === TEST 20: decr a nonexistent key 1104 | --- http_config eval: $::HttpConfig 1105 | --- config 1106 | location /t { 1107 | content_by_lua ' 1108 | local memcached = require "resty.memcached" 1109 | local memc = memcached:new() 1110 | 1111 | memc:set_timeout(1000) -- 1 sec 1112 | 1113 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1114 | if not ok then 1115 | ngx.say("failed to connect: ", err) 1116 | return 1117 | end 1118 | 1119 | local ok, err = memc:flush_all() 1120 | if not ok then 1121 | ngx.say("failed to flush all: ", err) 1122 | return 1123 | end 1124 | 1125 | local value, err = memc:decr("dog", 2) 1126 | if not value then 1127 | ngx.say("failed to decr dog: ", err) 1128 | return 1129 | end 1130 | 1131 | ngx.say("dog is now: ", value) 1132 | 1133 | local res, flags, err = memc:get("dog") 1134 | if err then 1135 | ngx.say("failed to get dog: ", err) 1136 | return 1137 | end 1138 | 1139 | if not res then 1140 | ngx.say("dog not found") 1141 | return 1142 | end 1143 | 1144 | ngx.say("dog: ", res, " (flags: ", flags, ")") 1145 | memc:close() 1146 | '; 1147 | } 1148 | --- request 1149 | GET /t 1150 | --- response_body 1151 | failed to decr dog: NOT_FOUND 1152 | --- no_error_log 1153 | [error] 1154 | 1155 | 1156 | 1157 | === TEST 21: general stats 1158 | --- http_config eval: $::HttpConfig 1159 | --- config 1160 | location /t { 1161 | content_by_lua ' 1162 | local memcached = require "resty.memcached" 1163 | local memc = memcached:new() 1164 | 1165 | memc:set_timeout(1000) -- 1 sec 1166 | 1167 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1168 | if not ok then 1169 | ngx.say("failed to connect: ", err) 1170 | return 1171 | end 1172 | 1173 | local ok, err = memc:set("dog", 32) 1174 | if not ok then 1175 | ngx.say("failed to set dog: ", err) 1176 | return 1177 | end 1178 | 1179 | local lines, err = memc:stats() 1180 | if not lines then 1181 | ngx.say("failed to stats: ", err) 1182 | return 1183 | end 1184 | 1185 | ngx.say("stats:\\n", table.concat(lines, "\\n")) 1186 | 1187 | memc:close() 1188 | '; 1189 | } 1190 | --- request 1191 | GET /t 1192 | --- response_body_like chop 1193 | ^stats: 1194 | STAT pid \d+ 1195 | (?:STAT [^\n]+\n)*$ 1196 | --- no_error_log 1197 | [error] 1198 | 1199 | 1200 | 1201 | === TEST 22: stats items 1202 | --- http_config eval: $::HttpConfig 1203 | --- config 1204 | location /t { 1205 | content_by_lua ' 1206 | local memcached = require "resty.memcached" 1207 | local memc = memcached:new() 1208 | 1209 | memc:set_timeout(1000) -- 1 sec 1210 | 1211 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1212 | if not ok then 1213 | ngx.say("failed to connect: ", err) 1214 | return 1215 | end 1216 | 1217 | local ok, err = memc:set("dog", 32) 1218 | if not ok then 1219 | ngx.say("failed to set dog: ", err) 1220 | return 1221 | end 1222 | 1223 | local lines, err = memc:stats("items") 1224 | if not lines then 1225 | ngx.say("failed to stats items: ", err) 1226 | return 1227 | end 1228 | 1229 | ngx.say("stats:\\n", table.concat(lines, "\\n")) 1230 | 1231 | memc:close() 1232 | '; 1233 | } 1234 | --- request 1235 | GET /t 1236 | --- response_body_like chop 1237 | ^stats: 1238 | (?:STAT items:[^\n]+\n)*$ 1239 | --- no_error_log 1240 | [error] 1241 | 1242 | 1243 | 1244 | === TEST 23: stats sizes 1245 | --- http_config eval: $::HttpConfig 1246 | --- config 1247 | location /t { 1248 | content_by_lua ' 1249 | local memcached = require "resty.memcached" 1250 | local memc = memcached:new() 1251 | 1252 | memc:set_timeout(1000) -- 1 sec 1253 | 1254 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1255 | if not ok then 1256 | ngx.say("failed to connect: ", err) 1257 | return 1258 | end 1259 | 1260 | local ok, err = memc:set("dog", 32) 1261 | if not ok then 1262 | ngx.say("failed to set dog: ", err) 1263 | return 1264 | end 1265 | 1266 | local lines, err = memc:stats("sizes") 1267 | if not lines then 1268 | ngx.say("failed to stats sizes: ", err) 1269 | return 1270 | end 1271 | 1272 | ngx.say("stats:\\n", table.concat(lines, "\\n")) 1273 | 1274 | memc:close() 1275 | '; 1276 | } 1277 | --- request 1278 | GET /t 1279 | --- response_body_like chop 1280 | ^stats: 1281 | (?:STAT \d+ \d+\n)*$ 1282 | --- no_error_log 1283 | [error] 1284 | 1285 | 1286 | 1287 | === TEST 24: version 1288 | --- http_config eval: $::HttpConfig 1289 | --- config 1290 | location /t { 1291 | content_by_lua ' 1292 | local memcached = require "resty.memcached" 1293 | local memc = memcached:new() 1294 | 1295 | memc:set_timeout(1000) -- 1 sec 1296 | 1297 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1298 | if not ok then 1299 | ngx.say("failed to connect: ", err) 1300 | return 1301 | end 1302 | 1303 | local ver, err = memc:version() 1304 | if not ver then 1305 | ngx.say("failed to get version: ", err) 1306 | return 1307 | end 1308 | 1309 | ngx.say("version: ", ver) 1310 | 1311 | memc:close() 1312 | '; 1313 | } 1314 | --- request 1315 | GET /t 1316 | --- response_body_like chop 1317 | ^version: \d+(?:\.\d+)+ 1318 | --- no_error_log 1319 | [error] 1320 | 1321 | 1322 | 1323 | === TEST 25: quit 1324 | --- http_config eval: $::HttpConfig 1325 | --- config 1326 | location /t { 1327 | content_by_lua ' 1328 | local memcached = require "resty.memcached" 1329 | local memc = memcached:new() 1330 | 1331 | memc:set_timeout(1000) -- 1 sec 1332 | 1333 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1334 | if not ok then 1335 | ngx.say("failed to connect: ", err) 1336 | return 1337 | end 1338 | 1339 | local ok, err = memc:quit() 1340 | if not ok then 1341 | ngx.say("failed to quit: ", err) 1342 | return 1343 | end 1344 | 1345 | local ver, err = memc:version() 1346 | if not ver then 1347 | ngx.say("failed to get version: ", err) 1348 | return 1349 | end 1350 | 1351 | local ok, err = memc:close() 1352 | if not ok then 1353 | ngx.say("failed to close: ", err) 1354 | return 1355 | end 1356 | 1357 | ngx.say("closed successfully") 1358 | '; 1359 | } 1360 | --- request 1361 | GET /t 1362 | --- response_body_like chop 1363 | ^failed to get version: (closed|timeout|broken pipe|connection reset by peer)$ 1364 | 1365 | 1366 | 1367 | === TEST 26: verbosity 1368 | --- http_config eval: $::HttpConfig 1369 | --- config 1370 | location /t { 1371 | content_by_lua ' 1372 | local memcached = require "resty.memcached" 1373 | local memc = memcached:new() 1374 | 1375 | memc:set_timeout(1000) -- 1 sec 1376 | 1377 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1378 | if not ok then 1379 | ngx.say("failed to connect: ", err) 1380 | return 1381 | end 1382 | 1383 | local ok, err = memc:verbosity(2) 1384 | if not ok then 1385 | ngx.say("failed to quit: ", err) 1386 | return 1387 | end 1388 | 1389 | ngx.say("successfully set verbosity to level 2") 1390 | 1391 | local ver, err = memc:version() 1392 | if not ver then 1393 | ngx.say("failed to get version: ", err) 1394 | return 1395 | end 1396 | 1397 | local ok, err = memc:close() 1398 | if not ok then 1399 | ngx.say("failed to close: ", err) 1400 | return 1401 | end 1402 | '; 1403 | } 1404 | --- request 1405 | GET /t 1406 | --- response_body 1407 | successfully set verbosity to level 2 1408 | --- no_error_log 1409 | [error] 1410 | 1411 | 1412 | 1413 | === TEST 27: multi get 1414 | --- http_config eval: $::HttpConfig 1415 | --- config 1416 | location /t { 1417 | content_by_lua ' 1418 | local memcached = require "resty.memcached" 1419 | local memc = memcached:new() 1420 | 1421 | memc:set_timeout(1000) -- 1 sec 1422 | 1423 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1424 | if not ok then 1425 | ngx.say("failed to connect: ", err) 1426 | return 1427 | end 1428 | 1429 | local ok, err = memc:flush_all() 1430 | if not ok then 1431 | ngx.say("failed to flush all: ", err) 1432 | return 1433 | end 1434 | 1435 | local ok, err = memc:set("dog", 32) 1436 | if not ok then 1437 | ngx.say("failed to set dog: ", err) 1438 | return 1439 | end 1440 | 1441 | local ok, err = memc:set("cat", "hello\\nworld\\n") 1442 | if not ok then 1443 | ngx.say("failed to set dog: ", err) 1444 | return 1445 | end 1446 | 1447 | for i = 1, 2 do 1448 | local results, err = memc:get({"dog", "blah", "cat"}) 1449 | if err then 1450 | ngx.say("failed to get keys: ", err) 1451 | return 1452 | end 1453 | 1454 | if not results then 1455 | ngx.say("results empty") 1456 | return 1457 | end 1458 | 1459 | ngx.say("dog: ", results.dog and table.concat(results.dog, " ") or "not found") 1460 | ngx.say("cat: ", results.cat and table.concat(results.cat, " ") or "not found") 1461 | ngx.say("blah: ", results.blah and table.concat(results.blah, " ") or "not found") 1462 | end 1463 | 1464 | local ok, err = memc:close() 1465 | if not ok then 1466 | ngx.say("failed to close: ", err) 1467 | return 1468 | end 1469 | '; 1470 | } 1471 | --- request 1472 | GET /t 1473 | --- response_body 1474 | dog: 32 0 1475 | cat: hello 1476 | world 1477 | 0 1478 | blah: not found 1479 | dog: 32 0 1480 | cat: hello 1481 | world 1482 | 0 1483 | blah: not found 1484 | --- no_error_log 1485 | [error] 1486 | 1487 | 1488 | 1489 | === TEST 28: multi get (special chars in keys) 1490 | --- http_config eval: $::HttpConfig 1491 | --- config 1492 | location /t { 1493 | content_by_lua ' 1494 | local memcached = require "resty.memcached" 1495 | local memc = memcached:new() 1496 | 1497 | memc:set_timeout(1000) -- 1 sec 1498 | 1499 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1500 | if not ok then 1501 | ngx.say("failed to connect: ", err) 1502 | return 1503 | end 1504 | 1505 | local ok, err = memc:flush_all() 1506 | if not ok then 1507 | ngx.say("failed to flush all: ", err) 1508 | return 1509 | end 1510 | 1511 | local ok, err = memc:set("dog A", 32) 1512 | if not ok then 1513 | ngx.say("failed to set dog: ", err) 1514 | return 1515 | end 1516 | 1517 | local ok, err = memc:set("cat B", "hello\\nworld\\n") 1518 | if not ok then 1519 | ngx.say("failed to set dog: ", err) 1520 | return 1521 | end 1522 | 1523 | local results, err = memc:get({"dog A", "blah", "cat B"}) 1524 | if err then 1525 | ngx.say("failed to get dog: ", err) 1526 | return 1527 | end 1528 | 1529 | if not results then 1530 | ngx.say("results empty") 1531 | return 1532 | end 1533 | 1534 | ngx.say("dog A: ", results["dog A"] and table.concat(results["dog A"], " ") or "not found") 1535 | ngx.say("cat B: ", results["cat B"] and table.concat(results["cat B"], " ") or "not found") 1536 | ngx.say("blah: ", results.blah and table.concat(results.blah, " ") or "not found") 1537 | 1538 | local ok, err = memc:close() 1539 | if not ok then 1540 | ngx.say("failed to close: ", err) 1541 | return 1542 | end 1543 | '; 1544 | } 1545 | --- request 1546 | GET /t 1547 | --- response_body 1548 | dog A: 32 0 1549 | cat B: hello 1550 | world 1551 | 0 1552 | blah: not found 1553 | --- no_error_log 1554 | [error] 1555 | 1556 | 1557 | 1558 | === TEST 29: connect timeout 1559 | --- http_config eval: $::HttpConfig 1560 | --- config 1561 | resolver $TEST_NGINX_RESOLVER; 1562 | location /t { 1563 | content_by_lua ' 1564 | local memcached = require "resty.memcached" 1565 | local memc = memcached:new() 1566 | 1567 | memc:set_timeout(100) -- 100 ms 1568 | 1569 | local ok, err = memc:connect("www.taobao.com", $TEST_NGINX_MEMCACHED_PORT) 1570 | if not ok then 1571 | ngx.say("failed to connect: ", err) 1572 | return 1573 | end 1574 | 1575 | local ok, err = memc:flush_all() 1576 | if not ok then 1577 | ngx.say("failed to flush all: ", err) 1578 | return 1579 | end 1580 | 1581 | local ok, err = memc:set("dog", 32) 1582 | if not ok then 1583 | ngx.say("failed to set dog: ", err) 1584 | return 1585 | end 1586 | 1587 | local res, flags, err = memc:get("dog") 1588 | if err then 1589 | ngx.say("failed to get dog: ", err) 1590 | return 1591 | end 1592 | 1593 | if not res then 1594 | ngx.say("dog not found") 1595 | return 1596 | end 1597 | 1598 | ngx.say("dog: ", res, " (flags: ", flags, ")") 1599 | memc:close() 1600 | '; 1601 | } 1602 | --- request 1603 | GET /t 1604 | --- response_body_like eval 1605 | qr/failed to connect: timeout|failed to connect: network is unreachable/ms 1606 | --- error_log eval 1607 | qr/lua tcp socket connect timed out|Network is unreachable/ms 1608 | 1609 | 1610 | 1611 | === TEST 30: set keepalive and get reused times 1612 | --- http_config eval: $::HttpConfig 1613 | --- config 1614 | resolver $TEST_NGINX_RESOLVER; 1615 | location /t { 1616 | content_by_lua ' 1617 | local memcached = require "resty.memcached" 1618 | local memc = memcached:new() 1619 | 1620 | memc:set_timeout(1000) -- 1 sec 1621 | 1622 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1623 | if not ok then 1624 | ngx.say("failed to connect: ", err) 1625 | return 1626 | end 1627 | 1628 | local times = memc:get_reused_times() 1629 | ngx.say("reused times: ", times) 1630 | 1631 | local ok, err = memc:set_keepalive() 1632 | if not ok then 1633 | ngx.say("failed to set keepalive: ", err) 1634 | return 1635 | end 1636 | 1637 | ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1638 | if not ok then 1639 | ngx.say("failed to connect: ", err) 1640 | return 1641 | end 1642 | 1643 | times = memc:get_reused_times() 1644 | ngx.say("reused times: ", times) 1645 | '; 1646 | } 1647 | --- request 1648 | GET /t 1649 | --- response_body 1650 | reused times: 0 1651 | reused times: 1 1652 | --- no_error_log 1653 | [error] 1654 | 1655 | 1656 | 1657 | === TEST 31: gets (single key, found) 1658 | --- http_config eval: $::HttpConfig 1659 | --- config 1660 | resolver $TEST_NGINX_RESOLVER; 1661 | location /t { 1662 | content_by_lua ' 1663 | local memcached = require "resty.memcached" 1664 | local memc = memcached:new() 1665 | 1666 | memc:set_timeout(1000) -- 1 sec 1667 | 1668 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1669 | if not ok then 1670 | ngx.say("failed to connect: ", err) 1671 | return 1672 | end 1673 | 1674 | local ok, err = memc:set("dog", 32) 1675 | if not ok then 1676 | ngx.say("failed to set dog: ", err) 1677 | return 1678 | end 1679 | 1680 | local res, flags, cas_uniq, err = memc:gets("dog") 1681 | if err then 1682 | ngx.say("failed to get dog: ", err) 1683 | return 1684 | end 1685 | 1686 | if not res then 1687 | ngx.say("dog not found") 1688 | return 1689 | end 1690 | 1691 | ngx.say("dog: ", res, " (flags: ", flags, ", cas_uniq: ", cas_uniq, ")") 1692 | '; 1693 | } 1694 | --- request 1695 | GET /t 1696 | --- response_body_like chop 1697 | ^dog: 32 \(flags: 0, cas_uniq: \d+\)$ 1698 | --- no_error_log 1699 | [error] 1700 | 1701 | 1702 | 1703 | === TEST 32: gets (single key, not found) 1704 | --- http_config eval: $::HttpConfig 1705 | --- config 1706 | resolver $TEST_NGINX_RESOLVER; 1707 | location /t { 1708 | content_by_lua ' 1709 | local memcached = require "resty.memcached" 1710 | local memc = memcached:new() 1711 | 1712 | memc:set_timeout(1000) -- 1 sec 1713 | 1714 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1715 | if not ok then 1716 | ngx.say("failed to connect: ", err) 1717 | return 1718 | end 1719 | 1720 | local ok, err = memc:flush_all() 1721 | if not ok then 1722 | ngx.say("failed to flush all: ", err) 1723 | return 1724 | end 1725 | 1726 | local res, flags, cas_uniq, err = memc:gets("dog") 1727 | if err then 1728 | ngx.say("failed to get dog: ", err) 1729 | return 1730 | end 1731 | 1732 | if not res then 1733 | ngx.say("dog not found") 1734 | return 1735 | end 1736 | 1737 | ngx.say("dog: ", res, " (flags: ", flags, ", cas_uniq: ", cas_uniq, ")") 1738 | '; 1739 | } 1740 | --- request 1741 | GET /t 1742 | --- response_body_like chop 1743 | dog not found 1744 | --- no_error_log 1745 | [error] 1746 | 1747 | 1748 | 1749 | === TEST 33: gets (multiple key) 1750 | --- http_config eval: $::HttpConfig 1751 | --- config 1752 | resolver $TEST_NGINX_RESOLVER; 1753 | location /t { 1754 | content_by_lua ' 1755 | local memcached = require "resty.memcached" 1756 | local memc = memcached:new() 1757 | 1758 | memc:set_timeout(1000) -- 1 sec 1759 | 1760 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1761 | if not ok then 1762 | ngx.say("failed to connect: ", err) 1763 | return 1764 | end 1765 | 1766 | local ok, err = memc:flush_all() 1767 | if not ok then 1768 | ngx.say("failed to flush all: ", err) 1769 | return 1770 | end 1771 | 1772 | local ok, err = memc:set("dog", 32) 1773 | if not ok then 1774 | ngx.say("failed to set dog: ", err) 1775 | return 1776 | end 1777 | 1778 | local ok, err = memc:set("cat", "hello\\nworld\\n") 1779 | if not ok then 1780 | ngx.say("failed to set dog: ", err) 1781 | return 1782 | end 1783 | 1784 | local results, err = memc:gets({"dog", "blah", "cat"}) 1785 | if err then 1786 | ngx.say("failed to get keys: ", err) 1787 | return 1788 | end 1789 | 1790 | if not results then 1791 | ngx.say("results empty") 1792 | return 1793 | end 1794 | 1795 | if results.dog then 1796 | ngx.say("dog: ", table.concat(results.dog, " ")) 1797 | else 1798 | ngx.say("dog not found") 1799 | end 1800 | 1801 | if results.blah then 1802 | ngx.say("blah: ", table.concat(results.blah, " ")) 1803 | else 1804 | ngx.say("blah not found") 1805 | end 1806 | 1807 | if results.cat then 1808 | ngx.say("cat: ", table.concat(results.cat, " ")) 1809 | else 1810 | ngx.say("cat not found") 1811 | end 1812 | 1813 | local ok, err = memc:close() 1814 | if not ok then 1815 | ngx.say("failed to close: ", err) 1816 | return 1817 | end 1818 | '; 1819 | } 1820 | --- request 1821 | GET /t 1822 | --- response_body_like chop 1823 | ^dog: 32 0 \d+ 1824 | blah not found 1825 | cat: hello 1826 | world 1827 | 0 \d+$ 1828 | --- no_error_log 1829 | [error] 1830 | 1831 | 1832 | 1833 | === TEST 34: gets (single key) + cas 1834 | --- http_config eval: $::HttpConfig 1835 | --- config 1836 | resolver $TEST_NGINX_RESOLVER; 1837 | location /t { 1838 | content_by_lua ' 1839 | local memcached = require "resty.memcached" 1840 | local memc = memcached:new() 1841 | 1842 | memc:set_timeout(1000) -- 1 sec 1843 | 1844 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1845 | if not ok then 1846 | ngx.say("failed to connect: ", err) 1847 | return 1848 | end 1849 | 1850 | local ok, err = memc:flush_all() 1851 | if not ok then 1852 | ngx.say("failed to flush all: ", err) 1853 | return 1854 | end 1855 | 1856 | local ok, err = memc:set("dog", 32) 1857 | if not ok then 1858 | ngx.say("failed to set dog: ", err) 1859 | return 1860 | end 1861 | 1862 | local value, flags, cas_uniq, err = memc:gets("dog") 1863 | if err then 1864 | ngx.say("failed to get dog: ", err) 1865 | return 1866 | end 1867 | 1868 | ngx.say("dog: ", value, " (flags: ", flags, ", cas_uniq: ", cas_uniq, ")") 1869 | 1870 | local ok, err = memc:cas("dog", "hello world", cas_uniq, 0, 78) 1871 | if not ok then 1872 | ngx.say("failed to cas: ", err) 1873 | return 1874 | end 1875 | 1876 | ngx.say("cas succeeded") 1877 | 1878 | local value, flags, err = memc:get("dog") 1879 | if err then 1880 | ngx.say("failed to get dog: ", err) 1881 | return 1882 | end 1883 | 1884 | ngx.say("dog: ", value, " (flags: ", flags, ")") 1885 | '; 1886 | } 1887 | --- request 1888 | GET /t 1889 | --- response_body_like chop 1890 | ^dog: 32 \(flags: 0, cas_uniq: \d+\) 1891 | cas succeeded 1892 | dog: hello world \(flags: 78\)$ 1893 | --- no_error_log 1894 | [error] 1895 | 1896 | 1897 | 1898 | === TEST 35: gets (multi key) + cas 1899 | --- http_config eval: $::HttpConfig 1900 | --- config 1901 | resolver $TEST_NGINX_RESOLVER; 1902 | location /t { 1903 | content_by_lua ' 1904 | local memcached = require "resty.memcached" 1905 | local memc = memcached:new() 1906 | 1907 | memc:set_timeout(1000) -- 1 sec 1908 | 1909 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1910 | if not ok then 1911 | ngx.say("failed to connect: ", err) 1912 | return 1913 | end 1914 | 1915 | local ok, err = memc:flush_all() 1916 | if not ok then 1917 | ngx.say("failed to flush all: ", err) 1918 | return 1919 | end 1920 | 1921 | local ok, err = memc:set("dog", 32) 1922 | if not ok then 1923 | ngx.say("failed to set dog: ", err) 1924 | return 1925 | end 1926 | 1927 | local results, err = memc:gets({"dog"}) 1928 | if err then 1929 | ngx.say("failed to get dog: ", err) 1930 | return 1931 | end 1932 | 1933 | local value, flags, cas_uniq, err = unpack(results.dog) 1934 | 1935 | ngx.say("dog: ", value, " (flags: ", flags, ", cas_uniq: ", cas_uniq, ")") 1936 | 1937 | local ok, err = memc:cas("dog", "hello world", cas_uniq, 0, 78) 1938 | if not ok then 1939 | ngx.say("failed to cas: ", err) 1940 | return 1941 | end 1942 | 1943 | ngx.say("cas succeeded") 1944 | 1945 | local value, flags, err = memc:get("dog") 1946 | if err then 1947 | ngx.say("failed to get dog: ", err) 1948 | return 1949 | end 1950 | 1951 | ngx.say("dog: ", value, " (flags: ", flags, ")") 1952 | '; 1953 | } 1954 | --- request 1955 | GET /t 1956 | --- response_body_like chop 1957 | ^dog: 32 \(flags: 0, cas_uniq: \d+\) 1958 | cas succeeded 1959 | dog: hello world \(flags: 78\)$ 1960 | --- no_error_log 1961 | [error] 1962 | 1963 | 1964 | 1965 | === TEST 36: gets (single key) + cas 1966 | --- http_config eval: $::HttpConfig 1967 | --- config 1968 | resolver $TEST_NGINX_RESOLVER; 1969 | location /t { 1970 | content_by_lua ' 1971 | local memcached = require "resty.memcached" 1972 | local memc = memcached:new() 1973 | 1974 | memc:set_timeout(1000) -- 1 sec 1975 | 1976 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 1977 | if not ok then 1978 | ngx.say("failed to connect: ", err) 1979 | return 1980 | end 1981 | 1982 | local ok, err = memc:flush_all() 1983 | if not ok then 1984 | ngx.say("failed to flush all: ", err) 1985 | return 1986 | end 1987 | 1988 | local ok, err = memc:set("dog", 32) 1989 | if not ok then 1990 | ngx.say("failed to set dog: ", err) 1991 | return 1992 | end 1993 | 1994 | local value, flags, cas_uniq, err = memc:gets("dog") 1995 | if err then 1996 | ngx.say("failed to get dog: ", err) 1997 | return 1998 | end 1999 | 2000 | ok, err = memc:set("dog", 117) 2001 | if not ok then 2002 | ngx.say("failed to set dog: ", err) 2003 | return 2004 | end 2005 | 2006 | ngx.say("dog: ", value, " (flags: ", flags, ", cas_uniq: ", cas_uniq, ")") 2007 | 2008 | local ok, err = memc:cas("dog", "hello world", cas_uniq, 0, 78) 2009 | if not ok then 2010 | ngx.say("failed to cas: ", err) 2011 | return 2012 | end 2013 | 2014 | ngx.say("cas succeeded") 2015 | 2016 | local value, flags, err = memc:get("dog") 2017 | if err then 2018 | ngx.say("failed to get dog: ", err) 2019 | return 2020 | end 2021 | 2022 | ngx.say("dog: ", value, " (flags: ", flags, ")") 2023 | '; 2024 | } 2025 | --- request 2026 | GET /t 2027 | --- response_body_like chop 2028 | ^dog: 32 \(flags: 0, cas_uniq: \d+\) 2029 | failed to cas: EXISTS$ 2030 | --- no_error_log 2031 | [error] 2032 | 2033 | 2034 | 2035 | === TEST 37: change escape method 2036 | --- http_config eval: $::HttpConfig 2037 | --- config 2038 | location /t { 2039 | content_by_lua ' 2040 | 2041 | function identity(str) 2042 | return str 2043 | end 2044 | 2045 | local memcached = require "resty.memcached" 2046 | local memc = memcached:new{ key_transform = { identity, identity }} 2047 | local key = "dog&cat" 2048 | 2049 | memc:set_timeout(1000) -- 1 sec 2050 | 2051 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 2052 | if not ok then 2053 | ngx.say("failed to connect: ", err) 2054 | return 2055 | end 2056 | 2057 | local ok, err = memc:flush_all() 2058 | if not ok then 2059 | ngx.say("failed to flush all: ", err) 2060 | return 2061 | end 2062 | 2063 | local ok, err = memc:set(key, 32) 2064 | if not ok then 2065 | ngx.say("failed to set dog: ", err) 2066 | return 2067 | end 2068 | 2069 | for i = 1, 2 do 2070 | local res, flags, err = memc:get(key) 2071 | if err then 2072 | ngx.say("failed to get", key, ": ", err) 2073 | return 2074 | end 2075 | 2076 | if not res then 2077 | ngx.say(key, " not found") 2078 | return 2079 | end 2080 | 2081 | ngx.say(key, ": ", res, " (flags: ", flags, ")") 2082 | end 2083 | 2084 | memc:close() 2085 | '; 2086 | } 2087 | --- request 2088 | GET /t 2089 | --- response_body 2090 | dog&cat: 32 (flags: 0) 2091 | dog&cat: 32 (flags: 0) 2092 | --- no_error_log 2093 | [error] 2094 | 2095 | 2096 | 2097 | === TEST 38: gets (multiple key) + change only unescape key 2098 | --- http_config eval: $::HttpConfig 2099 | --- config 2100 | resolver $TEST_NGINX_RESOLVER; 2101 | location /t { 2102 | content_by_lua ' 2103 | function identity(str) 2104 | return str 2105 | end 2106 | 2107 | local memcached = require "resty.memcached" 2108 | local memc = memcached:new{key_transform = {ngx.escape_uri, identity}} 2109 | 2110 | local key = "dog&cat" 2111 | 2112 | memc:set_timeout(1000) -- 1 sec 2113 | 2114 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 2115 | if not ok then 2116 | ngx.say("failed to connect: ", err) 2117 | return 2118 | end 2119 | 2120 | local ok, err = memc:flush_all() 2121 | if not ok then 2122 | ngx.say("failed to flush all: ", err) 2123 | return 2124 | end 2125 | 2126 | local ok, err = memc:set(key, 32) 2127 | if not ok then 2128 | ngx.say("failed to set ", key, ": ", err) 2129 | return 2130 | end 2131 | 2132 | local ok, err = memc:set("cat", "hello\\nworld\\n") 2133 | if not ok then 2134 | ngx.say("failed to set dog: ", err) 2135 | return 2136 | end 2137 | 2138 | local results, err = memc:gets({key, "blah", "cat"}) 2139 | if err then 2140 | ngx.say("failed to get keys: ", err) 2141 | return 2142 | end 2143 | 2144 | if not results then 2145 | ngx.say("results empty") 2146 | return 2147 | end 2148 | 2149 | if results[key] then 2150 | ngx.say(key, ": ", table.concat(results[key], " ")) 2151 | else 2152 | ngx.say(key, " not found") 2153 | end 2154 | 2155 | -- encode key for second run 2156 | key = ngx.escape_uri(key) 2157 | 2158 | if results[key] then 2159 | ngx.say(key, ": ", table.concat(results[key], " ")) 2160 | else 2161 | ngx.say(key, " not found") 2162 | end 2163 | 2164 | 2165 | if results.blah then 2166 | ngx.say("blah: ", table.concat(results.blah, " ")) 2167 | else 2168 | ngx.say("blah not found") 2169 | end 2170 | 2171 | if results.cat then 2172 | ngx.say("cat: ", table.concat(results.cat, " ")) 2173 | else 2174 | ngx.say("cat not found") 2175 | end 2176 | 2177 | local ok, err = memc:close() 2178 | if not ok then 2179 | ngx.say("failed to close: ", err) 2180 | return 2181 | end 2182 | '; 2183 | } 2184 | --- request 2185 | GET /t 2186 | --- response_body_like chop 2187 | ^dog&cat not found 2188 | dog%26cat: 32 0 \d+ 2189 | blah not found 2190 | cat: hello 2191 | world 2192 | 0 \d+$ 2193 | --- no_error_log 2194 | [error] 2195 | 2196 | 2197 | 2198 | === TEST 39: pipeline execute multi commands 2199 | --- http_config eval: $::HttpConfig 2200 | --- config 2201 | location /t { 2202 | content_by_lua_block { 2203 | local memcached = require "resty.memcached" 2204 | local memc = memcached:new() 2205 | 2206 | memc:set_timeout(1000) -- 1 sec 2207 | 2208 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT); 2209 | if not ok then 2210 | ngx.say("failed to connect: ", err) 2211 | return 2212 | end 2213 | local vals, err 2214 | err = memc:init_pipeline() 2215 | if err then 2216 | ngx.say(err) 2217 | return 2218 | end 2219 | 2220 | memc:set("age", 10, 30, 4) 2221 | memc:set("name", "value") 2222 | memc:get("age") 2223 | memc:get("name") 2224 | memc:incr("age", 3) 2225 | memc:add("name", "-haha") 2226 | memc:delete("name") 2227 | 2228 | vals, err = memc:commit_pipeline() 2229 | if err then 2230 | ngx.say(err) 2231 | return 2232 | end 2233 | 2234 | for i,v in ipairs(vals) do 2235 | ngx.say((vals[i][1] or "")..(vals[i][2] or "")..(vals[i][3] or "")) 2236 | end 2237 | vals, err = memc:delete("name") 2238 | if err then 2239 | ngx.say(err) 2240 | end 2241 | memc:close() 2242 | } 2243 | } 2244 | --- request 2245 | GET /t 2246 | --- response_body 2247 | 1 2248 | 1 2249 | 104 2250 | value0 2251 | 13 2252 | NOT_STORED 2253 | 1 2254 | NOT_FOUND 2255 | --- no_error_log 2256 | [error] 2257 | 2258 | 2259 | 2260 | === TEST 40: pipeline commands buffer empty 2261 | --- http_config eval: $::HttpConfig 2262 | --- config 2263 | location /t { 2264 | content_by_lua_block { 2265 | local memcached = require "resty.memcached" 2266 | local memc = memcached:new() 2267 | 2268 | memc:set_timeout(1000) -- 1 sec 2269 | 2270 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT); 2271 | if not ok then 2272 | ngx.say("failed to connect: ", err) 2273 | return 2274 | end 2275 | local vals, err 2276 | err = memc:init_pipeline() 2277 | if err then 2278 | ngx.say("init: ", err) 2279 | return 2280 | end 2281 | 2282 | vals, err = memc:commit_pipeline() 2283 | if err then 2284 | ngx.say("commit: ", err) 2285 | return 2286 | end 2287 | memc:close() 2288 | } 2289 | } 2290 | --- request 2291 | GET /t 2292 | --- response_body 2293 | commit: no more cmds 2294 | --- no_error_log 2295 | [error] 2296 | 2297 | 2298 | 2299 | === TEST 41: pipeline repeat commit 2300 | --- http_config eval: $::HttpConfig 2301 | --- config 2302 | location /t { 2303 | content_by_lua_block { 2304 | local memcached = require "resty.memcached" 2305 | local memc = memcached:new() 2306 | 2307 | memc:set_timeout(1000) -- 1 sec 2308 | 2309 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT); 2310 | if not ok then 2311 | ngx.say("failed to connect: ", err) 2312 | return 2313 | end 2314 | local vals, err 2315 | err = memc:init_pipeline() 2316 | if err then 2317 | ngx.say("init: ", err) 2318 | return 2319 | end 2320 | memc:set("touch_key", "touch_value") 2321 | memc:touch("touch_key", 60) 2322 | memc:stats("items") 2323 | vals, err = memc:commit_pipeline() 2324 | if err then 2325 | ngx.say("commit: ", err) 2326 | return 2327 | end 2328 | if vals[1][2] then 2329 | ngx.say('err: ', vals[1][2]) 2330 | else 2331 | ngx.say('return: ', vals[1][1]) 2332 | end 2333 | 2334 | if vals[2][2] then 2335 | ngx.say('err: ', vals[2][2]) 2336 | else 2337 | ngx.say('return: ', vals[2][1]) 2338 | end 2339 | 2340 | if vals[3][2] then 2341 | ngx.say('err: ', vals[3][2]) 2342 | else 2343 | ngx.say('return: ', #vals[3][1]) 2344 | end 2345 | vals, err = memc:commit_pipeline() 2346 | if err then 2347 | ngx.say("commit: ", err) 2348 | end 2349 | 2350 | memc:close() 2351 | } 2352 | } 2353 | --- request 2354 | GET /t 2355 | --- response_body_like chop 2356 | return: 1 2357 | return: 1 2358 | return: \d+ 2359 | commit: no pipeline 2360 | --- no_error_log 2361 | [error] 2362 | 2363 | 2364 | 2365 | === TEST 42: pipeline repeat init 2366 | --- http_config eval: $::HttpConfig 2367 | --- config 2368 | location /t { 2369 | content_by_lua_block { 2370 | local memcached = require "resty.memcached" 2371 | local memc = memcached:new() 2372 | 2373 | memc:set_timeout(1000) -- 1 sec 2374 | 2375 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT); 2376 | if not ok then 2377 | ngx.say("failed to connect: ", err) 2378 | return 2379 | end 2380 | local vals, err 2381 | err = memc:init_pipeline() 2382 | if err then 2383 | ngx.say("init: ", err) 2384 | return 2385 | end 2386 | 2387 | memc:set("init_key", "init_value") 2388 | memc:touch("init_key", 60) 2389 | 2390 | err = memc:init_pipeline() 2391 | if err then 2392 | ngx.say("init: ", err) 2393 | return 2394 | end 2395 | 2396 | memc:close() 2397 | } 2398 | } 2399 | --- request 2400 | GET /t 2401 | --- response_body 2402 | init: already init pipeline 2403 | --- no_error_log 2404 | [error] 2405 | 2406 | 2407 | 2408 | === TEST 43: pipeline cancel 2409 | --- http_config eval: $::HttpConfig 2410 | --- config 2411 | location /t { 2412 | content_by_lua_block { 2413 | local memcached = require "resty.memcached" 2414 | local memc = memcached:new() 2415 | 2416 | memc:set_timeout(1000) -- 1 sec 2417 | 2418 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT); 2419 | if not ok then 2420 | ngx.say("failed to connect: ", err) 2421 | return 2422 | end 2423 | local vals, err 2424 | err = memc:init_pipeline() 2425 | if err then 2426 | ngx.say("init: ", err) 2427 | return 2428 | end 2429 | 2430 | memc:set("cancel_key", "cancel_value") 2431 | memc:touch("cancel_key", 60) 2432 | 2433 | memc:cancel_pipeline() 2434 | _, err = memc:commit_pipeline() 2435 | if err then 2436 | ngx.say("init: ", err) 2437 | return 2438 | end 2439 | 2440 | memc:close() 2441 | } 2442 | } 2443 | --- request 2444 | GET /t 2445 | --- response_body 2446 | init: no pipeline 2447 | --- no_error_log 2448 | [error] 2449 | 2450 | 2451 | 2452 | === TEST 44: pipeline init buffer size error 2453 | --- http_config eval: $::HttpConfig 2454 | --- config 2455 | location /t { 2456 | content_by_lua_block { 2457 | local memcached = require "resty.memcached" 2458 | local memc = memcached:new() 2459 | 2460 | memc:set_timeout(1000) -- 1 sec 2461 | 2462 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT); 2463 | if not ok then 2464 | ngx.say("failed to connect: ", err) 2465 | return 2466 | end 2467 | local vals, err 2468 | err = memc:init_pipeline('x') 2469 | if err then 2470 | ngx.say("init: ", err) 2471 | return 2472 | end 2473 | 2474 | memc:close() 2475 | } 2476 | } 2477 | --- request 2478 | GET /t 2479 | --- response_body 2480 | init: bad n arg: number expected, but got string 2481 | --- no_error_log 2482 | [error] 2483 | -------------------------------------------------------------------------------- /t/tableset.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket::Lua; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(2); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; 18 | 19 | no_long_string(); 20 | 21 | run_tests(); 22 | 23 | __DATA__ 24 | 25 | === TEST 1: set with table 26 | --- http_config eval: $::HttpConfig 27 | --- config 28 | location /t { 29 | content_by_lua ' 30 | local memcached = require "resty.memcached" 31 | local memc = memcached:new() 32 | 33 | memc:set_timeout(1000) -- 1 sec 34 | 35 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 36 | if not ok then 37 | ngx.say("failed to connect: ", err) 38 | return 39 | end 40 | 41 | local ok, err = memc:flush_all() 42 | if not ok then 43 | ngx.say("failed to flush all: ", err) 44 | return 45 | end 46 | 47 | local ok, err = memc:set("dog", { "c", "a", "t" }) 48 | if not ok then 49 | ngx.say("failed to set dog: ", err) 50 | return 51 | end 52 | 53 | for i = 1, 2 do 54 | local res, flags, err = memc:get("dog") 55 | if err then 56 | ngx.say("failed to get dog: ", err) 57 | return 58 | end 59 | 60 | if not res then 61 | ngx.say("dog not found") 62 | return 63 | end 64 | 65 | ngx.say("dog: ", res, " (flags: ", flags, ")") 66 | end 67 | 68 | memc:close() 69 | '; 70 | } 71 | --- request 72 | GET /t 73 | --- response_body 74 | dog: cat (flags: 0) 75 | dog: cat (flags: 0) 76 | --- no_error_log 77 | [error] 78 | 79 | 80 | 81 | === TEST 2: set with nested table 82 | --- http_config eval: $::HttpConfig 83 | --- config 84 | location /t { 85 | content_by_lua ' 86 | local memcached = require "resty.memcached" 87 | local memc = memcached:new() 88 | 89 | memc:set_timeout(1000) -- 1 sec 90 | 91 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 92 | if not ok then 93 | ngx.say("failed to connect: ", err) 94 | return 95 | end 96 | 97 | local ok, err = memc:flush_all() 98 | if not ok then 99 | ngx.say("failed to flush all: ", err) 100 | return 101 | end 102 | 103 | local ok, err = memc:set("dog", { "c", "a", { "t"} }) 104 | if not ok then 105 | ngx.say("failed to set dog: ", err) 106 | return 107 | end 108 | 109 | for i = 1, 2 do 110 | local res, flags, err = memc:get("dog") 111 | if err then 112 | ngx.say("failed to get dog: ", err) 113 | return 114 | end 115 | 116 | if not res then 117 | ngx.say("dog not found") 118 | return 119 | end 120 | 121 | ngx.say("dog: ", res, " (flags: ", flags, ")") 122 | end 123 | 124 | memc:close() 125 | '; 126 | } 127 | --- request 128 | GET /t 129 | --- response_body 130 | dog: cat (flags: 0) 131 | dog: cat (flags: 0) 132 | --- no_error_log 133 | [error] 134 | -------------------------------------------------------------------------------- /t/tls.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket::Lua; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(2); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11212; 18 | 19 | no_long_string(); 20 | 21 | run_tests(); 22 | 23 | __DATA__ 24 | 25 | === TEST 1: basic 26 | --- http_config eval: $::HttpConfig 27 | --- config 28 | location /t { 29 | content_by_lua ' 30 | local memcached = require "resty.memcached" 31 | local memc = memcached:new() 32 | 33 | assert(memc:set_timeout(1000)) -- 1 sec 34 | 35 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 36 | if not ok then 37 | ngx.say("failed to connect: ", err) 38 | return 39 | end 40 | local session, err = memc:sslhandshake(nil, "test.com", false) 41 | if err ~= nil then 42 | ngx.say("failed to connect to memcached: ", err) 43 | return 44 | end 45 | 46 | local ok, err = memc:flush_all() 47 | if not ok then 48 | ngx.say("failed to flush all: ", err) 49 | return 50 | end 51 | 52 | local ok, err = memc:set("dog", 32) 53 | if not ok then 54 | ngx.say("failed to set dog: ", err) 55 | return 56 | end 57 | 58 | for i = 1, 2 do 59 | local res, flags, err = memc:get("dog") 60 | if err then 61 | ngx.say("failed to get dog: ", err) 62 | return 63 | end 64 | 65 | if not res then 66 | ngx.say("dog not found") 67 | return 68 | end 69 | 70 | ngx.say("dog: ", res, " (flags: ", flags, ")") 71 | end 72 | 73 | memc:close() 74 | '; 75 | } 76 | --- request 77 | GET /t 78 | --- response_body 79 | dog: 32 (flags: 0) 80 | dog: 32 (flags: 0) 81 | --- no_error_log 82 | [error] 83 | 84 | 85 | 86 | === TEST 2: verify server certificate failed 87 | --- http_config eval: $::HttpConfig 88 | --- config 89 | location /t { 90 | content_by_lua ' 91 | local memcached = require "resty.memcached" 92 | local memc = memcached:new() 93 | 94 | assert(memc:set_timeout(1000)) -- 1 sec 95 | 96 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 97 | if not ok then 98 | ngx.say("failed to connect: ", err) 99 | return 100 | end 101 | local session, err = memc:sslhandshake(nil, "test.com", true) 102 | if err ~= nil then 103 | ngx.say("failed to connect to memcached: ", err) 104 | return 105 | end 106 | 107 | local ok, err = memc:flush_all() 108 | if not ok then 109 | ngx.say("failed to flush all: ", err) 110 | return 111 | end 112 | 113 | local ok, err = memc:set("dog", 32) 114 | if not ok then 115 | ngx.say("failed to set dog: ", err) 116 | return 117 | end 118 | 119 | for i = 1, 2 do 120 | local res, flags, err = memc:get("dog") 121 | if err then 122 | ngx.say("failed to get dog: ", err) 123 | return 124 | end 125 | 126 | if not res then 127 | ngx.say("dog not found") 128 | return 129 | end 130 | 131 | ngx.say("dog: ", res, " (flags: ", flags, ")") 132 | end 133 | 134 | memc:close() 135 | '; 136 | } 137 | --- request 138 | GET /t 139 | --- response_body 140 | failed to connect to memcached: 19: self signed certificate in certificate chain 141 | --- error_log 142 | lua ssl certificate verify error: (19: self signed certificate in certificate chain) 143 | -------------------------------------------------------------------------------- /t/touch.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket::Lua; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(2); 7 | 8 | plan tests => repeat_each() * (2 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; 17 | 18 | no_long_string(); 19 | no_diff(); 20 | 21 | run_tests(); 22 | 23 | __DATA__ 24 | 25 | === TEST 1: basic 26 | --- http_config eval: $::HttpConfig 27 | --- config 28 | location /t { 29 | content_by_lua ' 30 | local key = "dog&cat&rabbit" 31 | local memcached = require "resty.memcached" 32 | local memc = memcached:new() 33 | memc:set_timeout(1000) -- 1 sec 34 | 35 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 36 | if not ok then 37 | ngx.say("failed to connect: ", err) 38 | return 39 | end 40 | 41 | local ok, err = memc:flush_all() 42 | if not ok then 43 | ngx.say("failed to flush all: ", err) 44 | return 45 | end 46 | 47 | local ok, err = memc:set(key, "value", 1) 48 | if not ok then 49 | ngx.say("failed to set ", key, ": ", err) 50 | return 51 | end 52 | 53 | local ok, err = memc:touch(key, 120) -- 120sec 54 | if not ok then 55 | ngx.say("failed to touch ", key, ": ", err) 56 | return 57 | end 58 | 59 | ngx.say("touch: ", ok) 60 | 61 | ngx.sleep(1.1) 62 | 63 | local val, err = memc:get(key) 64 | if not val then 65 | ngx.say("failed to get ", key, ": ", err) 66 | return 67 | end 68 | 69 | ngx.say("get key: ", val) 70 | '; 71 | } 72 | --- request 73 | GET /t 74 | --- response_body 75 | touch: 1 76 | get key: value 77 | 78 | 79 | 80 | === TEST 2: not exists 81 | --- http_config eval: $::HttpConfig 82 | --- config 83 | location /t { 84 | content_by_lua ' 85 | local key = "dog&cat&rabbit" 86 | local memcached = require "resty.memcached" 87 | local memc = memcached:new() 88 | memc:set_timeout(1000) -- 1 sec 89 | 90 | local ok, err = memc:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) 91 | if not ok then 92 | ngx.say("failed to connect: ", err) 93 | return 94 | end 95 | 96 | local ok, err = memc:flush_all() 97 | if not ok then 98 | ngx.say("failed to flush all: ", err) 99 | return 100 | end 101 | 102 | local ok, err = memc:touch(key, 120) --120sec 103 | if not ok then 104 | ngx.say("failed to touch ", key, ": ", err) 105 | return 106 | end 107 | 108 | ngx.say("touch: ", ok) 109 | '; 110 | } 111 | --- request 112 | GET /t 113 | --- response_body 114 | failed to touch dog&cat&rabbit: NOT_FOUND 115 | -------------------------------------------------------------------------------- /t/version.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket::Lua; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(2); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | 18 | no_long_string(); 19 | #no_diff(); 20 | 21 | run_tests(); 22 | 23 | __DATA__ 24 | 25 | === TEST 1: basic 26 | --- http_config eval: $::HttpConfig 27 | --- config 28 | location /t { 29 | content_by_lua ' 30 | local memc = require "resty.memcached" 31 | ngx.say(memc._VERSION) 32 | '; 33 | } 34 | --- request 35 | GET /t 36 | --- response_body_like chop 37 | ^\d+\.\d+$ 38 | --- no_error_log 39 | [error] 40 | -------------------------------------------------------------------------------- /valgrind.suppress: -------------------------------------------------------------------------------- 1 | { 2 | 3 | Memcheck:Param 4 | write(buf) 5 | fun:__write_nocancel 6 | fun:ngx_log_error_core 7 | fun:ngx_resolver_read_response 8 | } 9 | { 10 | 11 | Memcheck:Cond 12 | fun:ngx_sprintf_num 13 | fun:ngx_vslprintf 14 | fun:ngx_log_error_core 15 | fun:ngx_resolver_read_response 16 | fun:ngx_epoll_process_events 17 | fun:ngx_process_events_and_timers 18 | fun:ngx_single_process_cycle 19 | fun:main 20 | } 21 | { 22 | 23 | Memcheck:Addr1 24 | fun:ngx_vslprintf 25 | fun:ngx_snprintf 26 | fun:ngx_sock_ntop 27 | fun:ngx_event_accept 28 | } 29 | { 30 | 31 | Memcheck:Param 32 | write(buf) 33 | fun:__write_nocancel 34 | fun:ngx_log_error_core 35 | fun:ngx_resolver_read_response 36 | fun:ngx_event_process_posted 37 | fun:ngx_process_events_and_timers 38 | fun:ngx_single_process_cycle 39 | fun:main 40 | } 41 | { 42 | 43 | Memcheck:Cond 44 | fun:ngx_sprintf_num 45 | fun:ngx_vslprintf 46 | fun:ngx_log_error_core 47 | fun:ngx_resolver_read_response 48 | fun:ngx_event_process_posted 49 | fun:ngx_process_events_and_timers 50 | fun:ngx_single_process_cycle 51 | fun:main 52 | } 53 | { 54 | 55 | Memcheck:Leak 56 | fun:malloc 57 | fun:ngx_alloc 58 | obj:* 59 | } 60 | { 61 | 62 | exp-sgcheck:SorG 63 | fun:ngx_http_lua_ndk_set_var_get 64 | } 65 | { 66 | 67 | exp-sgcheck:SorG 68 | fun:ngx_http_variables_init_vars 69 | fun:ngx_http_block 70 | } 71 | { 72 | 73 | exp-sgcheck:SorG 74 | fun:ngx_conf_parse 75 | } 76 | { 77 | 78 | exp-sgcheck:SorG 79 | fun:ngx_vslprintf 80 | fun:ngx_log_error_core 81 | } 82 | { 83 | 84 | Memcheck:Leak 85 | fun:malloc 86 | fun:ngx_alloc 87 | fun:ngx_calloc 88 | fun:ngx_event_process_init 89 | } 90 | { 91 | 92 | Memcheck:Param 93 | epoll_ctl(event) 94 | fun:epoll_ctl 95 | } 96 | { 97 | 98 | Memcheck:Leak 99 | fun:malloc 100 | fun:ngx_alloc 101 | fun:ngx_event_process_init 102 | } 103 | { 104 | 105 | Memcheck:Cond 106 | fun:ngx_conf_flush_files 107 | fun:ngx_single_process_cycle 108 | } 109 | { 110 | 111 | Memcheck:Cond 112 | fun:memcpy 113 | fun:ngx_vslprintf 114 | fun:ngx_log_error_core 115 | fun:ngx_http_charset_header_filter 116 | } 117 | { 118 | 119 | Memcheck:Param 120 | socketcall.setsockopt(optval) 121 | fun:setsockopt 122 | fun:drizzle_state_connect 123 | } 124 | { 125 | 126 | Memcheck:Leak 127 | fun:malloc 128 | fun:ngx_alloc 129 | fun:ngx_pool_cleanup_add 130 | } 131 | { 132 | 133 | Memcheck:Cond 134 | fun:ngx_conf_flush_files 135 | fun:ngx_single_process_cycle 136 | fun:main 137 | } 138 | { 139 | 140 | Memcheck:Leak 141 | fun:malloc 142 | fun:ngx_alloc 143 | fun:ngx_palloc_large 144 | fun:ngx_palloc 145 | fun:ngx_array_push 146 | fun:ngx_http_get_variable_index 147 | fun:ngx_http_memc_add_variable 148 | fun:ngx_http_memc_init 149 | fun:ngx_http_block 150 | fun:ngx_conf_parse 151 | fun:ngx_init_cycle 152 | fun:main 153 | } 154 | { 155 | 156 | Memcheck:Leak 157 | fun:malloc 158 | fun:ngx_alloc 159 | fun:ngx_event_process_init 160 | fun:ngx_single_process_cycle 161 | fun:main 162 | } 163 | { 164 | 165 | Memcheck:Leak 166 | fun:malloc 167 | fun:ngx_alloc 168 | fun:ngx_crc32_table_init 169 | fun:main 170 | } 171 | { 172 | 173 | Memcheck:Leak 174 | fun:malloc 175 | fun:ngx_alloc 176 | fun:ngx_event_process_init 177 | fun:ngx_worker_process_init 178 | fun:ngx_worker_process_cycle 179 | fun:ngx_spawn_process 180 | fun:ngx_start_worker_processes 181 | fun:ngx_master_process_cycle 182 | fun:main 183 | } 184 | { 185 | 186 | Memcheck:Leak 187 | fun:malloc 188 | fun:ngx_alloc 189 | fun:ngx_palloc_large 190 | fun:ngx_palloc 191 | fun:ngx_pcalloc 192 | fun:ngx_hash_init 193 | fun:ngx_http_variables_init_vars 194 | fun:ngx_http_block 195 | fun:ngx_conf_parse 196 | fun:ngx_init_cycle 197 | fun:main 198 | } 199 | { 200 | 201 | Memcheck:Leak 202 | fun:malloc 203 | fun:ngx_alloc 204 | fun:ngx_palloc_large 205 | fun:ngx_palloc 206 | fun:ngx_pcalloc 207 | fun:ngx_http_upstream_drizzle_create_srv_conf 208 | fun:ngx_http_upstream 209 | fun:ngx_conf_parse 210 | fun:ngx_http_block 211 | fun:ngx_conf_parse 212 | fun:ngx_init_cycle 213 | fun:main 214 | } 215 | { 216 | 217 | Memcheck:Leak 218 | fun:malloc 219 | fun:ngx_alloc 220 | fun:ngx_palloc_large 221 | fun:ngx_palloc 222 | fun:ngx_pcalloc 223 | fun:ngx_hash_keys_array_init 224 | fun:ngx_http_variables_add_core_vars 225 | fun:ngx_http_core_preconfiguration 226 | fun:ngx_http_block 227 | fun:ngx_conf_parse 228 | fun:ngx_init_cycle 229 | fun:main 230 | } 231 | { 232 | 233 | Memcheck:Leak 234 | fun:malloc 235 | fun:ngx_alloc 236 | fun:ngx_palloc_large 237 | fun:ngx_palloc 238 | fun:ngx_array_push 239 | fun:ngx_hash_add_key 240 | fun:ngx_http_add_variable 241 | fun:ngx_http_echo_add_variables 242 | fun:ngx_http_echo_handler_init 243 | fun:ngx_http_block 244 | fun:ngx_conf_parse 245 | fun:ngx_init_cycle 246 | } 247 | { 248 | 249 | Memcheck:Leak 250 | fun:malloc 251 | fun:ngx_alloc 252 | fun:ngx_palloc_large 253 | fun:ngx_palloc 254 | fun:ngx_pcalloc 255 | fun:ngx_http_upstream_drizzle_create_srv_conf 256 | fun:ngx_http_core_server 257 | fun:ngx_conf_parse 258 | fun:ngx_http_block 259 | fun:ngx_conf_parse 260 | fun:ngx_init_cycle 261 | fun:main 262 | } 263 | { 264 | 265 | Memcheck:Leak 266 | fun:malloc 267 | fun:ngx_alloc 268 | fun:ngx_palloc_large 269 | fun:ngx_palloc 270 | fun:ngx_pcalloc 271 | fun:ngx_http_upstream_drizzle_create_srv_conf 272 | fun:ngx_http_block 273 | fun:ngx_conf_parse 274 | fun:ngx_init_cycle 275 | fun:main 276 | } 277 | { 278 | 279 | Memcheck:Leak 280 | fun:malloc 281 | fun:ngx_alloc 282 | fun:ngx_palloc_large 283 | fun:ngx_palloc 284 | fun:ngx_array_push 285 | fun:ngx_hash_add_key 286 | fun:ngx_http_variables_add_core_vars 287 | fun:ngx_http_core_preconfiguration 288 | fun:ngx_http_block 289 | fun:ngx_conf_parse 290 | fun:ngx_init_cycle 291 | fun:main 292 | } 293 | { 294 | 295 | Memcheck:Leak 296 | fun:malloc 297 | fun:ngx_alloc 298 | fun:ngx_palloc_large 299 | fun:ngx_palloc 300 | fun:ngx_pcalloc 301 | fun:ngx_init_cycle 302 | fun:main 303 | } 304 | { 305 | 306 | Memcheck:Leak 307 | fun:malloc 308 | fun:ngx_alloc 309 | fun:ngx_palloc_large 310 | fun:ngx_palloc 311 | fun:ngx_hash_init 312 | fun:ngx_http_upstream_init_main_conf 313 | fun:ngx_http_block 314 | fun:ngx_conf_parse 315 | fun:ngx_init_cycle 316 | fun:main 317 | } 318 | { 319 | 320 | Memcheck:Leak 321 | fun:malloc 322 | fun:ngx_alloc 323 | fun:ngx_palloc_large 324 | fun:ngx_palloc 325 | fun:ngx_pcalloc 326 | fun:ngx_http_drizzle_keepalive_init 327 | fun:ngx_http_upstream_drizzle_init 328 | fun:ngx_http_upstream_init_main_conf 329 | fun:ngx_http_block 330 | fun:ngx_conf_parse 331 | fun:ngx_init_cycle 332 | fun:main 333 | } 334 | { 335 | 336 | Memcheck:Leak 337 | fun:malloc 338 | fun:ngx_alloc 339 | fun:ngx_palloc_large 340 | fun:ngx_palloc 341 | fun:ngx_hash_init 342 | fun:ngx_http_variables_init_vars 343 | fun:ngx_http_block 344 | fun:ngx_conf_parse 345 | fun:ngx_init_cycle 346 | fun:main 347 | } 348 | { 349 | 350 | Memcheck:Cond 351 | fun:index 352 | fun:expand_dynamic_string_token 353 | fun:_dl_map_object 354 | fun:map_doit 355 | fun:_dl_catch_error 356 | fun:do_preload 357 | fun:dl_main 358 | fun:_dl_sysdep_start 359 | fun:_dl_start 360 | } 361 | { 362 | 363 | Memcheck:Leak 364 | match-leak-kinds: definite 365 | fun:malloc 366 | fun:ngx_alloc 367 | fun:ngx_set_environment 368 | fun:ngx_single_process_cycle 369 | } 370 | { 371 | 372 | Memcheck:Leak 373 | match-leak-kinds: definite 374 | fun:malloc 375 | fun:ngx_alloc 376 | fun:ngx_set_environment 377 | fun:ngx_worker_process_init 378 | fun:ngx_worker_process_cycle 379 | } 380 | --------------------------------------------------------------------------------