[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[Libevent-users] help with server



All,
I sent this a while ago. I'm currently working on a passthrough proxy
for a larger project. I've pasted the code here, but for whatever reason
when I connect my data does not get sent through to the other side. I
have debugged it to know that:
1) the requests are parsed correctly.
2) The connection is made.
3) The data is moved from one buffer to the next at the end of OnRead.
Does anyone have any thoughts? I'm struggling to figure out why the data
wouldn't be sent. Any tips would be greatly appreciated. I've dropped
the relevant code here.
Thanks,
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>

#include <cstring>
#include <cstdlib>
#include <stdexcept>
#include <string>
#include <vector>

#include "listener.h"
#include "logger.hpp"
#include "request.h"

#define BLOCKSIZE 1024*8 //max size for reading.

enum class RequestState
{
    None, //empty state
    Half, //waiting for connection
    Full //we have a connection, two way communication can continue.
};
/**
* Used internally to store state.
*
* In most basic form, holds state on origenation and connection eventbuff.
*/
struct State
{
    bufferevent* origenation;
    bufferevent* connection;
    Listener* listener;
    RequestState state;
};

static void AcceptConnectionCallback(evconnlistener *evtlistener,
evutil_socket_t fd, sockaddr *addr, int socklen, void *args)
{
    Listener* listener = (Listener*)args;
    listener->OnAccept(fd, addr, socklen);
}
static void AcceptErrorCallback(evconnlistener *evtlistener, void* arg)
{
    State* state = (State*) arg;
    state->listener->OnError(state);
}
static void ReadCallback(bufferevent *bufferevt, void *arg)
{
    State* state = (State*)arg;
    state->listener->OnRead(bufferevt, state);
}
static void EventCallback(bufferevent *bufferevt, short events, void *arg)
{
    State* state = (State*)arg;
    state->listener->OnEvent(bufferevt, events, state);
}

