/** * Invoked when a client connection is ready to be written to. */ static void handle_client_writebuf(ev_loop *lp, ev_io *watcher, int ready_events) { // Get the associated connection struct conn_info *conn = watcher->data; // Build the IO vectors to perform the write struct iovec vectors[2]; int num_vectors; circbuf_setup_writev_iovec(&conn->output, (struct iovec*)&vectors, &num_vectors); // Issue the write ssize_t write_bytes = writev(watcher->fd, (struct iovec*)&vectors, num_vectors); if (write_bytes > 0) { // Update the cursor circbuf_advance_read(&conn->output, write_bytes); // Check if we should reset the use_write_buf. // This is done when the buffer size is 0. if (conn->output.read_cursor == conn->output.write_cursor) { conn->use_write_buf = 0; ev_io_stop(lp, &conn->write_client); } } // Handle any errors if (write_bytes <= 0 && (errno != EAGAIN && errno != EINTR)) { syslog(LOG_ERR, "Failed to write() to connection [%d]! %s.", conn->client.fd, strerror(errno)); close_client_connection(conn); return; } }
/** * This method is used to read and consume the input buffer * @arg conn The client connection * @arg bytes The number of bytes to read * @arg buf Output parameter, sets the start of the buffer. * @arg should_free Output parameter, should the buffer be freed by the caller. * @return 0 on success, -1 if there is insufficient data. */ int read_client_bytes(statsite_conn_info *conn, int bytes, char** buf, int* should_free) { if (unlikely(bytes > circbuf_used_buf(&conn->input))) return -1; // Handle the wrap around case if (unlikely(conn->input.write_cursor < conn->input.read_cursor)) { // Check if we can use a contiguous chunk int end_size = conn->input.buf_size - conn->input.read_cursor; if (end_size >= bytes) { *buf = conn->input.buffer + conn->input.read_cursor; *should_free = 0; // Otherwise, allocate a dynamic slab, and copy } else { *buf = malloc(bytes); memcpy(*buf, conn->input.buffer + conn->input.read_cursor, end_size); memcpy(*buf + end_size, conn->input.buffer, bytes - end_size); *should_free = 1; } // Handle the contiguous case } else { *buf = conn->input.buffer + conn->input.read_cursor; *should_free = 0; } // Advance the read cursor circbuf_advance_read(&conn->input, bytes); return 0; }
/** * Invoked when a client connection is ready to be written to. */ static int handle_client_writebuf(ev_io *watch, worker_ev_userdata* data) { // Get the associated connection struct conn_info *conn = watch->data; // Build the IO vectors to perform the write struct iovec vectors[2]; int num_vectors; circbuf_setup_writev_iovec(&conn->output, (struct iovec*)&vectors, &num_vectors); // Issue the write ssize_t write_bytes = writev(watch->fd, (struct iovec*)&vectors, num_vectors); int reschedule = 0; if (write_bytes > 0) { // Update the cursor circbuf_advance_read(&conn->output, write_bytes); // Check if we should reset the use_write_buf. // This is done when the buffer size is 0. if (conn->output.read_cursor == conn->output.write_cursor) { conn->use_write_buf = 0; } else { reschedule = 1; } } // Handle any errors if (write_bytes <= 0 && (errno != EAGAIN && errno != EINTR)) { syslog(LOG_ERR, "Failed to write() to connection [%d]! %s.", conn->client.fd, strerror(errno)); close_client_connection(conn); decref_client_connection(conn); return 1; } // Check if we should reschedule or end if (reschedule) { schedule_async(data->netconf, SCHEDULE_WATCHER, &conn->write_client); } else { decref_client_connection(conn); } return 0; }
/** * This method is used to seek the input buffer without * consuming input. It can be used in conjunction with * peek_client_bytes to conditionally seek. * @arg conn The client connection * @arg bytes The number of bytes to seek * @return 0 on success, -1 if there is insufficient data. */ int seek_client_bytes(statsite_conn_info *conn, int bytes) { if (unlikely(bytes > circbuf_used_buf(&conn->input))) return -1; circbuf_advance_read(&conn->input, bytes); return 0; }