void Connection_handle_event(Connection *connection, EventType event, int ordinal) { if(CS_ABORTED == connection->state) { return; } DEBUG(("con event, fd: %d, state: %d, event: %d, readable: %d, writeable: %d, timeout: %d\n", connection->sockfd, connection->state, event, (event & EVENT_READ) ? 1 : 0, (event & EVENT_WRITE) ? 1 : 0, (event & EVENT_TIMEOUT) ? 1 : 0 )); if(event & EVENT_ERROR) { Connection_abort(connection, "event error"); return; } if(event & EVENT_TIMEOUT) { if(CS_CONNECTING == connection->state) { Connection_abort(connection, "connect timeout"); } else { Connection_abort(connection, "read/write timeout"); } return; } if(event & EVENT_WRITE) { Connection_write_data(connection, ordinal); } if(event & EVENT_READ) { Connection_read_data(connection, ordinal); } }
int Connection_create_socket(Connection *connection) { assert(connection != NULL); assert(CS_CLOSED == connection->state); //create socket connection->sockfd = socket(AF_INET, SOCK_STREAM, 0); if(connection->sockfd == -1) { Connection_abort(connection, "could not create socket"); return -1; } //set socket in non-blocking mode int flags; if ((flags = fcntl(connection->sockfd, F_GETFL, 0)) < 0) { Connection_abort(connection, "could not get socket flags for"); return -1; } if (fcntl(connection->sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { Connection_abort(connection, "could not set socket to non-blocking mode"); return -1; } return 0; }
void Connection_read_data(Connection *connection, int ordinal) { if(CS_ABORTED == connection->state) { return; } DEBUG(("connection read data fd: %d\n", connection->sockfd)); assert(connection->current_batch != NULL); assert(connection->current_executor != NULL); assert(CS_CONNECTED == connection->state); Buffer *buffer = Batch_read_buffer(connection->current_batch); assert(buffer != NULL); while(Batch_has_command(connection->current_batch)) { DEBUG(("exec rp\n")); Reply *reply = NULL; ReplyParserResult rp_res = ReplyParser_execute(connection->parser, Buffer_data(buffer), Buffer_position(buffer), &reply); switch(rp_res) { case RPR_ERROR: { Connection_abort(connection, "result parse error"); return; } case RPR_MORE: { DEBUG(("read data RPR_MORE buf recv\n")); size_t res = Buffer_recv(buffer, connection->sockfd); DEBUG(("read data RPR_MORE res: %d\n", res)); #ifndef NDEBUG Buffer_dump(buffer, 128); #endif if(res == -1) { if(errno == EAGAIN) { DEBUG(("read data expecting more data in future, adding event\n")); Executor_notify_event(connection->current_executor, connection, EVENT_READ, ordinal); return; } else { Connection_abort(connection, "read error, errno: [%d] %s", errno, strerror(errno)); return; } } else if(res == 0) { Connection_abort(connection, "read eof"); return; } break; } case RPR_REPLY: { DEBUG(("read data RPR_REPLY batch add reply\n")); Batch_add_reply(connection->current_batch, reply); break; } default: Connection_abort(connection, "unexpected result parser result, rpres: %d", rp_res); return; } } }
int Connection_create_socket(Connection *connection) { assert(connection != NULL); assert(CS_CLOSED == connection->state); //resolve address struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; if (getaddrinfo(connection->addr, connection->serv, &hints, &connection->addrinfo)) { Connection_abort(connection, "could not resolve address"); return -1; } //create socket struct addrinfo *addrinfo = connection->addrinfo; connection->sockfd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); if(connection->sockfd == -1) { Connection_abort(connection, "could not create socket"); return -1; } //set socket in non-blocking mode int flags; if ((flags = fcntl(connection->sockfd, F_GETFL, 0)) < 0) { Connection_abort(connection, "could not get socket flags for"); return -1; } if (fcntl(connection->sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { Connection_abort(connection, "could not set socket to non-blocking mode"); return -1; } return 0; }
void Connection_write_data(Connection *connection, int ordinal) { DEBUG(("connection write_data fd: %d\n", connection->sockfd)); assert(connection->current_batch != NULL); assert(connection->current_executor != NULL); if(CS_ABORTED == connection->state) { return; } if(CS_CLOSED == connection->state) { if(-1 == Connection_create_socket(connection)) { //already aborted in create_socket return; } //connect the socket struct addrinfo *addrinfo = connection->addrinfo; if(-1 == connect(connection->sockfd, addrinfo->ai_addr, addrinfo->ai_addrlen)) { //open the connection if(EINPROGRESS == errno) { //normal async connect connection->state = CS_CONNECTING; DEBUG(("async connecting, adding write event\n")); Executor_notify_event(connection->current_executor, connection, EVENT_WRITE, ordinal); DEBUG(("write event added, now returning\n")); return; } else { Connection_abort(connection, "connect error 1, errno [%d] %s", errno, strerror(errno)); return; } } else { //immediate connect succeeded DEBUG(("sync connected\n")); connection->state = CS_CONNECTED; } } if(CS_CONNECTING == connection->state) { //now check for error to see if we are really connected int error; socklen_t len = sizeof(int); if(-1 == getsockopt(connection->sockfd, SOL_SOCKET, SO_ERROR, &error, &len)) { Connection_abort(connection, "getsockopt error for connect result, errno: [%d] %s", errno, strerror(errno)); return; } if(error != 0) { Connection_abort(connection, "connect error 2, error: [%d] %s", error, strerror(error)); return; } else { connection->state = CS_CONNECTED; Executor_notify_event(connection->current_executor, connection, EVENT_READ, ordinal); } } if(CS_CONNECTED == connection->state) { Buffer *buffer = Batch_write_buffer(connection->current_batch); assert(buffer != NULL); while(Buffer_remaining(buffer)) { //still something to write size_t res = Buffer_send(buffer, connection->sockfd); DEBUG(("bfr send res: %d\n", res)); if(res == -1) { if(errno == EAGAIN) { Executor_notify_event(connection->current_executor, connection, EVENT_WRITE, ordinal); return; } else { Connection_abort(connection, "write error, errno: [%d] %s", errno, strerror(errno)); return; } } } } }