Annotation of embedaddon/lighttpd/src/mod_cml_lua.c, revision 1.1.1.3

1.1.1.3 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       3: #include "mod_cml.h"
                      4: #include "mod_cml_funcs.h"
                      5: #include "log.h"
                      6: #include "stream.h"
                      7: 
                      8: #include "stat_cache.h"
                      9: 
                     10: #include <assert.h>
                     11: #include <stdio.h>
                     12: #include <errno.h>
                     13: #include <time.h>
                     14: #include <string.h>
                     15: 
                     16: #define HASHLEN 16
                     17: typedef unsigned char HASH[HASHLEN];
                     18: #define HASHHEXLEN 32
                     19: typedef char HASHHEX[HASHHEXLEN+1];
                     20: #ifdef USE_OPENSSL
                     21: #define IN const
                     22: #else
                     23: #define IN
                     24: #endif
                     25: #define OUT
                     26: 
                     27: #ifdef HAVE_LUA_H
                     28: 
                     29: #include <lua.h>
                     30: #include <lualib.h>
                     31: #include <lauxlib.h>
                     32: 
                     33: static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
1.1.1.3 ! misho      34:        int curelem = lua_gettop(L);
        !            35:        int result;
1.1       misho      36: 
1.1.1.3 ! misho      37:        lua_getglobal(L, varname);
1.1       misho      38: 
1.1.1.3 ! misho      39:        if (lua_isstring(L, curelem)) {
        !            40:                buffer_copy_string(b, lua_tostring(L, curelem));
        !            41:                result = 0;
        !            42:        } else {
        !            43:                result = -1;
1.1       misho      44:        }
                     45: 
                     46:        lua_pop(L, 1);
1.1.1.3 ! misho      47:        force_assert(curelem == lua_gettop(L));
        !            48:        return result;
1.1       misho      49: }
                     50: 
                     51: static int lua_to_c_is_table(lua_State *L, const char *varname) {
1.1.1.3 ! misho      52:        int curelem = lua_gettop(L);
        !            53:        int result;
1.1       misho      54: 
1.1.1.3 ! misho      55:        lua_getglobal(L, varname);
1.1       misho      56: 
1.1.1.3 ! misho      57:        result = lua_istable(L, curelem) ? 1 : 0;
1.1       misho      58: 
1.1.1.3 ! misho      59:        lua_pop(L, 1);
        !            60:        force_assert(curelem == lua_gettop(L));
        !            61:        return result;
1.1       misho      62: }
                     63: 
                     64: static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, const char *val, size_t val_len) {
                     65:        lua_pushlstring(L, key, key_len);
                     66:        lua_pushlstring(L, val, val_len);
                     67:        lua_settable(L, tbl);
                     68: 
                     69:        return 0;
                     70: }
                     71: 
                     72: static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
                     73:        size_t is_key = 1;
1.1.1.3 ! misho      74:        size_t i, len;
1.1       misho      75:        char *key = NULL, *val = NULL;
                     76: 
                     77:        key = qrystr->ptr;
                     78: 
                     79:        /* we need the \0 */
1.1.1.3 ! misho      80:        len = buffer_string_length(qrystr);
        !            81:        for (i = 0; i <= len; i++) {
1.1       misho      82:                switch(qrystr->ptr[i]) {
                     83:                case '=':
                     84:                        if (is_key) {
                     85:                                val = qrystr->ptr + i + 1;
                     86: 
                     87:                                qrystr->ptr[i] = '\0';
                     88: 
                     89:                                is_key = 0;
                     90:                        }
                     91: 
                     92:                        break;
                     93:                case '&':
                     94:                case '\0': /* fin symbol */
                     95:                        if (!is_key) {
                     96:                                /* we need at least a = since the last & */
                     97: 
                     98:                                /* terminate the value */
                     99:                                qrystr->ptr[i] = '\0';
                    100: 
                    101:                                c_to_lua_push(L, tbl,
1.1.1.3 ! misho     102:                                        key, strlen(key),
        !           103:                                        val, strlen(val));
1.1       misho     104:                        }
                    105: 
                    106:                        key = qrystr->ptr + i + 1;
                    107:                        val = NULL;
                    108:                        is_key = 1;
                    109:                        break;
                    110:                }
                    111:        }
                    112: 
                    113:        return 0;
                    114: }
                    115: 
                    116: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
                    117:        lua_State *L;
                    118:        int ret = -1;
1.1.1.2   misho     119:        buffer *b;
1.1       misho     120: 
1.1.1.2   misho     121:        b = buffer_init();
1.1       misho     122:        /* push the lua file to the interpreter and see what happends */
                    123:        L = luaL_newstate();
                    124:        luaL_openlibs(L);
                    125: 
                    126:        /* register functions */
                    127:        lua_register(L, "md5", f_crypto_md5);
                    128:        lua_register(L, "file_mtime", f_file_mtime);
                    129:        lua_register(L, "file_isreg", f_file_isreg);
                    130:        lua_register(L, "file_isdir", f_file_isreg);
                    131:        lua_register(L, "dir_files", f_dir_files);
                    132: 
