Cleanup, fix handshake, better errors
This commit is contained in:
		@@ -87,6 +87,13 @@ struct backend_iface
 | 
				
			|||||||
	void (*destroy) (struct app_context *ctx);
 | 
						void (*destroy) (struct app_context *ctx);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Shorthand to set an error and return failure from the function
 | 
				
			||||||
 | 
					#define FAIL(...)                                                              \
 | 
				
			||||||
 | 
						BLOCK_START                                                                \
 | 
				
			||||||
 | 
							error_set (e, __VA_ARGS__);                                            \
 | 
				
			||||||
 | 
							return false;                                                          \
 | 
				
			||||||
 | 
						BLOCK_END
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum ws_handler_state
 | 
					enum ws_handler_state
 | 
				
			||||||
@@ -165,17 +172,8 @@ enum color_mode
 | 
				
			|||||||
	COLOR_NEVER                         ///< Never use coloured output
 | 
						COLOR_NEVER                         ///< Never use coloured output
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum backend
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	BACKEND_CURL,                       ///< Communication is handled by cURL
 | 
					 | 
				
			||||||
	BACKEND_WS                          ///< WebSockets
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct app_context
 | 
					static struct app_context
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
	enum backend backend;               ///< Our current backend
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	struct backend_iface *backend;      ///< Our current backend
 | 
						struct backend_iface *backend;      ///< Our current backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ws_context ws;               ///< WebSockets backend data
 | 
						struct ws_context ws;               ///< WebSockets backend data
 | 
				
			||||||
