void
read_response(Connection *connection, Statistics *stats, char *buffer, int buffer_len)
{
    int bytes_read = 0, bad_count = 0, msg_count = 0, close_count = 0;

    bytes_read = read(connection->sd, buffer, buffer_len - 1);

    if (bytes_read < 0) {
        error("Error reading from socket for connection %d\n", connection->index);
        stats->connections--;
        reopen_connection(connection);
        return;
    }

    if (bytes_read == 0) { // server disconnected us
        // reconnect
        info("Server disconnected as requested %d.\n", connection->index);
        stats->connections--;
        reopen_connection(connection);
        return;
    }

    stats->bytes_read += bytes_read;
    buffer[bytes_read] = '\0';
    debug("Read %d bytes\n", bytes_read);
    trace("Read Message: %s\n", buffer);

    bad_count = count_strinstr(buffer, "HTTP/1.1 4");
    bad_count += count_strinstr(buffer, "HTTP/1.1 5");

    if (bad_count > 0) {
        info("Recevied error. Buffer is %s\n", buffer);
        stats->connections--;
        reopen_connection(connection);
        return;
    }

    msg_count = count_strinstr(buffer, "**MSG**");
    stats->messages += msg_count;

    if ((close_count = count_strinstr(buffer, "**CLOSE**")) > 0) {
        connection->channel_count += close_count;
        info("%d Channel(s) has(have) been closed by server.\n", close_count);
        if (connection->channel_count >= (connection->channel_end - connection->channel_start + 1)) {
            info("Connection %d will be closed \n", connection->index);
            close_connection(connection);
            stats->connections--;
        }
    }
}
void
read_response(Connection *connection, Statistics *stats, char *buffer, int buffer_len)
{
    int bytes_read = 0, bad_count = 0, ok_count = 0;

    bytes_read = read(connection->sd, buffer, buffer_len - 1);

    if (bytes_read < 0) {
        error("Error reading from socket for connection %d\n", connection->index);
        reopen_connection(connection);
        return;
    }

    if (bytes_read == 0) { // server disconnected us
        // reconnect
        info("Server disconnected as requested %d.\n", connection->index);
        close_connection(connection);
        return;
    }

    stats->bytes_read += bytes_read;
    buffer[bytes_read] = '\0';
    debug("Read %d bytes\n", bytes_read);
    trace("Read Message: %s\n", buffer);

    bad_count = count_strinstr(buffer, "HTTP/1.1 4");
    bad_count += count_strinstr(buffer, "HTTP/1.1 5");

    if (bad_count > 0) {
    	info("Recevied error. Buffer is %s\n", buffer);
        reopen_connection(connection);
        return;
    }

    ok_count = count_strinstr(buffer, "HTTP/1.1 200 OK");
    stats->messages += ok_count;
}
void
subscribe_channels(Connection *connection, Statistics *stats)
{
    char buffer[BUFFER_SIZE];
    int len = 0, bytes_written = 0;
    long i = 0;

    len = sprintf(buffer, "GET /sub");
    for (i = connection->channel_start; i <= connection->channel_end; i++) {
        len += sprintf(buffer + len, "/my_channel_%ld", i);
    }

    len += sprintf(buffer + len, "?conn=%d HTTP/1.1\r\nHost: loadtest\r\n\r\n", connection->index);

    if (write_connection(connection, stats, buffer, len) == EXIT_FAILURE) {
        stats->connections--;
        reopen_connection(connection);
        return;
    }
}
void
write_message(Connection *connection, Statistics *stats)
{
    char buffer[BUFFER_SIZE];
    int len = 0, bytes_written = 0;

    if ((connection->channel_id <= connection->channel_start) || (connection->channel_id > connection->channel_end)) {
        connection->channel_id = connection->channel_start;
        connection->message_count++;

        // gives a message payload of 140 bytes
        connection->content_length = sprintf(connection->content_buffer, "**MSG** msg=%06d 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", connection->message_count);
        connection->content_buffer[connection->content_length] = '\0';
    }

    len = sprintf(buffer, "POST /pub?id=my_channel_%ld HTTP/1.1\r\nHost: loadtest\r\nContent-Length: %d\r\n\r\n%s", connection->channel_id, connection->content_length, connection->content_buffer);

    if (write_connection(connection, stats, buffer, len) == EXIT_FAILURE) {
        reopen_connection(connection);
        return;
    }

    connection->channel_id++;
}
int
main_program(int num_channels, int num_connections, const char *server_hostname, int server_port, int timeout)
{
    struct sockaddr_in server_address;
    int main_sd = -1, num_events = 0, i, j, event_mask, channels_per_connection, num, start_time = 0, iters_to_next_summary = 0;
    Connection *connections = NULL, *connection;
    Statistics stats = {0,0,0,0,0};
    int exitcode = EXIT_SUCCESS;
    struct epoll_event events[MAX_EVENTS];
    char buffer[BIG_BUFFER_SIZE];

    info("Subscriber starting up\n");
    info("Subscriber: %d connections to %d channels on server: %s:%d\n", num_connections, num_channels, server_hostname, server_port);

    if ((fill_server_address(server_hostname, server_port, &server_address)) != 0) {
        error2("ERROR host name not found\n");
    }

    if ((main_sd = epoll_create(200 /* this size is not used on Linux kernel 2.6.8+ */)) < 0) {
        error3("Failed %d creating main epoll socket\n", errno);
    }

    if ((connections = init_connections(num_connections, &server_address, main_sd)) == NULL) {
        error2("Failed to create to connections\n");
    }

    stats.requested_connections = num_connections;

    for (i = 0; i < num_connections; i++) {
        connections[i].channel_start = 0;
        connections[i].channel_end = num_channels - 1;
    }

    // infinite loop
    debug("Entering Infinite Loop\n");

    iters_to_next_summary = ITERATIONS_TILL_SUMMARY_PER_TIMEOUT/timeout;

    for(;;) {
        if ((num_events = epoll_wait(main_sd, events, MAX_EVENTS, timeout)) < 0) {
            error3("epoll_wait failed\n");
        }

        for (i = 0; i < num_events; i++) {
            event_mask = events[i].events;
            connection = (Connection *)(events[i].data.ptr);

            if (event_mask & EPOLLHUP) { // SERVER HUNG UP
                debug("EPOLLHUP\n");
                info("Server hung up on conncetion %d. Reconecting...\n", connection->index);
                sleep(1);
                stats.connections--;
                reopen_connection(connection);

                continue;
            }

            if (event_mask & EPOLLERR) {
                debug("EPOLLERR\n");
                info("Server returned an error on connection %d. Reconecting...\n", connection->index);
                stats.connections--;
                reopen_connection(connection);

                continue;
            }

            if (event_mask & EPOLLIN) { // READ
                debug("----------READ AVAILABLE-------\n");

                if (connection->state == CONNECTED) {
                    read_response(connection, &stats, buffer, BIG_BUFFER_SIZE);
                }
            }

            if (event_mask & EPOLLOUT) { // WRITE
                debug("----------WRITE AVAILABLE-------\n");

                if (start_time == 0) {
                    start_time = time(NULL);
                }

                if (connection->state == CONNECTING) {
                    connection->state = CONNECTED;
                    stats.connections++;
                    debug("Connection opened for index=%d\n", connection->index);

                    subscribe_channels(connection, &stats);

                    // remove write flag from event
                    if (change_connection(connection, EPOLLIN | EPOLLHUP) < 0) {
                        error2("Failed creating socket for connection = %d\n", connection->index);
                    }
                }
            }
        }

        if ((iters_to_next_summary-- <= 0)) {
            iters_to_next_summary = ITERATIONS_TILL_SUMMARY_PER_TIMEOUT/timeout;
            summary("Connections=%ld, Messages=%ld BytesRead=%ld Msg/Sec=%0.2f\n", stats.connections, stats.messages, stats.bytes_read, calc_message_per_second(stats.messages, start_time));
        }

        if (stats.connections == 0) {
            num = 0;
            for (j = 0; j < num_connections; j++) {
                if (connections[i].state != CLOSED) {
                    num++;
                    break;
                }
            }

            if (num == 0) {
                exitcode = EXIT_SUCCESS;
                goto exit;
            }
        }
    }

exit:
    if (connections != NULL) free(connections);

    return exitcode;
}