forked from cory/tildefriends
		
	Move the HTTP timeout into C where we can manage it better as writes are active. Fixes an accidental 45 second GET timeout from httpd.js.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4466 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		@@ -4,7 +4,7 @@ let gHandlers = [];
 | 
			
		||||
let gSocketHandlers = [];
 | 
			
		||||
let gBadRequests = {};
 | 
			
		||||
 | 
			
		||||
const kRequestTimeout = 15000;
 | 
			
		||||
const kRequestTimeout = 5000;
 | 
			
		||||
const kStallTimeout = 60000;
 | 
			
		||||
 | 
			
		||||
function logError(error) {
 | 
			
		||||
@@ -395,41 +395,10 @@ function handleConnection(client) {
 | 
			
		||||
	let parsing_header = true;
 | 
			
		||||
	let bodyToRead = -1;
 | 
			
		||||
	let body;
 | 
			
		||||
	let requestCount = -1;
 | 
			
		||||
	let readCount = 0;
 | 
			
		||||
	let isWebsocket = false;
 | 
			
		||||
 | 
			
		||||
	function resetTimeout(requestIndex) {
 | 
			
		||||
		if (isWebsocket) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (bodyToRead == -1) {
 | 
			
		||||
			setTimeout(function() {
 | 
			
		||||
				if (requestCount == requestIndex) {
 | 
			
		||||
					client.info = 'timed out';
 | 
			
		||||
					if (requestCount == 0) {
 | 
			
		||||
						badRequest(client, 'Timed out waiting for request.');
 | 
			
		||||
					} else {
 | 
			
		||||
						client.close();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}, kRequestTimeout);
 | 
			
		||||
		} else {
 | 
			
		||||
			let lastReadCount = readCount;
 | 
			
		||||
			setTimeout(function() {
 | 
			
		||||
				if (readCount == lastReadCount) {
 | 
			
		||||
					client.info = 'stalled';
 | 
			
		||||
					if (requestCount == 0) {
 | 
			
		||||
						badRequest(client, 'Request stalled.');
 | 
			
		||||
					} else {
 | 
			
		||||
						client.close();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}, kStallTimeout);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resetTimeout(++requestCount);
 | 
			
		||||
	client.setActivityTimeout(kRequestTimeout);
 | 
			
		||||
 | 
			
		||||
	function reset() {
 | 
			
		||||
		request = undefined;
 | 
			
		||||
@@ -438,7 +407,7 @@ function handleConnection(client) {
 | 
			
		||||
		bodyToRead = -1;
 | 
			
		||||
		body = undefined;
 | 
			
		||||
		client.info = 'reset';
 | 
			
		||||
		resetTimeout(++requestCount);
 | 
			
		||||
		client.setActivityTimeout(kRequestTimeout);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function finish() {
 | 
			
		||||
@@ -463,9 +432,6 @@ function handleConnection(client) {
 | 
			
		||||
	client.read(function(data) {
 | 
			
		||||
		readCount++;
 | 
			
		||||
		if (data) {
 | 
			
		||||
			if (bodyToRead != -1 && !isWebsocket) {
 | 
			
		||||
				resetTimeout(requestCount);
 | 
			
		||||
			}
 | 
			
		||||
			let newBuffer = new Uint8Array(inputBuffer.length + data.length);
 | 
			
		||||
			newBuffer.set(inputBuffer, 0);
 | 
			
		||||
			newBuffer.set(data, inputBuffer.length);
 | 
			
		||||
@@ -483,6 +449,7 @@ function handleConnection(client) {
 | 
			
		||||
							return;
 | 
			
		||||
						}
 | 
			
		||||
					} else if (typeof result === 'object')  {
 | 
			
		||||
						client.setActivityTimeout(kStallTimeout);
 | 
			
		||||
						request = [
 | 
			
		||||
							result.method,
 | 
			
		||||
							result.path,
 | 
			
		||||
@@ -509,7 +476,6 @@ function handleConnection(client) {
 | 
			
		||||
							}
 | 
			
		||||
							body = new Uint8Array(bodyToRead);
 | 
			
		||||
							client.info = 'waiting for body';
 | 
			
		||||
							resetTimeout(requestCount);
 | 
			
		||||
						} else if (headers["connection"]
 | 
			
		||||
							&& headers["connection"].toLowerCase().split(",").map(x => x.trim()).indexOf("upgrade") != -1
 | 
			
		||||
							&& headers["upgrade"]
 | 
			
		||||
@@ -520,7 +486,7 @@ function handleConnection(client) {
 | 
			
		||||
							let response = new Response(requestObject, client);
 | 
			
		||||
							handleWebSocketRequest(requestObject, response, client);
 | 
			
		||||
							/* Prevent the timeout from disconnecting us. */
 | 
			
		||||
							requestCount++;
 | 
			
		||||
							client.setActivityTimeout();
 | 
			
		||||
						} else {
 | 
			
		||||
							finish();
 | 
			
		||||
						}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ typedef enum _socket_direction_t {
 | 
			
		||||
typedef struct _socket_t {
 | 
			
		||||
	tf_task_t* _task;
 | 
			
		||||
	uv_tcp_t _socket;
 | 
			
		||||
	uv_timer_t _timer;
 | 
			
		||||
	tf_tls_session_t* _tls;
 | 
			
		||||
	promiseid_t _startTlsPromise;
 | 
			
		||||
	promiseid_t _closePromise;
 | 
			
		||||
@@ -37,6 +38,7 @@ typedef struct _socket_t {
 | 
			
		||||
	bool _noDelay;
 | 
			
		||||
	bool _reading;
 | 
			
		||||
	bool _listening;
 | 
			
		||||
	int _active;
 | 
			
		||||
	char _peerName[256];
 | 
			
		||||
	socket_direction_t _direction;
 | 
			
		||||
	JSValue _object;
 | 
			
		||||
@@ -44,6 +46,7 @@ typedef struct _socket_t {
 | 
			
		||||
	JSValue _onRead;
 | 
			
		||||
	JSValue _onError;
 | 
			
		||||
	uint64_t created_ms;
 | 
			
		||||
	uint64_t timeout_ms;
 | 
			
		||||
} socket_t;
 | 
			
		||||
 | 
			
		||||
static JSValue _socket_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
 | 
			
		||||
@@ -66,6 +69,7 @@ static JSValue _socket_getPeerCertificate(JSContext* context, JSValueConst this_
 | 
			
		||||
static JSValue _socket_getNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
 | 
			
		||||
static JSValue _socket_setNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
 | 
			
		||||
static JSValue _sockets_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
 | 
			
		||||
static JSValue _socket_setActivityTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
 | 
			
		||||
 | 
			
		||||
static void _socket_onClose(uv_handle_t* handle);
 | 
			
		||||
static void _socket_onShutdown(uv_shutdown_t* request, int status);
 | 
			
		||||
@@ -77,6 +81,9 @@ static void _socket_allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv
 | 
			
		||||
static void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer);
 | 
			
		||||
static void _socket_onWrite(uv_write_t* request, int status);
 | 
			
		||||
static void _socket_onTlsShutdown(uv_write_t* request, int status);
 | 
			
		||||
static void _socket_resetTimeout(socket_t* socket);
 | 
			
		||||
static void _socket_pauseTimeout(socket_t* socket);
 | 
			
		||||
static void _socket_resumeTimeout(socket_t* socket);
 | 
			
		||||
 | 
			
		||||
static void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length);
 | 
			
		||||
static int _socket_writeBytes(socket_t* socket, promiseid_t promise, int (*callback)(socket_t* socket, promiseid_t promise, const char*, size_t), JSValue value, int* outLength);
 | 
			
		||||
@@ -188,6 +195,7 @@ socket_t* _socket_create_internal(JSContext* context)
 | 
			
		||||
	JS_SetPropertyStr(context, object, "read", JS_NewCFunction(context, _socket_read, "read", 0));
 | 
			
		||||
	JS_SetPropertyStr(context, object, "onError", JS_NewCFunction(context, _socket_onError, "onError", 1));
 | 
			
		||||
	JS_SetPropertyStr(context, object, "write", JS_NewCFunction(context, _socket_write, "write", 1));
 | 
			
		||||
	JS_SetPropertyStr(context, object, "setActivityTimeout", JS_NewCFunction(context, _socket_setActivityTimeout, "setActivityTimeout", 1));
 | 
			
		||||
 | 
			
		||||
	JSAtom atom = JS_NewAtom(context, "isConnected");
 | 
			
		||||
	JS_DefinePropertyGetSet(context, object, atom, JS_NewCFunction(context, _socket_isConnected, "isConnected", 0), JS_NULL, 0);
 | 
			
		||||
@@ -210,6 +218,8 @@ socket_t* _socket_create_internal(JSContext* context)
 | 
			
		||||
	++_open_count;
 | 
			
		||||
	uv_tcp_init(tf_task_get_loop(socket->_task), &socket->_socket);
 | 
			
		||||
	socket->_socket.data = socket;
 | 
			
		||||
	uv_timer_init(tf_task_get_loop(socket->_task), &socket->_timer);
 | 
			
		||||
	socket->_timer.data = socket;
 | 
			
		||||
 | 
			
		||||
	return socket;
 | 
			
		||||
}
 | 
			
		||||
@@ -234,8 +244,14 @@ void _socket_close_internal(socket_t* socket)
 | 
			
		||||
	{
 | 
			
		||||
		uv_close((uv_handle_t*)&socket->_socket, _socket_onClose);
 | 
			
		||||
	}
 | 
			
		||||
	if (socket->_timer.data &&
 | 
			
		||||
		!uv_is_closing((uv_handle_t*)&socket->_timer))
 | 
			
		||||
	{
 | 
			
		||||
		uv_close((uv_handle_t*)&socket->_timer, _socket_onClose);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!socket->_socket.data &&
 | 
			
		||||
		!socket->_timer.data &&
 | 
			
		||||
		JS_IsUndefined(socket->_object))
 | 
			
		||||
	{
 | 
			
		||||
		--_count;
 | 
			
		||||
@@ -381,6 +397,7 @@ bool _socket_processSomeOutgoingTls(socket_t* socket, promiseid_t promise, uv_wr
 | 
			
		||||
			.len = result,
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		_socket_pauseTimeout(socket);
 | 
			
		||||
		int writeResult = uv_write(request, (uv_stream_t*)&socket->_socket, &writeBuffer, 1, callback);
 | 
			
		||||
		if (writeResult != 0)
 | 
			
		||||
		{
 | 
			
		||||
@@ -706,6 +723,7 @@ JSValue _socket_read(JSContext* context, JSValueConst this_val, int argc, JSValu
 | 
			
		||||
	JSValue read_result = tf_task_allocate_promise(socket->_task, &promise);
 | 
			
		||||
	if (!socket->_reading && socket->_socket.data)
 | 
			
		||||
	{
 | 
			
		||||
		_socket_resetTimeout(socket);
 | 
			
		||||
		int result = uv_read_start((uv_stream_t*)&socket->_socket, _socket_allocateBuffer, _socket_onRead);
 | 
			
		||||
		if (result != 0)
 | 
			
		||||
		{
 | 
			
		||||
@@ -733,6 +751,7 @@ void _socket_allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t*
 | 
			
		||||
void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer)
 | 
			
		||||
{
 | 
			
		||||
	socket_t* socket = stream->data;
 | 
			
		||||
	_socket_resetTimeout(socket);
 | 
			
		||||
	JSContext* context = tf_task_get_context(socket->_task);
 | 
			
		||||
	JSValue ref = JS_DupValue(context, socket->_object);
 | 
			
		||||
	if (readSize <= 0)
 | 
			
		||||
@@ -907,6 +926,7 @@ int _socket_writeInternal(socket_t* socket, promiseid_t promise, const char* dat
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	request->data = (void*)(intptr_t)promise;
 | 
			
		||||
	_socket_pauseTimeout(socket);
 | 
			
		||||
	int result = uv_write(request, (uv_stream_t*)&socket->_socket, &buffer, 1, _socket_onWrite);
 | 
			
		||||
	if (result != 0)
 | 
			
		||||
	{
 | 
			
		||||
@@ -970,6 +990,7 @@ JSValue _socket_write(JSContext* context, JSValueConst this_val, int argc, JSVal
 | 
			
		||||
void _socket_onWrite(uv_write_t* request, int status)
 | 
			
		||||
{
 | 
			
		||||
	socket_t* socket = request->handle->data;
 | 
			
		||||
	_socket_resumeTimeout(socket);
 | 
			
		||||
	promiseid_t promise = (intptr_t)request->data;
 | 
			
		||||
	if (promise != -1)
 | 
			
		||||
	{
 | 
			
		||||
@@ -985,6 +1006,26 @@ void _socket_onWrite(uv_write_t* request, int status)
 | 
			
		||||
	tf_free(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _socket_timeout(uv_timer_t* timer)
 | 
			
		||||
{
 | 
			
		||||
	socket_t* socket = timer->data;
 | 
			
		||||
	_socket_close_internal(socket);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JSValue _socket_setActivityTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
			
		||||
{
 | 
			
		||||
	socket_t* socket = JS_GetOpaque(this_val, _classId);
 | 
			
		||||
	int64_t timeout = 0;
 | 
			
		||||
	if (JS_ToInt64(context, &timeout, argv[0]) == 0 &&
 | 
			
		||||
		socket->timeout_ms > 0) {
 | 
			
		||||
		socket->timeout_ms = timeout;
 | 
			
		||||
		uv_timer_start(&socket->_timer, _socket_timeout, socket->timeout_ms, 0);
 | 
			
		||||
	} else {
 | 
			
		||||
		uv_timer_stop(&socket->_timer);
 | 
			
		||||
	}
 | 
			
		||||
	return JS_UNDEFINED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JSValue _socket_isConnected(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
			
		||||
{
 | 
			
		||||
	socket_t* socket = JS_GetOpaque(this_val, _classId);
 | 
			
		||||
@@ -1016,6 +1057,7 @@ void _socket_onClose(uv_handle_t* handle)
 | 
			
		||||
void _socket_onShutdown(uv_shutdown_t* request, int status)
 | 
			
		||||
{
 | 
			
		||||
	socket_t* socket = request->handle->data;
 | 
			
		||||
	_socket_resetTimeout(socket);
 | 
			
		||||
	promiseid_t promise = (intptr_t)request->data;
 | 
			
		||||
	if (status == 0)
 | 
			
		||||
	{
 | 
			
		||||
@@ -1102,3 +1144,27 @@ JSValue _sockets_get(JSContext* context, JSValueConst this_val, int argc, JSValu
 | 
			
		||||
	}
 | 
			
		||||
	return array;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _socket_resetTimeout(socket_t* socket)
 | 
			
		||||
{
 | 
			
		||||
	if (socket->timeout_ms && socket->_active == 0)
 | 
			
		||||
	{
 | 
			
		||||
		uv_timer_start(&socket->_timer, _socket_timeout, socket->timeout_ms, 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _socket_pauseTimeout(socket_t* socket)
 | 
			
		||||
{
 | 
			
		||||
	if (socket->_active++ == 1)
 | 
			
		||||
	{
 | 
			
		||||
		uv_timer_stop(&socket->_timer);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _socket_resumeTimeout(socket_t* socket)
 | 
			
		||||
{
 | 
			
		||||
	if (--socket->_active == 0 && socket->timeout_ms > 0)
 | 
			
		||||
	{
 | 
			
		||||
		uv_timer_start(&socket->_timer, _socket_timeout, socket->timeout_ms, 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user