LINUX.ORG.RU

HTTP-прокси на libevent. Матчасть Си.


0

3

Пишу HTTP-балансировщик с кэшем в memcached с использованием libevent. Могу создавать сервер и отдавать контент, могу забирать контент, могу кэшировать, а с матчастью плохо. В данном случае создается сервер 0.0.0.0:8080, при поступлении запроса нужно забрать данные с 127.0.0.0:80 и отдать клиенту, т.е. пока что это просто прокси. Есть примерно такой код (некоторые части выкинул):

void request_cb (struct evhttp_request *req, void *arg)
{
	if (req->response_code == 200)
	{
		struct evbuffer* buf3 = evhttp_request_get_input_buffer (req); // Забираем буфер

		int len = evbuffer_get_length(buf3);
		tmp = malloc(len+1);
		memcpy(tmp, evbuffer_pullup(buf3, -1), len);
		tmp[len] = '\0';
		printf("%s\n", tmp); // Печатаем, то что получили из буфера
		free(tmp);

		event_loopexit(NULL);
	}
}

void list_cb (struct evhttp_request *req, void *arg)
{
	/* Здесь описываем подключение к веб-серверу */
	struct evhttp_connection *conn;
	struct evhttp_request *req1;

	event_init();

	conn = evhttp_connection_new("127.0.0.1", 80);
	req1 = evhttp_request_new(request_cb, NULL); // После скачивания идем в коллбек request_cb
	evhttp_add_header(req1->output_headers, "Host", "127.0.0.1");
	evhttp_make_request(conn, req1, EVHTTP_REQ_GET, "/"); // Ну здесь пока просто / запрашиваем, не принципиально пока

	event_dispatch();

	/* Здесь описываем отправку данных клиенту */
	struct evbuffer *buf; // Создаем буфер
	buf = evbuffer_new();

	evbuffer_add_printf(buf, "%s", req->uri); // Допустим мы что-то в буфер заносим

	evhttp_send_reply(req, HTTP_OK, "OK", buf); // Отправляем клиенту ответ

	evbuffer_free(buf);
}

int main (int argc, char **argv)
{
	struct evhttp *httpd;

	event_init();

	httpd = evhttp_start("0.0.0.0", 8080);

	// Запускаем слушалку с коллбеком list_cb
	evhttp_set_gencb(httpd, list_cb, NULL);

	event_dispatch();

	evhttp_free(httpd);

	return 0;
}
В функции void list_cb я создаю подключение к серверу. Но данные с этого сервера я могу забрать только внутри void request_cb, а нужно чтобы в void list_cb, где я смогу закэшировать данные и сделать ответ клиенту. Понятно что в void list_cb нужно объявить переменную, и в вызове req1 = evhttp_request_new(request_cb, NULL); нужно NULL заменить на указатель этой переменной. Тогда в другой функции я смогу изменить данные и забрать там где мне нужно. Проблема в том, что я не знаю как это сделать. Подскажите как написать всю эту муть с указателями, а то сам я не смогу.

P.S. evhttp потому что удобнее с заголовками работать.

В list_cb определить

struct mydata_info mydata;

и передать &mydata в evhttp_request_new(). В request_cb() определить

struct mydata_info *mydata = arg;

и заполнить.

Но вообще Вы неправильно применяете libevent.

Sorcerer ★★★★★
()
Последнее исправление: Sorcerer (всего исправлений: 1)
Ответ на: комментарий от Sorcerer

Спасибо большое, работает. Буду дальше маны читать.

Но вообще Вы неправильно применяете libevent.

Можно вкратце, почему? :)

Black_Roland ★★★★
() автор топика

printf(«%s\n», tmp); -> puts(tmp);

Deleted
()
Ответ на: комментарий от Black_Roland

Можно вкратце, почему?

1. Функции event_init(), event_dispatch() и многие другие из event_compat.h использовать нежелательно, т.к. они работают с «текущим» event_base. И потом, они объявлены как deprecated. Лучше использовать event_base_new(), event_base_dispatch() - эти функции возвращают/принимают указатель на event_base. То же самое и с функцией evhttp_start() из http_compat.h: поищите аналоги в http.h

2. В вашей программе изнутри list_cb(), функции-обработчика события, привязанного к «текущему» event_base, вызывается новый event_init(), перетирающий текущий event_base. Поведение программы после возврата из list_cb() становится малопредсказуемым. Кроме мемликов есть вероятность получить и корки

3. Нет смысла создавать несколько event_base'ов, если вы не используете их в разных процессах или потоках. В list_cb() можно повесить созданное соединение на текущий event_base (вызывать event_dispatch() не надо), при этом в request_cb() не нужно вызывать event_loopexit(), а вместо этого там нужно выполнить обработку - ту, которую Вы делаете сейчас в list_cb() после выхода из event_dispatch(). Все необходимые данные передаются в request_cb() через arg.

Sorcerer ★★★★★
()

Заметил что есть подписавшиеся, решил выложить свой говнокод, может кому нужен. Программа писалась как курсовая работа, если гвоздями прибить, то оно даже работает.

Исходный код: http://pastebin.com/9pty4yiH
Для сборки: gcc -g balancer.c -o balancer -levent -lmemcached
Бенчмарк: [статика], [динамика]
В качестве бэкенда lighttpd запущенный на 3 портах. В качестве динамики phpinfo(). В качестве железа нетбук на атоме.

Что работает:
- Балансировка round-robin
- Кэш в memcached
Что не работает:
- Передача заголовков от клиента серверу (Cookie, UserAgent и пр.)
- Передача заголовков от сервера клиенту (ContentType и пр.)
- Нет конфига
- Весь нетекстовый контент от сервера неправильно парсится из буфера

Black_Roland ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.