static void echo_cb(struct ev_loop *loop, ev_uinet *w, int revents) { struct echo_connection *conn = w->data; struct uinet_demo_echo *echo = conn->echo; struct uinet_iovec iov; struct uinet_uio uio; int max_read; int max_write; int read_size; int error; #define BUFFER_SIZE (64*1024) char buffer[BUFFER_SIZE]; max_read = uinet_soreadable(w->so, 0); if (max_read <= 0) { /* the watcher should never be invoked if there is no error and there no bytes to be read */ assert(max_read != 0); if (conn->verbose) printf("%s: connection %llu: can't read, closing\n", echo->cfg.name, (unsigned long long)conn->id); goto err; } else { max_write = echo->sink ? max_read : uinet_sowritable(w->so, 0); if (-1 == max_write) { if (conn->verbose) printf("%s: connection %llu: can't write, closing\n", echo->cfg.name, (unsigned long long)conn->id); goto err; } else { read_size = imin(imin(max_read, max_write), BUFFER_SIZE); uio.uio_iov = &iov; iov.iov_base = buffer; iov.iov_len = read_size; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = read_size; error = uinet_soreceive(w->so, NULL, &uio, NULL); if (0 != error) { printf("%s: connection %llu: read error (%d), closing\n", echo->cfg.name, (unsigned long long)conn->id, error); goto err; } assert(uio.uio_resid == 0); if (!echo->sink) { uio.uio_iov = &iov; iov.iov_base = buffer; iov.iov_len = read_size; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = read_size; error = uinet_sosend(w->so, NULL, &uio, 0); if (0 != error) { printf("%s: connection %llu: write error (%d), closing\n", echo->cfg.name, (unsigned long long)conn->id, error); goto err; } } if (max_write < max_read) { /* limited by write space, so change to a * write watch on the socket, if we aren't * already one. */ if (w->events & EV_READ) { ev_uinet_stop(loop, w); w->events = EV_WRITE; ev_uinet_start(loop, w); } /* else, continue as a write watch */ } else if (!(w->events & EV_READ)) { /* write space wasn't a limitation this * time, so switch back to waiting on * EV_READ */ ev_uinet_stop(loop, w); w->events = EV_READ; ev_uinet_start(loop, w); } /* else, continue as a read watcher */ } } return; err: ev_uinet_stop(loop, w); ev_uinet_detach(w->ctx); uinet_soclose(w->so); free(conn); }
static void passive_receive_cb(struct ev_loop *loop, ev_uinet *w, int revents) { struct connection_context *conn = (struct connection_context *)w->data; #define BUFFER_SIZE (64*1024) uint8_t buffer[BUFFER_SIZE]; struct uinet_iovec iov; struct uinet_uio uio; int max_read; int read_size; int bytes_read; int error; int flags; int i; int print_threshold = 10; int printable; int skipped; max_read = uinet_soreadable(w->so, 0); if (max_read <= 0) { /* the watcher should never be invoked if there is no error and there no bytes to be read */ assert(max_read != 0); if (conn->verbose) printf("%s: can't read, closing\n", conn->label); goto err; } else { read_size = imin(max_read, BUFFER_SIZE - 1); uio.uio_iov = &iov; iov.iov_base = buffer; iov.iov_len = read_size; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = read_size; flags = UINET_MSG_HOLE_BREAK; error = uinet_soreceive(w->so, NULL, &uio, &flags); if (0 != error) { printf("%s: read error (%d), closing\n", conn->label, error); goto err; } bytes_read = read_size - uio.uio_resid; conn->bytes_read += bytes_read; if (conn->verbose > 2) print_tcp_state(w->so, conn->label); if (conn->verbose > 1) { printf("========================================================================================\n"); } if (conn->verbose) printf("To %s (%u bytes, %llu total, %s)\n", conn->label, bytes_read, (unsigned long long)conn->bytes_read, flags & UINET_MSG_HOLE_BREAK ? "HOLE" : "normal"); if (conn->verbose > 1) { buffer[bytes_read] = '\0'; printf("----------------------------------------------------------------------------------------\n"); skipped = 0; printable = 0; for (i = 0; i < bytes_read; i++) { if ((buffer[i] >= 0x20 && buffer[i] <= 0x7e) || buffer[i] == 0x0a || buffer[i] == 0x0d || buffer[i] == 0x09) { printable++; } else { /* * Print on printable-to-unprintable * transition if enough consecutive * printable chars were seen. */ if (printable >= print_threshold) { if (skipped) { printf("<%u>", skipped); } buffer[i] = '\0'; printf("%s", &buffer[i - printable]); } else { skipped += printable; } printable = 0; skipped++; } } if (skipped) { printf("<%u>", skipped); } buffer[i] = '\0'; printf("%s", &buffer[i - printable]); printf("\n"); printf("========================================================================================\n"); } } return; err: destroy_conn(conn); }
/* * The passive http extraction code works by alternately parsing the * passively reconstructed request and response streams. The same callback * (below) is used to drive the parsing of each stream. Parsing begins with * the request stream, and once a complete request has been parsed, the * parser and read watcher for the request stream are paused and the parser * and read watcher for the response stream are activated. Once an entire * response is parsed, the parser and read watcher for the response stream * are paused, and the parser and read watcher for the request stream are * activated. Along the way, response bodies that match the supplied list * of content types are extracted to files. * * This is example code whose purpose is to demonstrate upper layer protocol * processing using libuinet passive sockets functionality. Little to no * attempt is made to deal with a number of ugly realities involved in * robustly parsing http streams in the wild. */ static void passive_extract_cb(struct ev_loop *loop, ev_uinet *w, int revents) { struct connection_context *conn = (struct connection_context *)w->data; struct uinet_iovec iov; struct uinet_uio uio; int max_read; int read_size; int bytes_read; int error; int flags; size_t nparsed; max_read = uinet_soreadable(w->so, 0); if (max_read <= 0) { /* the watcher should never be invoked if there is no error and there no bytes to be read */ assert(max_read != 0); /* * There are no more complete requests/responses to be had, shut everything down. */ if (conn->verbose) printf("%s: can't read, closing\n", conn->label); goto err; } else { read_size = imin(max_read, conn->buffer_size - conn->buffer_index); uio.uio_iov = &iov; iov.iov_base = &conn->buffer[conn->buffer_index]; iov.iov_len = read_size; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = read_size; flags = UINET_MSG_HOLE_BREAK; error = uinet_soreceive(w->so, NULL, &uio, &flags); if (0 != error) { printf("%s: read error (%d), closing\n", conn->label, error); goto err; } if (flags & UINET_MSG_HOLE_BREAK) { printf("%s: hole in data, closing connections\n", conn->label); goto err; } bytes_read = read_size - uio.uio_resid; conn->buffer_count += bytes_read; conn->bytes_read += bytes_read; do { passive_extract_parse_buffer(conn); if (HTTP_PARSER_ERRNO(conn->parser) != HPE_OK) { if (HTTP_PARSER_ERRNO(conn->parser) == HPE_PAUSED) { if (conn->verbose > 1) printf("%s: completed parsing request or response\n", conn->label); http_parser_pause(conn->peer->parser, 0); passive_extract_parse_buffer(conn->peer); if (HTTP_PARSER_ERRNO(conn->peer->parser) == HPE_OK) { if (conn->verbose > 1) printf("%s: peer needs more data\n", conn->label); /* Peer parser needs more data */ ev_uinet_stop(conn->server->loop, &conn->watcher); ev_uinet_start(conn->server->loop, &conn->peer->watcher); break; } else if (HTTP_PARSER_ERRNO(conn->peer->parser) != HPE_PAUSED) { printf("Peer parse failure %s, closing connections\n", http_errno_name(HTTP_PARSER_ERRNO(conn->peer->parser))); goto err; } else { if (conn->verbose > 1) printf("%s: peer completed parsing request or response\n", conn->label); /* * The other parser has paused, so it's time for us to continue * parsing/receiving. */ http_parser_pause(conn->parser, 0); } } else { printf("Parse failure %s, closing connections\n", http_errno_name(HTTP_PARSER_ERRNO(conn->parser))); goto err; } } } while (conn->buffer_count); } return; err: /* * Deliver EOS to each parser. If a parser is paused or otherwise * in an error state, no work will be done. The main reason for * doing this is to correctly handle the case where response parsing * requires an EOS to complete. Under such circumstances, one of * the calls below will complete the work. */ http_parser_execute(conn->parser, conn->parser_settings, NULL, 0); http_parser_execute(conn->peer->parser, conn->peer->parser_settings, NULL, 0); destroy_conn(conn->peer); destroy_conn(conn); }
static void nproxy_copy_cb(struct ev_loop *loop, ev_uinet *w, int revents) { struct nproxy_connection *conn = w->data; struct nproxy_connection *other_side_conn = conn->other_side; struct nproxy_splice *splice = conn->splice; struct uinet_demo_nproxy *nproxy = splice->nproxy; struct uinet_socket *other_side_so = conn->other_side->copy_watcher.so; struct uinet_iovec iov; struct uinet_uio uio; int max_read; int max_write; int read_size; int error; #define BUFFER_SIZE (64*1024) char buffer[BUFFER_SIZE]; max_read = uinet_soreadable(w->so, 0); if (max_read <= 0) { /* the watcher should never be invoked if there is no error and there no bytes to be read */ assert(max_read != 0); if (splice->verbose) printf("%s: splice %llu: %s: can't read, closing splice\n", nproxy->cfg.name, (unsigned long long)splice->id, conn->name); goto err; } else { max_write = uinet_sowritable(other_side_so, 0); if (-1 == max_write) { if (splice->verbose) printf("%s: splice %llu: %s: can't write, closing splice\n", nproxy->cfg.name, (unsigned long long)splice->id, other_side_conn->name); goto err; } else { read_size = imin(imin(max_read, max_write), BUFFER_SIZE); /* read_size == 0 should only happen when max_write is 0 */ if (read_size > 0) { uio.uio_iov = &iov; iov.iov_base = buffer; iov.iov_len = read_size; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = read_size; error = uinet_soreceive(w->so, NULL, &uio, NULL); if (0 != error) { printf("%s: splice %llu: %s: read error (%d), closing splice\n", nproxy->cfg.name, (unsigned long long)splice->id, conn->name, error); goto err; } assert(uio.uio_resid == 0); uio.uio_iov = &iov; iov.iov_base = buffer; iov.iov_len = read_size; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = read_size; error = uinet_sosend(other_side_so, NULL, &uio, 0); if (0 != error) { printf("%s: splice %llu: %s: write error (%d), closing splice\n", nproxy->cfg.name, (unsigned long long)splice->id, other_side_conn->name, error); goto err; } } if (max_write < max_read) { /* * Limited by write space, so deactivate us * and activate the write watcher for the * other side, which will reactivate us when * it fires. */ ev_uinet_stop(loop, w); ev_uinet_start(loop, &other_side_conn->writable_watcher); } } } return; err: nproxy_splice_destroy(loop, splice); }