Cleanup, fix handshake, better errors

This commit is contained in:
Přemysl Eric Janouch 2015-04-05 00:23:29 +02:00
parent 147aa01150
commit 371e52d782
1 changed files with 34 additions and 49 deletions

View File

@ -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