Esempio n. 1
0
static liHandlerResult expire(liVRequest *vr, gpointer param, gpointer *context) {
	struct tm tm;
	time_t expire_date;
	guint len;
	gint max_age;
	GString *date_str = vr->wrk->tmp_str;
	expire_rule *rule = param;
	guint num = rule->num;
	time_t now = (time_t)li_cur_ts(vr->wrk);

	UNUSED(context);

	if (rule->base == EXPIRE_ACCESS) {
		expire_date = now + num;
		max_age = num;
	} else {
		/* modification */
		struct stat st;
		gint err;

		if (!vr->physical.path->len)
			return LI_HANDLER_GO_ON;

		switch (li_stat_cache_get(vr, vr->physical.path, &st, &err, NULL)) {
		case LI_HANDLER_GO_ON: break;
		case LI_HANDLER_WAIT_FOR_EVENT: return LI_HANDLER_WAIT_FOR_EVENT;
		default: return LI_HANDLER_GO_ON;
		}

		expire_date = st.st_mtime + num;

		if (expire_date < now)
			expire_date = now;

		max_age = expire_date - now;
	}

	/* format date */
	g_string_set_size(date_str, 255);

	if (!gmtime_r(&expire_date, &tm)) {
		VR_ERROR(vr, "gmtime_r(%"G_GUINT64_FORMAT") failed: %s", (guint64)expire_date, g_strerror(errno));
		return LI_HANDLER_GO_ON;
	}

	len = strftime(date_str->str, date_str->allocated_len, "%a, %d %b %Y %H:%M:%S GMT", &tm);
	if (len == 0)
		return LI_HANDLER_GO_ON;

	g_string_set_size(date_str, len);

	/* finally set the headers */
	li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Expires"), GSTR_LEN(date_str));
	g_string_truncate(date_str, 0);
	g_string_append_len(date_str, CONST_STR_LEN("max-age="));
	li_string_append_int(date_str, max_age);
	li_http_header_append(vr->response.headers, CONST_STR_LEN("Cache-Control"), GSTR_LEN(date_str));

	return LI_HANDLER_GO_ON;
}
Esempio n. 2
0
/* st, res, errno, msg = vr:stat(filename)
 *  st: stat data (nil if not available (yet))
 *  res: error code (HANDLE_GO_ON if successful)
 *  errno: errno returned by stat() (only for HANDLER_ERROR)
 *  msg: error message for errno
 */