@@ -437,23 +435,16 @@ read_string_escape_sequence (const char **cursor,
 | 
				
			|||||||
	case 'x':
 | 
						case 'x':
 | 
				
			||||||
	case 'X':
 | 
						case 'X':
 | 
				
			||||||
		if (!read_hexa_escape (cursor, output))
 | 
							if (!read_hexa_escape (cursor, output))
 | 
				
			||||||
		{
 | 
								FAIL ("invalid hexadecimal escape");
 | 
				
			||||||
			error_set (e, "invalid hexadecimal escape");
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case '\0':
 | 
						case '\0':
 | 
				
			||||||
		error_set (e, "premature end of escape sequence");
 | 
							FAIL ("premature end of escape sequence");
 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		(*cursor)--;
 | 
							(*cursor)--;
 | 
				
			||||||
		if (!read_octal_escape (cursor, output))
 | 
							if (!read_octal_escape (cursor, output))
 | 
				
			||||||
		{
 | 
								FAIL ("unknown escape sequence");
 | 
				
			||||||
			error_set (e, "unknown escape sequence");
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -677,39 +668,39 @@ backend_ws_on_headers_complete (http_parser *parser)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
backend_ws_finish_handshake (struct app_context *ctx)
 | 
					backend_ws_finish_handshake (struct app_context *ctx, struct error **e)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// TODO: return the errors as a "struct error"
 | 
					 | 
				
			||||||
	struct ws_context *self = &ctx->ws;
 | 
						struct ws_context *self = &ctx->ws;
 | 
				
			||||||
	if (self->hp.http_major != 1 || self->hp.http_minor < 1)
 | 
						if (self->hp.http_major != 1 || self->hp.http_minor < 1)
 | 
				
			||||||
		return false;
 | 
							FAIL ("incompatible HTTP version: %d.%d",
 | 
				
			||||||
 | 
								self->hp.http_major, self->hp.http_minor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (self->hp.status_code != 101)
 | 
						if (self->hp.status_code != 101)
 | 
				
			||||||
		// TODO: handle other codes?
 | 
							// TODO: handle other codes?
 | 
				
			||||||
		return false;
 | 
							FAIL ("unexpected status code: %d", self->hp.status_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *upgrade = str_map_find (&self->headers, "Upgrade");
 | 
						const char *upgrade = str_map_find (&self->headers, "Upgrade");
 | 
				
			||||||
	if (!upgrade || strcasecmp_ascii (upgrade, "websocket"))
 | 
						if (!upgrade || strcasecmp_ascii (upgrade, "websocket"))
 | 
				
			||||||
		return false;
 | 
							FAIL ("cannot upgrade connection to WebSocket");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *connection = str_map_find (&self->headers, "Connection");
 | 
						const char *connection = str_map_find (&self->headers, "Connection");
 | 
				
			||||||
	if (!connection || strcasecmp_ascii (connection, "Upgrade"))
 | 
						if (!connection || strcasecmp_ascii (connection, "Upgrade"))
 | 
				
			||||||
		// XXX: maybe we shouldn't be so strict and only check for presence
 | 
							// XXX: maybe we shouldn't be so strict and only check for presence
 | 
				
			||||||
		//   of the "Upgrade" token in this list
 | 
							//   of the "Upgrade" token in this list
 | 
				
			||||||
		return false;
 | 
							FAIL ("cannot upgrade connection to WebSocket");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *accept = str_map_find (&self->headers, "Accept");
 | 
						const char *accept = str_map_find (&self->headers, SEC_WS_ACCEPT);
 | 
				
			||||||
	char *accept_expected = ws_encode_response_key (self->key);
 | 
						char *accept_expected = ws_encode_response_key (self->key);
 | 
				
			||||||
	bool accept_ok = accept && !strcmp (accept, accept_expected);
 | 
						bool accept_ok = accept && !strcmp (accept, accept_expected);
 | 
				
			||||||
	free (accept_expected);
 | 
						free (accept_expected);
 | 
				
			||||||
	if (!accept_ok)
 | 
						if (!accept_ok)
 | 
				
			||||||
		return false;
 | 
							FAIL ("missing or invalid " SEC_WS_ACCEPT " header");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *extensions = str_map_find (&self->headers, SEC_WS_EXTENSIONS);
 | 
						const char *extensions = str_map_find (&self->headers, SEC_WS_EXTENSIONS);
 | 
				
			||||||
	const char *protocol = str_map_find (&self->headers, SEC_WS_PROTOCOL);
 | 
						const char *protocol = str_map_find (&self->headers, SEC_WS_PROTOCOL);
 | 
				
			||||||
	if (extensions || protocol)
 | 
						if (extensions || protocol)
 | 
				
			||||||
		// TODO: actually parse these fields
 | 
							// TODO: actually parse these fields
 | 
				
			||||||
		return false;
 | 
							FAIL ("unexpected WebSocket extension or protocol");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -734,8 +725,14 @@ backend_ws_on_data (struct app_context *ctx, const void *data, size_t len)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (self->hp.upgrade)
 | 
						if (self->hp.upgrade)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (!backend_ws_finish_handshake (ctx))
 | 
							struct error *e = NULL;
 | 
				
			||||||
 | 
							if (!backend_ws_finish_handshake (ctx, &e))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								print_debug ("WS handshake failed: %s", e->message);
 | 
				
			||||||
 | 
								error_free (e);
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		self->state = WS_HANDLER_OPEN;
 | 
							self->state = WS_HANDLER_OPEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// TODO: set the event loop to quit?
 | 
							// TODO: set the event loop to quit?
 | 
				
			||||||
@@ -846,11 +843,8 @@ backend_ws_establish_connection (struct app_context *ctx,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	int err = getaddrinfo (host, port, &gai_hints, &gai_result);
 | 
						int err = getaddrinfo (host, port, &gai_hints, &gai_result);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
	{
 | 
							FAIL ("%s: %s: %s",
 | 
				
			||||||
		error_set (e, "%s: %s: %s",
 | 
					 | 
				
			||||||
			"connection failed", "getaddrinfo", gai_strerror (err));
 | 
								"connection failed", "getaddrinfo", gai_strerror (err));
 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int sockfd;
 | 
						int sockfd;
 | 
				
			||||||
	for (gai_iter = gai_result; gai_iter; gai_iter = gai_iter->ai_next)
 | 
						for (gai_iter = gai_result; gai_iter; gai_iter = gai_iter->ai_next)
 | 
				
			||||||
@@ -890,10 +884,7 @@ backend_ws_establish_connection (struct app_context *ctx,
 | 
				
			|||||||
	freeaddrinfo (gai_result);
 | 
						freeaddrinfo (gai_result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!gai_iter)
 | 
						if (!gai_iter)
 | 
				
			||||||
	{
 | 
							FAIL ("connection failed");
 | 
				
			||||||
		error_set (e, "connection failed");
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx->ws.server_fd = sockfd;
 | 
						ctx->ws.server_fd = sockfd;
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
@@ -951,8 +942,8 @@ error_ssl_1:
 | 
				
			|||||||
	//   multiple errors on the OpenSSL stack.
 | 
						//   multiple errors on the OpenSSL stack.
 | 
				
			||||||
	if (!error_info)
 | 
						if (!error_info)
 | 
				
			||||||
		error_info = ERR_error_string (ERR_get_error (), NULL);
 | 
							error_info = ERR_error_string (ERR_get_error (), NULL);
 | 
				
			||||||
	error_set (e, "%s: %s", "could not initialize SSL", error_info);
 | 
					
 | 
				
			||||||
	return false;
 | 
						FAIL ("%s: %s", "could not initialize SSL", error_info);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
@@ -1359,12 +1350,6 @@ backend_curl_add_header (struct app_context *ctx, const char *header)
 | 
				
			|||||||
		exit_fatal ("cURL setup failed");
 | 
							exit_fatal ("cURL setup failed");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define RPC_FAIL(...)                                                          \
 | 
					 | 
				
			||||||
	BLOCK_START                                                                \
 | 
					 | 
				
			||||||
		error_set (e, __VA_ARGS__);                                            \
 | 
					 | 
				
			||||||
		return false;                                                          \
 | 
					 | 
				
			||||||
	BLOCK_END
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
backend_curl_make_call (struct app_context *ctx,
 | 
					backend_curl_make_call (struct app_context *ctx,
 | 
				
			||||||
	const char *request, bool expect_content, struct str *buf, struct error **e)
 | 
						const char *request, bool expect_content, struct str *buf, struct error **e)
 | 
				
			||||||
@@ -1375,20 +1360,20 @@ backend_curl_make_call (struct app_context *ctx,
 | 
				
			|||||||
		(curl_off_t) -1)
 | 
							(curl_off_t) -1)
 | 
				
			||||||
	 || curl_easy_setopt (curl, CURLOPT_WRITEDATA, buf)
 | 
						 || curl_easy_setopt (curl, CURLOPT_WRITEDATA, buf)
 | 
				
			||||||
	 || curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_callback))
 | 
						 || curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_callback))
 | 
				
			||||||
		RPC_FAIL ("cURL setup failed");
 | 
							FAIL ("cURL setup failed");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CURLcode ret;
 | 
						CURLcode ret;
 | 
				
			||||||
	if ((ret = curl_easy_perform (curl)))
 | 
						if ((ret = curl_easy_perform (curl)))
 | 
				
			||||||
		RPC_FAIL ("HTTP request failed: %s", ctx->curl.curl_error);
 | 
							FAIL ("HTTP request failed: %s", ctx->curl.curl_error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	long code;
 | 
						long code;
 | 
				
			||||||
	char *type;
 | 
						char *type;
 | 
				
			||||||
	if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code)
 | 
						if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code)
 | 
				
			||||||
	 || curl_easy_getinfo (curl, CURLINFO_CONTENT_TYPE, &type))
 | 
						 || curl_easy_getinfo (curl, CURLINFO_CONTENT_TYPE, &type))
 | 
				
			||||||
		RPC_FAIL ("cURL info retrieval failed");
 | 
							FAIL ("cURL info retrieval failed");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (code != 200)
 | 
						if (code != 200)
 | 
				
			||||||
		RPC_FAIL ("unexpected HTTP response code: %ld", code);
 | 
							FAIL ("unexpected HTTP response code: %ld", code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!expect_content)
 | 
						if (!expect_content)
 | 
				
			||||||
		;  // Let there be anything
 | 
							;  // Let there be anything
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user