|  | @@ -2918,45 +2918,100 @@ static int set_non_blocking_mode(SOCKET sock)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Write data to the IO channel - opened file descriptor, socket or SSL
 | 
	
		
			
				|  |  |   * descriptor. Return number of bytes written. */
 | 
	
		
			
				|  |  | -static int64_t
 | 
	
		
			
				|  |  | -push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len)
 | 
	
		
			
				|  |  | +static int push(struct mg_context *ctx,
 | 
	
		
			
				|  |  | +                FILE *fp,
 | 
	
		
			
				|  |  | +                SOCKET sock,
 | 
	
		
			
				|  |  | +                SSL *ssl,
 | 
	
		
			
				|  |  | +                const char *buf,
 | 
	
		
			
				|  |  | +                int len,
 | 
	
		
			
				|  |  | +                double timeout)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -	int64_t sent;
 | 
	
		
			
				|  |  | -	int n, k;
 | 
	
		
			
				|  |  | +	struct timespec start, now;
 | 
	
		
			
				|  |  | +	int n;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef _WIN32
 | 
	
		
			
				|  |  | +	typedef int len_t;
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +	typedef size_t len_t;
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	(void)ssl; /* Get rid of warning */
 | 
	
		
			
				|  |  | -	sent = 0;
 | 
	
		
			
				|  |  | -	while (sent < len) {
 | 
	
		
			
				|  |  | -		/* How many bytes we send in this iteration */
 | 
	
		
			
				|  |  | -		k = len - sent > INT_MAX ? INT_MAX : (int)(len - sent);
 | 
	
		
			
				|  |  | +	memset(&start, 0, sizeof(start));
 | 
	
		
			
				|  |  | +	memset(&now, 0, sizeof(now));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (timeout > 0) {
 | 
	
		
			
				|  |  | +		clock_gettime(CLOCK_MONOTONIC, &start);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	do {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #ifndef NO_SSL
 | 
	
		
			
				|  |  |  		if (ssl != NULL) {
 | 
	
		
			
				|  |  | -			n = SSL_write(ssl, buf + sent, k);
 | 
	
		
			
				|  |  | +			n = SSL_write(ssl, buf, len);
 | 
	
		
			
				|  |  |  		} else
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  		    if (fp != NULL) {
 | 
	
		
			
				|  |  | -			n = (int)fwrite(buf + sent, 1, (size_t)k, fp);
 | 
	
		
			
				|  |  | +			n = (int)fwrite(buf, 1, (size_t)len, fp);
 | 
	
		
			
				|  |  |  			if (ferror(fp))
 | 
	
		
			
				|  |  |  				n = -1;
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  | -#ifdef _WIN32
 | 
	
		
			
				|  |  | -			typedef int len_t;
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -			typedef size_t len_t;
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -			n = (int)send(sock, buf + sent, (len_t)k, MSG_NOSIGNAL);
 | 
	
		
			
				|  |  | +			n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if (n <= 0)
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | +		if (ctx->stop_flag) {
 | 
	
		
			
				|  |  | +			return -1;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if ((n > 0) || (n == 0 && len == 0)) {
 | 
	
		
			
				|  |  | +			/* some data has been read, or no data was requested */
 | 
	
		
			
				|  |  | +			return n;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if (n == 0) {
 | 
	
		
			
				|  |  | +			/* shutdown of the socket at client side */
 | 
	
		
			
				|  |  | +			return -1;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if (n < 0) {
 | 
	
		
			
				|  |  | +			/* socket error - check errno */
 | 
	
		
			
				|  |  | +			DEBUG_TRACE("send() failed, error %d", ERRNO);
 | 
	
		
			
				|  |  | +			return -1;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if (timeout > 0) {
 | 
	
		
			
				|  |  | +			clock_gettime(CLOCK_MONOTONIC, &now);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	} while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return -1;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int64_t push_all(struct mg_context *ctx,
 | 
	
		
			
				|  |  | +                        FILE *fp,
 | 
	
		
			
				|  |  | +                        SOCKET sock,
 | 
	
		
			
				|  |  | +                        SSL *ssl,
 | 
	
		
			
				|  |  | +                        const char *buf,
 | 
	
		
			
				|  |  | +                        int64_t len)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	double timeout = -1.0;
 | 
	
		
			
				|  |  | +	int64_t n, nwritten = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		sent += n;
 | 
	
		
			
				|  |  | +	if (ctx->config[REQUEST_TIMEOUT]) {
 | 
	
		
			
				|  |  | +		timeout = atoi(ctx->config[REQUEST_TIMEOUT]) / 1000.0;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	while (len > 0 && ctx->stop_flag == 0) {
 | 
	
		
			
				|  |  | +		n = push(ctx, fp, sock, ssl, buf + nwritten, (int)len, timeout);
 | 
	
		
			
				|  |  | +		if (n < 0) {
 | 
	
		
			
				|  |  | +			nwritten = n; /* Propagate the error */
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		} else if (n == 0) {
 | 
	
		
			
				|  |  | +			break; /* No more data to write */
 | 
	
		
			
				|  |  | +		} else {
 | 
	
		
			
				|  |  | +			nwritten += n;
 | 
	
		
			
				|  |  | +			len -= n;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	return sent;
 | 
	
		
			
				|  |  | +	return nwritten;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* Read from IO channel - opened file descriptor, socket, or SSL descriptor.
 | 
	
		
			
				|  |  |   * Return negative value on error, or number of bytes read on success. */
 | 
	
		
			
				|  |  |  static int
 | 
	
	
		
			
				|  | @@ -2965,6 +3020,12 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 | 
	
		
			
				|  |  |  	int nread;
 | 
	
		
			
				|  |  |  	struct timespec start, now;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#ifdef _WIN32
 | 
	
		
			
				|  |  | +	typedef int len_t;
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +	typedef size_t len_t;
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	memset(&start, 0, sizeof(start));
 | 
	
		
			
				|  |  |  	memset(&now, 0, sizeof(now));
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -2984,19 +3045,24 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 | 
	
		
			
				|  |  |  			nread = SSL_read(conn->ssl, buf, len);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  | -#ifdef _WIN32
 | 
	
		
			
				|  |  | -			typedef int len_t;
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -			typedef size_t len_t;
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  |  			nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  		if (conn->ctx->stop_flag) {
 | 
	
		
			
				|  |  |  			return -1;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		if (nread >= 0) {
 | 
	
		
			
				|  |  | +		if ((nread > 0) || (nread == 0 && len == 0)) {
 | 
	
		
			
				|  |  | +			/* some data has been read, or no data was requested */
 | 
	
		
			
				|  |  |  			return nread;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | +		if (nread == 0) {
 | 
	
		
			
				|  |  | +			/* shutdown of the socket at client side */
 | 
	
		
			
				|  |  | +			return -1;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if (nread < 0) {
 | 
	
		
			
				|  |  | +			/* socket error - check errno */
 | 
	
		
			
				|  |  | +			DEBUG_TRACE("recv() failed, error %d", ERRNO);
 | 
	
		
			
				|  |  | +			return -1;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  		if (timeout > 0) {
 | 
	
		
			
				|  |  |  			clock_gettime(CLOCK_MONOTONIC, &now);
 | 
	
		
			
				|  |  |  		}
 | 
	
	
		
			
				|  | @@ -3006,6 +3072,7 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 | 
	
		
			
				|  |  |  	return -1;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	int n, nread = 0;
 | 
	
	
		
			
				|  | @@ -3232,22 +3299,24 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len)
 | 
	
		
			
				|  |  |  		if (allowed > (int64_t)len) {
 | 
	
		
			
				|  |  |  			allowed = (int64_t)len;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		if ((total = push(NULL,
 | 
	
		
			
				|  |  | -		                  conn->client.sock,
 | 
	
		
			
				|  |  | -		                  conn->ssl,
 | 
	
		
			
				|  |  | -		                  (const char *)buf,
 | 
	
		
			
				|  |  | -		                  (int64_t)allowed)) == allowed) {
 | 
	
		
			
				|  |  | +		if ((total = push_all(conn->ctx,
 | 
	
		
			
				|  |  | +		                      NULL,
 | 
	
		
			
				|  |  | +		                      conn->client.sock,
 | 
	
		
			
				|  |  | +		                      conn->ssl,
 | 
	
		
			
				|  |  | +		                      (const char *)buf,
 | 
	
		
			
				|  |  | +		                      (int64_t)allowed)) == allowed) {
 | 
	
		
			
				|  |  |  			buf = (char *)buf + total;
 | 
	
		
			
				|  |  |  			conn->last_throttle_bytes += total;
 | 
	
		
			
				|  |  |  			while (total < (int64_t)len && conn->ctx->stop_flag == 0) {
 | 
	
		
			
				|  |  |  				allowed = conn->throttle > (int64_t)len - total
 | 
	
		
			
				|  |  |  				              ? (int64_t)len - total
 | 
	
		
			
				|  |  |  				              : conn->throttle;
 | 
	
		
			
				|  |  | -				if ((n = push(NULL,
 | 
	
		
			
				|  |  | -				              conn->client.sock,
 | 
	
		
			
				|  |  | -				              conn->ssl,
 | 
	
		
			
				|  |  | -				              (const char *)buf,
 | 
	
		
			
				|  |  | -				              (int64_t)allowed)) != allowed) {
 | 
	
		
			
				|  |  | +				if ((n = push_all(conn->ctx,
 | 
	
		
			
				|  |  | +				                  NULL,
 | 
	
		
			
				|  |  | +				                  conn->client.sock,
 | 
	
		
			
				|  |  | +				                  conn->ssl,
 | 
	
		
			
				|  |  | +				                  (const char *)buf,
 | 
	
		
			
				|  |  | +				                  (int64_t)allowed)) != allowed) {
 | 
	
		
			
				|  |  |  					break;
 | 
	
		
			
				|  |  |  				}
 | 
	
		
			
				|  |  |  				sleep(1);
 | 
	
	
		
			
				|  | @@ -3258,11 +3327,12 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len)
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  | -		total = push(NULL,
 | 
	
		
			
				|  |  | -		             conn->client.sock,
 | 
	
		
			
				|  |  | -		             conn->ssl,
 | 
	
		
			
				|  |  | -		             (const char *)buf,
 | 
	
		
			
				|  |  | -		             (int64_t)len);
 | 
	
		
			
				|  |  | +		total = push_all(conn->ctx,
 | 
	
		
			
				|  |  | +		                 NULL,
 | 
	
		
			
				|  |  | +		                 conn->client.sock,
 | 
	
		
			
				|  |  | +		                 conn->ssl,
 | 
	
		
			
				|  |  | +		                 (const char *)buf,
 | 
	
		
			
				|  |  | +		                 (int64_t)len);
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	return (int)total;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -5493,7 +5563,7 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 | 
	
		
			
				|  |  |  				buffered_len = (int)conn->content_len;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			body = conn->buf + conn->request_len + conn->consumed_content;
 | 
	
		
			
				|  |  | -			push(fp, sock, ssl, body, (int64_t)buffered_len);
 | 
	
		
			
				|  |  | +			push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len);
 | 
	
		
			
				|  |  |  			conn->consumed_content += buffered_len;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -5504,7 +5574,8 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 | 
	
		
			
				|  |  |  				to_read = (int)(conn->content_len - conn->consumed_content);
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			nread = pull(NULL, conn, buf, to_read, timeout);
 | 
	
		
			
				|  |  | -			if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
 | 
	
		
			
				|  |  | +			if (nread <= 0 ||
 | 
	
		
			
				|  |  | +			    push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) {
 | 
	
		
			
				|  |  |  				break;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			conn->consumed_content += nread;
 | 
	
	
		
			
				|  | @@ -8189,7 +8260,7 @@ static int set_ports_option(struct mg_context *ctx)
 | 
	
		
			
				|  |  |  			            * if someone already has the socket -- DTL */
 | 
	
		
			
				|  |  |  			           setsockopt(so.sock,
 | 
	
		
			
				|  |  |  			                      SOL_SOCKET,
 | 
	
		
			
				|  |  | -			                      SO_EXCLUSIVEADDRUSE,
 | 
	
		
			
				|  |  | +			                      SO_REUSEADDR /* TODO(high): check with unit test -> SO_EXCLUSIVEADDRUSE */,
 | 
	
		
			
				|  |  |  			                      (SOCK_OPT_TYPE)&on,
 | 
	
		
			
				|  |  |  			                      sizeof(on)) != 0 ||
 | 
	
		
			
				|  |  |  #else
 |