static int lua_vrequest_stat(lua_State *L) {
	liVRequest *vr;
	GString path;
	const char *filename;
	size_t filename_len;
	liHandlerResult res;
	int err = 0;
	struct stat st;

	if (lua_gettop(L) != 2) {
		lua_pushstring(L, "vr:stat(filename): incorrect number of arguments");
		lua_error(L);
	}

	vr = li_lua_get_vrequest(L, 1);
	if (!vr || !lua_isstring(L, 2)) {
		lua_pushstring(L, "vr:stat(filename): wrong argument types");
		lua_error(L);
	}

	filename = lua_tolstring(L, 2, &filename_len);
	path = li_const_gstring(filename, filename_len);

	res = li_stat_cache_get(vr, &path, &st, &err, NULL);
	switch (res) {
	case LI_HANDLER_GO_ON:
		li_lua_push_stat(L, &st);
		lua_pushinteger(L, res);
		return 2;
	case LI_HANDLER_WAIT_FOR_EVENT:
		lua_pushnil(L);
		lua_pushinteger(L, res);
		return 2;
	case LI_HANDLER_ERROR:
		lua_pushnil(L);
		lua_pushinteger(L, res);
		lua_pushinteger(L, err);
		lua_pushstring(L, g_strerror(err));
		return 4;
	case LI_HANDLER_COMEBACK:
		VR_ERROR(vr, "%s", "Unexpected return value from li_stat_cache_get: LI_HANDLER_COMEBACK");
		lua_pushnil(L);
		lua_pushinteger(L, LI_HANDLER_ERROR);
		return 2;
	}

	return 0;
}
Esempio n. 3
0
static liHandlerResult lua_handle(liVRequest *vr, gpointer param, gpointer *context) {
	lua_config *conf = (lua_config*) param;
	lua_worker_config *wc;
	gboolean timeout = FALSE;
	liHandlerResult res;
	UNUSED(context);

	wc = &conf->worker_config[vr->wrk->ndx];

	if (wc->act) timeout = (conf->ttl > 0 && wc->ts_loaded + conf->ttl < li_cur_ts(vr->wrk));

	if (!wc->act || timeout) {
		int err;
		struct stat st;
		time_t last_load;

		res = li_stat_cache_get(vr, conf->filename, &st, &err, NULL);
		switch (res) {
		case LI_HANDLER_ERROR:
			VR_ERROR(vr, "lua.handler: couldn't stat file '%s': %s", conf->filename->str, g_strerror(err));
			return LI_HANDLER_ERROR;
		case LI_HANDLER_WAIT_FOR_EVENT:
			return LI_HANDLER_WAIT_FOR_EVENT;
		default:
			break;
		}

		last_load = wc->ts_loaded;
		wc->ts_loaded = li_cur_ts(vr->wrk);
		if (timeout && st.st_mtime <= last_load) {
			goto loaded;
		}

		li_action_release(vr->wrk->srv, wc->act);
		wc->act = NULL;
		if (!li_config_lua_load(&vr->wrk->LL, vr->wrk->srv, vr->wrk, conf->filename->str, &wc->act, FALSE, conf->args) || !wc->act) {
			VR_ERROR(vr, "lua.handler: couldn't load '%s'", conf->filename->str);
			return LI_HANDLER_ERROR;
		}
	}

loaded:
	li_action_enter(vr, wc->act);

	return LI_HANDLER_GO_ON;
}
Esempio n. 4
0
static liHandlerResult flv(liVRequest *vr, gpointer param, gpointer *context) {
    gchar *start;
    guint len;
    goffset pos;
    liHandlerResult res;
    gboolean cachable;
    struct stat st;
    int err;
    int fd = -1;

    UNUSED(context);
    UNUSED(param);

    if (li_vrequest_is_handled(vr))
        return LI_HANDLER_GO_ON;

    res = li_stat_cache_get(vr, vr->physical.path, &st, &err, &fd);

    if (res == LI_HANDLER_WAIT_FOR_EVENT)
        return res;

    if (res == LI_HANDLER_ERROR) {
        /* open or fstat failed */

        if (fd != -1)
            close(fd);

        if (!li_vrequest_handle_direct(vr))
            return LI_HANDLER_ERROR;

        switch (err) {
        case ENOENT:
        case ENOTDIR:
            vr->response.http_status = 404;
            return LI_HANDLER_GO_ON;
        case EACCES:
            vr->response.http_status = 403;
            return LI_HANDLER_GO_ON;
        default:
            VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err));
            return LI_HANDLER_ERROR;
        }
    } else if (S_ISDIR(st.st_mode)) {
        if (fd != -1)
            close(fd);

        return LI_HANDLER_GO_ON;
    } else if (!S_ISREG(st.st_mode)) {
        if (fd != -1)
            close(fd);

        if (!li_vrequest_handle_direct(vr))
            return LI_HANDLER_ERROR;

        vr->response.http_status = 403;
    } else {
        liChunkFile *cf;

#ifdef FD_CLOEXEC
        fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif

        if (!li_vrequest_handle_direct(vr)) {
            close(fd);
            return LI_HANDLER_ERROR;
        }

        if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("start"), &start, &len)) {
            guint i;
            pos = 0;

            for (i = 0; i < len; i++) {
                if (start[i] >= '0' && start[i] <= '9') {
                    pos *= 10;
                    pos += start[i] - '0';
                }
            }
        } else {
            pos = 0;
        }

        li_etag_set_header(vr, &st, &cachable);
        if (cachable) {
            vr->response.http_status = 304;
            close(fd);
            return LI_HANDLER_GO_ON;
        }


        vr->response.http_status = 200;
        li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));

        if (pos < 0 || pos > st.st_size)
            pos = 0;

        if (pos != 0)
            li_chunkqueue_append_mem(vr->direct_out, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));

        cf = li_chunkfile_new(NULL, fd, FALSE);
        li_chunkqueue_append_chunkfile(vr->direct_out, cf, pos, st.st_size - pos);
        li_chunkfile_release(cf);
    }

    return LI_HANDLER_GO_ON;
}
Esempio n. 5
0
static liHandlerResult cache_etag_handle(liVRequest *vr, gpointer param, gpointer *context) {
	cache_etag_context *ctx = (cache_etag_context*) param;
	cache_etag_file *cfile = (cache_etag_file*) *context;
	GList *etag_entry;
	liHttpHeader *etag;
	struct stat st;
	GString *tmp_str = vr->wrk->tmp_str;
	liHandlerResult res;
	int err, fd;

	if (!cfile) {
		if (vr->request.http_method != LI_HTTP_METHOD_GET) return LI_HANDLER_GO_ON;

		LI_VREQUEST_WAIT_FOR_RESPONSE_HEADERS(vr);

		if (vr->response.http_status != 200) return LI_HANDLER_GO_ON;

		/* Don't cache static files if filter list is empty */
		if (NULL == vr->filters_out_first && vr->backend_source->out->is_closed && 0 == vr->backend_source->out->mem_usage)
			return LI_HANDLER_GO_ON;

		etag_entry = li_http_header_find_first(vr->response.headers, CONST_STR_LEN("etag"));
		if (!etag_entry) return LI_HANDLER_GO_ON; /* no etag -> no caching */
		if (li_http_header_find_next(etag_entry, CONST_STR_LEN("etag"))) {
			VR_ERROR(vr, "%s", "duplicate etag header in response, will not cache it");
			return LI_HANDLER_GO_ON;
		}
		etag = (liHttpHeader*) etag_entry->data;

		cfile = cache_etag_file_create(createFileName(vr, ctx->path, etag));
		*context = cfile;
	}

	res = li_stat_cache_get(vr, cfile->filename, &st, &err, &fd);
	if (res == LI_HANDLER_WAIT_FOR_EVENT)
		return res;

	if (res == LI_HANDLER_GO_ON) {
		liFilter *f;
		if (!S_ISREG(st.st_mode)) {
			VR_ERROR(vr, "Unexpected file type for cache file '%s' (mode %o)", cfile->filename->str, (unsigned int) st.st_mode);
			close(fd);
			return LI_HANDLER_GO_ON; /* no caching */
		}
		cfile->hit_fd = fd;
#ifdef FD_CLOEXEC
		fcntl(cfile->hit_fd, F_SETFD, FD_CLOEXEC);
#endif
		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "cache hit for '%s'", vr->request.uri.path->str);
		}
		cfile->hit_length = st.st_size;
		g_string_truncate(tmp_str, 0);
		li_string_append_int(tmp_str, st.st_size);
		li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(tmp_str));
		f = li_vrequest_add_filter_out(vr, cache_etag_filter_hit, NULL, NULL, NULL);
		if (NULL != f) {
			li_chunkqueue_append_file_fd(f->out, NULL, 0, cfile->hit_length, cfile->hit_fd);
			f->out->is_closed = TRUE;
			cfile->hit_fd = -1;
		}
		cache_etag_file_free(cfile);

		*context = NULL;
		return LI_HANDLER_GO_ON;
	}

	if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
		VR_DEBUG(vr, "cache miss for '%s'", vr->request.uri.path->str);
	}

	if (!cache_etag_file_start(vr, cfile)) {
		cache_etag_file_free(cfile);
		*context = NULL;
		return LI_HANDLER_GO_ON; /* no caching */
	}

	li_vrequest_add_filter_out(vr, cache_etag_filter_miss, cache_etag_filter_free, NULL, cfile);
	*context = NULL;

	return LI_HANDLER_GO_ON;
}