├── .gitignore ├── Makefile ├── Readme.md ├── lib └── resty │ └── mongol │ ├── bson.lua │ ├── colmt.lua │ ├── cursor.lua │ ├── dbmt.lua │ ├── get.lua │ ├── globalplus.lua │ ├── gridfs.lua │ ├── gridfs_file.lua │ ├── init.lua │ ├── ll.lua │ ├── misc.lua │ └── object_id.lua └── t ├── cluster.t ├── cursor.t ├── gridfs.t ├── gridfs_write.t ├── objid.t └── sanity.t /.gitignore: -------------------------------------------------------------------------------- 1 | t/servroot 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OPENRESTY_PREFIX=/usr/local/openresty 2 | 3 | PREFIX ?= /usr/local/openresty 4 | LUA_INCLUDE_DIR ?= $(PREFIX)/include 5 | LUA_LIB_DIR ?= $(PREFIX)/lualib 6 | INSTALL ?= install 7 | 8 | .PHONY: all test install 9 | 10 | all: ; 11 | 12 | install: all 13 | $(INSTALL) -d $(LUA_LIB_DIR)/resty/mongol 14 | $(INSTALL) lib/resty/mongol/*.lua $(LUA_LIB_DIR)/resty/mongol 15 | 16 | 17 | test: 18 | PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t 19 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Name 2 | ====== 3 | lua-resty-mongol - Lua Mongodb driver for ngx_lua base on the cosocket API 4 | 5 | Thanks to project Mongol by daurnimator 6 | 7 | Dependancies 8 | ====== 9 | 10 | luajit(or `attempt to yield across metamethod/C-call boundary error` will be produced.) 11 | 12 | [ngx_lua 0.5.0rc5](https://github.com/chaoslawful/lua-nginx-module/tags) or [ngx_openresty 1.0.11.7](http://openresty.org/#Download) is required. 13 | 14 | 15 | Installation 16 | ====== 17 | 18 | make install 19 | 20 | Usage 21 | ====== 22 | 23 | Add package path into nginx.conf. 24 | 25 | lua_package_path '/usr/local/openresty/lualib/?/init.lua;;'; 26 | 27 | or into lua files before requiring. 28 | 29 | local p = "/usr/local/openresty/lualib/" 30 | local m_package_path = package.path 31 | package.path = string.format("%s?.lua;%s?/init.lua;%s", 32 | p, p, m_package_path) 33 | 34 | Requring the module will return a function that connects to mongod: 35 | it takes a host (default localhost) and a port (default 27017); 36 | it returns a connection object. 37 | 38 | mongol = require "resty.mongol" 39 | conn = mongol:new() -- return a conntion object 40 | 41 | ###Connection objects have server wide methods. 42 | ------------ 43 | 44 | ####ok,err = conn:connect(host, port) 45 | Default host and port is: `localhost` and `27017`. 46 | 47 | ####ok,err = conn:set_timeout(msec) 48 | Sets socket connecting, reading, writing timeout value, unit is milliseconds. 49 | 50 | In case of success, returns 1. In case of errors, returns nil with a string describing the error. 51 | 52 | ####ok,err = conn:set_keepalive(msec, pool_size) 53 | Keeps the socket alive for `msec` by ngx_lua cosocket. 54 | 55 | In case of success, returns 1. In case of errors, returns nil with a string describing the error. 56 | 57 | ####times,err = conn:get_reused_times() 58 | Returns the socket reused times. 59 | 60 | In case of success, returns times. In case of errors, returns nil with a string describing the error. 61 | 62 | ####ok,err = conn:close() 63 | Closes the connection. 64 | 65 | In case of success, returns 1. In case of errors, returns nil with a string describing the error. 66 | 67 | ####bool, hosts = conn:ismaster() 68 | Returns a boolean indicating if this is the master server and a table of other hosts this server is replicating with 69 | or `nil, err` on failure. 70 | 71 | ####newconn = conn:getprimary ( [already_checked] ) 72 | Returns a new connection object that is connected to the primary server 73 | or `nil , errmsg` on failure. 74 | 75 | The returned connection object may be this connection object itself. 76 | 77 | 78 | ####databases = conn:databases ( ) 79 | Returns a table describing databases on the server. 80 | 81 | databases.name: string 82 | databases.empty: boolean 83 | databases.sizeOnDisk: number 84 | 85 | ####conn:shutdown() 86 | Shutsdown the server. Returns nothing. 87 | 88 | ####db = conn:new_db_handle(database_name) 89 | Returns a database object, or nil. 90 | 91 | ###Database objects perform actions on a database 92 | ------------ 93 | 94 | ####db:list() 95 | 96 | ####db:dropDatabase() 97 | 98 | ####db:add_user(username, password) 99 | 100 | ####ok, err = db:auth(username, password) 101 | Returns 1 in case of success, or nil with error message. 102 | 103 | ####ok, err = db:auth_scram_sha1(username, password) 104 | Returns 1 in case of success, or nil with error message. 105 | 106 | For authentication with MongoDB 2.8(3.0) or later.Authenticate using SCRAM-SHA-1 which is the default authentication mechanism supported by a cluster configured for authentication with MongoDB 2.8(3.0) or later. 107 | 108 | ####col = db:get_col(collection_name) 109 | Returns a collection object for more operations. 110 | 111 | ####gridfs = db:get_gridfs(fs) 112 | 113 | ###Collection objects 114 | ------------ 115 | 116 | ####n = col:count(query) 117 | 118 | ####ok, err = col:drop() 119 | Returns 1 in case of success, or nil with error message. 120 | 121 | ####n, err = col:update(selector, update, upsert, multiupdate, safe) 122 | Returns number of rows been updated or nil for error. 123 | 124 | - upsert, if set to `1`, the database will insert the supplied object into the collection if no matching document is found, default to `0`. 125 | - multiupdate, if set to `1`, the database will update all matching objects in the collection. Otherwise only updates first matching doc, default to `0`. Multi update only works with $ operators. 126 | - safe can be a boolean or integer, defaults to `0`. If `1`, the program will issue a cmd `getlasterror` to server to query the result. If `false`, return value `n` would always be `-1` 127 | 128 | ####n, err = col:insert(docs, continue_on_error, safe) 129 | Returns 0 for success, or nil with error message. 130 | 131 | - continue_on_error, if set, the database will not stop processing a bulk insert if one fails (eg due to duplicate IDs). 132 | - safe can be a boolean or integer, defaults to `0` or `false`. If `1` or ``true`, the program will issue a cmd `getlasterror` to server to query the result. If `false`, return value `n` would always be `-1` 133 | 134 | ####n, err = col:delete(selector, singleRemove, safe) 135 | Returns number of rows been deleted, or nil with error message. 136 | 137 | - singleRemove if set to 1, the database will remove only the first matching document in the collection. Otherwise all matching documents will be removed. Default to `0` 138 | - safe can be a boolean or integer, defaults to `0`. If `1`, the program will issue a cmd `getlasterror` to server to query the result. If `false`, return value `n` would always be `-1` 139 | 140 | ####r = col:find_one(query, returnfields) 141 | Returns a single element array, or nil. 142 | 143 | - returnfields is the fields to return, eg: `{n=0}` or `{n=1}` 144 | 145 | ####cursor = col:find(query, returnfields, num_each_query) 146 | Returns a cursor object for excuting query. 147 | 148 | - returnfields is the fields to return, eg: `{n=0}` or `{n=1}` 149 | - num_each_query is the max result number for each query of the cursor to avoid fetch a large result in memory, must larger than `1`, `0` for no limit, default to `100`. 150 | 151 | ####col:getmore(cursorID, [numberToReturn], [offset_i]) 152 | - cursorID is an 8 byte string representing the cursor to getmore on 153 | - numberToReturn is the number of results to return, defaults to -1 154 | - offset_i is the number to start numbering the returned table from, defaults to 1 155 | 156 | ####col:kill_cursors(cursorIDs) 157 | 158 | ###Cursor objects 159 | -------------------- 160 | 161 | ####index, item = cursor:next() 162 | Returns the next item and advances the cursor. 163 | 164 | ####cursor:pairs() 165 | A handy wrapper around cursor:next() that works in a generic for loop: 166 | 167 | for index, item in cursor:pairs() do 168 | 169 | ####cursor:limit(n) 170 | Limits the number of results returned. 171 | 172 | ####cursor = cursor:sort(fields) 173 | Returns an array with size `size` sorted by given field. 174 | 175 | - field is an array by which to sort, and this array size _MUST be 1_. The element in the array has as key the field name, and as value either `1` for ascending sort, or `-1` for descending sort. 176 | 177 | ###Object id 178 | ------------------- 179 | 180 | ####objid:tostring() 181 | ####objid:get_ts() 182 | ####objid:get_pid() 183 | ####objid:get_hostname() 184 | ####objid:get_inc() 185 | 186 | ###Grid FS Object 187 | ------------------- 188 | 189 | _under developing_ 190 | 191 | ####gridfs_file = gridfs:find_one(fields) 192 | Returns a gridfs file object. 193 | 194 | ####gridfs_file = gridfs:remove(fields, continue_on_err, safe) 195 | Returns number of files been deleted, or nil with error message. 196 | 197 | - singleRemove if set to 1, the database will remove only the first matching document in the collection. Otherwise all matching documents will be removed. Default to `0` 198 | - safe can be a boolean or integer, defaults to `0`. If `1`, the program will issue a cmd `getlasterror` to server to query the result. If `false`, return value `n` would always be `-1` 199 | 200 | ####bool = gridfs:get(file_handler, fields) 201 | Writes first object matchs fields into file_handler. This API will malloc a buffer in file size in memory. 202 | 203 | ####n, err = gridfs:insert(file_handler, meta, safe) 204 | Returns 0 for success, or nil with error message. 205 | 206 | - file_handler is file handler returned by io:open(). 207 | - meta is a table include `_id, filename, chunkSize, contentType, aliases, metadata` or anything the user wants to store. Default meta.filename is the object id in string. 208 | - safe can be a boolean or integer, defaults to `0`. If `1`, the program will issue a cmd `getlasterror` to server to query the result. If `false`, return value `n` would always be `-1` 209 | 210 | ####gridfs_file, err = gridfs:new(meta) 211 | Returns a new gridfs file object, or nil with error message. 212 | 213 | - meta is a table include `_id, filename, chunkSize, contentType, aliases, metadata` or anything the user wants to store. Default meta.filename is the object id in string. 214 | 215 | 216 | ###Grid FS File Object 217 | ------------------- 218 | 219 | ####n, err = gridfs_file:read(size, offset) 220 | Returns number of bytes read from mongodb, or nil with error message. 221 | 222 | - offse start from 0 223 | 224 | ####n, err = gridfs_file:write(buf, offset, size) 225 | Returns number of bytes writen into mongodb, or nil with error message. 226 | 227 | - offset is the file offset(should not beyond the end of the file), starting from 0. 228 | - size is the number of bytes to be writen. 229 | 230 | ####bool, err = gridfs_file:update_md5() 231 | Hashs the file content and updates the md5 in file collection. 232 | 233 | Notes 234 | --------------------------- 235 | - collections are string containing any value except "\0" 236 | - database_name are strings containing any character except "." and "\0" 237 | 238 | Known Issues 239 | --------------------------- 240 | 1. Auth bson message has reduntant value. 241 | 2. Could not inserting a null array, it always inserted as a document. 242 | 3. Gridfs_new api only create a meta info in file_col. 243 | 244 | Example 245 | --------------------------- 246 | local mongo = require "resty.mongol" 247 | conn = mongo:new() 248 | conn:set_timeout(1000) 249 | ok, err = conn:connect() 250 | if not ok then 251 | ngx.say("connect failed: "..err) 252 | end 253 | 254 | local db = conn:new_db_handle ( "test" ) 255 | col = db:get_col("test") 256 | 257 | r = col:find_one({name="dog"}) 258 | ngx.say(r["name"]) 259 | 260 | For Test Case 261 | -------------------- 262 | #####mongo config: 263 | > config = {_id: 'testset', members: [ 264 | {_id: 0, host: '10.6.2.51:27017'}, 265 | {_id: 1, host: '10.6.2.51:27018'}, 266 | {_id: 2, host: '10.6.2.51:27019'}] 267 | } 268 | > rs.initiate(config); 269 | 270 | #####start-mongo.sh: 271 | nohup bin/mongod --dbpath=/data/57cbd36d-5b70-4888-8537-bea71119363e/mongodb --oplogSize 10 --rest --replSet testset --port 27017 --keyFile key.file & 272 | nohup bin/mongod --dbpath=/data/0a9419ae-4ec3-48c2-ad8d-df68a09aed13/mongodb --oplogSize 10 --rest --replSet testset --port 27018 --keyFile key.file & 273 | nohup bin/mongod --dbpath=/data/8ee9efc0-a854-4c45-8893-7b4cb9ed0e5f/mongodb --oplogSize 10 --rest --replSet testset --port 27019 --keyFile key.file & 274 | 275 | #####mongo user: 276 | > use test 277 | > db.addUser("admin","admin") 278 | -------------------------------------------------------------------------------- /lib/resty/mongol/bson.lua: -------------------------------------------------------------------------------- 1 | local mod_name = (...):match ( "^(.*)%..-$" ) 2 | 3 | local assert , error = assert , error 4 | local pairs = pairs 5 | local getmetatable = getmetatable 6 | local type = type 7 | local tonumber , tostring = tonumber , tostring 8 | local t_insert = table.insert 9 | local t_concat = table.concat 10 | local strformat = string.format 11 | local strmatch = string.match 12 | local strbyte = string.byte 13 | local floor = math.floor 14 | 15 | local ll = require ( mod_name .. ".ll" ) 16 | local le_uint_to_num = ll.le_uint_to_num 17 | local le_int_to_num = ll.le_int_to_num 18 | local num_to_le_uint = ll.num_to_le_uint 19 | local num_to_le_int = ll.num_to_le_int 20 | local from_double = ll.from_double 21 | local to_double = ll.to_double 22 | 23 | local getlib = require ( mod_name .. ".get" ) 24 | local read_terminated_string = getlib.read_terminated_string 25 | 26 | local obid = require ( mod_name .. ".object_id" ) 27 | local new_object_id = obid.new 28 | local object_id_mt = obid.metatable 29 | local binary_mt = {} 30 | local utc_date = {} 31 | 32 | 33 | local function read_document ( get , numerical ) 34 | local bytes = le_uint_to_num ( get ( 4 ) ) 35 | 36 | local ho , hk , hv = false , false , false 37 | local t = { } 38 | while true do 39 | local op = get ( 1 ) 40 | if op == "\0" then break end 41 | 42 | local e_name = read_terminated_string ( get ) 43 | local v 44 | if op == "\1" then -- Double 45 | v = from_double ( get ( 8 ) ) 46 | elseif op == "\2" then -- String 47 | local len = le_uint_to_num ( get ( 4 ) ) 48 | v = get ( len - 1 ) 49 | assert ( get ( 1 ) == "\0" ) 50 | elseif op == "\3" then -- Embedded document 51 | v = read_document ( get , false ) 52 | elseif op == "\4" then -- Array 53 | v = read_document ( get , true ) 54 | elseif op == "\5" then -- Binary 55 | local len = le_uint_to_num ( get ( 4 ) ) 56 | local subtype = get ( 1 ) 57 | v = get ( len ) 58 | elseif op == "\7" then -- ObjectId 59 | v = new_object_id ( get ( 12 ) ) 60 | elseif op == "\8" then -- false 61 | local f = get ( 1 ) 62 | if f == "\0" then 63 | v = false 64 | elseif f == "\1" then 65 | v = true 66 | else 67 | error ( f:byte ( ) ) 68 | end 69 | elseif op == "\9" then -- UTC datetime milliseconds 70 | v = le_uint_to_num ( get ( 8 ) , 1 , 8 ) 71 | elseif op == "\10" then -- Null 72 | v = nil 73 | elseif op == "\16" then --int32 74 | v = le_int_to_num ( get ( 4 ) , 1 , 8 ) 75 | elseif op == "\17" then --int64 76 | v = le_int_to_num(get(8), 1, 8) 77 | elseif op == "\18" then --int64 78 | v = le_int_to_num(get(8), 1, 8) 79 | else 80 | error ( "Unknown BSON type: " .. strbyte ( op ) ) 81 | end 82 | 83 | if numerical then 84 | t [ tonumber ( e_name ) + 1] = v 85 | else 86 | t [ e_name ] = v 87 | end 88 | 89 | -- Check for special universal map 90 | if e_name == "_keys" then 91 | hk = v 92 | elseif e_name == "_vals" then 93 | hv = v 94 | else 95 | ho = true 96 | end 97 | end 98 | 99 | if not ho and hk and hv then 100 | t = { } 101 | for i=1,#hk do 102 | t [ hk [ i ] ] = hv [ i ] 103 | end 104 | end 105 | 106 | return t 107 | end 108 | 109 | local function get_utc_date(v) 110 | return setmetatable({v = v}, utc_date) 111 | end 112 | 113 | local function get_bin_data(v) 114 | return setmetatable({v = v, st = "\0"}, binary_mt) 115 | end 116 | 117 | local function from_bson ( get ) 118 | local t = read_document ( get , false ) 119 | return t 120 | end 121 | 122 | local to_bson 123 | local function pack ( k , v ) 124 | local ot = type ( v ) 125 | local mt = getmetatable ( v ) 126 | 127 | if ot == "number" then 128 | if floor(v) == v then 129 | if v >= -2^31 and v <= 2^31-1 then --int32 130 | return "\16" .. k .. "\0" .. num_to_le_int ( v ) 131 | else --int64 132 | return "\18" .. k .. "\0" .. num_to_le_int ( v, 8 ) 133 | end 134 | else 135 | return "\1" .. k .. "\0" .. to_double ( v ) 136 | end 137 | elseif ot == "nil" then 138 | return "\10" .. k .. "\0" 139 | elseif ot == "string" then 140 | return "\2" .. k .. "\0" .. num_to_le_uint ( #v + 1 ) .. v .. "\0" 141 | elseif ot == "boolean" then 142 | if v == false then 143 | return "\8" .. k .. "\0\0" 144 | else 145 | return "\8" .. k .. "\0\1" 146 | end 147 | elseif mt == object_id_mt then 148 | return "\7" .. k .. "\0" .. v.id 149 | elseif mt == utc_date then 150 | return "\9" .. k .. "\0" .. num_to_le_int(v.v, 8) 151 | elseif mt == binary_mt then 152 | return "\5" .. k .. "\0" .. num_to_le_uint(string.len(v.v)) .. 153 | v.st .. v.v 154 | elseif ot == "table" then 155 | local doc , array = to_bson(v) 156 | if array then 157 | return "\4" .. k .. "\0" .. doc 158 | else 159 | return "\3" .. k .. "\0" .. doc 160 | end 161 | elseif ot == "userdata" and tostring(v) == "userdata: NULL" then 162 | return "\10" .. k .. "\0" 163 | else 164 | error ( "Failure converting " .. ot ..": " .. tostring ( v ) ) 165 | end 166 | end 167 | 168 | function to_bson(ob) 169 | -- Find out if ob if an array; string->value map; or general table 170 | local onlyarray = true 171 | local seen_n , high_n = { } , 0 172 | local onlystring = true 173 | for k , v in pairs ( ob ) do 174 | local t_k = type ( k ) 175 | onlystring = onlystring and ( t_k == "string" ) 176 | if onlyarray then 177 | if t_k == "number" and k >= 0 then 178 | if k >= high_n then 179 | high_n = k 180 | end 181 | seen_n [ k ] = v 182 | else 183 | onlyarray = false 184 | end 185 | end 186 | if not onlyarray and not onlystring then break end 187 | end 188 | 189 | local retarray , m = false 190 | if onlystring then -- Do string first so the case of an empty table is done properly 191 | local r = { } 192 | for k , v in pairs ( ob ) do 193 | --ngx.log(ngx.ERR,"="..k..i) 194 | t_insert ( r , pack ( k , v ) ) 195 | end 196 | m = t_concat ( r ) 197 | elseif onlyarray then 198 | local r = { } 199 | 200 | local low = 1 201 | --if seen_n [ 0 ] then low = 0 end 202 | for i=low , high_n do 203 | r [ i ] = pack ( i - 1 , seen_n [ i ] ) 204 | end 205 | 206 | m = t_concat ( r , "" , low , high_n ) 207 | retarray = true 208 | else 209 | local ni = 1 210 | local keys , vals = { } , { } 211 | for k , v in pairs ( ob ) do 212 | keys [ ni ] = k 213 | vals [ ni ] = v 214 | ni = ni + 1 215 | end 216 | return to_bson ( { _keys = keys , _vals = vals } ) 217 | end 218 | 219 | return num_to_le_uint ( #m + 4 + 1 ) .. m .. "\0" , retarray 220 | end 221 | 222 | return { 223 | from_bson = from_bson ; 224 | to_bson = to_bson ; 225 | get_bin_data = get_bin_data; 226 | get_utc_date = get_utc_date; 227 | } 228 | -------------------------------------------------------------------------------- /lib/resty/mongol/colmt.lua: -------------------------------------------------------------------------------- 1 | local mod_name = (...):match ( "^(.*)%..-$" ) 2 | 3 | local misc = require ( mod_name .. ".misc" ) 4 | 5 | local assert , pcall = assert , pcall 6 | local ipairs , pairs = ipairs , pairs 7 | local t_insert , t_concat = table.insert , table.concat 8 | 9 | local attachpairs_start = misc.attachpairs_start 10 | 11 | local ll = require ( mod_name .. ".ll" ) 12 | local num_to_le_uint = ll.num_to_le_uint 13 | local num_to_le_int = ll.num_to_le_int 14 | local le_uint_to_num = ll.le_uint_to_num 15 | local le_bpeek = ll.le_bpeek 16 | 17 | local getlib = require ( mod_name .. ".get" ) 18 | local get_from_string = getlib.get_from_string 19 | 20 | local bson = require ( mod_name .. ".bson" ) 21 | local from_bson = bson.from_bson 22 | local to_bson = bson.to_bson 23 | 24 | local new_cursor = require ( mod_name .. ".cursor" ) 25 | 26 | local colmethods = { } 27 | local colmt = { __index = colmethods } 28 | 29 | 30 | local opcodes = { 31 | REPLY = 1 ; 32 | MSG = 1000 ; 33 | UPDATE = 2001 ; 34 | INSERT = 2002 ; 35 | QUERY = 2004 ; 36 | GET_MORE = 2005 ; 37 | DELETE = 2006 ; 38 | KILL_CURSORS = 2007 ; 39 | } 40 | 41 | local function compose_msg ( requestID , reponseTo , opcode , message ) 42 | return num_to_le_uint ( #message + 16 ) .. requestID .. reponseTo .. opcode .. message 43 | end 44 | 45 | local function full_collection_name ( self , collection ) 46 | local db = assert ( self.db , "Not current in a database" ) 47 | return db .. "." .. collection .. "\0" 48 | end 49 | 50 | local id = 0 51 | local function docmd ( conn , opcode , message , reponseTo ) 52 | id = id + 1 53 | local req_id = id 54 | local requestID = num_to_le_uint ( req_id ) 55 | reponseTo = reponseTo or "\255\255\255\255" 56 | opcode = num_to_le_uint ( assert ( opcodes [ opcode ] ) ) 57 | 58 | local m = compose_msg ( requestID , reponseTo , opcode , message ) 59 | local sent = assert ( conn.sock:send ( m ) ) 60 | return req_id , sent 61 | end 62 | 63 | local function read_msg_header ( sock ) 64 | local header = assert ( sock:receive ( 16 ) ) 65 | 66 | local length = le_uint_to_num ( header , 1 , 4 ) 67 | local requestID = le_uint_to_num ( header , 5 , 8 ) 68 | local reponseTo = le_uint_to_num ( header , 9 , 12 ) 69 | local opcode = le_uint_to_num ( header , 13 , 16 ) 70 | 71 | return length , requestID , reponseTo , opcode 72 | end 73 | 74 | local function handle_reply ( conn , req_id , offset_i ) 75 | offset_i = offset_i or 0 76 | 77 | local r_len , r_req_id , r_res_id , opcode = read_msg_header ( conn.sock ) 78 | assert ( req_id == r_res_id ) 79 | assert ( opcode == opcodes.REPLY ) 80 | local data = assert ( conn.sock:receive ( r_len - 16 ) ) 81 | local get = get_from_string ( data ) 82 | 83 | local responseFlags = get ( 4 ) 84 | local cursorid = get ( 8 ) 85 | 86 | local t = { } 87 | t.startingFrom = le_uint_to_num ( get ( 4 ) ) 88 | t.numberReturned = le_uint_to_num ( get ( 4 ) ) 89 | t.CursorNotFound = le_bpeek ( responseFlags , 0 ) 90 | t.QueryFailure = le_bpeek ( responseFlags , 1 ) 91 | t.ShardConfigStale = le_bpeek ( responseFlags , 2 ) 92 | t.AwaitCapable = le_bpeek ( responseFlags , 3 ) 93 | 94 | local r = { } 95 | for i = 1 , t.numberReturned do 96 | r[i] = from_bson(get) 97 | end 98 | 99 | return cursorid, r, t 100 | end 101 | 102 | function colmethods:insert(docs, continue_on_error, safe) 103 | if #docs < 1 then 104 | return nil, "docs needed" 105 | end 106 | 107 | safe = safe or 0 108 | continue_on_error = continue_on_error or 0 109 | local flags = 2^0*continue_on_error 110 | 111 | local t = { } 112 | for i , v in ipairs(docs) do 113 | t[i] = to_bson(v) 114 | end 115 | 116 | local m = num_to_le_uint(flags)..full_collection_name(self, self.col) 117 | ..t_concat(t) 118 | local id, send = docmd(self.conn, "INSERT", m) 119 | if send == 0 then 120 | return nil, "send message failed" 121 | end 122 | 123 | if safe ~= 0 then 124 | local r, err = self.db_obj:cmd({getlasterror=1}) 125 | if not r then 126 | return nil, err 127 | end 128 | 129 | if r["err"] then 130 | return nil, r["err"] 131 | else 132 | return r["n"] 133 | end 134 | else 135 | return -1 end 136 | end 137 | 138 | function colmethods:update(selector, update, upsert, multiupdate, safe) 139 | safe = safe or 0 140 | upsert = upsert or 0 141 | multiupdate = multiupdate or 0 142 | local flags = 2^0*upsert + 2^1*multiupdate 143 | 144 | selector = to_bson(selector) 145 | update = to_bson(update) 146 | 147 | local m = "\0\0\0\0" .. full_collection_name(self, self.col) 148 | .. num_to_le_uint ( flags ) .. selector .. update 149 | local id, send = docmd(self.conn, "UPDATE", m) 150 | if send == 0 then 151 | return nil, "send message failed" 152 | end 153 | 154 | if safe ~= 0 then 155 | local r, err = self.db_obj:cmd({getlasterror=1}) 156 | if not r then 157 | return nil, err 158 | end 159 | 160 | if r["err"] then 161 | return nil, r["err"] 162 | else 163 | return r["n"] 164 | end 165 | else return -1 end 166 | end 167 | 168 | function colmethods:delete(selector, single_remove, safe) 169 | safe = safe or 0 170 | single_remove = single_remove or 0 171 | local flags = 2^0*single_remove 172 | 173 | selector = to_bson(selector) 174 | 175 | local m = "\0\0\0\0" .. full_collection_name(self, self.col) 176 | .. num_to_le_uint(flags) .. selector 177 | 178 | local id, sent = docmd(self.conn, "DELETE", m) 179 | if sent == 0 then 180 | return nil, "send message failed" 181 | end 182 | 183 | if safe ~= 0 then 184 | local r, err = self.db_obj:cmd({getlasterror=1}) 185 | if not r then 186 | return nil, err 187 | end 188 | 189 | if r["err"] then 190 | return nil, r["err"] 191 | else 192 | return r["n"] 193 | end 194 | else return -1 end 195 | end 196 | 197 | function colmethods:kill_cursors(cursorIDs) 198 | local n = #cursorIDs 199 | cursorIDs = t_concat(cursorIDs) 200 | 201 | local m = "\0\0\0\0" .. full_collection_name(self, self.col) 202 | .. num_to_le_uint(n) .. cursorIDs 203 | 204 | return docmd(self.conn, "KILL_CURSORS", m ) 205 | end 206 | 207 | function colmethods:query(query, returnfields, numberToSkip, numberToReturn, options) 208 | numberToSkip = numberToSkip or 0 209 | 210 | local flags = 0 211 | if options then 212 | flags = 2^1*( options.TailableCursor and 1 or 0 ) 213 | + 2^2*( options.SlaveOk and 1 or 0 ) 214 | + 2^3*( options.OplogReplay and 1 or 0 ) 215 | + 2^4*( options.NoCursorTimeout and 1 or 0 ) 216 | + 2^5*( options.AwaitData and 1 or 0 ) 217 | + 2^6*( options.Exhaust and 1 or 0 ) 218 | + 2^7*( options.Partial and 1 or 0 ) 219 | end 220 | 221 | query = to_bson(query) 222 | if returnfields then 223 | returnfields = to_bson(returnfields) 224 | else 225 | returnfields = "" 226 | end 227 | 228 | local m = num_to_le_uint(flags) .. full_collection_name(self, self.col) 229 | .. num_to_le_uint(numberToSkip) .. num_to_le_int(numberToReturn or -1 ) 230 | .. query .. returnfields 231 | 232 | local req_id = docmd(self.conn, "QUERY", m) 233 | return handle_reply(self.conn, req_id, numberToSkip) 234 | end 235 | 236 | function colmethods:getmore(cursorID, numberToReturn, offset_i) 237 | local m = "\0\0\0\0" .. full_collection_name(self, self.col) 238 | .. num_to_le_int(numberToReturn or 0) .. cursorID 239 | 240 | local req_id = docmd(self.conn, "GET_MORE" , m) 241 | return handle_reply(self.conn, req_id, offset_i) 242 | end 243 | 244 | function colmethods:count(query) 245 | local r, err = self.db_obj:cmd(attachpairs_start({ 246 | count = self.col; 247 | query = query or { } ; 248 | } , "count" ) ) 249 | 250 | if not r then 251 | return nil, err 252 | end 253 | return r.n 254 | end 255 | 256 | function colmethods:drop() 257 | local r, err = self.db_obj:cmd({drop = self.col}) 258 | if not r then 259 | return nil, err 260 | end 261 | return 1 262 | end 263 | 264 | function colmethods:find(query, returnfields, num_each_query) 265 | num_each_query = num_each_query or 100 266 | if num_each_query == 1 then 267 | return nil, "num_each_query must larger than 1" 268 | end 269 | return new_cursor(self, query, returnfields, num_each_query) 270 | end 271 | 272 | function colmethods:find_one(query, returnfields) 273 | local id, results, t = self:query(query, returnfields, 0, 1) 274 | if id == "\0\0\0\0\0\0\0\0" and results[1] then 275 | return results[1] 276 | end 277 | return nil 278 | end 279 | 280 | return colmt 281 | -------------------------------------------------------------------------------- /lib/resty/mongol/cursor.lua: -------------------------------------------------------------------------------- 1 | local t_insert = table.insert 2 | local t_remove = table.remove 3 | local t_concat = table.concat 4 | local strbyte = string.byte 5 | local strformat = string.format 6 | 7 | 8 | local cursor_methods = { } 9 | local cursor_mt = { __index = cursor_methods } 10 | 11 | local function new_cursor(col, query, returnfields, num_each_query) 12 | return setmetatable ( { 13 | col = col ; 14 | query = { ['$query'] = query} ; 15 | returnfields = returnfields ; 16 | 17 | id = false ; 18 | results = { } ; 19 | 20 | done = false ; 21 | i = 0; 22 | limit_n = 0; 23 | skip_n = 0; 24 | num_each = num_each_query; 25 | } , cursor_mt ) 26 | end 27 | 28 | cursor_mt.__gc = function( self ) 29 | self.col:kill_cursors({ self.id }) 30 | end 31 | 32 | cursor_mt.__tostring = function ( ob ) 33 | local t = { } 34 | for i = 1 , 8 do 35 | t_insert(t, strformat("%02x", strbyte(ob.id, i, i))) 36 | end 37 | return "CursorId(" .. t_concat ( t ) .. ")" 38 | end 39 | 40 | function cursor_methods:limit(n) 41 | assert(n) 42 | self.limit_n = n 43 | end 44 | 45 | --todo 46 | --function cursor_methods:skip(n) 47 | 48 | function cursor_methods:sort(fields) 49 | self.query["$orderby"] = fields 50 | return self 51 | end 52 | 53 | function cursor_methods:next() 54 | if self.limit_n > 0 and self.i >= self.limit_n then return nil end 55 | 56 | local v = self.results [ self.i - self.skip_n + 1 ] 57 | if v ~= nil then 58 | self.i = self.i + 1 59 | self.results [ self.i - self.skip_n] = nil 60 | return self.i , v 61 | end 62 | 63 | if self.done then return nil end 64 | 65 | local t 66 | if not self.id then 67 | self.id, self.results, t = self.col:query(self.query, 68 | self.returnfields, self.i, self.num_each) 69 | if self.id == "\0\0\0\0\0\0\0\0" then 70 | self.done = true 71 | end 72 | else 73 | self.id, self.results, t = self.col:getmore(self.id, 74 | self.num_each, self.i) 75 | self.skip_n = self.i 76 | if self.id == "\0\0\0\0\0\0\0\0" then 77 | self.done = true 78 | elseif t.CursorNotFound then 79 | self.id = false 80 | end 81 | end 82 | return self:next() 83 | end 84 | 85 | function cursor_methods:pairs( ) 86 | return self.next, self 87 | end 88 | 89 | return new_cursor 90 | -------------------------------------------------------------------------------- /lib/resty/mongol/dbmt.lua: -------------------------------------------------------------------------------- 1 | local mod_name = (...):match ( "^(.*)%..-$" ) 2 | 3 | local misc = require ( mod_name .. ".misc" ) 4 | local attachpairs_start = misc.attachpairs_start 5 | 6 | local setmetatable = setmetatable 7 | local pcall = pcall 8 | 9 | local colmt = require ( mod_name .. ".colmt" ) 10 | local gridfs = require ( mod_name .. ".gridfs" ) 11 | 12 | local dbmethods = { } 13 | local dbmt = { __index = dbmethods } 14 | 15 | function dbmethods:cmd(q) 16 | local collection = "$cmd" 17 | local col = self:get_col(collection) 18 | 19 | local c_id , r , t = col:query(q) 20 | 21 | if t.QueryFailure then 22 | return nil, "Query Failure" 23 | elseif not r[1] then 24 | return nil, "No results returned" 25 | elseif r[1].ok == 0 then -- Failure 26 | return nil , r[1].errmsg , r[1] , t 27 | else 28 | return r[1] 29 | end 30 | end 31 | 32 | function dbmethods:listcollections ( ) 33 | local col = self:get_col("system.namespaces") 34 | return col:find( { } ) 35 | end 36 | 37 | function dbmethods:dropDatabase ( ) 38 | local r, err = self:cmd({ dropDatabase = true }) 39 | if not r then 40 | return nil, err 41 | end 42 | return 1 43 | end 44 | 45 | local function pass_digest ( username , password ) 46 | return ngx.md5(username .. ":mongo:" .. password) 47 | end 48 | 49 | -- XOR two byte strings together 50 | local function xor_bytestr( a, b ) 51 | local res = "" 52 | for i=1,#a do 53 | res = res .. string.char(bit.bxor(string.byte(a,i,i), string.byte(b, i, i))) 54 | end 55 | return res 56 | end 57 | 58 | -- A simple implementation of PBKDF2_HMAC_SHA1 59 | local function pbkdf2_hmac_sha1( pbkdf2_key, iterations, salt, len ) 60 | local u1 = ngx.hmac_sha1(pbkdf2_key, salt .. string.char(0) .. string.char(0) .. string.char(0) .. string.char(1)) 61 | local ui = u1 62 | for i=1,iterations-1 do 63 | u1 = ngx.hmac_sha1(pbkdf2_key, u1) 64 | ui = xor_bytestr(ui, u1) 65 | end 66 | if #ui < len then 67 | for i=1,len-(#ui) do 68 | ui = string.char(0) .. ui 69 | end 70 | end 71 | return ui 72 | end 73 | 74 | function dbmethods:add_user ( username , password ) 75 | local digest = pass_digest ( username , password ) 76 | return self:update ( "system.users" , { user = username } , { ["$set"] = { pwd = password } } , true ) 77 | end 78 | 79 | function dbmethods:auth(username, password) 80 | local r, err = self:cmd({ getnonce = true }) 81 | if not r then 82 | return nil, err 83 | end 84 | 85 | local digest = ngx.md5( r.nonce .. username .. pass_digest ( username , password ) ) 86 | 87 | r, err = self:cmd(attachpairs_start({ 88 | authenticate = true ; 89 | user = username ; 90 | nonce = r.nonce ; 91 | key = digest ; 92 | } , "authenticate" ) ) 93 | if not r then 94 | return nil, err 95 | end 96 | return 1 97 | end 98 | 99 | function dbmethods:auth_scram_sha1(username, password) 100 | local user = string.gsub(string.gsub(username, '=', '=3D'), ',' , '=2C') 101 | local nonce = ngx.encode_base64(string.sub(tostring(math.random()), 3 , 14)) 102 | local first_bare = "n=" .. user .. ",r=" .. nonce 103 | local sasl_start_payload = ngx.encode_base64("n,," .. first_bare) 104 | 105 | r, err = self:cmd(attachpairs_start({ 106 | saslStart = 1 ; 107 | mechanism = "SCRAM-SHA-1" ; 108 | autoAuthorize = 1 ; 109 | payload = sasl_start_payload ; 110 | } , "saslStart" ) ) 111 | if not r then 112 | return nil, err 113 | end 114 | 115 | local conversationId = r['conversationId'] 116 | local server_first = r['payload'] 117 | local parsed_s = ngx.decode_base64(server_first) 118 | local parsed_t = {} 119 | for k, v in string.gmatch(parsed_s, "(%w+)=([^,]*)") do 120 | parsed_t[k] = v 121 | end 122 | local iterations = tonumber(parsed_t['i']) 123 | local salt = parsed_t['s'] 124 | local rnonce = parsed_t['r'] 125 | 126 | if not string.sub(rnonce, 1, 12) == nonce then 127 | return nil, 'Server returned an invalid nonce.' 128 | end 129 | local without_proof = "c=biws,r=" .. rnonce 130 | local pbkdf2_key = pass_digest ( username , password ) 131 | local salted_pass = pbkdf2_hmac_sha1(pbkdf2_key, iterations, ngx.decode_base64(salt), 20) 132 | local client_key = ngx.hmac_sha1(salted_pass, "Client Key") 133 | local stored_key = ngx.sha1_bin(client_key) 134 | local auth_msg = first_bare .. ',' .. parsed_s .. ',' .. without_proof 135 | local client_sig = ngx.hmac_sha1(stored_key, auth_msg) 136 | local client_key_xor_sig = xor_bytestr(client_key, client_sig) 137 | local client_proof = "p=" .. ngx.encode_base64(client_key_xor_sig) 138 | local client_final = ngx.encode_base64(without_proof .. ',' .. client_proof) 139 | local server_key = ngx.hmac_sha1(salted_pass, "Server Key") 140 | local server_sig = ngx.encode_base64(ngx.hmac_sha1(server_key, auth_msg)) 141 | 142 | r, err = self:cmd(attachpairs_start({ 143 | saslContinue = 1 ; 144 | conversationId = conversationId ; 145 | payload = client_final ; 146 | } , "saslContinue" ) ) 147 | if not r then 148 | return nil, err 149 | end 150 | parsed_s = ngx.decode_base64(r['payload']) 151 | parsed_t = {} 152 | for k, v in string.gmatch(parsed_s, "(%w+)=([^,]*)") do 153 | parsed_t[k] = v 154 | end 155 | if parsed_t['v'] ~= server_sig then 156 | return nil, "Server returned an invalid signature." 157 | end 158 | 159 | if not r['done'] then 160 | r, err = self:cmd(attachpairs_start({ 161 | saslContinue = 1 ; 162 | conversationId = conversationId ; 163 | payload = ngx.encode_base64("") ; 164 | } , "saslContinue" ) ) 165 | if not r then 166 | return nil, err 167 | end 168 | if not r['done'] then 169 | return nil, 'SASL conversation failed to complete.' 170 | end 171 | return 1 172 | end 173 | return 1 174 | end 175 | 176 | function dbmethods:get_col(collection) 177 | if not collection then 178 | return nil, "collection needed" 179 | end 180 | 181 | return setmetatable ( { 182 | conn = self.conn; 183 | db_obj = self; 184 | db = self.db ; 185 | col = collection; 186 | } , colmt ) 187 | end 188 | 189 | function dbmethods:get_gridfs(fs) 190 | if not fs then 191 | return nil, "fs name needed" 192 | end 193 | 194 | return setmetatable({ 195 | conn = self.conn; 196 | db_obj = self; 197 | db = self.db; 198 | file_col = self:get_col(fs..".files"); 199 | chunk_col = self:get_col(fs..".chunks"); 200 | } , gridfs) 201 | end 202 | 203 | return dbmt 204 | -------------------------------------------------------------------------------- /lib/resty/mongol/get.lua: -------------------------------------------------------------------------------- 1 | local strsub = string.sub 2 | local t_insert = table.insert 3 | local t_concat = table.concat 4 | 5 | local function get_from_string ( s , i ) 6 | i = i or 1 7 | return function ( n ) 8 | if not n then -- Rest of string 9 | n = #s - i + 1 10 | end 11 | i = i + n 12 | assert ( i-1 <= #s , "Unable to read enough characters" ) 13 | return strsub ( s , i-n , i-1 ) 14 | end , function ( new_i ) 15 | if new_i then i = new_i end 16 | return i 17 | end 18 | end 19 | 20 | local function string_to_array_of_chars ( s ) 21 | local t = { } 22 | for i = 1 , #s do 23 | t [ i ] = strsub ( s , i , i ) 24 | end 25 | return t 26 | end 27 | 28 | local function read_terminated_string ( get , terminators ) 29 | local terminators = string_to_array_of_chars ( terminators or "\0" ) 30 | local str = { } 31 | local found = 0 32 | while found < #terminators do 33 | local c = get ( 1 ) 34 | if c == terminators [ found + 1 ] then 35 | found = found + 1 36 | else 37 | found = 0 38 | end 39 | t_insert ( str , c ) 40 | end 41 | return t_concat ( str , "" , 1 , #str - #terminators ) 42 | end 43 | 44 | return { 45 | get_from_string = get_from_string ; 46 | read_terminated_string = read_terminated_string ; 47 | } 48 | -------------------------------------------------------------------------------- /lib/resty/mongol/globalplus.lua: -------------------------------------------------------------------------------- 1 | -- globalsplus.lua 2 | -- Like globals.lua in Lua 5.1.4 but records fields in global tables too. 3 | -- Probably works but not well tested. Could be extended even further. 4 | -- 5 | -- usage: luac -p -l example.lua | lua globalsplus.lua 6 | -- 7 | -- D.Manura, 2010-07, public domain 8 | 9 | local function parse(line) 10 | local idx,linenum,opname,arga,argb,extra = 11 | line:match('^%s+(%d+)%s+%[(%d+)%]%s+(%w+)%s+([-%d]+)%s+([-%d]+)%s*(.*)') 12 | if idx then 13 | idx = tonumber(idx) 14 | linenum = tonumber(linenum) 15 | arga = tonumber(arga) 16 | argb = tonumber(argb) 17 | end 18 | local argc, const 19 | if extra then 20 | local extra2 21 | argc, extra2 = extra:match('^([-%d]+)%s*(.*)') 22 | if argc then argc = tonumber(argc); extra = extra2 end 23 | end 24 | if extra then 25 | const = extra:match('^; (.+)') 26 | end 27 | return {idx=idx,linenum=linenum,opname=opname,arga=arga,argb=argb,argc=argc,const=const} 28 | end 29 | 30 | local function getglobals(fh) 31 | local globals = {} 32 | local last 33 | for line in fh:lines() do 34 | local data = parse(line) 35 | if data.opname == 'GETGLOBAL' then 36 | data.gname = data.const 37 | last = data 38 | table.insert(globals, {linenum=last.linenum, name=data.const, isset=false}) 39 | elseif data.opname == 'SETGLOBAL' then 40 | last = data 41 | table.insert(globals, {linenum=last.linenum, name=data.const, isset=true}) 42 | elseif (data.opname == 'GETTABLE' or data.opname == 'SETTABLE') and last and 43 | last.gname and last.idx == data.idx-1 and last.arga == data.arga and data.const 44 | then 45 | local const = data.const:match('^"(.*)"') 46 | if const then 47 | data.gname = last.gname .. '.' .. const 48 | last = data 49 | table.insert(globals, {linenum=last.linenum, name=data.gname, isset=data.opname=='SETTABLE'}) 50 | end 51 | else 52 | last = nil 53 | end 54 | end 55 | return globals 56 | end 57 | 58 | local function rindex(t, name) 59 | for part in name:gmatch('%w+') do 60 | t = t[part] 61 | if t == nil then return nil end 62 | end 63 | return t 64 | end 65 | 66 | local whitelist = _G 67 | 68 | local globals = getglobals(io.stdin) 69 | table.sort(globals, function(a,b) return a.linenum < b.linenum end) 70 | for i,v in ipairs(globals) do 71 | local found = rindex(whitelist, v.name) 72 | print(v.linenum, v.name, v.isset and 'set' or 'get', found and 'defined' or 'undefined') 73 | end 74 | -------------------------------------------------------------------------------- /lib/resty/mongol/gridfs.lua: -------------------------------------------------------------------------------- 1 | local mod_name = (...):match ( "^(.*)%..-$" ) 2 | 3 | local md5 = require "resty.md5" 4 | local str = require "resty.string" 5 | local bson = require ( mod_name .. ".bson" ) 6 | local object_id = require ( mod_name .. ".object_id" ) 7 | local gridfs_file= require ( mod_name .. ".gridfs_file" ) 8 | 9 | local gridfs_mt = { } 10 | local gridfs = { __index = gridfs_mt } 11 | local get_bin_data = bson.get_bin_data 12 | local get_utc_date = bson.get_utc_date 13 | 14 | function gridfs_mt:find_one(fields) 15 | local r = self.file_col:find_one(fields) 16 | if not r then return nil end 17 | 18 | return setmetatable({ 19 | file_col = self.file_col; 20 | chunk_col = self.chunk_col; 21 | chunk_size = r.chunkSize; 22 | files_id = r._id; 23 | file_size = r.length; 24 | file_md5 = r.md5; 25 | file_name = r.filename; 26 | }, gridfs_file) 27 | end 28 | 29 | function gridfs_mt:get(fh, fields) 30 | local f = self:find_one(fields) 31 | if not f then return nil, "file not found" end 32 | local r = fh:write(f:read()) 33 | return r 34 | end 35 | 36 | function gridfs_mt:remove(fields, continue_on_err, safe) 37 | local r, err 38 | local n = 0 39 | if fields == {} then 40 | r,err = self.chunk_col:delete({}, continue_on_err, safe) 41 | if not r then return nil, "remove chunks failed: "..err end 42 | r,err = self.file_col:delete({}, continue_on_err, safe) 43 | if not r then return nil, "remove files failed: "..err end 44 | return r 45 | end 46 | 47 | local cursor = self.file_col:find(fields, {_id=1}) 48 | for k,v in cursor:pairs() do 49 | r,err = self.chunk_col:delete({files_id=v._id}, continue_on_err, safe) 50 | if not r then return nil, "remove chunks failed: "..err end 51 | r,err = self.file_col:delete({_id=v._id}, continue_on_err, safe) 52 | if not r then return nil, "remove files failed: "..err end 53 | n = n + 1 54 | end 55 | return n 56 | end 57 | 58 | function gridfs_mt:new(meta) 59 | meta = meta or {} 60 | meta._id = meta._id or object_id.new() 61 | meta.chunkSize = meta.chunkSize or 256*1024 62 | meta.filename = meta.filename or meta._id:tostring() 63 | 64 | meta.md5 = 0 65 | meta.uploadDate = get_utc_date(ngx.time() * 1000) 66 | meta.length = 0 67 | local r, err = self.file_col:insert({meta}, nil, true) 68 | if not r then return nil, err end 69 | 70 | return setmetatable({ 71 | file_col = self.file_col; 72 | chunk_col = self.chunk_col; 73 | chunk_size = meta.chunkSize; 74 | files_id = meta._id; 75 | file_size = 0; 76 | file_md5 = 0; 77 | file_name = meta.filename; 78 | }, gridfs_file) 79 | end 80 | 81 | function gridfs_mt:insert(fh, meta, safe) 82 | meta = meta or {} 83 | meta.chunkSize = meta.chunkSize or 256*1024 84 | meta._id = meta._id or object_id.new() 85 | meta.filename = meta.filename or meta._id:tostring() 86 | 87 | local n = 0 88 | local length = 0 89 | local r, err 90 | local md5_obj = md5:new() 91 | while true do 92 | local bytes = fh:read(meta.chunkSize) 93 | if not bytes then break end 94 | 95 | md5_obj:update(bytes) 96 | r, err = self.chunk_col:insert({{ files_id = meta._id, 97 | n = n, 98 | data = get_bin_data(bytes), 99 | }}, nil, safe) 100 | if safe and not r then return nil, err end 101 | 102 | n = n + 1 103 | length = length + string.len(bytes) 104 | end 105 | local md5hex = str.to_hex(md5_obj:final()) 106 | 107 | meta.md5 = md5hex 108 | meta.uploadDate = get_utc_date(ngx.time() * 1000) 109 | meta.length = length 110 | r, err = self.file_col:insert({meta}, nil, safe) 111 | if safe and not r then return nil, err end 112 | return r 113 | end 114 | 115 | return gridfs 116 | -------------------------------------------------------------------------------- /lib/resty/mongol/gridfs_file.lua: -------------------------------------------------------------------------------- 1 | local mod_name = (...):match ( "^(.*)%..-$" ) 2 | 3 | local md5 = require "resty.md5" 4 | local str = require "resty.string" 5 | local bson = require ( mod_name .. ".bson" ) 6 | 7 | local gridfs_file_mt = { } 8 | local gridfs_file = { __index = gridfs_file_mt } 9 | local get_bin_data = bson.get_bin_data 10 | 11 | -- write size bytes from the buf string into mongo, by the offset 12 | function gridfs_file_mt:write(buf, offset, size) 13 | size = size or string.len(buf) 14 | if offset > self.file_size then return nil, "invalid offset" end 15 | if size > #buf then return nil, "invalid size" end 16 | 17 | local cn -- number of chunks to be updated 18 | local af -- number of bytes to be updated in first chunk 19 | local bn = 0 -- bytes number of buf already updated 20 | local nv = {} 21 | local od, t, i, r, err 22 | local of = offset % self.chunk_size 23 | local n = math.floor(offset/self.chunk_size) 24 | 25 | if of == 0 and size % self.chunk_size == 0 then 26 | -- chunk1 chunk2 chunk3 27 | -- old data ====== ====== ====== 28 | -- write buf ====== ====== 29 | -- 30 | -- old data ====== ====== ====== 31 | -- write buf ====== 32 | 33 | cn = size/self.chunk_size 34 | for i = 1, cn do 35 | nv["$set"] = {data = get_bin_data(string.sub(buf, 36 | self.chunk_size*(i-1) + 1, 37 | self.chunk_size*(i-1) + self.chunk_size))} 38 | r, err = self.chunk_col:update({files_id = self.files_id, 39 | n = n+i-1}, nv, 1, 0, true) 40 | if not r then return nil,"write failed: "..err end 41 | end 42 | bn = size 43 | else 44 | 45 | if of + size > self.chunk_size then 46 | -- chunk1 chunk2 chunk3 47 | -- old data ====== ====== ====== 48 | -- write buf ======= 49 | -- ... -> of 50 | -- ... -> af 51 | af = self.chunk_size - of 52 | else 53 | af = size 54 | end 55 | 56 | cn = math.ceil((size + offset)/self.chunk_size) - n 57 | for i = 1, cn do 58 | if i == 1 then 59 | od = self.chunk_col:find_one( 60 | {files_id = self.files_id, n = n+i-1}) 61 | if of ~= 0 and od then 62 | if size + of >= self.chunk_size then 63 | -- chunk1 chunk2 chunk3 64 | -- old data ====== ====== ====== 65 | -- write buf ===== 66 | t = string.sub(od.data, 1, of) 67 | .. string.sub(buf, 1, af) 68 | else 69 | -- chunk1 chunk2 chunk3 70 | -- old data ====== ====== ====== 71 | -- write buf == 72 | t = string.sub(od.data, 1, of) 73 | .. string.sub(buf, 1, af) 74 | .. string.sub(od.data, size + of + 1) 75 | end 76 | bn = af 77 | elseif of == 0 and od then 78 | if size < self.chunk_size then 79 | -- chunk1 chunk2 chunk3 80 | -- old data ====== ====== ====== 81 | -- write buf === 82 | t = string.sub(buf, 1) 83 | .. string.sub(od.data, size + 1) 84 | bn = bn + size 85 | else 86 | -- chunk1 chunk2 chunk3 87 | -- old data ====== ====== ====== 88 | -- write buf ========= 89 | t = string.sub(buf, 1, self.chunk_size) 90 | bn = bn + self.chunk_size 91 | end 92 | else 93 | t = string.sub(buf, 1, self.chunk_size) 94 | bn = bn + #t --self.chunk_size 95 | end 96 | nv["$set"] = {data = get_bin_data(t)} 97 | r,err = self.chunk_col:update({files_id = self.files_id, 98 | n = n+i-1}, nv, 1, 0, true) 99 | if not r then return nil,"write failed: "..err end 100 | elseif i == cn then 101 | od = self.chunk_col:find_one( 102 | {files_id = self.files_id, n = n + i - 1} 103 | ) 104 | if od then 105 | t = string.sub(buf, bn + 1, size) 106 | .. string.sub(od.data, size - bn + 1) 107 | else 108 | t = string.sub(buf, bn + 1, size) 109 | end 110 | nv["$set"] = {data = get_bin_data(t)} 111 | r,err = self.chunk_col:update({files_id = self.files_id, 112 | n = n+i-1}, nv, 1, 0, true) 113 | if not r then return nil,"write failed: "..err end 114 | bn = size 115 | else 116 | nv["$set"] = {data = get_bin_data(string.sub(buf, 117 | bn + 1, bn + self.chunk_size))} 118 | r,err = self.chunk_col:update({files_id = self.files_id, 119 | n = n+i-1}, nv, 1, 0, true) 120 | if not r then return nil,"write failed: "..err end 121 | bn = bn + self.chunk_size 122 | end 123 | end 124 | end 125 | 126 | local nf = offset + bn 127 | if nf > self.file_size then 128 | nv["$set"] = {length = nf} 129 | r,err = self.file_col:update({_id = self.files_id},nv, 130 | 0, 0, true) 131 | if not r then return nil,"write failed: "..err end 132 | end 133 | 134 | nv["$set"] = {md5 = 0} 135 | r,err = self.file_col:update({_id = self.files_id},nv, 136 | 0, 0, true) 137 | if not r then return nil,"write failed: "..err end 138 | return bn 139 | end 140 | 141 | -- read size bytes from mongo by the offset 142 | function gridfs_file_mt:read(size, offset) 143 | size = size or self.file_size 144 | if size < 0 then 145 | return nil, "invalid size" 146 | end 147 | offset = offset or 0 148 | if offset < 0 or offset >= self.file_size then 149 | return nil, "invalid offset" 150 | end 151 | 152 | local n = math.floor(offset / self.chunk_size) 153 | local r 154 | local bytes = "" 155 | local rn = 0 156 | while true do 157 | r = self.chunk_col:find_one({files_id = self.files_id, n = n}) 158 | if not r then return nil, "read chunk failed" end 159 | if size - rn < self.chunk_size then 160 | -- bytes = bytes .. string.sub(r.data, 1, size - rn) 161 | -- fixed by hehr@aispeech 162 | bytes = bytes .. string.sub(r.data, offset + 1, offset + size - rn) 163 | rn = size 164 | else 165 | bytes = bytes .. r.data 166 | rn = rn + self.chunk_size 167 | end 168 | n = n + 1 169 | if rn >= size then break end 170 | end 171 | return bytes 172 | end 173 | 174 | function gridfs_file_mt:update_md5() 175 | local n = math.floor(self.file_size/self.chunk_size) 176 | local md5_obj = md5:new() 177 | local r, i, err 178 | 179 | for i = 0, n do 180 | r = self.chunk_col:find_one({files_id = self.files_id, n = i}) 181 | if not r then return false, "read chunk failed" end 182 | 183 | md5_obj:update(r.data) 184 | end 185 | local md5hex = str.to_hex(md5_obj:final()) 186 | 187 | local nv = {} 188 | nv["$set"] = {md5 = md5hex} 189 | self.file_md5 = md5hex 190 | r,err = self.file_col:update({_id = self.files_id}, nv, 0, 0, true) 191 | if not r then return false, "update failed: "..err end 192 | return true 193 | end 194 | 195 | return gridfs_file 196 | -------------------------------------------------------------------------------- /lib/resty/mongol/init.lua: -------------------------------------------------------------------------------- 1 | module("resty.mongol", package.seeall) 2 | 3 | local mod_name = (...) 4 | 5 | local assert , pcall = assert , pcall 6 | local ipairs , pairs = ipairs , pairs 7 | local setmetatable = setmetatable 8 | 9 | local socket = ngx.socket.tcp 10 | 11 | local connmethods = { } 12 | local connmt = { __index = connmethods } 13 | 14 | local dbmt = require ( mod_name .. ".dbmt" ) 15 | 16 | function connmethods:ismaster() 17 | local db = self:new_db_handle("admin") 18 | local r, err = db:cmd({ismaster = true}) 19 | if not r then 20 | return nil, err 21 | end 22 | return r.ismaster, r.hosts 23 | end 24 | 25 | local function parse_host ( str ) 26 | local host , port = str:match ( "([^:]+):?(%d*)" ) 27 | port = port or 27017 28 | return host , port 29 | end 30 | 31 | function connmethods:getprimary ( searched ) 32 | searched = searched or { [ self.host .. ":" .. self.port ] = true } 33 | 34 | local db = self:new_db_handle("admin") 35 | local r, err = db:cmd({ ismaster = true }) 36 | if not r then 37 | return nil, "query admin failed: "..err 38 | elseif r.ismaster then return self 39 | else 40 | for i , v in ipairs ( r.hosts ) do 41 | if not searched[v] then 42 | searched[v] = true 43 | local host, port = parse_host(v) 44 | local conn = new() 45 | local ok, err = conn:connect(host, port) 46 | if not ok then 47 | return nil, "connect failed: "..err..v 48 | end 49 | 50 | local found = conn:getprimary(searched) 51 | if found then 52 | return found 53 | end 54 | end 55 | end 56 | end 57 | return nil , "No master server found" 58 | end 59 | 60 | function connmethods:databases() 61 | local db = self:new_db_handle("admin") 62 | local r = assert ( db:cmd({ listDatabases = true } )) 63 | return r.databases 64 | end 65 | 66 | function connmethods:shutdown() 67 | local db = self:new_db_handle("admin") 68 | db:cmd({ shutdown = true } ) 69 | end 70 | 71 | function connmethods:new_db_handle ( db ) 72 | if not db then 73 | return nil 74 | end 75 | 76 | return setmetatable ( { 77 | conn = self ; 78 | db = db ; 79 | } , dbmt ) 80 | end 81 | 82 | function connmethods:set_timeout(timeout) 83 | local sock = self.sock 84 | if not sock then 85 | return nil, "not initialized" 86 | end 87 | 88 | return sock:settimeout(timeout) 89 | end 90 | 91 | function connmethods:set_keepalive(...) 92 | local sock = self.sock 93 | if not sock then 94 | return nil, "not initialized" 95 | end 96 | 97 | return sock:setkeepalive(...) 98 | end 99 | 100 | function connmethods:get_reused_times() 101 | local sock = self.sock 102 | if not sock then 103 | return nil, "not initialized" 104 | end 105 | 106 | return sock:getreusedtimes() 107 | end 108 | 109 | function connmethods:connect(host, port) 110 | self.host = host or self.host 111 | self.port = port or self.port 112 | local sock = self.sock 113 | 114 | return sock:connect(self.host, self.port) 115 | end 116 | 117 | function connmethods:close() 118 | local sock = self.sock 119 | if not sock then 120 | return nil, "not initialized" 121 | end 122 | 123 | return sock:close() 124 | end 125 | 126 | connmt.__call = connmethods.new_db_handle 127 | 128 | function new(self) 129 | return setmetatable ( { 130 | sock = socket(); 131 | host = "localhost"; 132 | port = 27017; 133 | } , connmt ) 134 | end 135 | 136 | -- to prevent use of casual module global variables 137 | getmetatable(resty.mongol).__newindex = function (table, key, val) 138 | error('attempt to write to undeclared variable "' .. key .. '": ' 139 | .. debug.traceback()) 140 | end 141 | -------------------------------------------------------------------------------- /lib/resty/mongol/ll.lua: -------------------------------------------------------------------------------- 1 | -- Library for reading low level data 2 | 3 | local assert = assert 4 | local unpack = unpack 5 | local floor = math.floor 6 | local strbyte , strchar = string.byte , string.char 7 | 8 | local ll = { } 9 | 10 | local le_uint_to_num = function ( s , i , j ) 11 | i , j = i or 1 , j or #s 12 | local b = { strbyte ( s , i , j ) } 13 | local n = 0 14 | for i=#b , 1 , -1 do 15 | n = n*2^8 + b [ i ] 16 | end 17 | return n 18 | end 19 | local le_int_to_num = function ( s , i , j ) 20 | i , j = i or 1 , j or #s 21 | local n = le_uint_to_num ( s , i , j ) 22 | local overflow = 2^(8*(j-i) + 7) 23 | if n > 2^overflow then 24 | n = - ( n % 2^overflow ) 25 | end 26 | return n 27 | end 28 | local num_to_le_uint = function ( n , bytes ) 29 | bytes = bytes or 4 30 | local b = { } 31 | for i=1 , bytes do 32 | b [ i ] , n = n % 2^8 , floor ( n / 2^8 ) 33 | end 34 | return strchar ( unpack ( b ) ) 35 | end 36 | local num_to_le_int = function ( n , bytes ) 37 | return num_to_le_uint ( n , bytes ) 38 | end 39 | 40 | local be_uint_to_num = function ( s , i , j ) 41 | i , j = i or 1 , j or #s 42 | local b = { strbyte ( s , i , j ) } 43 | local n = 0 44 | for i=1 , #b do 45 | n = n*2^8 + b [ i ] 46 | end 47 | return n 48 | end 49 | local num_to_be_uint = function ( n , bytes ) 50 | bytes = bytes or 4 51 | local b = { } 52 | for i=bytes , 1 , -1 do 53 | b [ i ] , n = n % 2^8 , floor ( n / 2^8 ) 54 | end 55 | assert ( n == 0 ) 56 | return strchar ( unpack ( b ) ) 57 | end 58 | 59 | -- Returns (as a number); bits i to j (indexed from 0) 60 | local extract_bits = function ( s , i , j ) 61 | j = j or i 62 | local i_byte = floor ( i / 8 ) + 1 63 | local j_byte = floor ( j / 8 ) + 1 64 | 65 | local n = be_uint_to_num ( s , i_byte , j_byte ) 66 | n = n % 2^( j_byte*8 - i ) 67 | n = floor ( n / 2^( (-(j+1) ) % 8 ) ) 68 | return n 69 | end 70 | 71 | -- Look at ith bit in given string (indexed from 0) 72 | -- Returns boolean 73 | local le_bpeek = function ( s , bitnum ) 74 | local byte = floor ( bitnum / 8 ) + 1 75 | local bit = bitnum % 8 76 | local char = strbyte ( s , byte ) 77 | return floor ( ( char % 2^(bit+1) ) / 2^bit ) == 1 78 | end 79 | -- Test with: 80 | --local sum = 0 for i=0,31 do v = le_bpeek ( num_to_le_uint ( N , 4 ) , i ) sum=sum + ( v and 1 or 0 )*2^i end assert ( sum == N ) 81 | 82 | local be_bpeek = function ( s , bitnum ) 83 | local byte = floor ( bitnum / 8 ) + 1 84 | local bit = 7-bitnum % 8 85 | local char = strbyte ( s , byte ) 86 | return floor ( ( char % 2^(bit+1) ) / 2^bit ) == 1 87 | end 88 | -- Test with: 89 | --local sum = 0 for i=0,31 do v = be_bpeek ( num_to_be_uint ( N , 4 ) , i ) sum=sum + ( v and 1 or 0 )*2^(31-i) end assert ( sum == N ) 90 | 91 | 92 | local hasffi , ffi = pcall ( require , "ffi" ) 93 | local to_double , from_double 94 | do 95 | local s , e , d 96 | if hasffi then 97 | d = ffi.new ( "double[1]" ) 98 | else 99 | -- Can't use with to_double as we can't strip debug info :( 100 | d = string.dump ( loadstring ( [[return 523123.123145345]] ) ) 101 | s , e = d:find ( "\3\54\208\25\126\204\237\31\65" ) 102 | s = d:sub ( 1 , s ) 103 | e = d:sub ( e+1 , -1 ) 104 | end 105 | 106 | function to_double ( n ) 107 | if hasffi then 108 | d [ 0 ] = n 109 | return ffi.string ( d , 8 ) 110 | else 111 | -- Should be the 8 bytes following the second \3 (LUA_TSTRING == '\3') 112 | local str = string.dump ( loadstring ( [[return ]] .. n ) ) 113 | local loc , en , mat = str:find ( "\3(........)" , str:find ( "\3" ) + 1 ) 114 | return mat 115 | end 116 | end 117 | function from_double ( str ) 118 | assert ( #str == 8 ) 119 | if hasffi then 120 | ffi.copy ( d , str , 8 ) 121 | return d [ 0 ] 122 | else 123 | str = s .. str .. e 124 | return loadstring ( str ) ( ) 125 | end 126 | end 127 | end 128 | 129 | return { 130 | le_uint_to_num = le_uint_to_num ; 131 | le_int_to_num = le_int_to_num ; 132 | num_to_le_uint = num_to_le_uint ; 133 | num_to_le_int = num_to_le_int ; 134 | 135 | be_uint_to_num = be_uint_to_num ; 136 | num_to_be_uint = num_to_be_uint ; 137 | 138 | extract_bits = extract_bits ; 139 | 140 | le_bpeek = le_bpeek ; 141 | be_bpeek = be_bpeek ; 142 | 143 | to_double = to_double ; 144 | from_double = from_double ; 145 | } 146 | -------------------------------------------------------------------------------- /lib/resty/mongol/misc.lua: -------------------------------------------------------------------------------- 1 | local mod_name = (...):match ( "^(.*)%..-$" ) 2 | 3 | local ll = require ( mod_name .. ".ll" ) 4 | local num_to_le_uint = ll.num_to_le_uint 5 | local num_to_le_int = ll.num_to_le_int 6 | local le_uint_to_num = ll.le_uint_to_num 7 | local le_bpeek = ll.le_bpeek 8 | 9 | 10 | local getmetatable , setmetatable = getmetatable , setmetatable 11 | local pairs = pairs 12 | local next = next 13 | 14 | do 15 | -- Test to see if __pairs is natively supported 16 | local supported = false 17 | local test = setmetatable ( { } , { __pairs = function ( ) supported = true end } ) 18 | pairs ( test ) 19 | if not supported then 20 | _G.pairs = function ( t ) 21 | local mt = getmetatable ( t ) 22 | if mt then 23 | local f = mt.__pairs 24 | if f then 25 | return f ( t ) 26 | end 27 | end 28 | return pairs ( t ) 29 | end 30 | -- Confirm we added it 31 | _G.pairs ( test ) 32 | assert ( supported ) 33 | end 34 | end 35 | 36 | 37 | local pairs_start = function ( t , sk ) 38 | local i = 0 39 | return function ( t , k , v ) 40 | i = i + 1 41 | local nk, nv 42 | if i == 1 then 43 | return sk, t[sk] 44 | elseif i == 2 then 45 | nk, nv = next(t) 46 | else 47 | nk, nv = next(t, k) 48 | end 49 | return nk,nv 50 | end , t 51 | end 52 | 53 | local function attachpairs_start ( o , k ) 54 | local mt = getmetatable ( o ) 55 | if not mt then 56 | mt = { } 57 | setmetatable ( o , mt ) 58 | end 59 | mt.__pairs = function ( t ) 60 | return pairs_start ( t , k ) 61 | end 62 | return o 63 | end 64 | 65 | return { 66 | pairs_start = pairs_start ; 67 | attachpairs_start = attachpairs_start ; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /lib/resty/mongol/object_id.lua: -------------------------------------------------------------------------------- 1 | local mod_name = (...):match ( "^(.*)%..-$" ) 2 | 3 | local setmetatable = setmetatable 4 | local strbyte = string.byte 5 | local strformat = string.format 6 | local t_insert = table.insert 7 | local t_concat = table.concat 8 | 9 | local hasposix , posix = pcall ( require , "posix" ) 10 | 11 | local ll = require ( mod_name .. ".ll" ) 12 | local num_to_le_uint = ll.num_to_le_uint 13 | local num_to_be_uint = ll.num_to_be_uint 14 | 15 | local function _tostring(ob) 16 | local t = {} 17 | for i = 1 , 12 do 18 | t_insert(t, strformat("%02x", strbyte(ob.id, i, i))) 19 | end 20 | return t_concat(t) 21 | end 22 | 23 | local function _get_ts(ob) 24 | return ll.be_uint_to_num(ob.id, 1, 4) 25 | end 26 | 27 | local function _get_hostname(ob) 28 | local t = {} 29 | for i = 5, 7 do 30 | t_insert(t, strformat("%02x", strbyte(ob.id, i, i))) 31 | end 32 | return t_concat(t) 33 | end 34 | 35 | local function _get_pid(ob) 36 | return ll.be_uint_to_num(ob.id, 8, 9) 37 | end 38 | 39 | local function _get_inc(ob) 40 | return ll.be_uint_to_num(ob.id, 10, 12) 41 | end 42 | 43 | local object_id_mt = { 44 | __tostring = _tostring; 45 | __eq = function ( a , b ) return a.id == b.id end ; 46 | __lt = function ( a , b ) return a.id < b.id end ; 47 | __le = function ( a , b ) return a.id <= b.id end ; 48 | } 49 | 50 | local machineid 51 | if hasposix then 52 | machineid = posix.uname("%n") 53 | else 54 | machineid = assert(io.popen("uname -n")):read("*l") 55 | end 56 | machineid = ngx.md5_bin(machineid):sub(1, 3) 57 | 58 | local pid = num_to_le_uint(bit.band(ngx.worker.pid(), 0xFFFF), 2) 59 | 60 | local inc = 0 61 | local function generate_id ( ) 62 | inc = inc + 1 63 | -- "A BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Note that the timestamp and counter fields must be stored big endian unlike the rest of BSON" 64 | inc = inc < 2^24 and inc or (2^24 - inc + 1) 65 | return num_to_be_uint ( os.time ( ) , 4 ) .. machineid .. pid .. num_to_be_uint ( inc , 3 ) 66 | end 67 | 68 | local function new_object_id(str) 69 | if str then 70 | assert(#str == 12) 71 | else 72 | str = generate_id() 73 | end 74 | return setmetatable({id = str, 75 | tostring = _tostring, 76 | get_ts = _get_ts, 77 | get_pid = _get_pid, 78 | get_hostname = _get_hostname, 79 | get_inc = _get_inc, 80 | } , object_id_mt) 81 | end 82 | 83 | return { 84 | new = new_object_id ; 85 | metatable = object_id_mt ; 86 | } 87 | -------------------------------------------------------------------------------- /t/cluster.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(1); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?/init.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MONGO_PORT} ||= 27017; 18 | 19 | no_long_string(); 20 | #no_diff(); 21 | 22 | run_tests(); 23 | 24 | __DATA__ 25 | 26 | === TEST 2: db auth failed 27 | --- http_config eval: $::HttpConfig 28 | --- config 29 | location /t { 30 | content_by_lua ' 31 | local mongo = require "resty.mongol" 32 | conn = mongo:new() 33 | conn:set_timeout(1000) 34 | 35 | ok, err = conn:connect("127.0.0.1") 36 | if not ok then 37 | ngx.say("connect failed: "..err) 38 | end 39 | 40 | local db = conn:new_db_handle("test") 41 | local r,err = db:auth("admin", "pass") 42 | if not r then ngx.say(err) 43 | else 44 | ngx.say(r) 45 | end 46 | '; 47 | } 48 | --- request 49 | GET /t 50 | --- response_body 51 | auth fails 52 | --- no_error_log 53 | [error] 54 | 55 | === TEST 5: is master 56 | --- http_config eval: $::HttpConfig 57 | --- config 58 | location /t { 59 | content_by_lua ' 60 | local mongo = require "resty.mongol" 61 | conn = mongo:new() 62 | conn:set_timeout(1000) 63 | 64 | ok, err = conn:connect("127.0.0.1") 65 | if not ok then 66 | ngx.say("connect failed: "..err) 67 | end 68 | 69 | r, h = conn:ismaster() 70 | if not r then 71 | ngx.say("query master failed: "..h) 72 | end 73 | 74 | ngx.say(r) 75 | for i,v in pairs(h) do 76 | ngx.say(v) 77 | end 78 | conn:close() 79 | '; 80 | } 81 | --- request 82 | GET /t 83 | --- response_body_like 84 | true 85 | --- no_error_log 86 | [error] 87 | 88 | === TEST 6: is not master 89 | --- http_config eval: $::HttpConfig 90 | --- config 91 | location /t { 92 | content_by_lua ' 93 | local mongo = require "resty.mongol" 94 | conn = mongo:new() 95 | conn:set_timeout(1000) 96 | 97 | ok, err = conn:connect("127.0.0.1", 27018) 98 | if not ok then 99 | ngx.say("connect failed: "..err) 100 | end 101 | 102 | r, h = conn:ismaster() 103 | if r == nil then 104 | ngx.say("query master failed: "..h) 105 | end 106 | 107 | ngx.say(r) 108 | for i,v in pairs(h) do 109 | ngx.say(v) 110 | end 111 | conn:close() 112 | '; 113 | } 114 | --- request 115 | GET /t 116 | --- response_body_like 117 | false 118 | --- no_error_log 119 | [error] 120 | 121 | === TEST 7: get primary 122 | --- http_config eval: $::HttpConfig 123 | --- config 124 | location /t { 125 | content_by_lua ' 126 | local mongo = require "resty.mongol" 127 | conn = mongo:new() 128 | conn:set_timeout(1000) 129 | 130 | ok, err = conn:connect("127.0.0.1", 27018) 131 | if not ok then 132 | ngx.say("connect failed: "..err) 133 | end 134 | 135 | r, h = conn:ismaster() 136 | if r == nil then 137 | ngx.say("query master failed: "..h) 138 | end 139 | 140 | if r then ngx.say("already master") return end 141 | 142 | newconn,err = conn:getprimary() 143 | if not newconn then 144 | ngx.say("get primary failed: "..err) 145 | end 146 | r, h = newconn:ismaster() 147 | if not r then 148 | ngx.say("get master failed") 149 | end 150 | 151 | ngx.say("get primary") 152 | conn:close() 153 | '; 154 | } 155 | --- request 156 | GET /t 157 | --- response_body 158 | get primary 159 | --- no_error_log 160 | [error] 161 | 162 | === TEST 8: db auth 163 | --- http_config eval: $::HttpConfig 164 | --- config 165 | lua_code_cache off; 166 | location /t { 167 | content_by_lua ' 168 | local mongo = require "resty.mongol" 169 | conn = mongo:new() 170 | conn:set_timeout(1000) 171 | 172 | ok, err = conn:connect("127.0.0.1") 173 | if not ok then 174 | ngx.say("connect failed: "..err) 175 | end 176 | 177 | local db = conn:new_db_handle("test") 178 | local r,err = db:auth("admin", "admin") 179 | if not r then ngx.say("auth failed") end 180 | ngx.say(r) 181 | '; 182 | } 183 | --- request 184 | GET /t 185 | --- response_body 186 | 1 187 | --- no_error_log 188 | [error] 189 | 190 | 191 | -------------------------------------------------------------------------------- /t/cursor.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(1); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?/init.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MONGO_PORT} ||= 27017; 18 | $ENV{TEST_NGINX_TIMEOUT} = 10000; 19 | 20 | no_long_string(); 21 | #no_diff(); 22 | 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: cursor limit 28 | --- http_config eval: $::HttpConfig 29 | --- config 30 | location /t { 31 | content_by_lua ' 32 | local mongo = require "resty.mongol" 33 | conn = mongo:new() 34 | conn:set_timeout(10000) 35 | local ok, err = conn:connect("127.0.0.1") 36 | 37 | if not ok then 38 | ngx.say("connect failed: "..err) 39 | end 40 | 41 | local db = conn:new_db_handle("test") 42 | local r = db:auth("admin", "admin") 43 | if not r then ngx.say("auth failed") end 44 | local col = db:get_col("test") 45 | 46 | r, err = col:delete({}, nil, true) 47 | if not r then ngx.say("delete failed: "..err) end 48 | 49 | local i, j 50 | local t = {} 51 | for i = 1,10 do 52 | j = 100 - i 53 | table.insert(t, {name="dog",n=i,m=j}) 54 | end 55 | r, err = col:insert(t, nil, true) 56 | if not r then ngx.say("insert failed: "..err) end 57 | 58 | r = col:find({name="dog"}) 59 | r:limit(3) 60 | for i , v in r:pairs() do 61 | ngx.say(v["n"]) 62 | end 63 | 64 | r = col:find({name="dog"}, nil, 0) 65 | r:limit(3) 66 | for i , v in r:pairs() do 67 | ngx.say(v["n"]) 68 | end 69 | 70 | r = col:find({name="dog"}, nil, 2) 71 | r:limit(5) 72 | for i , v in r:pairs() do 73 | ngx.say(v["n"]) 74 | end 75 | conn:close() 76 | '; 77 | } 78 | --- request 79 | GET /t 80 | --- response_body 81 | 1 82 | 2 83 | 3 84 | 1 85 | 2 86 | 3 87 | 1 88 | 2 89 | --- no_error_log 90 | [error] 91 | 92 | === TEST 2: cursor sort 93 | --- http_config eval: $::HttpConfig 94 | --- config 95 | location /t { 96 | content_by_lua ' 97 | local mongo = require "resty.mongol" 98 | conn = mongo:new() 99 | conn:set_timeout(10000) 100 | local ok, err = conn:connect("127.0.0.1") 101 | 102 | if not ok then 103 | ngx.say("connect failed: "..err) 104 | end 105 | 106 | local db = conn:new_db_handle("test") 107 | local r = db:auth("admin", "admin") 108 | if not r then ngx.say("auth failed") end 109 | local col = db:get_col("test") 110 | 111 | r, err = col:delete({}, nil, true) 112 | if not r then ngx.say("delete failed: "..err) end 113 | 114 | local i, j 115 | local t = {} 116 | for i = 1,10 do 117 | j = 10 - i 118 | table.insert(t, {name="dog",n=i,m=j}) 119 | end 120 | r, err = col:insert(t, nil, true) 121 | if not r then ngx.say("insert failed: "..err) end 122 | 123 | r = col:find({name="dog"}, nil, 5) 124 | local ret,err = r:sort({n=-1}) 125 | if not ret then ngx.say("sort failed: "..err) 126 | ngx.exit(ngx.HTTP_OK) end 127 | for i , v in pairs(ret) do 128 | ngx.say(v["n"]) 129 | end 130 | 131 | r = col:find({name="dog"}, nil, 5) 132 | ret,err = r:sort({n=1}) 133 | if not ret then ngx.say("sort failed: "..err) 134 | ngx.exit(ngx.HTTP_OK) end 135 | for i , v in pairs(ret) do 136 | ngx.say(v["n"]) 137 | end 138 | 139 | r = col:find({name="dog"}, nil, 5) 140 | r:limit(3) 141 | ret,err = r:sort({n=-1}) 142 | if not ret then ngx.say("sort failed: "..err) 143 | ngx.exit(ngx.HTTP_OK) end 144 | for i , v in pairs(ret) do 145 | ngx.say(v["n"]) 146 | end 147 | 148 | r = col:find({name="dog"}, nil, 3) 149 | r:limit(5) 150 | ret,err = r:sort({n=1}) 151 | if not ret then ngx.say("sort failed: "..err) 152 | ngx.exit(ngx.HTTP_OK) end 153 | for i , v in pairs(ret) do 154 | ngx.say(v["n"]) 155 | end 156 | 157 | conn:close() 158 | '; 159 | } 160 | --- request 161 | GET /t 162 | --- response_body 163 | 5 164 | 4 165 | 3 166 | 2 167 | 1 168 | 1 169 | 2 170 | 3 171 | 4 172 | 5 173 | 3 174 | 2 175 | 1 176 | 1 177 | 2 178 | 3 179 | --- no_error_log 180 | [error] 181 | 182 | === TEST 3: cursor sort after next 183 | --- http_config eval: $::HttpConfig 184 | --- config 185 | location /t { 186 | content_by_lua ' 187 | local mongo = require "resty.mongol" 188 | conn = mongo:new() 189 | conn:set_timeout(10000) 190 | local ok, err = conn:connect("127.0.0.1") 191 | 192 | if not ok then 193 | ngx.say("connect failed: "..err) 194 | end 195 | 196 | local db = conn:new_db_handle("test") 197 | local r = db:auth("admin", "admin") 198 | if not r then ngx.say("auth failed") end 199 | local col = db:get_col("test") 200 | 201 | r, err = col:delete({}, nil, true) 202 | if not r then ngx.say("delete failed: "..err) end 203 | 204 | local i, j 205 | local t = {} 206 | for i = 1,10 do 207 | j = 10 - i 208 | --r, err = col:insert({{name="dog",n=i,m=j}}, nil, true) 209 | --if not r then ngx.say("insert failed: "..err) end 210 | table.insert(t, {name="dog",n=i,m=j}) 211 | end 212 | r, err = col:insert(t, nil, true) 213 | if not r then ngx.say("insert failed: "..err) end 214 | 215 | r = col:find({name="dog"}, nil, 5) 216 | r:limit(5) 217 | for k,v in r:pairs() do 218 | ngx.say(v["n"]) 219 | break 220 | end 221 | 222 | local ret,err = r:sort({n=1}) 223 | if not ret then ngx.say("sort failed: "..err) 224 | ngx.exit(ngx.HTTP_OK) end 225 | for i , v in pairs(ret) do 226 | ngx.say(v["n"]) 227 | end 228 | 229 | for k,v in r:pairs() do 230 | ngx.say(v["n"]) 231 | end 232 | local ret,err = r:sort({n=-1}) 233 | if not ret then ngx.say("sort failed: "..err) 234 | ngx.exit(ngx.HTTP_OK) end 235 | for i , v in pairs(ret) do 236 | ngx.say(v["n"]) 237 | end 238 | conn:close() 239 | '; 240 | } 241 | --- timeout: 50 242 | --- request 243 | GET /t 244 | --- response_body 245 | 1 246 | 2 247 | 3 248 | 4 249 | 5 250 | 2 251 | 3 252 | 4 253 | 5 254 | sort failed: sort must be an array 255 | --- no_error_log 256 | [error] 257 | 258 | === TEST 4: cursor next over limit 259 | --- http_config eval: $::HttpConfig 260 | --- config 261 | location /t { 262 | content_by_lua ' 263 | local mongo = require "resty.mongol" 264 | conn = mongo:new() 265 | conn:set_timeout(10000) 266 | local ok, err = conn:connect("127.0.0.1") 267 | 268 | if not ok then 269 | ngx.say("connect failed: "..err) 270 | end 271 | 272 | local db = conn:new_db_handle("test") 273 | local r = db:auth("admin", "admin") 274 | if not r then ngx.say("auth failed") end 275 | local col = db:get_col("test") 276 | 277 | r, err = col:delete({}, nil, true) 278 | if not r then ngx.say("delete failed: "..err) end 279 | 280 | local i, j 281 | local t = {} 282 | for i = 1,10 do 283 | j = 100 - i 284 | table.insert(t, {name="dog",n=i,m=j}) 285 | end 286 | r, err = col:insert(t, nil, true) 287 | if not r then ngx.say("insert failed: "..err) end 288 | 289 | r = col:find({name="dog"}) 290 | r:limit(3) 291 | for i , v in r:pairs() do 292 | ngx.say(v["n"]) 293 | end 294 | 295 | k,v = r:next() 296 | ngx.say(v) 297 | 298 | r = col:find({name="dog"}) 299 | for i = 1, 10 do 300 | k,v = r:next() 301 | ngx.say(v["n"]) 302 | end 303 | 304 | i,v = r:next() 305 | ngx.say(v) 306 | 307 | conn:close() 308 | '; 309 | } 310 | --- request 311 | GET /t 312 | --- response_body 313 | 1 314 | 2 315 | 3 316 | nil 317 | 1 318 | 2 319 | 3 320 | 4 321 | 5 322 | 6 323 | 7 324 | 8 325 | 9 326 | 10 327 | nil 328 | --- no_error_log 329 | [error] 330 | 331 | -------------------------------------------------------------------------------- /t/gridfs.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(1); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?/init.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MONGO_PORT} ||= 27017; 18 | $ENV{TEST_NGINX_TIMEOUT} = 10000; 19 | 20 | no_long_string(); 21 | #no_diff(); 22 | 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: insert and remove one and get file < chunksize 28 | --- http_config eval: $::HttpConfig 29 | --- config 30 | location /t { 31 | content_by_lua ' 32 | local mongo = require "resty.mongol" 33 | conn = mongo:new() 34 | conn:set_timeout(10000) 35 | local ok, err = conn:connect("127.0.0.1") 36 | 37 | if not ok then 38 | ngx.say("connect failed: "..err) 39 | end 40 | 41 | local db = conn:new_db_handle("test") 42 | local r = db:auth("admin", "admin") 43 | if not r then ngx.say("auth failed") end 44 | local fs = db:get_gridfs("fs") 45 | 46 | r, err = fs:remove({}, nil, true) 47 | if not r then ngx.say("delete failed: "..err) end 48 | 49 | local f,err = io.open("Readme.md", "rb") 50 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 51 | r, err = fs:insert(f, nil, true) 52 | if not r then ngx.say("fs insert failed: "..err) end 53 | ngx.say(r) 54 | io.close(f) 55 | 56 | r, err = fs:remove({}, nil, true) 57 | if not r then ngx.say("delete failed: "..err) end 58 | ngx.say(r) 59 | 60 | local f,err = io.open("Readme.md", "rb") 61 | r, err = fs:insert(f, {filename="testfile"}, false) 62 | if not r then ngx.say("fs insert failed: "..err) end 63 | ngx.say(r) 64 | io.close(f) 65 | 66 | f = io.open("/tmp/testfile", "wb") 67 | r = fs:get(f, {filename="testfile"}) 68 | if not r then ngx.say("get file failed: "..err) end 69 | io.close(f) 70 | '; 71 | } 72 | --- request 73 | GET /t 74 | --- response_body 75 | 0 76 | 1 77 | -1 78 | --- no_error_log 79 | [error] 80 | 81 | === TEST 2: insert and get file > chunksize 82 | --- http_config eval: $::HttpConfig 83 | --- config 84 | location /t { 85 | content_by_lua ' 86 | local mongo = require "resty.mongol" 87 | conn = mongo:new() 88 | conn:set_timeout(10000) 89 | local ok, err = conn:connect("127.0.0.1") 90 | 91 | if not ok then 92 | ngx.say("connect failed: "..err) 93 | end 94 | 95 | local db = conn:new_db_handle("test") 96 | local r = db:auth("admin", "admin") 97 | if not r then ngx.say("auth failed") end 98 | local fs = db:get_gridfs("fs") 99 | 100 | r, err = fs:remove({}, nil, true) 101 | if not r then ngx.say("delete failed: "..err) end 102 | 103 | local f,err = io.open("Readme.md", "rb") 104 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 105 | r, err = fs:insert(f, {chunkSize = 256, filename="testfile"}, true) 106 | if not r then ngx.say("fs insert failed: "..err) end 107 | ngx.say(r) 108 | io.close(f) 109 | 110 | f = io.open("/tmp/testfile", "wb") 111 | r = fs:get(f, {filename="testfile"}) 112 | if not r then ngx.say("get file failed: "..err) end 113 | ngx.say(r) 114 | io.close(f) 115 | 116 | r, err = fs:remove({filename="testfile"}, nil, true) 117 | if not r then ngx.say("delete failed: "..err) end 118 | ngx.say(r) 119 | '; 120 | } 121 | --- request 122 | GET /t 123 | --- response_body 124 | 0 125 | true 126 | 1 127 | --- no_error_log 128 | [error] 129 | 130 | === TEST 3: insert and get file = chunksize 131 | --- http_config eval: $::HttpConfig 132 | --- config 133 | location /t { 134 | content_by_lua ' 135 | local mongo = require "resty.mongol" 136 | conn = mongo:new() 137 | conn:set_timeout(10000) 138 | local ok, err = conn:connect("127.0.0.1") 139 | 140 | if not ok then 141 | ngx.say("connect failed: "..err) 142 | end 143 | 144 | local db = conn:new_db_handle("test") 145 | local r = db:auth("admin", "admin") 146 | if not r then ngx.say("auth failed") end 147 | local fs = db:get_gridfs("fs") 148 | 149 | r, err = fs:remove({}, nil, true) 150 | if not r then ngx.say("delete failed: "..err) end 151 | 152 | local f,err = io.open("Readme.md", "rb") 153 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 154 | 155 | local current = f:seek() -- get current position 156 | local size = f:seek("end") -- get file size 157 | f:seek("set", current) -- restore position 158 | 159 | r, err = fs:insert(f, {chunkSize = size, filename="testfile"}, true) 160 | if not r then ngx.say("fs insert failed: "..err) end 161 | ngx.say(r) 162 | io.close(f) 163 | 164 | f = io.open("/tmp/testfile", "wb") 165 | r = fs:get(f, {filename="testfile"}) 166 | if not r then ngx.say("get file failed: "..err) end 167 | io.close(f) 168 | 169 | '; 170 | } 171 | --- request 172 | GET /t 173 | --- response_body 174 | 0 175 | --- no_error_log 176 | [error] 177 | 178 | === TEST 4: remove file not existed 179 | --- http_config eval: $::HttpConfig 180 | --- config 181 | location /t { 182 | content_by_lua ' 183 | local mongo = require "resty.mongol" 184 | conn = mongo:new() 185 | conn:set_timeout(10000) 186 | local ok, err = conn:connect("127.0.0.1") 187 | 188 | if not ok then 189 | ngx.say("connect failed: "..err) 190 | end 191 | 192 | local db = conn:new_db_handle("test") 193 | local r = db:auth("admin", "admin") 194 | if not r then ngx.say("auth failed") end 195 | local fs = db:get_gridfs("fs") 196 | 197 | r, err = fs:remove({filename="notexisted"}, nil, true) 198 | if not r then ngx.say("delete failed: "..err) end 199 | ngx.say(r) 200 | '; 201 | } 202 | --- request 203 | GET /t 204 | --- response_body 205 | 0 206 | --- no_error_log 207 | [error] 208 | 209 | -------------------------------------------------------------------------------- /t/gridfs_write.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(1); 7 | 8 | plan tests => repeat_each() * (4 * blocks()) - 1; 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?/init.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MONGO_PORT} ||= 27017; 18 | $ENV{TEST_NGINX_TIMEOUT} = 10000; 19 | 20 | no_long_string(); 21 | #no_diff(); 22 | 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | 28 | === TEST 1: write chunk < 1, offset = 0 29 | --- http_config eval: $::HttpConfig 30 | --- config 31 | location /t { 32 | content_by_lua ' 33 | local mongo = require "resty.mongol" 34 | conn = mongo:new() 35 | conn:set_timeout(10000) 36 | local ok, err = conn:connect("127.0.0.1") 37 | 38 | if not ok then 39 | ngx.say("connect failed: "..err) 40 | end 41 | 42 | local db = conn:new_db_handle("test") 43 | local r = db:auth("admin", "admin") 44 | if not r then ngx.say("auth failed") end 45 | local fs = db:get_gridfs("fs") 46 | 47 | r, err = fs:remove({}, nil, true) 48 | if not r then ngx.say("delete failed: "..err) end 49 | 50 | local f,err = io.open("t/servroot/html/test.txt", "rb") 51 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 52 | 53 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 54 | if not r then ngx.say("fs insert failed: "..err) end 55 | ngx.say(r) 56 | io.close(f) 57 | 58 | local gf = fs:find_one({filename="testfile"}) 59 | gf:write("abc", 0) 60 | 61 | f = io.open("/tmp/testfile", "wb") 62 | r = fs:get(f, {filename="testfile"}) 63 | if not r then ngx.say("get file failed: "..err) end 64 | io.close(f) 65 | 66 | '; 67 | } 68 | --- user_files 69 | >>> test.txt 70 | 12345678901234567890 71 | --- request 72 | GET /t 73 | --- response_body 74 | 0 75 | --- no_error_log 76 | --- output_files 77 | >>> /tmp/testfile 78 | abc45678901234567890 79 | --- no_error_log 80 | [error] 81 | 82 | === TEST 2: write chunk < 1, offset > 0 83 | --- http_config eval: $::HttpConfig 84 | --- config 85 | location /t { 86 | content_by_lua ' 87 | local mongo = require "resty.mongol" 88 | conn = mongo:new() 89 | conn:set_timeout(10000) 90 | local ok, err = conn:connect("127.0.0.1") 91 | 92 | if not ok then 93 | ngx.say("connect failed: "..err) 94 | end 95 | 96 | local db = conn:new_db_handle("test") 97 | local r = db:auth("admin", "admin") 98 | if not r then ngx.say("auth failed") end 99 | local fs = db:get_gridfs("fs") 100 | 101 | r, err = fs:remove({}, nil, true) 102 | if not r then ngx.say("delete failed: "..err) end 103 | 104 | local f,err = io.open("t/servroot/html/test.txt", "rb") 105 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 106 | 107 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 108 | if not r then ngx.say("fs insert failed: "..err) end 109 | ngx.say(r) 110 | io.close(f) 111 | 112 | local gf = fs:find_one({filename="testfile"}) 113 | gf:write("abc", 2) 114 | 115 | f = io.open("/tmp/testfile", "wb") 116 | r = fs:get(f, {filename="testfile"}) 117 | if not r then ngx.say("get file failed: "..err) end 118 | io.close(f) 119 | 120 | '; 121 | } 122 | --- user_files 123 | >>> test.txt 124 | 12345678901234567890 125 | --- request 126 | GET /t 127 | --- response_body 128 | 0 129 | --- no_error_log 130 | --- output_files 131 | >>> /tmp/testfile 132 | 12abc678901234567890 133 | --- no_error_log 134 | [error] 135 | 136 | === TEST 4: write chunk = 2, offset = 0 137 | --- http_config eval: $::HttpConfig 138 | --- config 139 | location /t { 140 | content_by_lua ' 141 | local mongo = require "resty.mongol" 142 | conn = mongo:new() 143 | conn:set_timeout(10000) 144 | local ok, err = conn:connect("127.0.0.1") 145 | 146 | if not ok then 147 | ngx.say("connect failed: "..err) 148 | end 149 | 150 | local db = conn:new_db_handle("test") 151 | local r = db:auth("admin", "admin") 152 | if not r then ngx.say("auth failed") end 153 | local fs = db:get_gridfs("fs") 154 | 155 | r, err = fs:remove({}, nil, true) 156 | if not r then ngx.say("delete failed: "..err) end 157 | 158 | local f,err = io.open("t/servroot/html/test.txt", "rb") 159 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 160 | 161 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 162 | if not r then ngx.say("fs insert failed: "..err) end 163 | ngx.say(r) 164 | io.close(f) 165 | 166 | local gf = fs:find_one({filename="testfile"}) 167 | gf:write("abcabcdefdef", 0) 168 | 169 | f = io.open("/tmp/testfile", "wb") 170 | r = fs:get(f, {filename="testfile"}) 171 | if not r then ngx.say("get file failed: "..err) end 172 | io.close(f) 173 | 174 | '; 175 | } 176 | --- user_files 177 | >>> test.txt 178 | 12345678901234567890 179 | --- request 180 | GET /t 181 | --- response_body 182 | 0 183 | --- no_error_log 184 | --- output_files 185 | >>> /tmp/testfile 186 | abcabcdefdef34567890 187 | --- no_error_log 188 | [error] 189 | 190 | === TEST 5: write chunk > 1, offset = 0 191 | --- http_config eval: $::HttpConfig 192 | --- config 193 | location /t { 194 | content_by_lua ' 195 | local mongo = require "resty.mongol" 196 | conn = mongo:new() 197 | conn:set_timeout(10000) 198 | local ok, err = conn:connect("127.0.0.1") 199 | 200 | if not ok then 201 | ngx.say("connect failed: "..err) 202 | end 203 | 204 | local db = conn:new_db_handle("test") 205 | local r = db:auth("admin", "admin") 206 | if not r then ngx.say("auth failed") end 207 | local fs = db:get_gridfs("fs") 208 | 209 | r, err = fs:remove({}, nil, true) 210 | if not r then ngx.say("delete failed: "..err) end 211 | 212 | local f,err = io.open("t/servroot/html/test.txt", "rb") 213 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 214 | 215 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 216 | if not r then ngx.say("fs insert failed: "..err) end 217 | ngx.say(r) 218 | io.close(f) 219 | 220 | local gf = fs:find_one({filename="testfile"}) 221 | gf:write("abcabcdef", 0) 222 | 223 | f = io.open("/tmp/testfile", "wb") 224 | r = fs:get(f, {filename="testfile"}) 225 | if not r then ngx.say("get file failed: "..err) end 226 | io.close(f) 227 | 228 | '; 229 | } 230 | --- user_files 231 | >>> test.txt 232 | 12345678901234567890 233 | --- request 234 | GET /t 235 | --- response_body 236 | 0 237 | --- no_error_log 238 | --- output_files 239 | >>> /tmp/testfile 240 | abcabcdef01234567890 241 | --- no_error_log 242 | [error] 243 | 244 | === TEST 6: write chunk > 1, offset > 0 245 | --- http_config eval: $::HttpConfig 246 | --- config 247 | location /t { 248 | content_by_lua ' 249 | local mongo = require "resty.mongol" 250 | conn = mongo:new() 251 | conn:set_timeout(10000) 252 | local ok, err = conn:connect("127.0.0.1") 253 | 254 | if not ok then 255 | ngx.say("connect failed: "..err) 256 | end 257 | 258 | local db = conn:new_db_handle("test") 259 | local r = db:auth("admin", "admin") 260 | if not r then ngx.say("auth failed") end 261 | local fs = db:get_gridfs("fs") 262 | 263 | r, err = fs:remove({}, nil, true) 264 | if not r then ngx.say("delete failed: "..err) end 265 | 266 | local f,err = io.open("t/servroot/html/test.txt", "rb") 267 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 268 | 269 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 270 | if not r then ngx.say("fs insert failed: "..err) end 271 | ngx.say(r) 272 | io.close(f) 273 | 274 | local gf = fs:find_one({filename="testfile"}) 275 | gf:write("abcabcdef", 3) 276 | 277 | f = io.open("/tmp/testfile", "wb") 278 | r = fs:get(f, {filename="testfile"}) 279 | if not r then ngx.say("get file failed: "..err) end 280 | io.close(f) 281 | 282 | '; 283 | } 284 | --- user_files 285 | >>> test.txt 286 | 12345678901234567890 287 | --- request 288 | GET /t 289 | --- response_body 290 | 0 291 | --- no_error_log 292 | --- output_files 293 | >>> /tmp/testfile 294 | 123abcabcdef34567890 295 | --- no_error_log 296 | [error] 297 | 298 | === TEST 7: write chunk > 2, offset > 0 299 | --- http_config eval: $::HttpConfig 300 | --- config 301 | location /t { 302 | content_by_lua ' 303 | local mongo = require "resty.mongol" 304 | conn = mongo:new() 305 | conn:set_timeout(10000) 306 | local ok, err = conn:connect("127.0.0.1") 307 | 308 | if not ok then 309 | ngx.say("connect failed: "..err) 310 | end 311 | 312 | local db = conn:new_db_handle("test") 313 | local r = db:auth("admin", "admin") 314 | if not r then ngx.say("auth failed") end 315 | local fs = db:get_gridfs("fs") 316 | 317 | r, err = fs:remove({}, nil, true) 318 | if not r then ngx.say("delete failed: "..err) end 319 | 320 | local f,err = io.open("t/servroot/html/test.txt", "rb") 321 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 322 | 323 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 324 | if not r then ngx.say("fs insert failed: "..err) end 325 | ngx.say(r) 326 | io.close(f) 327 | 328 | local gf = fs:find_one({filename="testfile"}) 329 | gf:write("abcdefghijkl", 5) 330 | 331 | f = io.open("/tmp/testfile", "wb") 332 | r = fs:get(f, {filename="testfile"}) 333 | if not r then ngx.say("get file failed: "..err) end 334 | io.close(f) 335 | 336 | '; 337 | } 338 | --- user_files 339 | >>> test.txt 340 | 12345678901234567890 341 | --- request 342 | GET /t 343 | --- response_body 344 | 0 345 | --- no_error_log 346 | --- output_files 347 | >>> /tmp/testfile 348 | 12345abcdefghijkl890 349 | --- no_error_log 350 | [error] 351 | 352 | === TEST 8: write chunk > 2, offset > 0, size > file_size 353 | --- http_config eval: $::HttpConfig 354 | --- config 355 | location /t { 356 | content_by_lua ' 357 | local mongo = require "resty.mongol" 358 | conn = mongo:new() 359 | conn:set_timeout(10000) 360 | local ok, err = conn:connect("127.0.0.1") 361 | 362 | if not ok then 363 | ngx.say("connect failed: "..err) 364 | end 365 | 366 | local db = conn:new_db_handle("test") 367 | local r = db:auth("admin", "admin") 368 | if not r then ngx.say("auth failed") end 369 | local fs = db:get_gridfs("fs") 370 | 371 | r, err = fs:remove({}, nil, true) 372 | if not r then ngx.say("delete failed: "..err) end 373 | 374 | local f,err = io.open("t/servroot/html/test.txt", "rb") 375 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 376 | 377 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 378 | if not r then ngx.say("fs insert failed: "..err) end 379 | ngx.say(r) 380 | io.close(f) 381 | 382 | local gf = fs:find_one({filename="testfile"}) 383 | gf:write("abcdefghijklmnopq", 5) 384 | 385 | f = io.open("/tmp/testfile", "wb") 386 | r = fs:get(f, {filename="testfile"}) 387 | if not r then ngx.say("get file failed: "..err) end 388 | io.close(f) 389 | 390 | '; 391 | } 392 | --- user_files 393 | >>> test.txt 394 | 12345678901234567890 395 | --- request 396 | GET /t 397 | --- response_body 398 | 0 399 | --- no_error_log 400 | --- output_files 401 | >>> /tmp/testfile chop 402 | 12345abcdefghijklmnopq 403 | --- no_error_log 404 | [error] 405 | 406 | === TEST 9: write chunk > 2, offset = 0, size > file_size 407 | --- http_config eval: $::HttpConfig 408 | --- config 409 | location /t { 410 | content_by_lua ' 411 | local mongo = require "resty.mongol" 412 | conn = mongo:new() 413 | conn:set_timeout(10000) 414 | local ok, err = conn:connect("127.0.0.1") 415 | 416 | if not ok then 417 | ngx.say("connect failed: "..err) 418 | end 419 | 420 | local db = conn:new_db_handle("test") 421 | local r = db:auth("admin", "admin") 422 | if not r then ngx.say("auth failed") end 423 | local fs = db:get_gridfs("fs") 424 | 425 | r, err = fs:remove({}, nil, true) 426 | if not r then ngx.say("delete failed: "..err) end 427 | 428 | local f,err = io.open("t/servroot/html/test.txt", "rb") 429 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 430 | 431 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 432 | if not r then ngx.say("fs insert failed: "..err) end 433 | ngx.say(r) 434 | io.close(f) 435 | 436 | local gf = fs:find_one({filename="testfile"}) 437 | gf:write("abcdefghijklmnopqrstuvw", 0) 438 | 439 | f = io.open("/tmp/testfile", "wb") 440 | r = fs:get(f, {filename="testfile"}) 441 | if not r then ngx.say("get file failed: "..err) end 442 | io.close(f) 443 | 444 | '; 445 | } 446 | --- user_files 447 | >>> test.txt 448 | 12345678901234567890 449 | --- request 450 | GET /t 451 | --- response_body 452 | 0 453 | --- no_error_log 454 | --- output_files 455 | >>> /tmp/testfile chop 456 | abcdefghijklmnopqrstuvw 457 | --- no_error_log 458 | [error] 459 | 460 | === TEST 10: write chunk > old chunk, offset = 0 461 | --- http_config eval: $::HttpConfig 462 | --- config 463 | location /t { 464 | content_by_lua ' 465 | local mongo = require "resty.mongol" 466 | conn = mongo:new() 467 | conn:set_timeout(10000) 468 | local ok, err = conn:connect("127.0.0.1") 469 | 470 | if not ok then 471 | ngx.say("connect failed: "..err) 472 | end 473 | 474 | local db = conn:new_db_handle("test") 475 | local r = db:auth("admin", "admin") 476 | if not r then ngx.say("auth failed") end 477 | local fs = db:get_gridfs("fs") 478 | 479 | r, err = fs:remove({}, nil, true) 480 | if not r then ngx.say("delete failed: "..err) end 481 | 482 | local f,err = io.open("t/servroot/html/test.txt", "rb") 483 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 484 | 485 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 486 | if not r then ngx.say("fs insert failed: "..err) end 487 | ngx.say(r) 488 | io.close(f) 489 | 490 | local gf = fs:find_one({filename="testfile"}) 491 | gf:write("abcdefghijklmnopqrstuvw", 0) 492 | 493 | f = io.open("/tmp/testfile", "wb") 494 | r = fs:get(f, {filename="testfile"}) 495 | if not r then ngx.say("get file failed: "..err) end 496 | io.close(f) 497 | 498 | '; 499 | } 500 | --- user_files 501 | >>> test.txt 502 | 1234567890 503 | --- request 504 | GET /t 505 | --- response_body 506 | 0 507 | --- no_error_log 508 | --- output_files 509 | >>> /tmp/testfile chop 510 | abcdefghijklmnopqrstuvw 511 | --- no_error_log 512 | [error] 513 | 514 | === TEST 11: write chunk > old chunk, offset > 0 515 | --- http_config eval: $::HttpConfig 516 | --- config 517 | location /t { 518 | content_by_lua ' 519 | local mongo = require "resty.mongol" 520 | conn = mongo:new() 521 | conn:set_timeout(10000) 522 | local ok, err = conn:connect("127.0.0.1") 523 | 524 | if not ok then 525 | ngx.say("connect failed: "..err) 526 | end 527 | 528 | local db = conn:new_db_handle("test") 529 | local r = db:auth("admin", "admin") 530 | if not r then ngx.say("auth failed") end 531 | local fs = db:get_gridfs("fs") 532 | 533 | r, err = fs:remove({}, nil, true) 534 | if not r then ngx.say("delete failed: "..err) end 535 | 536 | local f,err = io.open("t/servroot/html/test.txt", "rb") 537 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 538 | 539 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 540 | if not r then ngx.say("fs insert failed: "..err) end 541 | ngx.say(r) 542 | io.close(f) 543 | 544 | local gf = fs:find_one({filename="testfile"}) 545 | gf:write("abcdefghijklmnopqrstuvw", 8) 546 | 547 | f = io.open("/tmp/testfile", "wb") 548 | r = fs:get(f, {filename="testfile"}) 549 | if not r then ngx.say("get file failed: "..err) end 550 | io.close(f) 551 | 552 | '; 553 | } 554 | --- user_files 555 | >>> test.txt 556 | 1234567890 557 | --- request 558 | GET /t 559 | --- response_body 560 | 0 561 | --- no_error_log 562 | --- output_files 563 | >>> /tmp/testfile chop 564 | 12345678abcdefghijklmnopqrstuvw 565 | --- no_error_log 566 | [error] 567 | 568 | === TEST 12: write chunk = 1, offset > 0, size > file size 569 | --- http_config eval: $::HttpConfig 570 | --- config 571 | location /t { 572 | content_by_lua ' 573 | local mongo = require "resty.mongol" 574 | conn = mongo:new() 575 | conn:set_timeout(10000) 576 | local ok, err = conn:connect("127.0.0.1") 577 | 578 | if not ok then 579 | ngx.say("connect failed: "..err) 580 | end 581 | 582 | local db = conn:new_db_handle("test") 583 | local r = db:auth("admin", "admin") 584 | if not r then ngx.say("auth failed") end 585 | local fs = db:get_gridfs("fs") 586 | 587 | r, err = fs:remove({}, nil, true) 588 | if not r then ngx.say("delete failed: "..err) end 589 | 590 | local f,err = io.open("t/servroot/html/test.txt", "rb") 591 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 592 | 593 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 594 | if not r then ngx.say("fs insert failed: "..err) end 595 | ngx.say(r) 596 | io.close(f) 597 | 598 | local gf = fs:find_one({filename="testfile"}) 599 | gf:write("abcdefgh", 2) 600 | 601 | f = io.open("/tmp/testfile", "wb") 602 | r = fs:get(f, {filename="testfile"}) 603 | if not r then ngx.say("get file failed: "..err) end 604 | io.close(f) 605 | 606 | '; 607 | } 608 | --- user_files 609 | >>> test.txt 610 | 12345 611 | --- request 612 | GET /t 613 | --- response_body 614 | 0 615 | --- no_error_log 616 | --- output_files 617 | >>> /tmp/testfile chop 618 | 12abcdefgh 619 | --- no_error_log 620 | [error] 621 | 622 | === TEST 13: write and update md5 623 | --- http_config eval: $::HttpConfig 624 | --- config 625 | location /t { 626 | content_by_lua ' 627 | local mongo = require "resty.mongol" 628 | conn = mongo:new() 629 | conn:set_timeout(10000) 630 | local ok, err = conn:connect("127.0.0.1") 631 | 632 | if not ok then 633 | ngx.say("connect failed: "..err) 634 | end 635 | 636 | local db = conn:new_db_handle("test") 637 | local r = db:auth("admin", "admin") 638 | if not r then ngx.say("auth failed") end 639 | local fs = db:get_gridfs("fs") 640 | 641 | r, err = fs:remove({}, nil, true) 642 | if not r then ngx.say("delete failed: "..err) end 643 | 644 | local f,err = io.open("t/servroot/html/test.txt", "rb") 645 | if not f then ngx.say("fs open failed: "..err) ngx.exit(ngx.HTTP_OK) end 646 | 647 | r, err = fs:insert(f, {chunkSize = 6, filename="testfile"}, true) 648 | if not r then ngx.say("fs insert failed: "..err) end 649 | ngx.say(r) 650 | io.close(f) 651 | 652 | local gf = fs:find_one({filename="testfile"}) 653 | gf:write("abc", 2) 654 | 655 | f = io.open("/tmp/testfile", "wb") 656 | r = fs:get(f, {filename="testfile"}) 657 | if not r then ngx.say("get file failed: "..err) end 658 | io.close(f) 659 | 660 | r, err = gf:update_md5() 661 | if not r then ngx.say("update md5 failed: "..err) end 662 | ngx.say(gf.file_md5) 663 | '; 664 | } 665 | --- user_files 666 | >>> test.txt 667 | 123 668 | --- request 669 | GET /t 670 | --- response_body 671 | 0 672 | 42c56c61ee49c16375960c809c6a3eb0 673 | --- no_error_log 674 | --- output_files 675 | >>> /tmp/testfile chop 676 | 12abc 677 | --- no_error_log 678 | [error] 679 | 680 | 681 | === TEST 14: new and write and update md5 682 | --- http_config eval: $::HttpConfig 683 | --- config 684 | location /t { 685 | content_by_lua ' 686 | local mongo = require "resty.mongol" 687 | conn = mongo:new() 688 | conn:set_timeout(10000) 689 | local ok, err = conn:connect("127.0.0.1") 690 | 691 | if not ok then 692 | ngx.say("connect failed: "..err) 693 | end 694 | 695 | local db = conn:new_db_handle("test") 696 | local r = db:auth("admin", "admin") 697 | if not r then ngx.say("auth failed") end 698 | local fs = db:get_gridfs("fs") 699 | 700 | r, err = fs:remove({}, nil, true) 701 | if not r then ngx.say("delete failed: "..err) end 702 | 703 | local gf = fs:new({chunkSize = 6, filename = "testfile"}) 704 | 705 | r,err = gf:write("abc", 0) 706 | if not r then ngx.say("write failed: "..err) end 707 | ngx.say(r) 708 | 709 | f = io.open("/tmp/testfile", "wb") 710 | r,err = fs:get(f, {filename="testfile"}) 711 | if not r then ngx.say("get file failed: "..err) end 712 | io.close(f) 713 | 714 | r, err = gf:update_md5() 715 | if not r then ngx.say("update md5 failed: "..err) end 716 | ngx.say(gf.file_md5) 717 | '; 718 | } 719 | --- user_files 720 | --- request 721 | GET /t 722 | --- response_body 723 | 3 724 | 900150983cd24fb0d6963f7d28e17f72 725 | --- no_error_log 726 | --- output_files 727 | >>> /tmp/testfile chop 728 | abc 729 | --- no_error_log 730 | [error] 731 | 732 | === TEST 15: write chunk = 1, offset = 0, size = chunk size 733 | --- http_config eval: $::HttpConfig 734 | --- config 735 | location /t { 736 | content_by_lua ' 737 | local mongo = require "resty.mongol" 738 | conn = mongo:new() 739 | conn:set_timeout(10000) 740 | local ok, err = conn:connect("127.0.0.1") 741 | 742 | if not ok then 743 | ngx.say("connect failed: "..err) 744 | end 745 | 746 | local db = conn:new_db_handle("test") 747 | local r = db:auth("admin", "admin") 748 | if not r then ngx.say("auth failed") end 749 | local fs = db:get_gridfs("fs") 750 | 751 | r, err = fs:remove({}, nil, true) 752 | if not r then ngx.say("delete failed: "..err) end 753 | 754 | local gridfs = fs:new({chunkSize = 20, filename = "testfile"}) 755 | local r, err = gridfs:write("ABCDEFGHIJKLMNOPQRST", 0) --gridfs.file_size) 756 | ngx.say(r) 757 | 758 | f = io.open("/tmp/testfile", "wb") 759 | r = fs:get(f, {filename="testfile"}) 760 | if not r then ngx.say("get file failed: "..err) end 761 | io.close(f) 762 | '; 763 | } 764 | --- request 765 | GET /t 766 | --- response_body 767 | 20 768 | --- output_files 769 | >>> /tmp/testfile chop 770 | ABCDEFGHIJKLMNOPQRST 771 | --- no_error_log 772 | [error] 773 | 774 | === TEST 16: write chunk = 2, offset = chunk size, size = 2*chunk size 775 | --- http_config eval: $::HttpConfig 776 | --- config 777 | location /t { 778 | content_by_lua ' 779 | local mongo = require "resty.mongol" 780 | conn = mongo:new() 781 | conn:set_timeout(10000) 782 | local ok, err = conn:connect("127.0.0.1") 783 | 784 | if not ok then 785 | ngx.say("connect failed: "..err) 786 | end 787 | 788 | local db = conn:new_db_handle("test") 789 | local r = db:auth("admin", "admin") 790 | if not r then ngx.say("auth failed") end 791 | local fs = db:get_gridfs("fs") 792 | 793 | r, err = fs:remove({}, nil, true) 794 | if not r then ngx.say("delete failed: "..err) end 795 | 796 | local gridfs = fs:new({chunkSize = 10, filename = "testfile"}) 797 | local r, err = gridfs:write("ABCDEFGHIJKLMNOPQRST", 10) --gridfs.file_size) 798 | ngx.say(r) 799 | ngx.say(err) 800 | 801 | --f = io.open("/tmp/testfile", "wb") 802 | --r = fs:get(f, {filename="testfile"}) 803 | --if not r then ngx.say("get file failed: "..err) end 804 | --io.close(f) 805 | '; 806 | } 807 | --- request 808 | GET /t 809 | --- response_body 810 | nil 811 | invalid offset 812 | --- no_error_log 813 | [error] 814 | -------------------------------------------------------------------------------- /t/objid.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(1); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?/init.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MONGO_PORT} ||= 27017; 18 | 19 | no_long_string(); 20 | #no_diff(); 21 | 22 | run_tests(); 23 | 24 | __DATA__ 25 | 26 | === TEST 1: object id 27 | --- http_config eval: $::HttpConfig 28 | --- config 29 | location /t { 30 | content_by_lua ' 31 | local mongo = require "resty.mongol" 32 | conn = mongo:new() 33 | conn:set_timeout(10000) 34 | local ok, err = conn:connect("127.0.0.1") 35 | 36 | if not ok then 37 | ngx.say("connect failed: "..err) 38 | end 39 | 40 | local db = conn:new_db_handle("test") 41 | local r = db:auth("admin", "admin") 42 | if not r then ngx.say("auth failed") end 43 | local col = db:get_col("test") 44 | 45 | r, err = col:delete({}, nil, true) 46 | if not r then ngx.say("delete failed: "..err) end 47 | 48 | r, err = col:insert({{name="dog",n=10,m=20}, {name="cat"}}, 49 | nil, true) 50 | if not r then ngx.say("insert failed: "..err) end 51 | ngx.say(r) 52 | 53 | r = col:find_one({name="dog"}) 54 | 55 | ngx.say(r["_id"].id) 56 | ngx.say(r["_id"]:tostring()) 57 | ngx.say(r["_id"]:get_ts()) 58 | ngx.say(r["_id"]:get_hostname()) 59 | ngx.say(r["_id"]:get_pid()) 60 | ngx.say(r["_id"]:get_inc()) 61 | ngx.say(r["name"]) 62 | 63 | r = col:find_one({_id=r["id"]}) 64 | ngx.say(r["name"]) 65 | 66 | conn:close() 67 | '; 68 | } 69 | --- request 70 | GET /t 71 | --- response_body_like 72 | dog 73 | dog 74 | --- no_error_log 75 | [error] 76 | 77 | -------------------------------------------------------------------------------- /t/sanity.t: -------------------------------------------------------------------------------- 1 | # vim:set ft= ts=4 sw=4 et: 2 | 3 | use Test::Nginx::Socket; 4 | use Cwd qw(cwd); 5 | 6 | repeat_each(1); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | my $pwd = cwd(); 11 | 12 | our $HttpConfig = qq{ 13 | lua_package_path "$pwd/lib/?/init.lua;;"; 14 | }; 15 | 16 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; 17 | $ENV{TEST_NGINX_MONGO_PORT} ||= 27017; 18 | 19 | no_long_string(); 20 | #no_diff(); 21 | 22 | run_tests(); 23 | 24 | __DATA__ 25 | 26 | === TEST 1: col insert 27 | --- http_config eval: $::HttpConfig 28 | --- config 29 | location /t { 30 | content_by_lua ' 31 | local mongo = require "resty.mongol" 32 | conn = mongo:new() 33 | conn:set_timeout(10000) 34 | ok, err = conn:connect("127.0.0.1") 35 | 36 | if not ok then 37 | ngx.say("connect failed: "..err) 38 | end 39 | 40 | local db = conn:new_db_handle("test") 41 | local col = db:get_col("test") 42 | 43 | local r,err = col:insert({{name="dog",n=10,m=20}}, nil, true) 44 | if not r then ngx.say("insert failed: "..err) end 45 | 46 | r = db:auth("admin", "admin") 47 | if not r then ngx.say("auth failed") end 48 | 49 | r, err = col:delete({}, nil, true) 50 | if not r then ngx.say("delete failed: "..err) end 51 | 52 | r, err = col:insert({{name="dog",n=10,m=20}, {name="cat"}}, 53 | nil, true) 54 | if not r then ngx.say("insert failed: "..err) end 55 | ngx.say(r) 56 | 57 | r = col:find_one({name="dog"}) 58 | ngx.say(r["name"]) 59 | conn:close() 60 | '; 61 | } 62 | --- request 63 | GET /t 64 | --- response_body 65 | insert failed: need to login 66 | 0 67 | dog 68 | --- no_error_log 69 | [error] 70 | 71 | === TEST 3: socket failed 72 | --- http_config eval: $::HttpConfig 73 | --- config 74 | location /t { 75 | content_by_lua ' 76 | local mongo = require "resty.mongol" 77 | conn = mongo:new() 78 | conn:set_timeout(1000) 79 | ok, err = conn:connect("127.0.0.1", 27016) 80 | 81 | if not ok then 82 | ngx.say("connect failed: "..err) 83 | end 84 | '; 85 | } 86 | --- request 87 | GET /t 88 | --- response_body 89 | connect failed: connection refused 90 | --- error_log 91 | [error] 92 | 93 | === TEST 4: socket reuse 94 | --- http_config eval: $::HttpConfig 95 | --- config 96 | location /t { 97 | content_by_lua ' 98 | local mongo = require "resty.mongol" 99 | conn = mongo:new() 100 | conn:set_timeout(1000) 101 | 102 | ok, err = conn:connect("127.0.0.1") 103 | if not ok then 104 | ngx.say("connect failed: "..err) 105 | end 106 | ngx.say(conn:get_reused_times()) 107 | 108 | ok, err = conn:set_keepalive() 109 | if not ok then 110 | ngx.say("set keepalive failed: "..err) 111 | end 112 | 113 | ok, err = conn:connect("127.0.0.1") 114 | if not ok then 115 | ngx.say("connect failed: "..err) 116 | end 117 | ngx.say(conn:get_reused_times()) 118 | '; 119 | } 120 | --- request 121 | GET /t 122 | --- response_body 123 | 0 124 | 1 125 | --- no_error_log 126 | [error] 127 | 128 | 129 | === TEST 9: col count 130 | --- http_config eval: $::HttpConfig 131 | --- config 132 | location /t { 133 | content_by_lua ' 134 | local mongo = require "resty.mongol" 135 | conn = mongo:new() 136 | conn:set_timeout(1000) 137 | 138 | ok, err = conn:connect("127.0.0.1") 139 | if not ok then 140 | ngx.say("connect failed: "..err) 141 | end 142 | 143 | local db = conn:new_db_handle("test") 144 | local r = db:auth("admin", "admin") 145 | if not r then 146 | ngx.say("auth failed") 147 | ngx.exit(ngx.OK) 148 | end 149 | col = db:get_col("test") 150 | 151 | col:delete({name="sheep"}) 152 | col:insert({{name="sheep"}}) 153 | local n, err = col:count({name="sheep"}) 154 | if not n then 155 | ngx.say("count fail: "..err) 156 | end 157 | ngx.say(n) 158 | '; 159 | } 160 | --- request 161 | GET /t 162 | --- response_body 163 | 1 164 | --- no_error_log 165 | [error] 166 | 167 | === TEST 10: col update and with $inc 168 | --- http_config eval: $::HttpConfig 169 | --- config 170 | location /t { 171 | content_by_lua ' 172 | local mongo = require "resty.mongol" 173 | conn = mongo:new() 174 | conn:set_timeout(1000) 175 | 176 | local r, err = conn:connect("127.0.0.1") 177 | if not r then ngx.say("connect failed: "..err) end 178 | 179 | local db = conn:new_db_handle("test") 180 | local col = db:get_col("test") 181 | 182 | r,err = col:update({name="dog"},{name="cat"}, nil, nil, true) 183 | if not r then ngx.say("update failed: "..err) end 184 | 185 | r = db:auth("admin", "admin") 186 | if not r then ngx.say("auth failed") end 187 | 188 | r,err = col:delete({}) 189 | if not r then ngx.say("delete failed: "..err) end 190 | 191 | r,err = col:insert({{name="dog"}}) 192 | if not r then ngx.say("insert failed: "..err) end 193 | 194 | r,err = col:update({name="dog"},{name="cat"}, nil, nil, true) 195 | if not r then ngx.say("update failed: "..err) end 196 | ngx.say(r) 197 | 198 | r = col:find_one({name="cat"}) 199 | ngx.say(r["name"]) 200 | 201 | r,err = col:update({name="sheep"},{name="cat"}, 1, nil, true) 202 | if not r then ngx.say("update failed: "..err) end 203 | ngx.say(r) 204 | 205 | r,err = col:update({name="sheep"},{name="cat"}, nil, nil, true) 206 | if not r then ngx.say("update failed: "..err) end 207 | ngx.say(r) 208 | 209 | 210 | col:insert({{name="dog",n=1}}) 211 | 212 | local update = {} 213 | update["$inc"] = {n=1} 214 | r,err = col:update({name="dog"},update, nil, nil, true) 215 | if not r then ngx.say("update failed: "..err) end 216 | ngx.say(r) 217 | 218 | r = col:find({name="dog"}) 219 | for i , v in r:pairs() do 220 | if v["n"] then 221 | ngx.say(v["n"]) 222 | end 223 | end 224 | 225 | col:insert({{name="dog",n=10}}) 226 | r,err = col:update({name="dog"}, update, nil, 1, true) 227 | if not r then ngx.say("update failed: "..err) end 228 | ngx.say(r) 229 | '; 230 | } 231 | --- request 232 | GET /t 233 | --- response_body 234 | update failed: need to login 235 | 1 236 | cat 237 | 1 238 | 0 239 | 1 240 | 2 241 | 2 242 | --- no_error_log 243 | [error] 244 | 245 | === TEST 11: col find with limit(getmore) 246 | --- http_config eval: $::HttpConfig 247 | --- config 248 | location /t { 249 | content_by_lua ' 250 | local mongo = require "resty.mongol" 251 | conn = mongo:new() 252 | conn:set_timeout(1000) 253 | 254 | ok, err = conn:connect("127.0.0.1") 255 | if not ok then 256 | ngx.say("connect failed: "..err) 257 | end 258 | 259 | local db = conn:new_db_handle("test") 260 | local r = db:auth("admin", "admin") 261 | if not r then 262 | ngx.say("auth failed") 263 | ngx.exit(ngx.OK) 264 | end 265 | 266 | col = db:get_col("test") 267 | col:delete({name="puppy"}) 268 | 269 | for i = 1, 10 do 270 | col:insert({{name="puppy"}}) 271 | end 272 | r = col:find({name="puppy"}, nil, 4) 273 | local j = 0 274 | for i , v in r:pairs() do 275 | j = j +1 276 | end 277 | 278 | ngx.say(j) 279 | '; 280 | } 281 | --- request 282 | GET /t 283 | --- response_body 284 | 4 285 | --- no_error_log 286 | [error] 287 | 288 | === TEST 12: col find with field 289 | --- http_config eval: $::HttpConfig 290 | --- config 291 | location /t { 292 | content_by_lua ' 293 | local mongo = require "resty.mongol" 294 | conn = mongo:new() 295 | conn:set_timeout(1000) 296 | 297 | ok, err = conn:connect("127.0.0.1") 298 | if not ok then 299 | ngx.say("connect failed: "..err) 300 | end 301 | 302 | local db = conn:new_db_handle("test") 303 | local r = db:auth("admin", "admin") 304 | if not r then 305 | ngx.say("auth failed") 306 | ngx.exit(ngx.OK) 307 | end 308 | 309 | col = db:get_col("test") 310 | col:delete({name="puppy"}) 311 | 312 | for i = 1, 10 do 313 | col:insert({{name="puppy", n=i, m="foo"}}) 314 | end 315 | 316 | r = col:find({name="puppy"}, {n=0}, 3) 317 | for i , v in r:pairs() do 318 | ngx.say(v["n"]) 319 | ngx.say(v["name"]) 320 | end 321 | 322 | r = col:find({name="puppy"}, {n=1}, 3) 323 | for i , v in r:pairs() do 324 | ngx.say(v["n"]) 325 | ngx.say(v["name"]) 326 | end 327 | '; 328 | } 329 | --- request 330 | GET /t 331 | --- response_body 332 | nil 333 | puppy 334 | nil 335 | puppy 336 | nil 337 | puppy 338 | 1 339 | nil 340 | 2 341 | nil 342 | 3 343 | nil 344 | --- no_error_log 345 | [error] 346 | 347 | === TEST 13: col drop 348 | --- http_config eval: $::HttpConfig 349 | --- config 350 | location /t { 351 | content_by_lua ' 352 | local mongo = require "resty.mongol" 353 | conn = mongo:new() 354 | conn:set_timeout(1000) 355 | 356 | ok, err = conn:connect("127.0.0.1") 357 | if not ok then 358 | ngx.say("connect failed: "..err) 359 | end 360 | 361 | local db = conn:new_db_handle("test") 362 | local r = db:auth("admin", "admin") 363 | if not r then 364 | ngx.say("auth failed") 365 | ngx.exit(ngx.OK) 366 | end 367 | 368 | col = db:get_col("test") 369 | col:insert({{name="puppy", n=i, m="foo"}}) 370 | local r,err = col:drop() 371 | 372 | if not r then 373 | ngx.say(err) 374 | else 375 | ngx.say(r) 376 | end 377 | 378 | local r ,err = col:drop() 379 | if not r then 380 | ngx.say(err) 381 | else 382 | ngx.say(r) 383 | end 384 | '; 385 | } 386 | --- request 387 | GET /t 388 | --- response_body 389 | 1 390 | ns not found 391 | --- no_error_log 392 | [error] 393 | 394 | === TEST 14: col find_one 395 | --- http_config eval: $::HttpConfig 396 | --- config 397 | location /t { 398 | content_by_lua ' 399 | local mongo = require "resty.mongol" 400 | conn = mongo:new() 401 | conn:set_timeout(10000) 402 | 403 | local ok, err = conn:connect("127.0.0.1",27017) 404 | if not ok then 405 | ngx.say("connect failed: "..err) 406 | end 407 | 408 | local db = conn:new_db_handle("test") 409 | local r = db:auth("admin", "admin") 410 | if not r then 411 | ngx.say("auth failed") 412 | ngx.exit(ngx.HTTP_OK) 413 | end 414 | 415 | local col = db:get_col("test") 416 | r,err = col:delete({}) 417 | if not r then 418 | ngx.say("delete failed: "..err) 419 | ngx.exit(ngx.HTTP_OK) 420 | end 421 | 422 | col:insert({{name="puppy", n=1, m="foo"}}) 423 | col:insert({{name="puppy", n=2, m="foo"}}) 424 | 425 | r = col:find_one({name="puppy"}, {n=1}) 426 | if not r then 427 | ngx.say("not found") 428 | end 429 | ngx.say(r["n"]) 430 | 431 | r = col:find_one({name="puppy"}, {n=0}) 432 | if not r then 433 | ngx.say("not found") 434 | end 435 | ngx.say(r["n"]) 436 | 437 | r = col:find_one({name="p"}) 438 | if not r then 439 | ngx.say("not found") 440 | end 441 | '; 442 | } 443 | --- request 444 | GET /t 445 | --- response_body 446 | 1 447 | nil 448 | not found 449 | --- no_error_log 450 | [error] 451 | 452 | === TEST 15: col delete safe 453 | --- http_config eval: $::HttpConfig 454 | --- config 455 | location /t { 456 | content_by_lua ' 457 | local mongo = require "resty.mongol" 458 | conn = mongo:new() 459 | conn:set_timeout(10000) 460 | 461 | local ok, err = conn:connect("127.0.0.1") 462 | if not ok then 463 | ngx.say("connect failed: "..err) 464 | end 465 | 466 | local db = conn:new_db_handle("test") 467 | 468 | local col = db:get_col("test") 469 | r,err = col:delete({}, nil, 1) 470 | if not r then 471 | ngx.say("delete failed: "..err) 472 | end 473 | 474 | local r = db:auth("admin", "admin") 475 | if not r then 476 | ngx.say("auth failed") 477 | ngx.exit(ngx.HTTP_OK) 478 | end 479 | 480 | r,err = col:delete({}) 481 | col:insert({{name="puppy", n=1, m="foo"}}) 482 | col:insert({{name="puppy", n=1, m="foo"}}) 483 | col:insert({{name="puppy", n=2, m="foo"}}) 484 | r = col:delete({name="puppy"}, 0, 1) 485 | 486 | if not r then 487 | ngx.say("delete failed: "..err) 488 | ngx.exit(ngx.HTTP_OK) 489 | end 490 | ngx.say(r) 491 | 492 | col:insert({{name="puppy", n=1, m="foo"}}) 493 | col:insert({{name="puppy", n=1, m="foo"}}) 494 | col:insert({{name="puppy", n=2, m="foo"}}) 495 | r = col:delete({name="puppy"}, 1, true) 496 | 497 | if not r then 498 | ngx.say("delete failed: "..err) 499 | ngx.exit(ngx.HTTP_OK) 500 | end 501 | ngx.say(r) 502 | 503 | col:insert({{name="puppy", n=1, m="foo"}}) 504 | col:insert({{name="puppy", n=1, m="foo"}}) 505 | col:insert({{name="puppy", n=2, m="foo"}}) 506 | r = col:delete({name="puppy"}, 1, false) 507 | 508 | if not r then 509 | ngx.say("delete failed: "..err) 510 | ngx.exit(ngx.HTTP_OK) 511 | end 512 | ngx.say(r) 513 | '; 514 | } 515 | --- request 516 | GET /t 517 | --- response_body 518 | delete failed: need to login 519 | 3 520 | 1 521 | -1 522 | --- no_error_log 523 | [error] 524 | 525 | === TEST 16: col insert array 526 | --- http_config eval: $::HttpConfig 527 | --- config 528 | location /t { 529 | content_by_lua ' 530 | local mongo = require "resty.mongol" 531 | conn = mongo:new() 532 | conn:set_timeout(10000) 533 | ok, err = conn:connect("127.0.0.1") 534 | 535 | if not ok then 536 | ngx.say("connect failed: "..err) 537 | end 538 | 539 | local db = conn:new_db_handle("test") 540 | local col = db:get_col("test") 541 | 542 | r = db:auth("admin", "admin") 543 | if not r then ngx.say("auth failed") end 544 | 545 | r, err = col:delete({}, nil, true) 546 | if not r then ngx.say("delete failed: "..err) end 547 | 548 | local t = {} 549 | table.insert(t,{a = "aa"}) 550 | table.insert(t,{b = "bb"}) 551 | local t1 = {[0]="a0","a1","a2"} 552 | local t2 = {} 553 | t2[2]="a20" 554 | t2[3] = "a21" 555 | t2[4] = "a22" 556 | 557 | r, err = col:insert({{name="dog",n="10",tab=t,tab1=t1,tab2=t2}}, nil, true) 558 | if not r then ngx.say("insert failed: "..err) end 559 | ngx.say(r) 560 | 561 | r = col:find_one({name="dog"}) 562 | ngx.say(r["name"]) 563 | ngx.say(r["tab"][1].a) 564 | ngx.say(r["tab1"][0]) 565 | ngx.say(r["tab2"][0]) 566 | ngx.say(r["tab2"][2]) 567 | 568 | --local update = {} 569 | --update["$push"] = {tab="a3"} 570 | --r,err = col:update({name="dog"},update, nil, nil, true) 571 | --if not r then ngx.say("update failed: "..err) end 572 | --ngx.say(r) 573 | 574 | --update["$push"] = {tab="a4"} 575 | --r,err = col:update({name="dog"},update, nil, nil, true) 576 | 577 | conn:close() 578 | '; 579 | } 580 | --- request 581 | GET /t 582 | --- response_body 583 | 0 584 | dog 585 | aa 586 | a0 587 | nil 588 | a20 589 | --- no_error_log 590 | [error] 591 | 592 | === TEST 17: col insert array and pop 593 | --- http_config eval: $::HttpConfig 594 | --- config 595 | location /t { 596 | content_by_lua ' 597 | local mongo = require "resty.mongol" 598 | conn = mongo:new() 599 | conn:set_timeout(10000) 600 | ok, err = conn:connect("127.0.0.1") 601 | 602 | if not ok then 603 | ngx.say("connect failed: "..err) 604 | end 605 | 606 | local db = conn:new_db_handle("test") 607 | local col = db:get_col("test") 608 | 609 | r = db:auth("admin", "admin") 610 | if not r then ngx.say("auth failed") end 611 | 612 | r, err = col:delete({}, nil, true) 613 | if not r then ngx.say("delete failed: "..err) end 614 | 615 | local t = {0} 616 | 617 | r, err = col:insert({{name="dog",n="10",tab=t}}, nil, true) 618 | if not r then ngx.say("insert failed: "..err) end 619 | ngx.say(r) 620 | 621 | local update = {} 622 | update["$pop"] = {tab=1} 623 | r,err = col:update({name="dog"},update, nil, nil, true) 624 | if not r then ngx.say("update failed: "..err) end 625 | r,err = col:update({name="dog"},update, nil, nil, true) 626 | if not r then ngx.say("update failed: "..err) end 627 | 628 | r = col:find_one({name="dog"}) 629 | ngx.say(r["tab"][1]) 630 | 631 | conn:close() 632 | '; 633 | } 634 | --- request 635 | GET /t 636 | --- response_body 637 | 0 638 | nil 639 | --- no_error_log 640 | [error] 641 | 642 | === TEST 18: col insert array and push 643 | --- http_config eval: $::HttpConfig 644 | --- config 645 | location /t { 646 | content_by_lua ' 647 | local mongo = require "resty.mongol" 648 | conn = mongo:new() 649 | conn:set_timeout(10000) 650 | ok, err = conn:connect("127.0.0.1") 651 | 652 | if not ok then 653 | ngx.say("connect failed: "..err) 654 | end 655 | 656 | local db = conn:new_db_handle("test") 657 | local col = db:get_col("test") 658 | 659 | r = db:auth("admin", "admin") 660 | if not r then ngx.say("auth failed") end 661 | 662 | r, err = col:delete({}, nil, true) 663 | if not r then ngx.say("delete failed: "..err) end 664 | 665 | local t = {} 666 | table.insert(t,{a = "aa"}) 667 | table.insert(t,{b = "bb"}) 668 | local t1 = {[0]="a10","a11","a12"} 669 | local t2 = {} 670 | t2[2] = "a22" 671 | t2[3] = "a23" 672 | t2[4] = "a24" 673 | 674 | r, err = col:insert({{name="dog",n="10",tab=t,tab1=t1,tab2=t2}}, nil, true) 675 | if not r then ngx.say("insert failed: "..err) end 676 | ngx.say(r) 677 | 678 | local update = {} 679 | update["$push"] = {tab="a3",tab1="a13",tab2="a25"} 680 | r,err = col:update({name="dog"},update, nil, nil, true) 681 | if not r then ngx.say("update failed: "..err) end 682 | ngx.say(r) 683 | 684 | r = col:find_one({name="dog"}) 685 | if not r then ngx.say("find failed: "..err) end 686 | ngx.say(r["tab"][2].b) 687 | ngx.say(r["tab"][3]) 688 | ngx.say(r["tab1"][2]) 689 | ngx.say(r["tab1"][3]) 690 | ngx.say(r["tab2"][4]) 691 | ngx.say(r["tab2"][5]) 692 | 693 | 694 | conn:close() 695 | '; 696 | } 697 | --- request 698 | GET /t 699 | --- response_body 700 | 0 701 | 1 702 | bb 703 | a3 704 | a12 705 | a13 706 | a24 707 | a25 708 | --- no_error_log 709 | [error] 710 | 711 | === TEST 19: col insert table and set 712 | --- http_config eval: $::HttpConfig 713 | --- config 714 | location /t { 715 | content_by_lua ' 716 | local mongo = require "resty.mongol" 717 | conn = mongo:new() 718 | conn:set_timeout(10000) 719 | ok, err = conn:connect("127.0.0.1") 720 | 721 | if not ok then 722 | ngx.say("connect failed: "..err) 723 | end 724 | 725 | local db = conn:new_db_handle("test") 726 | local col = db:get_col("test") 727 | 728 | r = db:auth("admin", "admin") 729 | if not r then ngx.say("auth failed") end 730 | 731 | r, err = col:delete({}, nil, true) 732 | if not r then ngx.say("delete failed: "..err) end 733 | 734 | local t = {a=1,b=2} 735 | 736 | r, err = col:insert({{name="dog",n="10",tab=t}}, nil, true) 737 | if not r then ngx.say("insert failed: "..err) end 738 | ngx.say(r) 739 | 740 | local update = {} 741 | update["$set"] = {["tab.a"] = 2} 742 | --update["$set"]["tab.a"] = 2 743 | r,err = col:update({name="dog"},update, nil, nil, true) 744 | if not r then ngx.say("update failed: "..err) end 745 | 746 | r = col:find_one({name="dog"}) 747 | ngx.say(r.tab.a) 748 | 749 | conn:close() 750 | '; 751 | } 752 | --- request 753 | GET /t 754 | --- response_body 755 | 0 756 | 2 757 | --- no_error_log 758 | [error] 759 | 760 | === TEST 20: access 761 | --- http_config eval: $::HttpConfig 762 | --- config 763 | location /t { 764 | access_by_lua ' 765 | local mongo = require "resty.mongol" 766 | conn = mongo:new() 767 | conn:set_timeout(10000) 768 | ok, err = conn:connect("127.0.0.1") 769 | 770 | if not ok then 771 | ngx.say("connect failed: "..err) 772 | end 773 | 774 | local db = conn:new_db_handle("test") 775 | local col = db:get_col("test") 776 | 777 | r = db:auth("admin", "admin") 778 | if not r then ngx.say("auth failed") end 779 | 780 | r, err = col:delete({}, nil, true) 781 | if not r then ngx.say("delete failed: "..err) end 782 | 783 | local t = {a=1,b=2} 784 | 785 | r, err = col:insert({{name="dog",n="10"},tab=t}, nil, true) 786 | if not r then ngx.say("insert failed: "..err) end 787 | 788 | r = col:find_one({name="dog"}) 789 | ngx.ctx.foo = r 790 | '; 791 | content_by_lua ' 792 | local search = ngx.ctx.foo 793 | ngx.say(search["n"]) 794 | '; 795 | } 796 | --- request 797 | GET /t 798 | --- response_body 799 | 10 800 | --- no_error_log 801 | [error] 802 | 803 | === TEST 21: query by skip and retnum 804 | --- http_config eval: $::HttpConfig 805 | --- config 806 | location /t { 807 | content_by_lua ' 808 | local mongo = require "resty.mongol" 809 | conn = mongo:new() 810 | conn:set_timeout(10000) 811 | ok, err = conn:connect("127.0.0.1") 812 | 813 | if not ok then 814 | ngx.say("connect failed: "..err) 815 | end 816 | 817 | local db = conn:new_db_handle("test") 818 | local col = db:get_col("test") 819 | 820 | r = db:auth("admin", "admin") 821 | if not r then ngx.say("auth failed") end 822 | 823 | r, err = col:delete({}, nil, true) 824 | if not r then ngx.say("delete failed: "..err) end 825 | 826 | local t = {a=1,b=2} 827 | 828 | for i = 1, 10 do 829 | col:insert({{name="puppy"}}) 830 | end 831 | 832 | sel = {name="puppy"} 833 | id, results, t = col:query(sel,{_id=1},0,1) 834 | ngx.say(#results) 835 | 836 | id, results, t = col:query(sel,{_id=1},5,5) 837 | ngx.say(#results) 838 | 839 | conn:close() 840 | '; 841 | } 842 | --- request 843 | GET /t 844 | --- response_body 845 | 1 846 | 5 847 | --- no_error_log 848 | [error] 849 | 850 | --------------------------------------------------------------------------------