コード例 #1
0
ファイル: mod_vhost.c プロジェクト: giuse88/lighttpd2
static liHandlerResult vhost_map(liVRequest *vr, gpointer param, gpointer *context) {
	liValue *v;
	vhost_map_data *md = param;
	gboolean debug = _OPTION(vr, md->plugin, 0).boolean;
	UNUSED(context);

	v = g_hash_table_lookup(md->hash, vr->request.uri.host);

	if (NULL != v) {
		if (debug) {
			VR_DEBUG(vr, "vhost_map: host %s found in hashtable", vr->request.uri.host->str);
		}
		li_action_enter(vr, v->data.val_action.action);
	} else if (NULL != md->default_action) {
		if (debug) {
			VR_DEBUG(vr, "vhost_map: host %s not found in hashtable, executing default action", vr->request.uri.host->str);
		}
		li_action_enter(vr, md->default_action->data.val_action.action);
	} else {
		if (debug) {
			VR_DEBUG(vr, "vhost_map: neither host %s found in hashtable nor default action specified, doing nothing", vr->request.uri.host->str);
		}
	}

	return LI_HANDLER_GO_ON;
}
コード例 #2
0
ファイル: mod_vhost.c プロジェクト: giuse88/lighttpd2
static liHandlerResult vhost_map_regex(liVRequest *vr, gpointer param, gpointer *context) {
	guint i;
	vhost_map_regex_data *mrd = param;
	GArray *list = mrd->list;
	gboolean debug = _OPTION(vr, mrd->plugin, 0).boolean;
	liValue *v = NULL;
	vhost_map_regex_entry *entry = NULL;

	UNUSED(context);

	/* loop through all rules to find a match */
	for (i = 0; i < list->len; i++) {
		entry = &g_array_index(list, vhost_map_regex_entry, i);

		if (!g_regex_match(entry->regex, vr->request.uri.host->str, 0, NULL))
			continue;

		v = entry->action;

		break;
	}

	if (NULL != v) {
		if (debug) {
			VR_DEBUG(vr, "vhost_map_regex: host %s matches pattern \"%s\"", vr->request.uri.host->str, g_regex_get_pattern(entry->regex));
		}
		li_action_enter(vr, v->data.val_action.action);
	} else if (NULL != mrd->default_action) {
		if (debug) {
			VR_DEBUG(vr, "vhost_map_regex: host %s didn't match, executing default action", vr->request.uri.host->str);
		}
		li_action_enter(vr, mrd->default_action->data.val_action.action);
	} else {
		if (debug) {
			VR_DEBUG(vr, "vhost_map_regex: neither did %s match nor default action specified, doing nothing", vr->request.uri.host->str);
		}
	}

	return LI_HANDLER_GO_ON;
}
コード例 #3
0
ファイル: mod_lua.c プロジェクト: AlexShiLucky/lighttpd2
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;
}
コード例 #4
0
ファイル: virtualrequest_lua.c プロジェクト: Sciumo/lighttpd2
static int lua_vrequest_enter_action(lua_State *L) {
	liVRequest *vr;
	liAction *act;

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

	vr = li_lua_get_vrequest(L, 1);
	act = li_lua_get_action(L, 2);
	if (!vr || !act) {
		lua_pushstring(L, "wrong arguments");
		lua_error(L);
	}

	li_action_enter(vr, act);

	return 0;
}
コード例 #5
0
ファイル: connection.c プロジェクト: angel1991521/lighttpd2
/* tcp/ssl -> http "parser" */
static void _connection_http_in_cb(liStream *stream, liStreamEvent event) {
	liConnection *con = LI_CONTAINER_OF(stream, liConnection, in);
	liChunkQueue *raw_in, *in;
	liVRequest *vr = con->mainvr;

	switch (event) {
	case LI_STREAM_NEW_DATA:
		/* handle below */
		break;
	case LI_STREAM_DISCONNECTED_SOURCE:
		connection_close(con);
		return;
	case LI_STREAM_DESTROY:
		con->info.req = NULL;
		li_job_later(&con->wrk->loop.jobqueue, &con->job_reset);
		return;
	default:
		return;
	}

	if (NULL == stream->source) return;

	/* raw_in never gets closed normally - if we receive EOF from the client it means it cancelled the request */
	raw_in = stream->source->out;
	if (raw_in->is_closed) {
		connection_close(con);
		return;
	}

	/* always close "in" after request body end. reopen it on keep-alive */
	in = con->in.out;

	if (0 == raw_in->length) return; /* no (new) data */

	if (LI_CON_STATE_UPGRADED == con->state) {
		li_chunkqueue_steal_all(in, raw_in);
		li_stream_notify(stream);
		return;
	}

	if (con->state == LI_CON_STATE_KEEP_ALIVE) {
		/* stop keep alive timeout watchers */
		if (con->keep_alive_data.link) {
			g_queue_delete_link(&con->wrk->keep_alive_queue, con->keep_alive_data.link);
			con->keep_alive_data.link = NULL;
		}
		con->keep_alive_data.timeout = 0;
		li_event_stop(&con->keep_alive_data.watcher);

		con->keep_alive_requests++;
		/* disable keep alive if limit is reached */
		if (con->keep_alive_requests == CORE_OPTION(LI_CORE_OPTION_MAX_KEEP_ALIVE_REQUESTS).number)
			con->info.keep_alive = FALSE;

		/* reopen stream for request body */
		li_chunkqueue_reset(in);
		/* reset stuff from keep-alive and record timestamp */
		li_vrequest_start(con->mainvr);

		con->state = LI_CON_STATE_READ_REQUEST_HEADER;

		/* put back in io timeout queue */
		li_connection_update_io_wait(con);
	} else if (con->state == LI_CON_STATE_REQUEST_START) {
		con->state = LI_CON_STATE_READ_REQUEST_HEADER;
		li_connection_update_io_wait(con);
	}

	if (con->state == LI_CON_STATE_READ_REQUEST_HEADER) {
		liHandlerResult res;

		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "%s", "reading request header");
		}

		res = li_http_request_parse(vr, &con->req_parser_ctx);

		/* max uri length 8 kilobytes */
		/* TODO: check this and similar in request_parse and response_parse */
		if (vr->request.uri.raw->len > 8*1024) {
			VR_INFO(vr,
				"request uri too large. limit: 8kb, received: %s",
				li_counter_format(vr->request.uri.raw->len, COUNTER_BYTES, vr->wrk->tmp_str)->str
			);

			con->info.keep_alive = FALSE;
			vr->response.http_status = 414; /* Request-URI Too Large */
			con->state = LI_CON_STATE_WRITE;
			li_connection_update_io_wait(con);
			li_stream_again(&con->out);
			return;
		}

		switch(res) {
		case LI_HANDLER_GO_ON:
			break; /* go on */
		case LI_HANDLER_WAIT_FOR_EVENT:
			return;
		case LI_HANDLER_ERROR:
		case LI_HANDLER_COMEBACK: /* unexpected */
			/* unparsable header */
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "parsing header failed");
			}

			con->wrk->stats.requests++;
			con->info.keep_alive = FALSE;
			/* set status 400 if not already set to e.g. 413 */
			if (vr->response.http_status == 0)
				vr->response.http_status = 400;
			con->state = LI_CON_STATE_WRITE;
			li_connection_update_io_wait(con);
			li_stream_again(&con->out);
			return;
		}

		con->wrk->stats.requests++;

		/* headers ready */
		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "%s", "validating request header");
		}
		if (!li_request_validate_header(con)) {
			/* set status 400 if not already set */
			if (vr->response.http_status == 0)
				vr->response.http_status = 400;
			con->state = LI_CON_STATE_WRITE;
			con->info.keep_alive = FALSE;
			li_connection_update_io_wait(con);
			li_stream_again(&con->out);
			return;
		}

		/* When does a client ask for 100 Continue? probably not while trying to ddos us
		 * as post content probably goes to a dynamic backend anyway, we don't
		 * care about the rare cases we could determine that we don't want a request at all
		 * before sending it to a backend - so just send the stupid header
		 */
		if (con->expect_100_cont) {
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "send 100 Continue");
			}
			li_chunkqueue_append_mem(con->out.out, CONST_STR_LEN("HTTP/1.1 100 Continue\r\n\r\n"));
			con->expect_100_cont = FALSE;

			li_stream_notify(&con->out);
		}

		con->state = LI_CON_STATE_HANDLE_MAINVR;
		li_connection_update_io_wait(con);
		li_action_enter(vr, con->srv->mainaction);

		li_vrequest_handle_request_headers(vr);
	}

	if (con->state != LI_CON_STATE_READ_REQUEST_HEADER && !in->is_closed) {
		goffset newbytes = 0;

		if (-1 == vr->request.content_length) {
			if (!in->is_closed) {
				if (!li_filter_chunked_decode(vr, in, raw_in, &con->in_chunked_decode_state)) {
					if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
						VR_DEBUG(vr, "%s", "failed decoding chunked request body");
					}
					li_connection_error(con);
					return;
				}
				if (in->is_closed) vr->request.content_length = in->bytes_in;
				newbytes = 1; /* always notify */
			}
		} else {
			if (in->bytes_in < vr->request.content_length) {
				newbytes = li_chunkqueue_steal_len(in, raw_in, vr->request.content_length - in->bytes_in);
			}
			if (in->bytes_in == vr->request.content_length) {
				in->is_closed = TRUE;
			}
		}
		if (newbytes > 0 || in->is_closed) {
			li_stream_notify(&con->in);
		}
	}
}
コード例 #6
0
ファイル: mod_memcached.c プロジェクト: Aivaras/lighttpd2
static liHandlerResult mc_handle_lookup(liVRequest *vr, gpointer param, gpointer *context) {
	memcached_ctx *ctx = param;
	memcache_request *req = *context;

	if (req) {
		static const GString default_mime_str = { CONST_STR_LEN("application/octet-stream"), 0 };

		liBuffer *buf = req->buffer;
		const GString *mime_str;

		if (NULL != req->req) return LI_HANDLER_WAIT_FOR_EVENT; /* not done yet */

		g_slice_free(memcache_request, req);
		*context = NULL;

		if (NULL == buf) {
			/* miss */
			if (ctx->act_miss) li_action_enter(vr, ctx->act_miss);
			return LI_HANDLER_GO_ON;
		}

		if (!li_vrequest_handle_direct(vr)) {
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "memcached.lookup: request already handled");
			}
			li_buffer_release(buf);
			return LI_HANDLER_GO_ON;
		}

		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "%s", "memcached.lookup: key found, handling request");
		}

		li_chunkqueue_append_buffer(vr->direct_out, buf);

		vr->response.http_status = 200;

		mime_str = li_mimetype_get(vr, vr->request.uri.path);
		if (!mime_str) mime_str = &default_mime_str;
		li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str));

		/* hit */
		if (ctx->act_found) li_action_enter(vr, ctx->act_found);
		return LI_HANDLER_GO_ON;
	} else {
		liMemcachedCon *con;
		GError *err = NULL;

		if (li_vrequest_is_handled(vr)) {
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "memcached.lookup: request already handled");
			}
			return LI_HANDLER_GO_ON;
		}

		con = mc_ctx_prepare(ctx, vr->wrk);
		mc_ctx_build_key(vr->wrk->tmp_str, ctx, vr);

		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "memcached.lookup: looking up key '%s'", vr->wrk->tmp_str->str);
		}

		req = g_slice_new0(memcache_request);
		req->req = li_memcached_get(con, vr->wrk->tmp_str, memcache_callback, req, &err);

		if (NULL == req->req) {
			if (NULL != err) {
				if (LI_MEMCACHED_DISABLED != err->code) {
					VR_ERROR(vr, "memcached.lookup: get failed: %s", err->message);
				}
				g_clear_error(&err);
			} else {
				VR_ERROR(vr, "memcached.lookup: get failed: %s", "Unkown error");
			}
			g_slice_free(memcache_request, req);

			/* miss */
			if (ctx->act_miss) li_action_enter(vr, ctx->act_miss);

			return LI_HANDLER_GO_ON;
		}
		req->vr = vr;

		*context = req;

		return LI_HANDLER_WAIT_FOR_EVENT;
	}
}
コード例 #7
0
ファイル: mod_limit.c プロジェクト: AlexShiLucky/lighttpd2
static liHandlerResult mod_limit_action_handle(liVRequest *vr, gpointer param, gpointer *context) {
	gboolean limit_reached = FALSE;
	mod_limit_context *ctx = (mod_limit_context*) param;
	GPtrArray *arr = g_ptr_array_index(vr->plugin_ctx, ctx->plugin->id);
	gint cons;
	mod_limit_req_ip_data *rid;
	liSocketAddress *remote_addr = &vr->coninfo->remote_addr;
	gpointer addr;
	guint32 bits;

	UNUSED(context);

	if (li_vrequest_is_handled(vr)) {
		VR_DEBUG(vr, "%s", "mod_limit: already have a content handler - ignoring limits. Put limit.* before content handlers such as 'static', 'fastcgi' or 'proxy'");
		return LI_HANDLER_GO_ON;
	}

	/* IPv4 or IPv6? */
	switch (remote_addr->addr->plain.sa_family) {
	case AF_INET:
		addr = &remote_addr->addr->ipv4.sin_addr.s_addr;
		bits = 32;
		break;
	case AF_INET6:
		addr = &remote_addr->addr->ipv6.sin6_addr.s6_addr;
		bits = 128;
		break;
	default:
		if (ctx->type == ML_TYPE_CON_IP || ctx->type == ML_TYPE_REQ_IP) {
			VR_DEBUG(vr, "%s", "mod_limit only supports ipv4 or ipv6 clients");
			return LI_HANDLER_ERROR;
		}
		addr = NULL;
		bits = 0;
	}

	if (!arr) {
		/* request is not in any context yet, create new array */
		arr = g_ptr_array_sized_new(2);
		g_ptr_array_index(vr->plugin_ctx, ctx->plugin->id) = arr;
	}

	switch (ctx->type) {
	case ML_TYPE_CON:
#ifdef GLIB_VERSION_2_30
		/* since 2.30 g_atomic_int_add does the same as g_atomic_int_exchange_and_add,
		 * before it didn't return the old value. this fixes the deprecation warning. */
		if (g_atomic_int_add(&ctx->pool.con, 1) > ctx->limit) {
			g_atomic_int_add(&ctx->pool.con, -1);
			limit_reached = TRUE;
			VR_DEBUG(vr, "limit.con: limit reached (%d active connections)", ctx->limit);
		}
#else
		if (g_atomic_int_exchange_and_add(&ctx->pool.con, 1) > ctx->limit) {
			g_atomic_int_add(&ctx->pool.con, -1);
			limit_reached = TRUE;
			VR_DEBUG(vr, "limit.con: limit reached (%d active connections)", ctx->limit);
		}
#endif
		break;
	case ML_TYPE_CON_IP:
		g_mutex_lock(ctx->mutex);
		cons = GPOINTER_TO_INT(li_radixtree_lookup_exact(ctx->pool.con_ip, addr, bits));
		if (cons < ctx->limit) {
			li_radixtree_insert(ctx->pool.con_ip, addr, bits, GINT_TO_POINTER(cons+1));
		} else {
			limit_reached = TRUE;
			VR_DEBUG(vr, "limit.con_ip: limit reached (%d active connections)", ctx->limit);
		}
		g_mutex_unlock(ctx->mutex);
		break;
	case ML_TYPE_REQ:
		g_mutex_lock(ctx->mutex);
		if (li_cur_ts(vr->wrk) - ctx->pool.req.ts > 1.0) {
			/* reset pool */
			ctx->pool.req.ts = li_cur_ts(vr->wrk);
			ctx->pool.req.num = 1;
		} else {
			ctx->pool.req.num++;
			if (ctx->pool.req.num > ctx->limit) {
				limit_reached = TRUE;
				VR_DEBUG(vr, "limit.req: limit reached (%d req/s)", ctx->limit);
			}
		}
		g_mutex_unlock(ctx->mutex);
		break;
	case ML_TYPE_REQ_IP:
		g_mutex_lock(ctx->mutex);
		rid = li_radixtree_lookup_exact(ctx->pool.req_ip, addr, bits);
		if (!rid) {
			/* IP not known */
			rid = g_slice_new0(mod_limit_req_ip_data);
			rid->requests = 1;
			rid->ip = li_sockaddr_dup(*remote_addr);
			rid->ctx = ctx;
			rid->timeout_elem.data = rid;
			li_radixtree_insert(ctx->pool.req_ip, addr, bits, rid);
			li_waitqueue_push(&(((mod_limit_data*)ctx->plugin->data)->timeout_queues[vr->wrk->ndx]), &rid->timeout_elem);
		} else if (rid->requests < ctx->limit) {
			rid->requests++;
		} else {
			limit_reached = TRUE;
			VR_DEBUG(vr, "limit.req_ip: limit reached (%d req/s)", ctx->limit);
		}
		g_mutex_unlock(ctx->mutex);
		break;
	}

	if (limit_reached) {
		/* limit reached, we either execute the defined action or return a 503 error page */
		if (ctx->action_limit_reached) {
			/* execute action */
			li_action_enter(vr, ctx->action_limit_reached);
		} else {
			/* return 503 error page */
			if (!li_vrequest_handle_direct(vr)) {
				return LI_HANDLER_ERROR;
			}

			vr->response.http_status = 503;
		}
	} else {
		g_ptr_array_add(arr, ctx);
		g_atomic_int_inc(&ctx->refcount);
	}

	return LI_HANDLER_GO_ON;
}