1.1.1.3 ! misho     133: #ifdef USE_MEMCACHED
        !           134:        lua_pushlightuserdata(L, p->conf.memc);
1.1       misho     135:        lua_pushcclosure(L, f_memcache_get_long, 1);
1.1.1.3 ! misho     136:        lua_setglobal(L, "memcache_get_long");
1.1       misho     137: 
1.1.1.3 ! misho     138:        lua_pushlightuserdata(L, p->conf.memc);
1.1       misho     139:        lua_pushcclosure(L, f_memcache_get_string, 1);
1.1.1.3 ! misho     140:        lua_setglobal(L, "memcache_get_string");
1.1       misho     141: 
1.1.1.3 ! misho     142:        lua_pushlightuserdata(L, p->conf.memc);
1.1       misho     143:        lua_pushcclosure(L, f_memcache_exists, 1);
1.1.1.3 ! misho     144:        lua_setglobal(L, "memcache_exists");
1.1       misho     145: #endif
1.1.1.3 ! misho     146: 
1.1       misho     147:        /* register CGI environment */
                    148:        lua_newtable(L);
1.1.1.3 ! misho     149:        {
        !           150:                int header_tbl = lua_gettop(L);
1.1       misho     151: 
1.1.1.3 ! misho     152:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
        !           153:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
        !           154:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
        !           155:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
        !           156:                if (!buffer_string_is_empty(con->request.pathinfo)) {
        !           157:                        c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
        !           158:                }
1.1       misho     159: 
1.1.1.3 ! misho     160:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
        !           161:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
        !           162:        }
        !           163:        lua_setglobal(L, "request");
1.1       misho     164: 
                    165:        /* register GET parameter */
                    166:        lua_newtable(L);
1.1.1.3 ! misho     167:        {
        !           168:                int get_tbl = lua_gettop(L);
1.1       misho     169: 
1.1.1.3 ! misho     170:                buffer_copy_buffer(b, con->uri.query);
        !           171:                cache_export_get_params(L, get_tbl, b);
        !           172:                buffer_reset(b);
        !           173:        }
        !           174:        lua_setglobal(L, "get");
1.1       misho     175: 
                    176:        /* 2 default constants */
1.1.1.3 ! misho     177:        lua_pushinteger(L, 0);
        !           178:        lua_setglobal(L, "CACHE_HIT");
        !           179: 
        !           180:        lua_pushinteger(L, 1);
        !           181:        lua_setglobal(L, "CACHE_MISS");
1.1       misho     182: 
                    183:        /* load lua program */
1.1.1.3 ! misho     184:        ret = luaL_loadfile(L, fn->ptr);
        !           185:        if (0 != ret) {
        !           186:                log_error_write(srv, __FILE__, __LINE__, "sbsS",
        !           187:                        "failed loading cml_lua script",
        !           188:                        fn,
        !           189:                        ":",
        !           190:                        lua_tostring(L, -1));
        !           191:                goto error;
        !           192:        }
1.1       misho     193: 
1.1.1.3 ! misho     194:        if (lua_pcall(L, 0, 1, 0)) {
        !           195:                log_error_write(srv, __FILE__, __LINE__, "sbsS",
        !           196:                        "failed running cml_lua script",
        !           197:                        fn,
        !           198:                        ":",
        !           199:                        lua_tostring(L, -1));
1.1       misho     200:                goto error;
                    201:        }
                    202: 
                    203:        /* get return value */
