Exemplo 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;
}
Exemplo n.º 2
0
static GString *al_format_log(liVRequest *vr, al_data *ald, GArray *format) {
	GString *str = g_string_sized_new(255);
	liResponse *resp = &vr->response;
	liRequest *req = &vr->request;
	liPhysical *phys = &vr->physical;

	for (guint i = 0; i < format->len; i++) {
		GString *tmp_gstr2 = NULL;
		gchar *tmp_str = NULL;
		guint len = 0;

		al_format_entry *e = &g_array_index(format, al_format_entry, i);
		if (e->type == AL_ENTRY_FORMAT) {
			switch (e->format.type) {
			case AL_FORMAT_PERCENT:
				g_string_append_c(str, '%');
				break;
			case AL_FORMAT_REMOTE_ADDR:
				g_string_append_len(str, GSTR_LEN(vr->coninfo->remote_addr_str));
				break;
			case AL_FORMAT_LOCAL_ADDR:
				g_string_append_len(str, GSTR_LEN(vr->coninfo->local_addr_str));
				break;
			case AL_FORMAT_BYTES_RESPONSE:
				li_string_append_int(str, vr->vr_out->bytes_out);
				break;
			case AL_FORMAT_BYTES_RESPONSE_CLF:
				if (vr->vr_out->bytes_out)
					li_string_append_int(str, vr->vr_out->bytes_out);
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_DURATION_MICROSECONDS:
				li_string_append_int(str, (CUR_TS(vr->wrk) - vr->ts_started) * 1000 * 1000);
				break;
			case AL_FORMAT_ENV:
				tmp_gstr2 = li_environment_get(&vr->env, GSTR_LEN(e->key));
				if (tmp_gstr2)
					al_append_escaped(str, tmp_gstr2);
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_FILENAME:
				if (phys->path->len)
					g_string_append_len(str, GSTR_LEN(phys->path));
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_REQUEST_HEADER:
				li_http_header_get_all(vr->wrk->tmp_str, req->headers, GSTR_LEN(e->key));
				if (vr->wrk->tmp_str->len)
					al_append_escaped(str, vr->wrk->tmp_str);
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_METHOD:
				g_string_append_len(str, GSTR_LEN(req->http_method_str));
				break;
			case AL_FORMAT_RESPONSE_HEADER:
				li_http_header_get_all(vr->wrk->tmp_str, resp->headers, GSTR_LEN(e->key));
				if (vr->wrk->tmp_str->len)
					al_append_escaped(str, vr->wrk->tmp_str);
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_LOCAL_PORT:
				switch (vr->coninfo->local_addr.addr->plain.sa_family) {
				case AF_INET: li_string_append_int(str, ntohs(vr->coninfo->local_addr.addr->ipv4.sin_port)); break;
				#ifdef HAVE_IPV6
				case AF_INET6: li_string_append_int(str, ntohs(vr->coninfo->local_addr.addr->ipv6.sin6_port)); break;
				#endif
				default: g_string_append_c(str, '-'); break;
				}
				break;
			case AL_FORMAT_QUERY_STRING:
				if (req->uri.query->len)
					al_append_escaped(str, req->uri.query);
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_FIRST_LINE:
				g_string_append_len(str, GSTR_LEN(req->http_method_str));
				g_string_append_c(str, ' ');
				al_append_escaped(str, req->uri.raw_orig_path);
				g_string_append_c(str, ' ');
				tmp_str = li_http_version_string(req->http_version, &len);
				g_string_append_len(str, tmp_str, len);
				break;
			case AL_FORMAT_STATUS_CODE:
				li_string_append_int(str, resp->http_status);
				break;
			case AL_FORMAT_TIME:
				/* todo: implement format string */
				tmp_gstr2 = li_worker_current_timestamp(vr->wrk, LI_LOCALTIME, ald->ts_ndx);
				g_string_append_len(str, GSTR_LEN(tmp_gstr2));
				break;
			case AL_FORMAT_DURATION_SECONDS:
				li_string_append_int(str, CUR_TS(vr->wrk) - vr->ts_started);
				break;
			case AL_FORMAT_AUTHED_USER:
				tmp_gstr2 = li_environment_get(&vr->env, CONST_STR_LEN("REMOTE_USER"));
				if (tmp_gstr2)
					g_string_append_len(str, GSTR_LEN(tmp_gstr2));
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_PATH:
				g_string_append_len(str, GSTR_LEN(req->uri.path));
				break;
			case AL_FORMAT_SERVER_NAME:
				if (CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_NAME).string)
					g_string_append_len(str, GSTR_LEN(CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_NAME).string));
				else
					g_string_append_len(str, GSTR_LEN(req->uri.host));
				break;
			case AL_FORMAT_HOSTNAME:
				if (req->uri.host->len)
					g_string_append_len(str, GSTR_LEN(req->uri.host));
				else
					g_string_append_c(str, '-');
				break;
			case AL_FORMAT_CONNECTION_STATUS: {
					/* was request completed? */
					liConnection *con = li_connection_from_vrequest(vr); /* try to get a connection object */

					if (con && (con->in->is_closed && con->raw_out->is_closed && 0 == con->raw_out->length)) {
						g_string_append_c(str, 'X');
					} else {
						g_string_append_c(str, vr->coninfo->keep_alive ? '+' : '-');
					}
				}
				break;
			case AL_FORMAT_BYTES_IN:
				li_string_append_int(str, vr->coninfo->stats.bytes_in);
				break;
			case AL_FORMAT_BYTES_OUT:
				li_string_append_int(str, vr->coninfo->stats.bytes_out);
				break;
			default:
				/* not implemented:
				{ 'C', FALSE, AL_FORMAT_COOKIE }
				{ 't', FALSE, AL_FORMAT_TIME }, (partially implemented)
				*/
				g_string_append_c(str, '?');
				break;
			}
		} else {
			/* append normal string */
			g_string_append_len(str, GSTR_LEN(e->key));
		}
	}

	return str;
}
Exemplo n.º 3
0
static liHandlerResult dirlist(liVRequest *vr, gpointer param, gpointer *context) {
	GString *listing;
	liStatCacheEntry *sce;
	dirlist_data *dd;

	UNUSED(context);

	switch (vr->request.http_method) {
	case LI_HTTP_METHOD_GET:
	case LI_HTTP_METHOD_HEAD:
		break;
	default:
		return LI_HANDLER_GO_ON;
	}

	if (li_vrequest_is_handled(vr)) return LI_HANDLER_GO_ON;

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

	switch (li_stat_cache_get_dirlist(vr, vr->physical.path, &sce)) {
	case LI_HANDLER_GO_ON: break;
	case LI_HANDLER_WAIT_FOR_EVENT: return LI_HANDLER_WAIT_FOR_EVENT;
	default: return LI_HANDLER_ERROR;
	}

	dd = param;

	if (sce->data.failed) {
		/* stat failed */
		int e = sce->data.err;

		li_stat_cache_entry_release(vr, sce);

		switch (e) {
		case ENOENT:
		case ENOTDIR:
			return LI_HANDLER_GO_ON;
		case EACCES:
			if (!li_vrequest_handle_direct(vr)) return LI_HANDLER_ERROR;
			vr->response.http_status = 403;
			return LI_HANDLER_GO_ON;
		default:
			VR_ERROR(vr, "stat('%s') failed: %s", sce->data.path->str, g_strerror(sce->data.err));
			return LI_HANDLER_ERROR;
		}
	} else if (!S_ISDIR(sce->data.st.st_mode)) {
		li_stat_cache_entry_release(vr, sce);
		return LI_HANDLER_GO_ON;
	} else if (vr->request.uri.path->len == 0 || vr->request.uri.path->str[vr->request.uri.path->len-1] != '/') {
		li_stat_cache_entry_release(vr, sce);
		li_vrequest_redirect_directory(vr);
		return LI_HANDLER_GO_ON;
	} else {
		/* everything ok, we have the directory listing */
		gboolean cachable;
		guint i, j;
		liStatCacheEntryData *sced;
		GString *mime_str, *encoded;
		GArray *directories, *files;
		gchar sizebuf[sizeof("999.9K")+1];
		gchar datebuf[sizeof("2005-Jan-01 22:23:24")+1];
		guint datebuflen;
		struct tm tm;
		gboolean hide;

		if (!li_vrequest_handle_direct(vr)) {
			li_stat_cache_entry_release(vr, sce);
			return LI_HANDLER_ERROR;
		}
		vr->response.http_status = 200;

		if (dd->debug)
			VR_DEBUG(vr, "dirlist for \"%s\", %u entries", sce->data.path->str, sce->dirlist->len);

		li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(dd->content_type));
		li_etag_set_header(vr, &sce->data.st, &cachable);
		if (cachable) {
			vr->response.http_status = 304;
			li_stat_cache_entry_release(vr, sce);
			return LI_HANDLER_GO_ON;
		}

		/* temporary string for encoded names */
		encoded = g_string_sized_new(64-1);

		/* seperate directories from other files */
		directories = g_array_sized_new(FALSE, FALSE, sizeof(guint), 16);
		files = g_array_sized_new(FALSE, FALSE, sizeof(guint), sce->dirlist->len);
		for (i = 0; i < sce->dirlist->len; i++) {
			sced = &g_array_index(sce->dirlist, liStatCacheEntryData, i);
			hide = FALSE;

			/* ingore entries where the stat() failed */
			if (sced->failed)
				continue;

			if (dd->hide_dotfiles && sced->path->str[0] == '.')
				continue;

			if (dd->hide_tildefiles && sced->path->str[sced->path->len-1] == '~')
				continue;

			for (j = 0; j < dd->exclude_suffix->len; j++) {
				if (li_string_suffix(sced->path, GSTR_LEN((GString*)g_ptr_array_index(dd->exclude_suffix, j)))) {
					hide = TRUE;
					break;
				}
			}

			if (hide)
				continue;

			for (j = 0; j < dd->exclude_prefix->len; j++) {
				if (li_string_prefix(sced->path, GSTR_LEN((GString*)g_ptr_array_index(dd->exclude_prefix, j)))) {
					hide = TRUE;
					break;
				}
			}

			if (hide)
				continue;

			if (S_ISDIR(sced->st.st_mode)) {
				if (dd->hide_directories) continue;
				g_array_append_val(directories, i);
			} else {
				if ((dd->include_header || dd->hide_header) && g_str_equal(sced->path, "HEADER.txt")) {
					if (dd->hide_header) continue;
				} else if ((dd->include_readme || dd->hide_readme) && g_str_equal(sced->path, "README.txt")) {
					if (dd->hide_readme) continue;
				}
				g_array_append_val(files, i);
			}
		}

		listing = g_string_sized_new(4*1024-1);
		g_string_append_printf(listing, html_header_start, vr->request.uri.path->str);

		if (dd->css) {
			/* custom css */
			g_string_append_len(listing, CONST_STR_LEN("		<link rel=\"stylesheet\" type=\"text/css\" href=\""));
			g_string_append_len(listing, GSTR_LEN(dd->css));
			g_string_append_len(listing, CONST_STR_LEN("\" />\n"));
		} else {
			/* default css */
			g_string_append_len(listing, CONST_STR_LEN(html_css));
		}
		g_string_append_len(listing, CONST_STR_LEN(html_header_end));

		try_append_file(vr, &listing, "HEADER.txt", dd->encode_header);

		g_string_append_printf(listing, html_table_start, vr->request.uri.path->str);

		if (0 != strcmp("/", vr->request.uri.path->str)) {
			g_string_append_printf(listing, html_table_row, '0', "../",
				"Parent Directory", (gint64)0, "", (gint64)0, "-", "Directory");
		}

		/* list directories */
		if (!dd->hide_directories) {
			for (i = 0; i < directories->len; i++) {
				sced = &g_array_index(sce->dirlist, liStatCacheEntryData, g_array_index(directories, guint, i));

				localtime_r(&(sced->st.st_mtime), &tm);
				datebuflen = strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
				datebuf[datebuflen] = '\0';

				g_string_append_len(listing, CONST_STR_LEN("				<tr group=\"1\"><td><a href=\""));
				li_string_encode(sced->path->str, encoded, LI_ENCODING_URI);
				g_string_append_len(listing, GSTR_LEN(encoded));
				g_string_append_len(listing, CONST_STR_LEN("/\">"));
				li_string_encode(sced->path->str, encoded, LI_ENCODING_HTML);
				g_string_append_len(listing, GSTR_LEN(encoded));
				g_string_append_len(listing, CONST_STR_LEN("</a></td><td class=\"modified\" val=\""));
				li_string_append_int(listing, sced->st.st_mtime);
				g_string_append_len(listing, CONST_STR_LEN("\">"));
				g_string_append_len(listing, datebuf, datebuflen);
				g_string_append_len(listing, CONST_STR_LEN("</td>"
					"<td class=\"size\" val=\"0\">-</td>"
					"<td class=\"type\">Directory</td></tr>\n"));
			}
		}

		/*g_string_append_len(listing, CONST_STR_LEN("<tr><td colspan=\"4\">&nbsp;</td></tr>\n"));*/

		/* list files */
		for (i = 0; i < files->len; i++) {
			sced = &g_array_index(sce->dirlist, liStatCacheEntryData, g_array_index(files, guint, i));
			mime_str = li_mimetype_get(vr, sced->path);

			localtime_r(&(sced->st.st_mtime), &tm);
			datebuflen = strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
			datebuf[datebuflen] = '\0';

			dirlist_format_size(sizebuf, sced->st.st_size);

			g_string_append_len(listing, CONST_STR_LEN("				<tr group=\"2\"><td><a href=\""));
			li_string_encode(sced->path->str, encoded, LI_ENCODING_URI);
			g_string_append_len(listing, GSTR_LEN(encoded));
			g_string_append_len(listing, CONST_STR_LEN("\">"));
			li_string_encode(sced->path->str, encoded, LI_ENCODING_HTML);
			g_string_append_len(listing, GSTR_LEN(encoded));
			g_string_append_len(listing, CONST_STR_LEN(
				"</a></td>"
				"<td class=\"modified\" val=\""));
			li_string_append_int(listing, sced->st.st_mtime);
			g_string_append_len(listing, CONST_STR_LEN("\">"));
			g_string_append_len(listing, datebuf, datebuflen);
			g_string_append_len(listing, CONST_STR_LEN("</td><td class=\"size\" val=\""));
			li_string_append_int(listing, sced->st.st_size);
			g_string_append_len(listing, CONST_STR_LEN("\">"));
			g_string_append(listing, sizebuf);
			g_string_append_len(listing, CONST_STR_LEN("</td><td class=\"type\">"));
			if (mime_str) {
				g_string_append_len(listing, GSTR_LEN(mime_str));
			} else {
				g_string_append_len(listing, CONST_STR_LEN("application/octet-stream"));
			}
			g_string_append_len(listing, CONST_STR_LEN("</td></tr>\n"));

			/*
			g_string_append_printf(listing, html_table_row,
				sced->path->str, sced->path->str,
				(gint64)sced->st.st_mtime, datebuf,
				sced->st.st_size, sizebuf,
				mime_str ? mime_str->str : "application/octet-stream");
			*/
		}

		g_string_append_len(listing, CONST_STR_LEN(html_table_end));

		try_append_file(vr, &listing, "README.txt", dd->encode_readme);

		if (dd->include_sort) {
			g_string_append_len(listing, CONST_STR_LEN(javascript_sort));
		}

		g_string_append_printf(listing, html_footer, CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_TAG).string->str);

		li_chunkqueue_append_string(vr->direct_out, listing);
		g_string_free(encoded, TRUE);
		g_array_free(directories, TRUE);
		g_array_free(files, TRUE);
	}

	li_stat_cache_entry_release(vr, sce);

	return LI_HANDLER_GO_ON;
}
Exemplo n.º 4
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;
}