+#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "parse.h"
#include "serverpool.h"
#include "state.pb.h"
+#include "util.h"
#include "version.h"
using namespace std;
void HTTPInput::close_socket()
{
- int ret;
- do {
- ret = close(sock);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- log_perror("close()");
- }
+ safe_close(sock);
}
InputProto HTTPInput::serialize() const
addrinfo *base_ai = ai;
// Connect to everything in turn until we have a socket.
- while (ai && !should_stop) {
+ while (ai && !should_stop()) {
int sock = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
// Could be e.g. EPROTONOSUPPORT. The show must go on.
continue;
}
+ // Now do a non-blocking connect. This is important because we want to be able to be
+ // woken up, even though it's rather cumbersome.
+
+ // Set the socket as nonblocking.
+ int one = 1;
+ if (ioctl(sock, FIONBIO, &one) == -1) {
+ log_perror("ioctl(FIONBIO)");
+ safe_close(sock);
+ return -1;
+ }
+
+ // Do a non-blocking connect.
do {
err = connect(sock, ai->ai_addr, ai->ai_addrlen);
} while (err == -1 && errno == EINTR);
- if (err != -1) {
- freeaddrinfo(base_ai);
- return sock;
+ if (err == -1 && errno != EINPROGRESS) {
+ log_perror("connect");
+ safe_close(sock);
+ continue;
}
- do {
- err = close(sock);
- } while (err == -1 && errno == EINTR);
+ // Wait for the connect to complete, or an error to happen.
+ for ( ;; ) {
+ bool complete = wait_for_activity(sock, POLLIN | POLLOUT, NULL);
+ if (should_stop()) {
+ safe_close(sock);
+ return -1;
+ }
+ if (complete) {
+ break;
+ }
+ }
- if (err == -1) {
- log_perror("close");
- // Can still continue.
+ // Check whether it ended in an error or not.
+ socklen_t err_size = sizeof(err);
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &err_size) == -1) {
+ log_perror("getsockopt");
+ safe_close(sock);
+ continue;
}
+ errno = err;
+
+ if (err == 0) {
+ // Successful connect.
+ freeaddrinfo(base_ai);
+ return sock;
+ }
+
+ safe_close(sock);
ai = ai->ai_next;
}
}
}
+ // Set “Connection: close”.
+ // TODO: Make case-insensitive.
+ parameters.erase("Connection");
+ parameters.insert(make_pair("Connection", "close"));
+
// Construct the new HTTP header.
http_header = "HTTP/1.0 200 OK\r\n";
for (multimap<string, string>::iterator it = parameters.begin();
void HTTPInput::do_work()
{
- while (!should_stop) {
+ 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)) {
+ bool activity = wait_for_activity(sock, (state == SENDING_REQUEST) ? POLLOUT : POLLIN, NULL);
+ if (!activity) {
+ // Most likely, should_stop was set.
continue;
}
- if (nfds == -1) {
- log_perror("poll");
- state = CLOSING_SOCKET;
- }
}
switch (state) {
break;
}
case CLOSING_SOCKET: {
- int err;
- do {
- err = close(sock);
- } while (err == -1 && errno == EINTR);
-
- if (err == -1) {
- log_perror("close");
- }
-
+ close_socket();
state = NOT_CONNECTED;
break;
}
// 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) {
+ if (state == NOT_CONNECTED && !should_stop()) {
log(INFO, "[%s] Waiting 0.2 second and restarting...", url.c_str());
- usleep(200000);
+ timespec timeout_ts;
+ timeout_ts.tv_sec = 0;
+ timeout_ts.tv_nsec = 200000000;
+ wait_for_wakeup(&timeout_ts);
}
}
}