Exemple #1
0
int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
	lua_State *L;
	int ret = -1;
	buffer *b;

	b = buffer_init();
	/* push the lua file to the interpreter and see what happends */
	L = luaL_newstate();
	luaL_openlibs(L);

	/* register functions */
	lua_register(L, "md5", f_crypto_md5);
	lua_register(L, "file_mtime", f_file_mtime);
	lua_register(L, "file_isreg", f_file_isreg);
	lua_register(L, "file_isdir", f_file_isreg);
	lua_register(L, "dir_files", f_dir_files);

#ifdef USE_MEMCACHED
	lua_pushlightuserdata(L, p->conf.memc);
	lua_pushcclosure(L, f_memcache_get_long, 1);
	lua_setglobal(L, "memcache_get_long");

	lua_pushlightuserdata(L, p->conf.memc);
	lua_pushcclosure(L, f_memcache_get_string, 1);
	lua_setglobal(L, "memcache_get_string");

	lua_pushlightuserdata(L, p->conf.memc);
	lua_pushcclosure(L, f_memcache_exists, 1);
	lua_setglobal(L, "memcache_exists");
#endif

	/* register CGI environment */
	lua_newtable(L);
	{
		int header_tbl = lua_gettop(L);

		c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
		if (!buffer_string_is_empty(con->request.pathinfo)) {
			c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
		}

		c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
	}
	lua_setglobal(L, "request");

	/* register GET parameter */
	lua_newtable(L);
	{
		int get_tbl = lua_gettop(L);

		buffer_copy_buffer(b, con->uri.query);
		cache_export_get_params(L, get_tbl, b);
		buffer_reset(b);
	}
	lua_setglobal(L, "get");

	/* 2 default constants */
	lua_pushinteger(L, 0);
	lua_setglobal(L, "CACHE_HIT");

	lua_pushinteger(L, 1);
	lua_setglobal(L, "CACHE_MISS");

	/* load lua program */
	ret = luaL_loadfile(L, fn->ptr);
	if (0 != ret) {
		log_error_write(srv, __FILE__, __LINE__, "sbsS",
			"failed loading cml_lua script",
			fn,
			":",
			lua_tostring(L, -1));
		goto error;
	}

	if (lua_pcall(L, 0, 1, 0)) {
		log_error_write(srv, __FILE__, __LINE__, "sbsS",
			"failed running cml_lua script",
			fn,
			":",
			lua_tostring(L, -1));
		goto error;
	}

	/* get return value */
	ret = (int)lua_tointeger(L, -1);
	lua_pop(L, 1);

	/* fetch the data from lua */
	lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);

	if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
	}

	if (ret == 0) {
		/* up to now it is a cache-hit, check if all files exist */

		int curelem;
		time_t mtime = 0;

		if (!lua_to_c_is_table(L, "output_include")) {
			log_error_write(srv, __FILE__, __LINE__, "s",
				"output_include is missing or not a table");
			ret = -1;

			goto error;
		}

		lua_getglobal(L, "output_include");
		curelem = lua_gettop(L);

		/* HOW-TO build a etag ?
		 * as we don't just have one file we have to take the stat()
		 * from all base files, merge them and build the etag from
		 * it later.
		 *
		 * The mtime of the content is the mtime of the freshest base file
		 *
		 * */

		lua_pushnil(L);  /* first key */
		while (lua_next(L, curelem) != 0) {
			/* key' is at index -2 and value' at index -1 */

			if (lua_isstring(L, -1)) {
				const char *s = lua_tostring(L, -1);
				struct stat st;
				int fd;

				/* the file is relative, make it absolute */
				if (s[0] != '/') {
					buffer_copy_buffer(b, p->basedir);
					buffer_append_string(b, lua_tostring(L, -1));
				} else {
					buffer_copy_string(b, lua_tostring(L, -1));
				}

				fd = stat_cache_open_rdonly_fstat(srv, con, b, &st);
				if (fd < 0) {
					/* stat failed */

					switch(errno) {
					case ENOENT:
						/* a file is missing, call the handler to generate it */
						if (!buffer_string_is_empty(p->trigger_handler)) {
							ret = 1; /* cache-miss */

							log_error_write(srv, __FILE__, __LINE__, "s",
									"a file is missing, calling handler");

							break;
						} else {
							/* handler not set -> 500 */
							ret = -1;

							log_error_write(srv, __FILE__, __LINE__, "s",
									"a file missing and no handler set");

							break;
						}
						break;
					default:
						break;
					}
				} else {
					chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
					if (st.st_mtime > mtime) mtime = st.st_mtime;
				}
			} else {
				/* not a string */
				ret = -1;
				log_error_write(srv, __FILE__, __LINE__, "s",
						"not a string");
				break;
			}

			lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
		}

		lua_settop(L, curelem - 1);

		if (ret == 0) {
			data_string *ds;
			char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];

			con->file_finished = 1;

			ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
			if (0 == mtime) mtime = time(NULL); /* default last-modified to now */

			/* no Last-Modified specified */
			if (NULL == ds) {

				strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));

				response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
				ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
				force_assert(NULL != ds);
			}

			if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, ds->value)) {
				/* ok, the client already has our content,
				 * no need to send it again */

				chunkqueue_reset(con->write_queue);
				ret = 0; /* cache-hit */
			}
		} else {
			chunkqueue_reset(con->write_queue);
		}
	}

	if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
		/* cache-miss */
		buffer_copy_buffer(con->uri.path, p->baseurl);
		buffer_append_string_buffer(con->uri.path, p->trigger_handler);

		buffer_copy_buffer(con->physical.path, p->basedir);
		buffer_append_string_buffer(con->physical.path, p->trigger_handler);

		chunkqueue_reset(con->write_queue);
	}

