|
|
4a2fec |
From 062b6fd62b387f4ea67f800ca956c87b28451a9c Mon Sep 17 00:00:00 2001
|
|
|
4a2fec |
From: "Daniel P. Berrange" <berrange@redhat.com>
|
|
|
4a2fec |
Date: Wed, 20 Dec 2017 17:56:43 +0100
|
|
|
4a2fec |
Subject: [PATCH 03/42] io: send proper HTTP response for websocket errors
|
|
|
4a2fec |
MIME-Version: 1.0
|
|
|
4a2fec |
Content-Type: text/plain; charset=UTF-8
|
|
|
4a2fec |
Content-Transfer-Encoding: 8bit
|
|
|
4a2fec |
|
|
|
4a2fec |
RH-Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
4a2fec |
Message-id: <20171220175702.29663-2-berrange@redhat.com>
|
|
|
4a2fec |
Patchwork-id: 78454
|
|
|
4a2fec |
O-Subject: [RHV-7.5 qemu-kvm-rhev PATCH v2 01/20] io: send proper HTTP response for websocket errors
|
|
|
4a2fec |
Bugzilla: 1518649
|
|
|
4a2fec |
RH-Acked-by: John Snow <jsnow@redhat.com>
|
|
|
4a2fec |
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
|
|
|
4a2fec |
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
4a2fec |
|
|
|
4a2fec |
When any error occurs while processing the websockets handshake,
|
|
|
4a2fec |
QEMU just terminates the connection abruptly. This is in violation
|
|
|
4a2fec |
of the HTTP specs and does not help the client understand what they
|
|
|
4a2fec |
did wrong. This is particularly bad when the client gives the wrong
|
|
|
4a2fec |
path, as a "404 Not Found" would be very helpful.
|
|
|
4a2fec |
|
|
|
4a2fec |
Refactor the handshake code so that it always sends a response to
|
|
|
4a2fec |
the client unless there was an I/O error.
|
|
|
4a2fec |
|
|
|
4a2fec |
Fixes bug: #1715186
|
|
|
4a2fec |
|
|
|
4a2fec |
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
|
|
4a2fec |
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
|
|
|
4a2fec |
(cherry picked from commit f69a8bde29354493ff8aea64cc9cb3b531d16337)
|
|
|
4a2fec |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
4a2fec |
---
|
|
|
4a2fec |
io/channel-websock.c | 185 ++++++++++++++++++++++++++++++++++++++-------------
|
|
|
4a2fec |
1 file changed, 139 insertions(+), 46 deletions(-)
|
|
|
4a2fec |
|
|
|
4a2fec |
diff --git a/io/channel-websock.c b/io/channel-websock.c
|
|
|
4a2fec |
index 5a3badb..f5fac5b 100644
|
|
|
4a2fec |
--- a/io/channel-websock.c
|
|
|
4a2fec |
+++ b/io/channel-websock.c
|
|
|
4a2fec |
@@ -25,6 +25,8 @@
|
|
|
4a2fec |
#include "crypto/hash.h"
|
|
|
4a2fec |
#include "trace.h"
|
|
|
4a2fec |
|
|
|
4a2fec |
+#include <time.h>
|
|
|
4a2fec |
+
|
|
|
4a2fec |
|
|
|
4a2fec |
/* Max amount to allow in rawinput/rawoutput buffers */
|
|
|
4a2fec |
#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
|
|
|
4a2fec |
@@ -44,13 +46,40 @@
|
|
|
4a2fec |
#define QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE "Upgrade"
|
|
|
4a2fec |
#define QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET "websocket"
|
|
|
4a2fec |
|
|
|
4a2fec |
-#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE \
|
|
|
4a2fec |
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
|
|
4a2fec |
+ "Server: QEMU VNC\r\n" \
|
|
|
4a2fec |
+ "Date: %s\r\n"
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK \
|
|
|
4a2fec |
"HTTP/1.1 101 Switching Protocols\r\n" \
|
|
|
4a2fec |
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
|
|
4a2fec |
"Upgrade: websocket\r\n" \
|
|
|
4a2fec |
"Connection: Upgrade\r\n" \
|
|
|
4a2fec |
"Sec-WebSocket-Accept: %s\r\n" \
|
|
|
4a2fec |
"Sec-WebSocket-Protocol: binary\r\n" \
|
|
|
4a2fec |
"\r\n"
|
|
|
4a2fec |
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND \
|
|
|
4a2fec |
+ "HTTP/1.1 404 Not Found\r\n" \
|
|
|
4a2fec |
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
|
|
4a2fec |
+ "Connection: close\r\n" \
|
|
|
4a2fec |
+ "\r\n"
|
|
|
4a2fec |
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST \
|
|
|
4a2fec |
+ "HTTP/1.1 400 Bad Request\r\n" \
|
|
|
4a2fec |
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
|
|
4a2fec |
+ "Connection: close\r\n" \
|
|
|
4a2fec |
+ "Sec-WebSocket-Version: " \
|
|
|
4a2fec |
+ QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION \
|
|
|
4a2fec |
+ "\r\n"
|
|
|
4a2fec |
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR \
|
|
|
4a2fec |
+ "HTTP/1.1 500 Internal Server Error\r\n" \
|
|
|
4a2fec |
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
|
|
4a2fec |
+ "Connection: close\r\n" \
|
|
|
4a2fec |
+ "\r\n"
|
|
|
4a2fec |
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE \
|
|
|
4a2fec |
+ "HTTP/1.1 403 Request Entity Too Large\r\n" \
|
|
|
4a2fec |
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
|
|
4a2fec |
+ "Connection: close\r\n" \
|
|
|
4a2fec |
+ "\r\n"
|
|
|
4a2fec |
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM "\r\n"
|
|
|
4a2fec |
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_END "\r\n\r\n"
|
|
|
4a2fec |
#define QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION "13"
|
|
|
4a2fec |
@@ -123,8 +152,46 @@ enum {
|
|
|
4a2fec |
QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA
|
|
|
4a2fec |
};
|
|
|
4a2fec |
|
|
|
4a2fec |
+static void qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
+ const char *resmsg,
|
|
|
4a2fec |
+ ...)
|
|
|
4a2fec |
+{
|
|
|
4a2fec |
+ va_list vargs;
|
|
|
4a2fec |
+ char *response;
|
|
|
4a2fec |
+ size_t responselen;
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+ va_start(vargs, resmsg);
|
|
|
4a2fec |
+ response = g_strdup_vprintf(resmsg, vargs);
|
|
|
4a2fec |
+ responselen = strlen(response);
|
|
|
4a2fec |
+ buffer_reserve(&ioc->encoutput, responselen);
|
|
|
4a2fec |
+ buffer_append(&ioc->encoutput, response, responselen);
|
|
|
4a2fec |
+ va_end(vargs);
|
|
|
4a2fec |
+}
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+static gchar *qio_channel_websock_date_str(void)
|
|
|
4a2fec |
+{
|
|
|
4a2fec |
+ struct tm tm;
|
|
|
4a2fec |
+ time_t now = time(NULL);
|
|
|
4a2fec |
+ char datebuf[128];
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+ gmtime_r(&now, &tm;;
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+ strftime(datebuf, sizeof(datebuf), "%a, %d %b %Y %H:%M:%S GMT", &tm;;
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+ return g_strdup(datebuf);
|
|
|
4a2fec |
+}
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+static void qio_channel_websock_handshake_send_res_err(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
+ const char *resdata)
|
|
|
4a2fec |
+{
|
|
|
4a2fec |
+ char *date = qio_channel_websock_date_str();
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res(ioc, resdata, date);
|
|
|
4a2fec |
+ g_free(date);
|
|
|
4a2fec |
+}
|
|
|
4a2fec |
+
|
|
|
4a2fec |
static size_t
|
|
|
4a2fec |
-qio_channel_websock_extract_headers(char *buffer,
|
|
|
4a2fec |
+qio_channel_websock_extract_headers(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
+ char *buffer,
|
|
|
4a2fec |
QIOChannelWebsockHTTPHeader *hdrs,
|
|
|
4a2fec |
size_t nhdrsalloc,
|
|
|
4a2fec |
Error **errp)
|
|
|
4a2fec |
@@ -145,7 +212,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
|
|
4a2fec |
nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
|
|
4a2fec |
if (!nl) {
|
|
|
4a2fec |
error_setg(errp, "Missing HTTP header delimiter");
|
|
|
4a2fec |
- return 0;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
*nl = '\0';
|
|
|
4a2fec |
|
|
|
4a2fec |
@@ -158,18 +225,20 @@ qio_channel_websock_extract_headers(char *buffer,
|
|
|
4a2fec |
|
|
|
4a2fec |
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_METHOD)) {
|
|
|
4a2fec |
error_setg(errp, "Unsupported HTTP method %s", buffer);
|
|
|
4a2fec |
- return 0;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
buffer = tmp + 1;
|
|
|
4a2fec |
tmp = strchr(buffer, ' ');
|
|
|
4a2fec |
if (!tmp) {
|
|
|
4a2fec |
error_setg(errp, "Missing HTTP version delimiter");
|
|
|
4a2fec |
- return 0;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
*tmp = '\0';
|
|
|
4a2fec |
|
|
|
4a2fec |
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_PATH)) {
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res_err(
|
|
|
4a2fec |
+ ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND);
|
|
|
4a2fec |
error_setg(errp, "Unexpected HTTP path %s", buffer);
|
|
|
4a2fec |
return 0;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
@@ -178,7 +247,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
|
|
4a2fec |
|
|
|
4a2fec |
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_VERSION)) {
|
|
|
4a2fec |
error_setg(errp, "Unsupported HTTP version %s", buffer);
|
|
|
4a2fec |
- return 0;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
|
|
4a2fec |
@@ -203,7 +272,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
|
|
4a2fec |
sep = strchr(buffer, ':');
|
|
|
4a2fec |
if (!sep) {
|
|
|
4a2fec |
error_setg(errp, "Malformed HTTP header");
|
|
|
4a2fec |
- return 0;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
*sep = '\0';
|
|
|
4a2fec |
sep++;
|
|
|
4a2fec |
@@ -213,7 +282,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
|
|
4a2fec |
|
|
|
4a2fec |
if (nhdrs >= nhdrsalloc) {
|
|
|
4a2fec |
error_setg(errp, "Too many HTTP headers");
|
|
|
4a2fec |
- return 0;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
hdr = &hdrs[nhdrs++];
|
|
|
4a2fec |
@@ -231,6 +300,11 @@ qio_channel_websock_extract_headers(char *buffer,
|
|
|
4a2fec |
} while (nl != NULL);
|
|
|
4a2fec |
|
|
|
4a2fec |
return nhdrs;
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+ bad_request:
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res_err(
|
|
|
4a2fec |
+ ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);
|
|
|
4a2fec |
+ return 0;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
static const char *
|
|
|
4a2fec |
@@ -250,14 +324,14 @@ qio_channel_websock_find_header(QIOChannelWebsockHTTPHeader *hdrs,
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
|
|
|
4a2fec |
-static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
- const char *key,
|
|
|
4a2fec |
- Error **errp)
|
|
|
4a2fec |
+static void qio_channel_websock_handshake_send_res_ok(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
+ const char *key,
|
|
|
4a2fec |
+ Error **errp)
|
|
|
4a2fec |
{
|
|
|
4a2fec |
char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
|
|
|
4a2fec |
QIO_CHANNEL_WEBSOCK_GUID_LEN + 1];
|
|
|
4a2fec |
- char *accept = NULL, *response = NULL;
|
|
|
4a2fec |
- size_t responselen;
|
|
|
4a2fec |
+ char *accept = NULL;
|
|
|
4a2fec |
+ char *date = qio_channel_websock_date_str();
|
|
|
4a2fec |
|
|
|
4a2fec |
g_strlcpy(combined_key, key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + 1);
|
|
|
4a2fec |
g_strlcat(combined_key, QIO_CHANNEL_WEBSOCK_GUID,
|
|
|
4a2fec |
@@ -271,105 +345,108 @@ static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
QIO_CHANNEL_WEBSOCK_GUID_LEN,
|
|
|
4a2fec |
&accept,
|
|
|
4a2fec |
errp) < 0) {
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res_err(
|
|
|
4a2fec |
+ ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR);
|
|
|
4a2fec |
+ return;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
- response = g_strdup_printf(QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE, accept);
|
|
|
4a2fec |
- responselen = strlen(response);
|
|
|
4a2fec |
- buffer_reserve(&ioc->encoutput, responselen);
|
|
|
4a2fec |
- buffer_append(&ioc->encoutput, response, responselen);
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res(
|
|
|
4a2fec |
+ ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK, date, accept);
|
|
|
4a2fec |
|
|
|
4a2fec |
+ g_free(date);
|
|
|
4a2fec |
g_free(accept);
|
|
|
4a2fec |
- g_free(response);
|
|
|
4a2fec |
-
|
|
|
4a2fec |
- return 0;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
-static int qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
- char *buffer,
|
|
|
4a2fec |
- Error **errp)
|
|
|
4a2fec |
+static void qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
+ char *buffer,
|
|
|
4a2fec |
+ Error **errp)
|
|
|
4a2fec |
{
|
|
|
4a2fec |
QIOChannelWebsockHTTPHeader hdrs[32];
|
|
|
4a2fec |
size_t nhdrs = G_N_ELEMENTS(hdrs);
|
|
|
4a2fec |
const char *protocols = NULL, *version = NULL, *key = NULL,
|
|
|
4a2fec |
*host = NULL, *connection = NULL, *upgrade = NULL;
|
|
|
4a2fec |
|
|
|
4a2fec |
- nhdrs = qio_channel_websock_extract_headers(buffer, hdrs, nhdrs, errp);
|
|
|
4a2fec |
+ nhdrs = qio_channel_websock_extract_headers(ioc, buffer, hdrs, nhdrs, errp);
|
|
|
4a2fec |
if (!nhdrs) {
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ return;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
protocols = qio_channel_websock_find_header(
|
|
|
4a2fec |
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL);
|
|
|
4a2fec |
if (!protocols) {
|
|
|
4a2fec |
error_setg(errp, "Missing websocket protocol header data");
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
version = qio_channel_websock_find_header(
|
|
|
4a2fec |
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_VERSION);
|
|
|
4a2fec |
if (!version) {
|
|
|
4a2fec |
error_setg(errp, "Missing websocket version header data");
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
key = qio_channel_websock_find_header(
|
|
|
4a2fec |
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_KEY);
|
|
|
4a2fec |
if (!key) {
|
|
|
4a2fec |
error_setg(errp, "Missing websocket key header data");
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
host = qio_channel_websock_find_header(
|
|
|
4a2fec |
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_HOST);
|
|
|
4a2fec |
if (!host) {
|
|
|
4a2fec |
error_setg(errp, "Missing websocket host header data");
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
connection = qio_channel_websock_find_header(
|
|
|
4a2fec |
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION);
|
|
|
4a2fec |
if (!connection) {
|
|
|
4a2fec |
error_setg(errp, "Missing websocket connection header data");
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
upgrade = qio_channel_websock_find_header(
|
|
|
4a2fec |
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE);
|
|
|
4a2fec |
if (!upgrade) {
|
|
|
4a2fec |
error_setg(errp, "Missing websocket upgrade header data");
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) {
|
|
|
4a2fec |
error_setg(errp, "No '%s' protocol is supported by client '%s'",
|
|
|
4a2fec |
QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols);
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) {
|
|
|
4a2fec |
error_setg(errp, "Version '%s' is not supported by client '%s'",
|
|
|
4a2fec |
QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION, version);
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
if (strlen(key) != QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN) {
|
|
|
4a2fec |
error_setg(errp, "Key length '%zu' was not as expected '%d'",
|
|
|
4a2fec |
strlen(key), QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN);
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
if (!g_strrstr(connection, QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE)) {
|
|
|
4a2fec |
error_setg(errp, "No connection upgrade requested '%s'", connection);
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
if (!g_str_equal(upgrade, QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET)) {
|
|
|
4a2fec |
error_setg(errp, "Incorrect upgrade method '%s'", upgrade);
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ goto bad_request;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
- return qio_channel_websock_handshake_send_response(ioc, key, errp);
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res_ok(ioc, key, errp);
|
|
|
4a2fec |
+ return;
|
|
|
4a2fec |
+
|
|
|
4a2fec |
+ bad_request:
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res_err(
|
|
|
4a2fec |
+ ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
@@ -393,20 +470,20 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
|
|
|
4a2fec |
QIO_CHANNEL_WEBSOCK_HANDSHAKE_END);
|
|
|
4a2fec |
if (!handshake_end) {
|
|
|
4a2fec |
if (ioc->encinput.offset >= 4096) {
|
|
|
4a2fec |
+ qio_channel_websock_handshake_send_res_err(
|
|
|
4a2fec |
+ ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE);
|
|
|
4a2fec |
error_setg(errp,
|
|
|
4a2fec |
"End of headers not found in first 4096 bytes");
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
+ return 1;
|
|
|
4a2fec |
} else {
|
|
|
4a2fec |
return 0;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
}
|
|
|
4a2fec |
*handshake_end = '\0';
|
|
|
4a2fec |
|
|
|
4a2fec |
- if (qio_channel_websock_handshake_process(ioc,
|
|
|
4a2fec |
- (char *)ioc->encinput.buffer,
|
|
|
4a2fec |
- errp) < 0) {
|
|
|
4a2fec |
- return -1;
|
|
|
4a2fec |
- }
|
|
|
4a2fec |
+ qio_channel_websock_handshake_process(ioc,
|
|
|
4a2fec |
+ (char *)ioc->encinput.buffer,
|
|
|
4a2fec |
+ errp);
|
|
|
4a2fec |
|
|
|
4a2fec |
buffer_advance(&ioc->encinput,
|
|
|
4a2fec |
handshake_end - (char *)ioc->encinput.buffer +
|
|
|
4a2fec |
@@ -438,8 +515,15 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
|
|
|
4a2fec |
|
|
|
4a2fec |
buffer_advance(&wioc->encoutput, ret);
|
|
|
4a2fec |
if (wioc->encoutput.offset == 0) {
|
|
|
4a2fec |
- trace_qio_channel_websock_handshake_complete(ioc);
|
|
|
4a2fec |
- qio_task_complete(task);
|
|
|
4a2fec |
+ if (wioc->io_err) {
|
|
|
4a2fec |
+ trace_qio_channel_websock_handshake_fail(ioc);
|
|
|
4a2fec |
+ qio_task_set_error(task, wioc->io_err);
|
|
|
4a2fec |
+ wioc->io_err = NULL;
|
|
|
4a2fec |
+ qio_task_complete(task);
|
|
|
4a2fec |
+ } else {
|
|
|
4a2fec |
+ trace_qio_channel_websock_handshake_complete(ioc);
|
|
|
4a2fec |
+ qio_task_complete(task);
|
|
|
4a2fec |
+ }
|
|
|
4a2fec |
return FALSE;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT);
|
|
|
4a2fec |
@@ -458,6 +542,11 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
|
|
|
4a2fec |
|
|
|
4a2fec |
ret = qio_channel_websock_handshake_read(wioc, &err;;
|
|
|
4a2fec |
if (ret < 0) {
|
|
|
4a2fec |
+ /*
|
|
|
4a2fec |
+ * We only take this path on a fatal I/O error reading from
|
|
|
4a2fec |
+ * client connection, as most of the time we have an
|
|
|
4a2fec |
+ * HTTP 4xx err response to send instead
|
|
|
4a2fec |
+ */
|
|
|
4a2fec |
trace_qio_channel_websock_handshake_fail(ioc);
|
|
|
4a2fec |
qio_task_set_error(task, err);
|
|
|
4a2fec |
qio_task_complete(task);
|
|
|
4a2fec |
@@ -469,6 +558,10 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
|
|
|
4a2fec |
return TRUE;
|
|
|
4a2fec |
}
|
|
|
4a2fec |
|
|
|
4a2fec |
+ if (err) {
|
|
|
4a2fec |
+ error_propagate(&wioc->io_err, err);
|
|
|
4a2fec |
+ }
|
|
|
4a2fec |
+
|
|
|
4a2fec |
trace_qio_channel_websock_handshake_reply(ioc);
|
|
|
4a2fec |
qio_channel_add_watch(
|
|
|
4a2fec |
wioc->master,
|
|
|
4a2fec |
--
|
|
|
4a2fec |
1.8.3.1
|
|
|
4a2fec |
|