static void li_memcached_con_free(liMemcachedCon* con) { if (!con) return; if (-1 != li_event_io_fd(&con->con_watcher)) { close(li_event_io_fd(&con->con_watcher)); li_event_clear(&con->con_watcher); con->fd = -1; } send_queue_reset(&con->out); cancel_all_requests(con); li_buffer_release(con->buf); li_buffer_release(con->line); li_buffer_release(con->remaining); li_buffer_release(con->data); reset_item(&con->curitem); li_sockaddr_clear(&con->addr); g_string_free(con->tmpstr, TRUE); g_clear_error(&con->err); g_slice_free(liMemcachedCon, con); }
static void proxy_connection_new(liVRequest *vr, liBackendConnection *bcon, proxy_context *ctx) { proxy_connection* scon = g_slice_new0(proxy_connection); liIOStream *iostream; liStream *outplug; liStream *http_out; proxy_context_acquire(ctx); scon->ctx = ctx; scon->bcon = bcon; iostream = li_iostream_new(vr->wrk, li_event_io_fd(&bcon->watcher), proxy_io_cb, scon); /* insert proxy header before actual data */ outplug = li_stream_plug_new(&vr->wrk->loop); li_stream_connect(outplug, &iostream->stream_out); proxy_send_headers(vr, outplug->out); li_stream_notify_later(outplug); http_out = li_stream_http_response_handle(&iostream->stream_in, vr, TRUE, FALSE); li_vrequest_handle_indirect(vr, NULL); li_vrequest_indirect_connect(vr, outplug, http_out); li_iostream_release(iostream); li_stream_release(outplug); li_stream_release(http_out); }
static void close_con(liMemcachedCon *con) { if (con->line) con->line->used = 0; if (con->remaining) con->remaining->used = 0; if (con->data) con->data->used = 0; if (con->buf) con->buf->used = 0; reset_item(&con->curitem); send_queue_reset(&con->out); memcached_stop_io(con); close(li_event_io_fd(&con->con_watcher)); con->fd = -1; li_event_io_set_fd(&con->con_watcher, -1); con->cur_req = NULL; cancel_all_requests(con); memcached_connect(con); }
static void memcached_io_cb(liEventBase *watcher, int events) { liMemcachedCon *con = LI_CONTAINER_OF(li_event_io_from(watcher), liMemcachedCon, con_watcher); if (1 == g_atomic_int_get(&con->refcount) && li_event_active(&con->con_watcher)) { memcached_stop_io(con); return; } if (-1 == con->fd) { memcached_connect(con); return; } li_memcached_con_acquire(con); /* make sure con isn't freed in the middle of something */ if (events & LI_EV_WRITE) { int i; ssize_t written, len; gchar *data; send_item *si; si = g_queue_peek_head(&con->out); for (i = 0; si && (i < 10); i++) { /* don't send more than 10 chunks */ data = si->buf->addr + si->pos; len = si->len; written = write(li_event_io_fd(&con->con_watcher), data, len); if (written < 0) { switch (errno) { case EINTR: continue; case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif goto write_eagain; default: /* Fatal error, connection has to be closed */ g_clear_error(&con->err); g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_CONNECTION, "Couldn't write socket '%s': %s", li_sockaddr_to_string(con->addr, con->tmpstr, TRUE)->str, g_strerror(errno)); close_con(con); goto out; } } else { si->pos += written; si->len -= written; if (0 == si->len) { send_queue_item_free(si); g_queue_pop_head(&con->out); si = g_queue_peek_head(&con->out); } } } write_eagain: send_queue_clean(&con->out); } if (events & LI_EV_READ) { do { handle_read(con); } while (con->remaining && con->remaining->used > 0); } out: memcached_update_io(con); li_memcached_con_release(con); }
static void memcached_connect(liMemcachedCon *con) { int s; struct sockaddr addr; socklen_t len; li_tstamp now = li_event_now(li_event_get_loop(&con->con_watcher)); if (-1 != con->fd) return; s = li_event_io_fd(&con->con_watcher); if (-1 == s) { /* reconnect limit */ if (now < con->last_con_start + 1) { if (con->err) { con->err->code = LI_MEMCACHED_DISABLED; } else { g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_DISABLED, "Disabled right now"); } return; } con->last_con_start = now; do { s = socket(con->addr.addr->plain.sa_family, SOCK_STREAM, 0); } while (-1 == s && errno == EINTR); if (-1 == s) { g_clear_error(&con->err); g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_CONNECTION, "Couldn't open socket: %s", g_strerror(errno)); return; } li_fd_init(s); li_event_io_set_fd(&con->con_watcher, s); if (-1 == connect(s, &con->addr.addr->plain, con->addr.len)) { switch (errno) { case EINPROGRESS: case EALREADY: case EINTR: memcached_start_io(con); li_event_io_add_events(&con->con_watcher, LI_EV_READ | LI_EV_WRITE); break; case EISCONN: break; default: g_clear_error(&con->err); g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_CONNECTION, "Couldn't connect to '%s': %s", li_sockaddr_to_string(con->addr, con->tmpstr, TRUE)->str, g_strerror(errno)); close(s); li_event_io_set_fd(&con->con_watcher, -1); break; } } else { /* connect succeeded */ con->fd = s; g_clear_error(&con->err); memcached_update_io(con); } return; } /* create new connection: * see http://www.cyberconf.org/~cynbe/ref/nonblocking-connects.html */ /* Check to see if we can determine our peer's address. */ len = sizeof(addr); if (getpeername(s, &addr, &len) == -1) { /* connect failed; find out why */ int err; len = sizeof(err); #ifdef SO_ERROR if (-1 == getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&err, &len)) { err = errno; } #else { char ch; errno = 0; read(s, &ch, 1); err = errno; } #endif g_clear_error(&con->err); g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_CONNECTION, "Couldn't connect socket to '%s': %s", li_sockaddr_to_string(con->addr, con->tmpstr, TRUE)->str, g_strerror(err)); close(s); memcached_stop_io(con); li_event_io_set_fd(&con->con_watcher, -1); } else { /* connect succeeded */ con->fd = s; g_clear_error(&con->err); memcached_update_io(con); } }
static void angel_connection_io_cb(liEventBase *watcher, int events) { liAngelConnection *acon = LI_CONTAINER_OF(li_event_io_from(watcher), liAngelConnection, fd_watcher); if (events | LI_EV_WRITE) { GString *out_str; int i; ssize_t written, len; gchar *data; gboolean out_queue_empty; angel_connection_send_item_t *send_item; int fd = li_event_io_fd(&acon->fd_watcher); g_mutex_lock(acon->mutex); send_item = g_queue_peek_head(acon->out); g_mutex_unlock(acon->mutex); for (i = 0; send_item && (i < 10); i++) { /* don't send more than 10 chunks */ switch (send_item->type) { case ANGEL_CONNECTION_ITEM_GSTRING: out_str = send_item->value.string.buf; written = send_item->value.string.pos; data = out_str->str + written; len = out_str->len - written; while (len > 0) { written = write(fd, data, len); if (written < 0) { switch (errno) { case EINTR: continue; case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif goto write_eagain; default: /* Fatal error, connection has to be closed */ li_event_clear(&acon->out_notify_watcher); li_event_clear(&acon->fd_watcher); acon->close_cb(acon, NULL); /* TODO: set err */ return; } } else { data += written; len -= written; send_item->value.string.pos += written; } } break; case ANGEL_CONNECTION_ITEM_FDS: while (send_item->value.fds.pos < send_item->value.fds.fds->len) { switch (li_send_fd(fd, g_array_index(send_item->value.fds.fds, int, send_item->value.fds.pos))) { case 0: send_item->value.fds.pos++; continue; case -1: /* Fatal error, connection has to be closed */ li_event_clear(&acon->out_notify_watcher); li_event_clear(&acon->fd_watcher); acon->close_cb(acon, NULL); /* TODO: set err */ return; case -2: goto write_eagain; } } break; } g_mutex_lock(acon->mutex); send_queue_clean(acon->out); send_item = g_queue_peek_head(acon->out); g_mutex_unlock(acon->mutex); } write_eagain: g_mutex_lock(acon->mutex); send_queue_clean(acon->out); out_queue_empty = (0 == acon->out->length); g_mutex_unlock(acon->mutex); if (out_queue_empty) li_event_io_rem_events(&acon->fd_watcher, LI_EV_WRITE); }