1.1.1.3 ! misho     204:        ret = (int)lua_tointeger(L, -1);
1.1       misho     205:        lua_pop(L, 1);
                    206: 
                    207:        /* fetch the data from lua */
                    208:        lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
                    209: 
                    210:        if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
                    211:                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
                    212:        }
                    213: 
                    214:        if (ret == 0) {
                    215:                /* up to now it is a cache-hit, check if all files exist */
                    216: 
                    217:                int curelem;
                    218:                time_t mtime = 0;
                    219: 
                    220:                if (!lua_to_c_is_table(L, "output_include")) {
                    221:                        log_error_write(srv, __FILE__, __LINE__, "s",
                    222:                                "output_include is missing or not a table");
                    223:                        ret = -1;
                    224: 
                    225:                        goto error;
                    226:                }
                    227: 
1.1.1.3 ! misho     228:                lua_getglobal(L, "output_include");
1.1       misho     229:                curelem = lua_gettop(L);
                    230: 
                    231:                /* HOW-TO build a etag ?
                    232:                 * as we don't just have one file we have to take the stat()
                    233:                 * from all base files, merge them and build the etag from
                    234:                 * it later.
                    235:                 *
                    236:                 * The mtime of the content is the mtime of the freshest base file
                    237:                 *
                    238:                 * */
                    239: 
                    240:                lua_pushnil(L);  /* first key */
                    241:                while (lua_next(L, curelem) != 0) {
                    242:                        /* key' is at index -2 and value' at index -1 */
                    243: 
                    244:                        if (lua_isstring(L, -1)) {
                    245:                                const char *s = lua_tostring(L, -1);
1.1.1.3 ! misho     246:                                struct stat st;
        !           247:                                int fd;
1.1       misho     248: 
                    249:                                /* the file is relative, make it absolute */
                    250:                                if (s[0] != '/') {
1.1.1.3 ! misho     251:                                        buffer_copy_buffer(b, p->basedir);
1.1       misho     252:                                        buffer_append_string(b, lua_tostring(L, -1));
                    253:                                } else {
                    254:                                        buffer_copy_string(b, lua_tostring(L, -1));
                    255:                                }
                    256: 
1.1.1.3 ! misho     257:                                fd = stat_cache_open_rdonly_fstat(srv, con, b, &st);
        !           258:                                if (fd < 0) {
1.1       misho     259:                                        /* stat failed */
                    260: 
                    261:                                        switch(errno) {
                    262:                                        case ENOENT:
                    263:                                                /* a file is missing, call the handler to generate it */
1.1.1.3 ! misho     264:                                                if (!buffer_string_is_empty(p->trigger_handler)) {
1.1       misho     265:                                                        ret = 1; /* cache-miss */
                    266: 
                    267:                                                        log_error_write(srv, __FILE__, __LINE__, "s",
                    268:                                                                        "a file is missing, calling handler");
                    269: 
                    270:                                                        break;
                    271:                                                } else {
                    272:                                                        /* handler not set -> 500 */
                    273:                                                        ret = -1;
                    274: 
                    275:                                                        log_error_write(srv, __FILE__, __LINE__, "s",
                    276:                                                                        "a file missing and no handler set");
                    277: 
                    278:                                                        break;
                    279:                                                }
                    280:                                                break;
                    281:                                        default:
                    282:                                                break;
                    283:                                        }
                    284:                                } else {
1.1.1.3 ! misho     285:                                        chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
        !           286:                                        if (st.st_mtime > mtime) mtime = st.st_mtime;
1.1       misho     287:                                }
                    288:                        } else {
                    289:                                /* not a string */
                    290:                                ret = -1;
                    291:                                log_error_write(srv, __FILE__, __LINE__, "s",
                    292:                                                "not a string");
                    293:                                break;
                    294:                        }
                    295: 
                    296:                        lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
                    297:                }
                    298: 
                    299:                lua_settop(L, curelem - 1);
                    300: 
                    301:                if (ret == 0) {
                    302:                        data_string *ds;
                    303:                        char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
                    304: 
                    305:                        con->file_finished = 1;
                    306: 
                    307:                        ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
1.1.1.2   misho     308:                        if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
1.1       misho     309: 
                    310:                        /* no Last-Modified specified */
1.1.1.2   misho     311:                        if (NULL == ds) {
1.1       misho     312: 
                    313:                                strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
                    314: 
                    315:                                response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
1.1.1.3 ! misho     316:                                ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
        !           317:                                force_assert(NULL != ds);
1.1       misho     318:                        }
                    319: 
1.1.1.3 ! misho     320:                        if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, ds->value)) {
1.1       misho     321:                                /* ok, the client already has our content,
                    322:                                 * no need to send it again */
                    323: 
                    324:                                chunkqueue_reset(con->write_queue);
                    325:                                ret = 0; /* cache-hit */
                    326:                        }
                    327:                } else {
                    328:                        chunkqueue_reset(con->write_queue);
                    329:                }
                    330:        }
                    331: 
1.1.1.3 ! misho     332:        if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
1.1       misho     333:                /* cache-miss */
1.1.1.3 ! misho     334:                buffer_copy_buffer(con->uri.path, p->baseurl);
1.1       misho     335:                buffer_append_string_buffer(con->uri.path, p->trigger_handler);
                    336: 
1.1.1.3 ! misho     337:                buffer_copy_buffer(con->physical.path, p->basedir);
1.1       misho     338:                buffer_append_string_buffer(con->physical.path, p->trigger_handler);
                    339: 
                    340:                chunkqueue_reset(con->write_queue);
                    341:        }
                    342: 
                    343: error:
                    344:        lua_close(L);
                    345: 
                    346:        buffer_free(b);
                    347: 
                    348:        return ret /* cache-error */;
                    349: }
                    350: #else
                    351: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
                    352:        UNUSED(srv);
                    353:        UNUSED(con);
                    354:        UNUSED(p);
                    355:        UNUSED(fn);
                    356:        /* error */
                    357:        return -1;
                    358: }
                    359: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>