- while (!should_stop) {
- if (state == SENDING_REQUEST || state == RECEIVING_HEADER || state == RECEIVING_DATA) {
- // Since we are non-blocking, we need to wait for the right state first.
- // Wait up to 50 ms, then check should_stop.
- pollfd pfd;
- pfd.fd = sock;
- pfd.events = (state == SENDING_REQUEST) ? POLLOUT : POLLIN;
- pfd.events |= POLLRDHUP;
-
- int nfds = poll(&pfd, 1, 50);
- if (nfds == 0 || (nfds == -1 && errno == EINTR)) {
- continue;
- }
- if (nfds == -1) {
- perror("poll");
- state = CLOSING_SOCKET;
- }
- }
-
- switch (state) {
- case NOT_CONNECTED:
- request.clear();
- request_bytes_sent = 0;
- response.clear();
-
- if (!parse_url(url, &host, &port, &path)) {
- fprintf(stderr, "Failed to parse URL '%s'\n", url.c_str());
- break;
- }
-
- sock = lookup_and_connect(host, port);
- if (sock != -1) {
- // Yay, successful connect. Try to set it as nonblocking.
- int one = 1;
- if (ioctl(sock, FIONBIO, &one) == -1) {
- perror("ioctl(FIONBIO)");
- state = CLOSING_SOCKET;
- } else {
- state = SENDING_REQUEST;
- request = "GET " + path + " HTTP/1.0\r\nUser-Agent: cubemap\r\n\r\n";
- request_bytes_sent = 0;
- }
- }
- break;
- case SENDING_REQUEST: {
- size_t to_send = request.size() - request_bytes_sent;
- int ret;
-
- do {
- ret = write(sock, request.data() + request_bytes_sent, to_send);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- perror("write");
- state = CLOSING_SOCKET;
- continue;
- }
-
- assert(ret >= 0);
- request_bytes_sent += ret;
-
- if (request_bytes_sent == request.size()) {
- state = RECEIVING_HEADER;
- }
- break;
- }
- case RECEIVING_HEADER: {
- char buf[4096];
- int ret;
-
- do {
- ret = read(sock, buf, sizeof(buf));
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- perror("read");
- state = CLOSING_SOCKET;
- continue;
- }
-
- if (ret == 0) {
- // This really shouldn't happen...
- fprintf(stderr, "Socket unexpectedly closed while reading header\n");
- state = CLOSING_SOCKET;
- continue;
- }
-
- RequestParseStatus status = wait_for_double_newline(&response, buf, ret);
-
- if (status == RP_OUT_OF_SPACE) {
- fprintf(stderr, "WARNING: fd %d sent overlong response!\n", sock);
- state = CLOSING_SOCKET;
- continue;
- } else if (status == RP_NOT_FINISHED_YET) {
- continue;
- }
-
- // OK, so we're fine, but there might be some of the actual data after the response.
- // We'll need to deal with that separately.
- string extra_data;
- if (status == RP_EXTRA_DATA) {
- char *ptr = static_cast<char *>(
- memmem(response.data(), response.size(), "\r\n\r\n", 4));
- assert(ptr != NULL);
- extra_data = string(ptr, &response[0] + response.size());
- response.resize(ptr - response.data());
- }
-
- if (!parse_response(response)) {
- state = CLOSING_SOCKET;
- continue;
- }
-
- if (!extra_data.empty()) {
- process_data(&extra_data[0], extra_data.size());
- }
-
- state = RECEIVING_DATA;
- break;
- }
- case RECEIVING_DATA: {
- char buf[4096];
- int ret;
-
- do {
- ret = read(sock, buf, sizeof(buf));
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- perror("read");
- state = CLOSING_SOCKET;
- continue;
- }
-
- if (ret == 0) {
- // This really shouldn't happen...
- fprintf(stderr, "Socket unexpectedly closed while reading header\n");
- state = CLOSING_SOCKET;
- continue;
- }
-
- process_data(buf, ret);
- break;
- }
- case CLOSING_SOCKET: {
- int err;
- do {
- err = close(sock);
- } while (err == -1 && errno == EINTR);
-
- if (err == -1) {
- perror("close");
- }
-
- state = NOT_CONNECTED;
- break;
- }
- default:
- assert(false);
- }
-
- // If we are still in NOT_CONNECTED, either something went wrong,
- // or the connection just got closed.
- // The earlier steps have already given the error message, if any.
- if (state == NOT_CONNECTED && !should_stop) {
- fprintf(stderr, "Waiting 0.2 second and restarting...\n");
- usleep(200000);
- }