Listener::Listener(int port):_port(port)
{
}
int Listener::GetPort() const
{
    return _port;
}
void Listener::Listen(event_base* base)
{
    _base = base;

    memset((void*)&_addr, 0, sizeof(_addr));
    _addr.sin_family = PF_INET;
    _addr.sin_addr.s_addr = INADDR_ANY;
    _addr.sin_port = htons(_port);

    _listener = evconnlistener_new_bind(base, AcceptConnectionCallback,
(void*)this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, (struct
sockaddr *)&_addr, sizeof(_addr));
    if (!_listener)
        {
            int err = EVUTIL_SOCKET_ERROR();
            throw std::runtime_error("Could not create listener, got
error: "+std::string(evutil_socket_error_to_string(err))+".");
        }

    evconnlistener_set_error_cb(_listener, AcceptErrorCallback);
}

void Listener::OnAccept(evutil_socket_t fd, sockaddr *addr, int socklen)
{
    sockaddr_in* saddr = (sockaddr_in*)addr;
    char addrstr[INET6_ADDRSTRLEN+1];
    inet_ntop(saddr->sin_family, &(saddr->sin_addr), addrstr,
INET6_ADDRSTRLEN);
    LogInfo("Accepting connection from ", addrstr, ":",
ntohs(saddr->sin_port), ".");

    State* state = new State();
    bufferevent *origenation = nullptr;

    origenation = bufferevent_socket_new(_base, fd, BEV_OPT_CLOSE_ON_FREE);
    state->connection = nullptr;
    state->listener = this;
    state->origenation = origenation;
    state->state = RequestState::Half;

    bufferevent_setcb(origenation, ReadCallback, NULL, EventCallback,
(void*)state);
    bufferevent_enable(origenation, EV_READ|EV_WRITE);
}
void Listener::OnError(State* state)
{
    int err = EVUTIL_SOCKET_ERROR();
    event_base_loopexit(_base, NULL);
    throw std::runtime_error("Got error:
"+std::string(evutil_socket_error_to_string(err)));
}
void Listener::OnRead(bufferevent *bufferevt, State* state)
{
    evbuffer *src = nullptr;
    evbuffer* dst = nullptr;
    size_t len = 0;

//attach the evbuffers to the right sides of the connection.
    if (state->origenation == bufferevt)
        {
//data flowing from proxy to server.
            src = bufferevent_get_input(bufferevt);
            if (state->connection)
                {
                    dst = bufferevent_get_output(state->connection);
                }
        }
    else
        {
//data flowing from server to proxy.
            src = bufferevent_get_input(state->connection);
            dst = bufferevent_get_output(state->origenation);
        }

//we have data, we can drain it here and deal with it later.
    len = evbuffer_get_length(src);
//we have a two-way communication mechenism going, check for data from
client first.
    if (bufferevt == state->origenation)
        {
            if (state->state == RequestState::Half)
                {
//our state is half-open.
//this means that we need to pull the data from the request and resend
headers.
                    sockaddr_in addr;
                    addrinfo hints, *servinfo;
                    int result = 0;

                    memset((void*)&addr, 0, sizeof(addr));
                    memset((void*)&hints, 0, sizeof(hints));

                    hints.ai_family = AF_UNSPEC;
                    hints.ai_socktype = SOCK_STREAM;

                    char* data = new char[len+2];
                    evbuffer_copyout(src, data, len);
                    Request* request = new Request(data);
                    delete [] data;

                    std::string hoststr = request->GetHeaderValue("host");
//we may have a host:port type.
                    size_t pos = hoststr.find(":", 0);
                    if (pos == std::string::npos)
                        {
                            addr.sin_port = htons(80);
                        }
                    else
                        {
                            std::string portstr = hoststr.substr(pos+1,
std::string::npos);
                            hoststr = hoststr.substr(0, pos-1);
                            addr.sin_port = htons(atoi(portstr.c_str()));
                        }

                    result = getaddrinfo(hoststr.c_str(), NULL, &hints,
&servinfo);
                    if (result)
                        {
                            std::string response =
"<html>\n<head>\n<title>Error!</title>\n</head>\n<body>\n";
                            response += "<h1>An error has
occured.</h1>\n<p>\n";
                            response +=
std::string(gai_strerror(result)) + "</p>\n</body>\n</html>\n";
                            dst = bufferevent_get_output(bufferevt);
                            evbuffer_copyout(dst,
(void*)response.c_str(), response.length());
                            evbuffer_drain(src, len);
                            return;
                        }

//we have a proper address, lets connect.
                    state->connection = bufferevent_socket_new(_base,
-1, BEV_OPT_CLOSE_ON_FREE);
                    result =
bufferevent_socket_connect(state->connection, servinfo->ai_addr,
servinfo->ai_addrlen);
                    if (result == -1)
                        {
                            std::string response;
                            response =
"<html>\n<head>\n<title>Connection
error!</title>\n</head>\n<body>\n<p>Could not connect to specified
socket.</p>\n</body>\n</html>\n";
                            dst = bufferevent_get_output(bufferevt);
                            evbuffer_copyout(dst,
(void*)response.c_str(), response.length());
                            evbuffer_drain(src, len);
                        }
                    state->state = RequestState::Full;
                    dst = bufferevent_get_output(state->connection);
                    bufferevent_setcb(state->connection, ReadCallback,
NULL, EventCallback, (void*)state);
                    bufferevent_enable(state->connection, EV_READ|EV_WRITE);
                }
        }

    evbuffer_add_buffer(dst, src);
//    evbuffer_drain(src, len);
}

void Listener::OnEvent(bufferevent *bufferevt, short events, State*state)
{
    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
        {
            if (state->origenation == bufferevt)
                {
//close all.
                    LogDebug("Client closed connection, closing both
ends.");
                    bufferevent_free(bufferevt);
                    if (state->connection)
                        {
                            LogDebug("Killing end of client.");
                            bufferevent_free(state->connection);
                        }
                    delete state;
                }
        }
    else
        {
            LogDebug("Server closed other end, setting state to half.");
            bufferevent_free(bufferevt);
            state->connection = nullptr;
            state->state = RequestState::Half;
        }
}
-- 
Take care,
Ty
Twitter: @sorressean
Web: https://tysdomain.com
Pubkey: https://tysdomain.com/files/pubkey.asc
***********************************************************************
To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
unsubscribe libevent-users    in the body.