|  | @@ -3724,6 +3724,10 @@ static SOCKET conn2(struct mg_context *ctx  /* may be null */, const char *host,
 | 
	
		
			
				|  |  |      struct hostent *he;
 | 
	
		
			
				|  |  |      SOCKET sock = INVALID_SOCKET;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    if (ebuf_len>0) {
 | 
	
		
			
				|  |  | +        *ebuf = 0;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      if (host == NULL) {
 | 
	
		
			
				|  |  |          snprintf(ebuf, ebuf_len, "%s", "NULL host");
 | 
	
		
			
				|  |  |      } else if (use_ssl && SSLv23_client_method == NULL) {
 | 
	
	
		
			
				|  | @@ -4296,18 +4300,22 @@ static int read_request(FILE *fp, struct mg_connection *conn,
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      int request_len, n = 0;
 | 
	
		
			
				|  |  |      time_t last_action_time = 0;
 | 
	
		
			
				|  |  | -    double request_timout = 0.0;
 | 
	
		
			
				|  |  | +    double request_timout;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (conn->ctx->config[REQUEST_TIMEOUT]) {
 | 
	
		
			
				|  |  |          /* value of request_timout is in seconds, config in milliseconds */
 | 
	
		
			
				|  |  |          request_timout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +        request_timout = -1.0;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      request_len = get_request_len(buf, *nread);
 | 
	
		
			
				|  |  | -    while (conn->ctx->stop_flag == 0 &&
 | 
	
		
			
				|  |  | -           *nread < bufsiz && request_len == 0 &&
 | 
	
		
			
				|  |  | -           difftime(last_action_time, conn->birth_time) <= request_timout &&
 | 
	
		
			
				|  |  | -           (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
 | 
	
		
			
				|  |  | +    while ((conn->ctx->stop_flag == 0) &&
 | 
	
		
			
				|  |  | +           (*nread < bufsiz) &&
 | 
	
		
			
				|  |  | +           (request_len == 0) &&
 | 
	
		
			
				|  |  | +           ((difftime(last_action_time, conn->birth_time) <= request_timout) || (request_timout < 0)) &&
 | 
	
		
			
				|  |  | +           ((n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0)
 | 
	
		
			
				|  |  | +          ) {
 | 
	
		
			
				|  |  |          *nread += n;
 | 
	
		
			
				|  |  |          assert(*nread <= bufsiz);
 | 
	
		
			
				|  |  |          request_len = get_request_len(buf, *nread);
 | 
	
	
		
			
				|  | @@ -6924,6 +6932,40 @@ static void reset_per_request_attributes(struct mg_connection *conn)
 | 
	
		
			
				|  |  |      conn->data_len = 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int set_sock_timeout(SOCKET sock, int milliseconds)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int r1, r2;
 | 
	
		
			
				|  |  | +#ifdef _WIN32
 | 
	
		
			
				|  |  | +    DWORD t = milliseconds;
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +#if defined(TCP_USER_TIMEOUT)
 | 
	
		
			
				|  |  | +    unsigned int uto = (unsigned int)milliseconds;
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +    struct timeval t;
 | 
	
		
			
				|  |  | +    t.tv_sec = milliseconds / 1000;
 | 
	
		
			
				|  |  | +    t.tv_usec = (milliseconds * 1000) % 1000000;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
 | 
	
		
			
				|  |  | +       max. time waiting for the acknowledged of TCP data before the connection
 | 
	
		
			
				|  |  | +       will be forcefully closed and ETIMEDOUT is returned to the application.
 | 
	
		
			
				|  |  | +       If this option is not set, the default timeout of 20-30 minutes is used.
 | 
	
		
			
				|  |  | +    */
 | 
	
		
			
				|  |  | +    /* #define TCP_USER_TIMEOUT (18) */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if defined(TCP_USER_TIMEOUT)
 | 
	
		
			
				|  |  | +    setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    r1 = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
 | 
	
		
			
				|  |  | +    r2 = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return r1 || r2;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void close_socket_gracefully(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  #if defined(_WIN32)
 | 
	
	
		
			
				|  | @@ -7140,10 +7182,26 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *
 | 
	
		
			
				|  |  |  int mg_get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      /* Implementation of API function for HTTP clients */
 | 
	
		
			
				|  |  | +    int err, ret;
 | 
	
		
			
				|  |  | +    struct mg_context *octx = conn->ctx;
 | 
	
		
			
				|  |  | +    struct mg_context rctx = *(conn->ctx);
 | 
	
		
			
				|  |  | +    char txt[32];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (timeout >= 0) {
 | 
	
		
			
				|  |  | +        snprintf(txt, sizeof(txt), "%i", timeout);
 | 
	
		
			
				|  |  | +        rctx.config[REQUEST_TIMEOUT] = txt;
 | 
	
		
			
				|  |  | +        set_sock_timeout(conn->client.sock, timeout);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +        rctx.config[REQUEST_TIMEOUT] = NULL;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    conn->ctx = &rctx;
 | 
	
		
			
				|  |  | +    ret = getreq(conn, ebuf, ebuf_len, &err);
 | 
	
		
			
				|  |  | +    conn->ctx = octx;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /* TODO: Define proper return values - maybe return length?
 | 
	
		
			
				|  |  |               For the first test use <0 for error and >0 for OK */
 | 
	
		
			
				|  |  | -    int err;
 | 
	
		
			
				|  |  | -    return (getreq(conn, ebuf, ebuf_len, &err) == 0) ? -1 : +1;
 | 
	
		
			
				|  |  | +    return (ret == 0) ? -1 : +1;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  struct mg_connection *mg_download(const char *host, int port, int use_ssl,
 | 
	
	
		
			
				|  | @@ -7490,37 +7548,6 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp)
 | 
	
		
			
				|  |  |      (void) pthread_mutex_unlock(&ctx->thread_mutex);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static int set_sock_timeout(SOCKET sock, int milliseconds)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -    int r1, r2;
 | 
	
		
			
				|  |  | -#ifdef _WIN32
 | 
	
		
			
				|  |  | -    DWORD t = milliseconds;
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -#if defined(TCP_USER_TIMEOUT)
 | 
	
		
			
				|  |  | -    unsigned int uto = (unsigned int)milliseconds;
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -    struct timeval t;
 | 
	
		
			
				|  |  | -    t.tv_sec = milliseconds / 1000;
 | 
	
		
			
				|  |  | -    t.tv_usec = (milliseconds * 1000) % 1000000;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
 | 
	
		
			
				|  |  | -       max. time waiting for the acknowledged of TCP data before the connection
 | 
	
		
			
				|  |  | -       will be forcefully closed and ETIMEDOUT is returned to the application.
 | 
	
		
			
				|  |  | -       If this option is not set, the default timeout of 20-30 minutes is used.
 | 
	
		
			
				|  |  | -    */
 | 
	
		
			
				|  |  | -    /* #define TCP_USER_TIMEOUT (18) */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#if defined(TCP_USER_TIMEOUT)
 | 
	
		
			
				|  |  | -    setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    r1 = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
 | 
	
		
			
				|  |  | -    r2 = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    return r1 || r2;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void accept_new_connection(const struct socket *listener,
 | 
	
		
			
				|  |  |                                    struct mg_context *ctx)
 | 
	
	
		
			
				|  | @@ -7529,6 +7556,7 @@ static void accept_new_connection(const struct socket *listener,
 | 
	
		
			
				|  |  |      char src_addr[IP_ADDR_STR_LEN];
 | 
	
		
			
				|  |  |      socklen_t len = sizeof(so.rsa);
 | 
	
		
			
				|  |  |      int on = 1;
 | 
	
		
			
				|  |  | +    int timeout;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
 | 
	
		
			
				|  |  |      } else if (!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) {
 | 
	
	
		
			
				|  | @@ -7546,6 +7574,7 @@ static void accept_new_connection(const struct socket *listener,
 | 
	
		
			
				|  |  |              mg_cry(fc(ctx), "%s: getsockname() failed: %s",
 | 
	
		
			
				|  |  |                     __func__, strerror(ERRNO));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          /* Set TCP keep-alive. This is needed because if HTTP-level keep-alive
 | 
	
		
			
				|  |  |             is enabled, and client resets the connection, server won't get
 | 
	
		
			
				|  |  |             TCP FIN or RST and will keep the connection open forever. With TCP
 | 
	
	
		
			
				|  | @@ -7557,7 +7586,17 @@ static void accept_new_connection(const struct socket *listener,
 | 
	
		
			
				|  |  |                     "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
 | 
	
		
			
				|  |  |                     __func__, strerror(ERRNO));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        set_sock_timeout(so.sock, atoi(ctx->config[REQUEST_TIMEOUT]));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (ctx->config[REQUEST_TIMEOUT]) {
 | 
	
		
			
				|  |  | +            timeout = atoi(ctx->config[REQUEST_TIMEOUT]);
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            timeout = -1;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (timeout>0) {
 | 
	
		
			
				|  |  | +            set_sock_timeout(so.sock, atoi(ctx->config[REQUEST_TIMEOUT]));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          produce_socket(ctx, &so);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 |