error:
	lua_close(L);

	buffer_free(b);

	return ret /* cache-error */;
}
void http_response_send_file (server *srv, connection *con, buffer *path) {
    stat_cache_entry *sce = NULL;
    buffer *mtime = NULL;
    data_string *ds;
    int allow_caching = (0 == con->http_status || 200 == con->http_status);

    if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) {
        con->http_status = (errno == ENOENT) ? 404 : 403;

        log_error_write(srv, __FILE__, __LINE__, "sbsb",
                        "not a regular file:", con->uri.path,
                        "->", path);

        return;
    }

    /* we only handline regular files */
#ifdef HAVE_LSTAT
    if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
        con->http_status = 403;

        if (con->conf.log_request_handling) {
            log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied due symlink restriction");
            log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", path);
        }

        return;
    }
#endif
    if (!S_ISREG(sce->st.st_mode)) {
        con->http_status = 403;

        if (con->conf.log_file_not_found) {
            log_error_write(srv, __FILE__, __LINE__, "sbsb",
                            "not a regular file:", con->uri.path,
                            "->", sce->name);
        }

        return;
    }

    /* mod_compress might set several data directly, don't overwrite them */

    /* set response content-type, if not set already */

    if (NULL == array_get_element(con->response.headers, "Content-Type")) {
        if (buffer_string_is_empty(sce->content_type)) {
            /* we are setting application/octet-stream, but also announce that
             * this header field might change in the seconds few requests
             *
             * This should fix the aggressive caching of FF and the script download
             * seen by the first installations
             */
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));

            allow_caching = 0;
        } else {
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
        }
    }

    if (con->conf.range_requests) {
        response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
    }

    if (allow_caching) {
        if (con->etag_flags != 0 && !buffer_string_is_empty(sce->etag)) {
            if (NULL == array_get_element(con->response.headers, "ETag")) {
                /* generate e-tag */
                etag_mutate(con->physical.etag, sce->etag);

                response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
            }
        }

        /* prepare header */
        if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
            mtime = strftime_cache_get(srv, sce->st.st_mtime);
            response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
        } else {
            mtime = ds->value;
        }

        if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
            return;
        }
    }

    if (con->request.http_range && con->conf.range_requests
            && (200 == con->http_status || 0 == con->http_status)
            && NULL == array_get_element(con->response.headers, "Content-Encoding")) {
        int do_range_request = 1;
        /* check if we have a conditional GET */

        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
            /* if the value is the same as our ETag, we do a Range-request,
             * otherwise a full 200 */

            if (ds->value->ptr[0] == '"') {
                /**
                 * client wants a ETag
                 */
                if (!con->physical.etag) {
                    do_range_request = 0;
                } else if (!buffer_is_equal(ds->value, con->physical.etag)) {
                    do_range_request = 0;
                }
            } else if (!mtime) {
                /**
                 * we don't have a Last-Modified and can match the If-Range:
                 *
                 * sending all
                 */
                do_range_request = 0;
            } else if (!buffer_is_equal(ds->value, mtime)) {
                do_range_request = 0;
            }
        }

        if (do_range_request) {
            /* content prepared, I'm done */
            con->file_finished = 1;

            if (0 == http_response_parse_range(srv, con, path, sce)) {
                con->http_status = 206;
            }
            return;
        }
    }

    /* if we are still here, prepare body */

    /* we add it here for all requests
     * the HEAD request will drop it afterwards again
     */
    if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) {
        con->http_status = 200;
        con->file_finished = 1;
    } else {
        con->http_status = 403;
    }
}