void fastrouter_loop() { int nevents; int interesting_fd; int new_connection; ssize_t len; int i; time_t delta; char bbuf[UMAX16]; char *tcp_port; char *tmp_socket_name; int tmp_socket_name_len; struct uwsgi_subscribe_req usr; char *magic_table[0xff]; struct uwsgi_rb_timer *min_timeout; void *events; struct msghdr msg; union { struct cmsghdr cmsg; char control [CMSG_SPACE (sizeof (int))]; } msg_control; struct cmsghdr *cmsg; struct sockaddr_un fr_addr; socklen_t fr_addr_len = sizeof(struct sockaddr_un); struct fastrouter_session *fr_session; struct fastrouter_session *fr_table[2048]; struct iovec iov[2]; int soopt; socklen_t solen = sizeof(int); int ufr_subserver = -1; for(i=0;i<2048;i++) { fr_table[i] = NULL; } ufr.queue = event_queue_init(); struct uwsgi_fastrouter_socket *ufr_sock = ufr.sockets; while(ufr_sock) { if (ufr_sock->name[0] == '=') { int shared_socket = atoi(ufr_sock->name+1); if (shared_socket >= 0) { ufr_sock->fd = uwsgi_get_shared_socket_fd_by_num(shared_socket); if (ufr_sock->fd == -1) { uwsgi_log("unable to use shared socket %d\n", shared_socket); } } } else { tcp_port = strchr(ufr_sock->name, ':'); if (tcp_port) { ufr_sock->fd = bind_to_tcp(ufr_sock->name, uwsgi.listen_queue, tcp_port); } else { ufr_sock->fd = bind_to_unix(ufr_sock->name, uwsgi.listen_queue, uwsgi.chmod_socket, uwsgi.abstract_socket); } } uwsgi_log("uwsgi fastrouter/proxy bound on %s\n", ufr_sock->name); if (!ufr.cheap) { event_queue_add_fd_read(ufr.queue, ufr_sock->fd); } else { uwsgi_log("[uwsgi-fastrouter] cheap mode requested. Waiting for subscriptions...\n"); ufr.i_am_cheap = 1; } ufr_sock = ufr_sock->next; } events = event_queue_alloc(ufr.nevents); ufr.timeouts = uwsgi_init_rb_timer(); if (!ufr.socket_timeout) ufr.socket_timeout = 30; if (ufr.subscription_server) { ufr_subserver = bind_to_udp(ufr.subscription_server, 0, 0); event_queue_add_fd_read(ufr.queue, ufr_subserver); if (!ufr.subscription_slot) ufr.subscription_slot = 30; // check for node status every 10 seconds //ufr.subscriptions_check = add_check_timeout(10); } if (ufr.pattern) { init_magic_table(magic_table); } for (;;) { min_timeout = uwsgi_min_rb_timer(ufr.timeouts); if (min_timeout == NULL ) { delta = -1; } else { delta = min_timeout->key - time(NULL); if (delta <= 0) { expire_timeouts(fr_table); delta = 0; } } nevents = event_queue_wait_multi(ufr.queue, delta, events, ufr.nevents); if (nevents == 0) { expire_timeouts(fr_table); } for (i=0;i<nevents;i++) { tmp_socket_name = NULL; interesting_fd = event_queue_interesting_fd(events, i); int taken = 0; struct uwsgi_fastrouter_socket *uwsgi_sock = ufr.sockets; while(uwsgi_sock) { if (interesting_fd == uwsgi_sock->fd) { new_connection = accept(interesting_fd, (struct sockaddr *) &fr_addr, &fr_addr_len); if (new_connection < 0) { continue; } fr_table[new_connection] = alloc_fr_session(); fr_table[new_connection]->fd = new_connection; fr_table[new_connection]->instance_fd = -1; fr_table[new_connection]->status = FASTROUTER_STATUS_RECV_HDR; fr_table[new_connection]->h_pos = 0; fr_table[new_connection]->pos = 0; fr_table[new_connection]->un = NULL; fr_table[new_connection]->instance_failed = 0; fr_table[new_connection]->instance_address_len = 0; fr_table[new_connection]->hostname_len = 0; fr_table[new_connection]->hostname = NULL; fr_table[new_connection]->timeout = add_timeout(fr_table[new_connection]); event_queue_add_fd_read(ufr.queue, new_connection); taken = 1; break; } uwsgi_sock = uwsgi_sock->next; } if (taken) { continue; } if (interesting_fd == ufr_subserver) { len = recv(ufr_subserver, bbuf, 4096, 0); #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(ufr.queue, ufr_subserver); #endif if (len > 0) { memset(&usr, 0, sizeof(struct uwsgi_subscribe_req)); uwsgi_hooked_parse(bbuf+4, len-4, fastrouter_manage_subscription, &usr); if (uwsgi_add_subscribe_node(&ufr.subscriptions, &usr, ufr.subscription_regexp) && ufr.i_am_cheap) { struct uwsgi_fastrouter_socket *ufr_sock = ufr.sockets; while(ufr_sock) { event_queue_add_fd_read(ufr.queue, ufr_sock->fd); ufr_sock = ufr_sock->next; } ufr.i_am_cheap = 0; uwsgi_log("[uwsgi-fastrouter] leaving cheap mode...\n"); } } } else { fr_session = fr_table[interesting_fd]; // something is going wrong... if (fr_session == NULL) continue; if (event_queue_interesting_fd_has_error(events, i)) { close_session(fr_table, fr_session); continue; } fr_session->timeout = reset_timeout(fr_session); switch(fr_session->status) { case FASTROUTER_STATUS_RECV_HDR: len = recv(fr_session->fd, (char *)(&fr_session->uh) + fr_session->h_pos, 4-fr_session->h_pos, 0); if (len <= 0) { uwsgi_error("recv()"); close_session(fr_table, fr_session); break; } fr_session->h_pos += len; if (fr_session->h_pos == 4) { #ifdef UWSGI_DEBUG uwsgi_log("modifier1: %d pktsize: %d modifier2: %d\n", fr_session->uh.modifier1, fr_session->uh.pktsize, fr_session->uh.modifier2); #endif fr_session->status = FASTROUTER_STATUS_RECV_VARS; } break; case FASTROUTER_STATUS_RECV_VARS: len = recv(fr_session->fd, fr_session->buffer + fr_session->pos, fr_session->uh.pktsize - fr_session->pos, 0); if (len <= 0) { uwsgi_error("recv()"); close_session(fr_table, fr_session); break; } fr_session->pos += len; if (fr_session->pos == fr_session->uh.pktsize) { if (uwsgi_hooked_parse(fr_session->buffer, fr_session->uh.pktsize, fr_get_hostname, (void *) fr_session)) { close_session(fr_table, fr_session); break; } if (fr_session->hostname_len == 0) { close_session(fr_table, fr_session); break; } #ifdef UWSGI_DEBUG //uwsgi_log("requested domain %.*s\n", fr_session->hostname_len, fr_session->hostname); #endif if (ufr.use_cache) { fr_session->instance_address = uwsgi_cache_get(fr_session->hostname, fr_session->hostname_len, &fr_session->instance_address_len); char *cs_mod = uwsgi_str_contains(fr_session->instance_address, fr_session->instance_address_len, ','); if (cs_mod) { fr_session->modifier1 = uwsgi_str_num(cs_mod+1, (fr_session->instance_address_len - (cs_mod - fr_session->instance_address))-1); fr_session->instance_address_len = (cs_mod - fr_session->instance_address); } } else if (ufr.pattern) { magic_table['s'] = uwsgi_concat2n(fr_session->hostname, fr_session->hostname_len, "", 0); tmp_socket_name = magic_sub(ufr.pattern, ufr.pattern_len, &tmp_socket_name_len, magic_table); free(magic_table['s']); fr_session->instance_address_len = tmp_socket_name_len; fr_session->instance_address = tmp_socket_name; } else if (ufr.subscription_server) { fr_session->un = uwsgi_get_subscribe_node(&ufr.subscriptions, fr_session->hostname, fr_session->hostname_len, ufr.subscription_regexp); if (fr_session->un && fr_session->un->len) { fr_session->instance_address = fr_session->un->name; fr_session->instance_address_len = fr_session->un->len; fr_session->modifier1 = fr_session->un->modifier1; } } else if (ufr.base) { tmp_socket_name = uwsgi_concat2nn(ufr.base, ufr.base_len, fr_session->hostname, fr_session->hostname_len, &tmp_socket_name_len); fr_session->instance_address_len = tmp_socket_name_len; fr_session->instance_address = tmp_socket_name; } else if (ufr.code_string_code && ufr.code_string_function) { if (uwsgi.p[ufr.code_string_modifier1]->code_string) { fr_session->instance_address = uwsgi.p[ufr.code_string_modifier1]->code_string("uwsgi_fastrouter", ufr.code_string_code, ufr.code_string_function, fr_session->hostname, fr_session->hostname_len); if (fr_session->instance_address) { fr_session->instance_address_len = strlen(fr_session->instance_address); char *cs_mod = uwsgi_str_contains(fr_session->instance_address, fr_session->instance_address_len, ','); if (cs_mod) { fr_session->modifier1 = uwsgi_str_num(cs_mod+1, (fr_session->instance_address_len - (cs_mod - fr_session->instance_address))-1); fr_session->instance_address_len = (cs_mod - fr_session->instance_address); } } } } // no address found if (!fr_session->instance_address_len) { close_session(fr_table, fr_session); break; } fr_session->pass_fd = is_unix(fr_session->instance_address, fr_session->instance_address_len); fr_session->instance_fd = uwsgi_connectn(fr_session->instance_address, fr_session->instance_address_len, 0, 1); if (tmp_socket_name) free(tmp_socket_name); if (fr_session->instance_fd < 0) { /* if (ufr.subscription_server) { if (fr_session->un && fr_session->un->len > 0) { uwsgi_log("[uwsgi-fastrouter] %.*s => marking %.*s as failed\n", (int) fr_session->hostname_len, fr_session->hostname, (int) fr_session->instance_address_len,fr_session->instance_address); uwsgi_remove_subscribe_node(&ufr.subscriptions, fr_session->un); if (ufr.subscriptions == NULL && ufr.cheap && !ufr.i_am_cheap) { uwsgi_log("[uwsgi-fastrouter] no more nodes available. Going cheap...\n"); struct uwsgi_fastrouter_socket *ufr_sock = ufr.sockets; while(ufr_sock) { event_queue_del_fd(ufr.queue, ufr_sock->fd, event_queue_read()); ufr_sock = ufr_sock->next; } ufr.i_am_cheap = 1; } } } */ fr_session->instance_failed = 1; close_session(fr_table, fr_session); break; } fr_session->status = FASTROUTER_STATUS_CONNECTING; fr_table[fr_session->instance_fd] = fr_session; event_queue_add_fd_write(ufr.queue, fr_session->instance_fd); } break; case FASTROUTER_STATUS_CONNECTING: if (interesting_fd == fr_session->instance_fd) { if (getsockopt(fr_session->instance_fd, SOL_SOCKET, SO_ERROR, (void *) (&soopt), &solen) < 0) { uwsgi_error("getsockopt()"); fr_session->instance_failed = 1; close_session(fr_table, fr_session); break; } if (soopt) { uwsgi_log("unable to connect() to uwsgi instance: %s\n", strerror(soopt)); fr_session->instance_failed = 1; close_session(fr_table, fr_session); break; } fr_session->uh.modifier1 = fr_session->modifier1; iov[0].iov_base = &fr_session->uh; iov[0].iov_len = 4; iov[1].iov_base = fr_session->buffer; iov[1].iov_len = fr_session->uh.pktsize; // increment node requests counter if (fr_session->un) fr_session->un->requests++; // fd passing: PERFORMANCE EXTREME BOOST !!! if (fr_session->pass_fd && !uwsgi.no_fd_passing) { msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 2; msg.msg_flags = 0; msg.msg_control = &msg_control; msg.msg_controllen = sizeof (msg_control); cmsg = CMSG_FIRSTHDR (&msg); cmsg->cmsg_len = CMSG_LEN (sizeof (int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cmsg), &fr_session->fd, sizeof(int)); if (sendmsg(fr_session->instance_fd, &msg, 0) < 0) { uwsgi_error("sendmsg()"); } close_session(fr_table, fr_session); break; } if (writev(fr_session->instance_fd, iov, 2) < 0) { uwsgi_error("writev()"); close_session(fr_table, fr_session); break; } event_queue_fd_write_to_read(ufr.queue, fr_session->instance_fd); fr_session->status = FASTROUTER_STATUS_RESPONSE; } break; case FASTROUTER_STATUS_RESPONSE: // data from instance if (interesting_fd == fr_session->instance_fd) { len = recv(fr_session->instance_fd, fr_session->buffer, 0xffff, 0); if (len <= 0) { if (len < 0) uwsgi_error("recv()"); close_session(fr_table, fr_session); break; } len = send(fr_session->fd, fr_session->buffer, len, 0); if (len <= 0) { if (len < 0) uwsgi_error("send()"); close_session(fr_table, fr_session); break; } // update transfer statistics if (fr_session->un) fr_session->un->transferred += len; } // body from client else if (interesting_fd == fr_session->fd) { //uwsgi_log("receiving body...\n"); len = recv(fr_session->fd, fr_session->buffer, 0xffff, 0); if (len <= 0) { if (len < 0) uwsgi_error("recv()"); close_session(fr_table, fr_session); break; } len = send(fr_session->instance_fd, fr_session->buffer, len, 0); if (len <= 0) { if (len < 0) uwsgi_error("send()"); close_session(fr_table, fr_session); break; } } break; // fallback to destroy !!! default: uwsgi_log("unknown event: closing session\n"); close_session(fr_table, fr_session); break; } } } } }
int uwsgi_cr_set_hooks(struct corerouter_peer *peer, ssize_t (*read_hook)(struct corerouter_peer *), ssize_t (*write_hook)(struct corerouter_peer *)) { struct corerouter_session *cs = peer->session; struct uwsgi_corerouter *ucr = cs->corerouter; //uwsgi_log("uwsgi_cr_set_hooks(%d, %p, %p)\n", peer->fd, read_hook, write_hook); if (read_hook) { peer->last_hook_read = read_hook; } if (write_hook) { peer->last_hook_write = write_hook; } int read_changed = 1; int write_changed = 1; if (read_hook && peer->hook_read) { read_changed = 0; } else if (!read_hook && !peer->hook_read) { read_changed = 0; } if (write_hook && peer->hook_write) { write_changed = 0; } else if (!write_hook && !peer->hook_write) { write_changed = 0; } if (!read_changed && !write_changed) { goto unchanged; } int has_read = 0; int has_write = 0; if (peer->hook_read) { has_read = 1; } if (peer->hook_write) { has_write = 1; } if (!read_hook && !write_hook) { if (has_read) { if (event_queue_del_fd(ucr->queue, peer->fd, event_queue_read())) return -1; } if (has_write) { if (event_queue_del_fd(ucr->queue, peer->fd, event_queue_write())) return -1; } } else if (read_hook && write_hook) { if (has_read) { if (event_queue_fd_read_to_readwrite(ucr->queue, peer->fd)) return -1; } else if (has_write) { if (event_queue_fd_write_to_readwrite(ucr->queue, peer->fd)) return -1; } } else if (read_hook) { if (has_write) { if (write_changed) { if (event_queue_fd_write_to_read(ucr->queue, peer->fd)) return -1; } else { if (event_queue_fd_write_to_readwrite(ucr->queue, peer->fd)) return -1; } } else { if (event_queue_add_fd_read(ucr->queue, peer->fd)) return -1; } } else if (write_hook) { if (has_read) { if (read_changed) { if (event_queue_fd_read_to_write(ucr->queue, peer->fd)) return -1; } else { if (event_queue_fd_read_to_readwrite(ucr->queue, peer->fd)) return -1; } } else { if (event_queue_add_fd_write(ucr->queue, peer->fd)) return -1; } } unchanged: peer->hook_read = read_hook; peer->hook_write = write_hook; return 0; }
/* the offload task starts soon after the call to connect() status: 0 -> waiting for connection on fd 1 -> sending request to fd (write event) 2 -> start waiting for read on s and fd 3 -> write to s 4 -> write to fd */ static int u_offload_transfer_do(struct uwsgi_thread *ut, struct uwsgi_offload_request *uor, int fd) { ssize_t rlen; // setup if (fd == -1) { event_queue_add_fd_write(ut->queue, uor->fd); return 0; } switch(uor->status) { // waiting for connection case 0: if (fd == uor->fd) { uor->status = 1; // ok try to send the request right now... return u_offload_transfer_do(ut, uor, fd); } return -1; // write event (or just connected) case 1: if (fd == uor->fd) { rlen = write(uor->fd, uor->ubuf->buf + uor->written, uor->ubuf->pos-uor->written); if (rlen > 0) { uor->written += rlen; if (uor->written >= (size_t)uor->ubuf->pos) { uor->status = 2; if (event_queue_add_fd_read(ut->queue, uor->s)) return -1; if (event_queue_fd_write_to_read(ut->queue, uor->fd)) return -1; } return 0; } else if (rlen < 0) { uwsgi_offload_retry uwsgi_error("u_offload_transfer_do() -> write()"); } } return -1; // read event from s or fd case 2: if (!uor->buf) { uor->buf = uwsgi_malloc(4096); } if (fd == uor->fd) { rlen = read(uor->fd, uor->buf, 4096); if (rlen > 0) { uor->to_write = rlen; uor->pos = 0; uwsgi_offload_0r_1w(uor->fd, uor->s) uor->status = 3; return 0; } if (rlen < 0) { uwsgi_offload_retry uwsgi_error("u_offload_transfer_do() -> read()/fd"); } } else if (fd == uor->s) { rlen = read(uor->s, uor->buf, 4096); if (rlen > 0) { uor->to_write = rlen; uor->pos = 0; uwsgi_offload_0r_1w(uor->s, uor->fd) uor->status = 4; return 0; } if (rlen < 0) { uwsgi_offload_retry uwsgi_error("u_offload_transfer_do() -> read()/s"); } } return -1; // write event on s case 3: rlen = write(uor->s, uor->buf + uor->pos, uor->to_write); if (rlen > 0) { uor->to_write -= rlen; uor->pos += rlen; if (uor->to_write == 0) { if (event_queue_fd_write_to_read(ut->queue, uor->s)) return -1; if (event_queue_add_fd_read(ut->queue, uor->fd)) return -1; uor->status = 2; } return 0; } else if (rlen < 0) { uwsgi_offload_retry uwsgi_error("u_offload_transfer_do() -> write()/s"); } return -1; // write event on fd case 4: rlen = write(uor->fd, uor->buf + uor->pos, uor->to_write); if (rlen > 0) { uor->to_write -= rlen; uor->pos += rlen; if (uor->to_write == 0) { if (event_queue_fd_write_to_read(ut->queue, uor->fd)) return -1; if (event_queue_add_fd_read(ut->queue, uor->s)) return -1; uor->status = 2; } return 0; } else if (rlen < 0) { uwsgi_offload_retry uwsgi_error("u_offload_transfer_do() -> write()/fd"); } return -1; default: break; } return -1; }
void uwsgi_rawrouter_switch_events(struct uwsgi_corerouter *ucr, struct corerouter_session *cs, int interesting_fd) { socklen_t solen = sizeof(int); ssize_t len; char buf[8192]; switch (cs->status) { case COREROUTER_STATUS_RECV_HDR: #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(ufr->queue, cs->fd); #endif // use the address as hostname cs->hostname = cs->ugs->name; cs->hostname_len = cs->ugs->name_len; // the mapper hook if (ucr->mapper(ucr, cs)) break; // no address found if (!cs->instance_address_len) { // if fallback nodes are configured, trigger them if (ucr->fallback) { cs->instance_failed = 1; } corerouter_close_session(ucr, cs); break; } cs->instance_fd = uwsgi_connectn(cs->instance_address, cs->instance_address_len, 0, 1); if (cs->instance_fd < 0) { cs->instance_failed = 1; cs->soopt = errno; corerouter_close_session(ucr, cs); break; } cs->status = COREROUTER_STATUS_CONNECTING; ucr->cr_table[cs->instance_fd] = cs; event_queue_add_fd_write(ucr->queue, cs->instance_fd); break; case COREROUTER_STATUS_CONNECTING: if (interesting_fd == cs->instance_fd) { if (getsockopt(cs->instance_fd, SOL_SOCKET, SO_ERROR, (void *) (&cs->soopt), &solen) < 0) { uwsgi_error("getsockopt()"); cs->instance_failed = 1; corerouter_close_session(ucr, cs); break; } if (cs->soopt) { cs->instance_failed = 1; corerouter_close_session(ucr, cs); break; } // increment node requests counter if (cs->un) cs->un->requests++; event_queue_fd_write_to_read(ucr->queue, cs->instance_fd); cs->status = COREROUTER_STATUS_RESPONSE; } break; case COREROUTER_STATUS_RESPONSE: // data from instance if (interesting_fd == cs->instance_fd) { len = recv(cs->instance_fd, buf, 8192, 0); #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(ufr->queue, cs->instance_fd); #endif if (len <= 0) { if (len < 0) uwsgi_error("recv()"); corerouter_close_session(ucr, cs); break; } len = send(cs->fd, buf, len, 0); if (len <= 0) { if (len < 0) uwsgi_error("send()"); corerouter_close_session(ucr, cs); break; } // update transfer statistics if (cs->un) cs->un->transferred += len; } // body from client else if (interesting_fd == cs->fd) { //uwsgi_log("receiving body...\n"); len = recv(cs->fd, buf, 8192, 0); #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(ufr->queue, cs->fd); #endif if (len <= 0) { if (len < 0) uwsgi_error("recv()"); corerouter_close_session(ucr, cs); break; } len = send(cs->instance_fd, buf, len, 0); if (len <= 0) { if (len < 0) uwsgi_error("send()"); corerouter_close_session(ucr, cs); break; } } break; // fallback to destroy !!! default: uwsgi_log("unknown event: closing session\n"); corerouter_close_session(ucr, cs); break; } }