/** * \brief function called when the socket receive some data * \param source the socket event * \param handle the cleint handler * \param buf data received from the socket * \param bufsize the size of the data set from the socket */ void client_callback(SockEvtSource *source, void *handle, void *buf, int buf_size) { (void)source; Client* self = (Client*)handle; // logdebug ("'%s': received %d octets of data\n", source->name, buf_size); // logdebug ("'%s': %s\n", source->name, to_octets (buf, buf_size)); proxy_message_loop (source->name, self, buf, buf_size); mbuf_repack_message (self->mbuf); if (self->state == C_PROTOCOL_ERROR) { socket_close (self->recv_socket); logerror("'%s': protocol error, proxy server will disconnect upstream client\n", source->name); eventloop_socket_remove (self->recv_event); // Note: this free()'s source! self->recv_event = NULL; } fwrite (buf, sizeof (char), buf_size, self->file); pthread_mutex_lock (&self->mutex); pthread_cond_signal (&self->condvar); pthread_mutex_unlock (&self->mutex); }
/** * \brief Call back function when the status of the socket change * \param source the socket event * \param status the status of the socket * \param error the value of the error if there is * \param handle the Client handler structure */ void status_callback(SockEvtSource *source, SocketStatus status, int error, void *handle) { (void)error; switch (status) { case SOCKET_CONN_CLOSED: { Client* self = (Client*)handle; if (self->recv_event != NULL) { socket_close (source->socket); logdebug("socket '%s' closed\n", source->name); eventloop_socket_remove (source); // Note: this free()'s source! } /* Signal the sender thread for this client that the client disconnected */ pthread_mutex_lock (&self->mutex); self->state = C_DISCONNECTED; pthread_cond_signal (&self->condvar); pthread_mutex_unlock (&self->mutex); break; default: break; } } }
/** Run the global EventLoop until eventloop_stop() or eventloop_terminate() is called. * * The loop is based around the poll(3) system call. It monitor event sources * such as Channel or Timers, registered in the respective fields of the global * EventLoop object self. It first consider all timers to find whether some * have expired and to set the timeout for the poll(3) call. It then calls * poll(3) on the file descriptors (STDIN or sockets) related to active * Channels, and runs the relevant callbacks for those with pending events. It * finally executes the callback functions of the expired timers. The loop * will not return until eventloop_stop() or eventloop_terminate() is called. * In the former case, it will try to wait until all active sockets are close, * while not in the latter. * * \return the (non-zero) value passed to eventloop_stop() or eventloop_terminate() * * \see eventloop_init, eventloop_stop, eventloop_terminate * \eventloop_on_stdin, eventloop_on_monitor_in_channel, eventloop_on_read_in_channel, eventloop_on_out_channel * \see o_el_timer_callback, o_el_read_socket_callback, o_el_monitor_socket_callback, o_el_state_socket_callback, o_el_timer_callback * \see poll(3) */ int eventloop_run() { int i; self.stopping = 0; self.force_stop = 0; self.start = self.now = self.last_reaped = time(NULL); while (!self.stopping || (self.size>0 && !self.force_stop)) { // Check for active timers int timeout = -1; TimerInt* t = self.timers; while (t != NULL) { if (t->is_active) { int delta = 1000 * (t->due_time - self.now); if (delta < 0) delta = 0; // overdue if (delta < timeout || timeout < 0) timeout = delta; } t = t->next; } if (timeout != -1) o_log(O_LOG_DEBUG3, "EventLoop: Timeout = %d\n", timeout); if (self.fds_dirty) if (update_fds()<1 && timeout < 0) /* No FD nor timeout */ continue; o_log(O_LOG_DEBUG4, "EventLoop: About to poll() on %d FDs with a timeout of %ds\n", self.size, timeout); /* for(i=0; i < self.size; i++) { o_log(O_LOG_DEBUG4, "EventLoop: FD %d->%s\n", self.fds[i].fd, self.fds_channels[i]->name); } */ int count = poll(self.fds, self.size, timeout); self.now = time(NULL); if (count < 1) { o_log(O_LOG_DEBUG4, "EventLoop: Timeout\n"); } else { // Check sockets i = 0; o_log(O_LOG_DEBUG4, "EventLoop: Got events\n"); for (; i < self.size; i++) { Channel* ch = self.fds_channels[i]; if (self.fds[i].revents & POLLERR) { char buf[32]; SocketStatus status; int len; if ((len = recv(self.fds[i].fd, buf, 32, 0)) <= 0) { switch (errno) { case ECONNREFUSED: status = SOCKET_CONN_REFUSED; break; default: status = SOCKET_UNKNOWN; if (!ch->status_cbk) { o_log(O_LOG_ERROR, "EventLoop: While reading from socket '%s': (%d) %s\n", ch->name, errno, strerror(errno)); } } eventloop_socket_activate((SockEvtSource*)ch, 0); do_status_callback (ch, status, errno); } else { o_log(O_LOG_ERROR, "EventLoop: Expected error on socket '%s' but read '%s'\n", ch->name, buf); } } else if (self.fds[i].revents & POLLHUP) { eventloop_socket_activate((SockEvtSource*)ch, 0); /* Client closed the connection, but there might still be bytes for us to read from our end of the connection. */ int len; int fd = self.fds[i].fd; char buf[MAX_READ_BUFFER_SIZE]; do { if (fd == 0) { len = read(fd, buf, MAX_READ_BUFFER_SIZE); } else { len = recv(fd, buf, 512, 0); } if (len > 0) { o_log(O_LOG_DEBUG3, "EventLoop: Received last %i bytes\n", len); do_read_callback (ch, buf, len); } } while (len > 0); do_status_callback (ch, SOCKET_CONN_CLOSED, 0); } else if (self.fds[i].revents & POLLIN) { char buf[MAX_READ_BUFFER_SIZE]; if (ch->read_cbk) { int len; int fd = self.fds[i].fd; if (fd == 0) { // stdin len = read(fd, buf, MAX_READ_BUFFER_SIZE); } else { // socket len = recv(fd, buf, 512, 0); } ch->last_activity = self.now; if (len > 0) { o_log(O_LOG_DEBUG3, "EventLoop: Received %i bytes\n", len); do_read_callback (ch, buf, len); } else if (len == 0 && ch->socket != NULL) { // skip stdin // closed down eventloop_socket_activate((SockEvtSource*)ch, 0); do_status_callback (ch, SOCKET_CONN_CLOSED, 0); } else if (len < 0) { if (errno == ENOTSOCK) { o_log(O_LOG_ERROR, "EventLoop: Monitored socket '%s' is now invalid; " "removing from monitored set\n", ch->name); eventloop_socket_remove ((SockEvtSource*)ch); } else { o_log(O_LOG_ERROR, "EventLoop: Unrecognized read error not handled (errno=%d)\n", errno); } } } else { do_monitor_callback (ch); } } else if (ch->is_shutting_down) { /* The socket was shutting down, and nothing new has appeared; * We flushed the buffers, mark it as removable */ eventloop_socket_release((SockEvtSource*)ch); } if (self.fds[i].revents & POLLOUT) { do_status_callback(ch, SOCKET_WRITEABLE, 0); if (0 != ch->last_activity) { /* If we track the activity of this socket */ ch->last_activity = self.now; } } if (self.fds[i].revents & POLLNVAL) { o_log(O_LOG_WARN, "EventLoop: socket '%s' invalid, deactivating...\n", ch->name); eventloop_socket_activate((SockEvtSource*)ch, 0); do_status_callback(ch, SOCKET_DROPPED, 0); } /* We reap idle channels as we go through the list. XXX: There might * be a corner case where all FDs are already used, and some of them * idle, however a new a new connection (on one listening socket early * in the list) would be dropped before cleanup triggered by its * arrival freed the resources it needs (from the idle sockets further * towards the end of the list. See #959.*/ if (ch->last_activity != 0 && self.socket_timeout > 0 && self.now - ch->last_activity > self.socket_timeout) { o_log(O_LOG_DEBUG2, "EventLoop: Socket '%s' idle for %ds, reaping...\n", ch->name, self.now - ch->last_activity); do_status_callback(ch, SOCKET_IDLE, 0); } } for (i = 0; i < self.size; i++) { Channel* ch = self.fds_channels[i]; if (ch->is_removable) eventloop_socket_remove ((SockEvtSource*)ch); } } if (timeout >= 0) { // check timers TimerInt* t = self.timers; while (t != NULL) { if (t->is_active) { if (t->due_time <= self.now) { // fires o_log(O_LOG_DEBUG2, "EventLoop: Timer '%s' fired\n", t->name); if (t->callback) t->callback((TimerEvtSource*)t, t->handle); if (t->is_periodic) { while ((t->due_time += t->period) < self.now) { // should really only happen during debugging o_log(O_LOG_WARN, "EventLoop: Skipped timer period for '%s'\n", t->name); } } else { t->is_active = 0; } } } t = t->next; } } } return self.stopping; }