get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/811490/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 811490,
    "url": "http://patchwork.ozlabs.org/api/patches/811490/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20170908103011.25821-2-berrange@redhat.com/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20170908103011.25821-2-berrange@redhat.com>",
    "list_archive_url": null,
    "date": "2017-09-08T10:30:09",
    "name": "[v2,1/3] io: send proper HTTP response for websocket errors",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "e2860fcc9d52580330e16e4538cb66c0d52aa1a8",
    "submitter": {
        "id": 2694,
        "url": "http://patchwork.ozlabs.org/api/people/2694/?format=api",
        "name": "Daniel P. Berrangé",
        "email": "berrange@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20170908103011.25821-2-berrange@redhat.com/mbox/",
    "series": [
        {
            "id": 2177,
            "url": "http://patchwork.ozlabs.org/api/series/2177/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=2177",
            "date": "2017-09-08T10:30:09",
            "name": "Improve websock response / error handling",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/2177/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/811490/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/811490/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org",
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=nongnu.org\n\t(client-ip=2001:4830:134:3::11; helo=lists.gnu.org;\n\tenvelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n\treceiver=<UNKNOWN>)",
            "ext-mx01.extmail.prod.ext.phx2.redhat.com;\n\tdmarc=none (p=none dis=none) header.from=redhat.com",
            "ext-mx01.extmail.prod.ext.phx2.redhat.com;\n\tspf=fail smtp.mailfrom=berrange@redhat.com"
        ],
        "Received": [
            "from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11])\n\t(using TLSv1 with cipher AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xpYXH2S9Hz9s7f\n\tfor <incoming@patchwork.ozlabs.org>;\n\tFri,  8 Sep 2017 20:31:43 +1000 (AEST)",
            "from localhost ([::1]:44415 helo=lists.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.71) (envelope-from\n\t<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>)\n\tid 1dqGZZ-00069C-D3\n\tfor incoming@patchwork.ozlabs.org; Fri, 08 Sep 2017 06:31:41 -0400",
            "from eggs.gnu.org ([2001:4830:134:3::10]:56311)\n\tby lists.gnu.org with esmtp (Exim 4.71)\n\t(envelope-from <berrange@redhat.com>) id 1dqGYK-0005Zx-8B\n\tfor qemu-devel@nongnu.org; Fri, 08 Sep 2017 06:30:30 -0400",
            "from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71)\n\t(envelope-from <berrange@redhat.com>) id 1dqGYE-0006bh-PY\n\tfor qemu-devel@nongnu.org; Fri, 08 Sep 2017 06:30:24 -0400",
            "from mx1.redhat.com ([209.132.183.28]:39442)\n\tby eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32)\n\t(Exim 4.71) (envelope-from <berrange@redhat.com>) id 1dqGYE-0006bI-GB\n\tfor qemu-devel@nongnu.org; Fri, 08 Sep 2017 06:30:18 -0400",
            "from smtp.corp.redhat.com\n\t(int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16])\n\t(using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby mx1.redhat.com (Postfix) with ESMTPS id 75ABF81DE4;\n\tFri,  8 Sep 2017 10:30:17 +0000 (UTC)",
            "from t460.redhat.com (unknown [10.33.36.66])\n\tby smtp.corp.redhat.com (Postfix) with ESMTP id F0A2E58830;\n\tFri,  8 Sep 2017 10:30:15 +0000 (UTC)"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.3.2 mx1.redhat.com 75ABF81DE4",
        "From": "\"Daniel P. Berrange\" <berrange@redhat.com>",
        "To": "qemu-devel@nongnu.org",
        "Date": "Fri,  8 Sep 2017 11:30:09 +0100",
        "Message-Id": "<20170908103011.25821-2-berrange@redhat.com>",
        "In-Reply-To": "<20170908103011.25821-1-berrange@redhat.com>",
        "References": "<20170908103011.25821-1-berrange@redhat.com>",
        "X-Scanned-By": "MIMEDefang 2.79 on 10.5.11.16",
        "X-Greylist": "Sender IP whitelisted, not delayed by milter-greylist-4.5.16\n\t(mx1.redhat.com [10.5.110.25]);\n\tFri, 08 Sep 2017 10:30:17 +0000 (UTC)",
        "X-detected-operating-system": "by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic]\n\t[fuzzy]",
        "X-Received-From": "209.132.183.28",
        "Subject": "[Qemu-devel] [PATCH v2 1/3] io: send proper HTTP response for\n\twebsocket errors",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.21",
        "Precedence": "list",
        "List-Id": "<qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n\t<mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.nongnu.org/archive/html/qemu-devel/>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n\t<mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Cc": "Brian Rak <brak@vultr.com>, =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?=\n\t<f4bug@amsat.org>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "\"Qemu-devel\"\n\t<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>"
    },
    "content": "When any error occurs while processing the websockets handshake,\nQEMU just terminates the connection abruptly. This is in violation\nof the HTTP specs and does not help the client understand what they\ndid wrong. This is particularly bad when the client gives the wrong\npath, as a \"404 Not Found\" would be very helpful.\n\nRefactor the handshake code so that it always sends a response to\nthe client unless there was an I/O error.\n\nFixes bug: #1715186\n\nSigned-off-by: Daniel P. Berrange <berrange@redhat.com>\n---\n io/channel-websock.c | 181 ++++++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 135 insertions(+), 46 deletions(-)",
    "diff": "diff --git a/io/channel-websock.c b/io/channel-websock.c\nindex 5a3badbec2..24920c78df 100644\n--- a/io/channel-websock.c\n+++ b/io/channel-websock.c\n@@ -44,13 +44,40 @@\n #define QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE \"Upgrade\"\n #define QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET \"websocket\"\n \n-#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE  \\\n+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \\\n+    \"Server: QEMU VNC\\r\\n\"                       \\\n+    \"Date: %s\\r\\n\"\n+\n+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK    \\\n     \"HTTP/1.1 101 Switching Protocols\\r\\n\"      \\\n+    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON    \\\n     \"Upgrade: websocket\\r\\n\"                    \\\n     \"Connection: Upgrade\\r\\n\"                   \\\n     \"Sec-WebSocket-Accept: %s\\r\\n\"              \\\n     \"Sec-WebSocket-Protocol: binary\\r\\n\"        \\\n     \"\\r\\n\"\n+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND \\\n+    \"HTTP/1.1 404 Not Found\\r\\n\"                    \\\n+    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON        \\\n+    \"Connection: close\\r\\n\"                         \\\n+    \"\\r\\n\"\n+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST \\\n+    \"HTTP/1.1 400 Bad Request\\r\\n\"                    \\\n+    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON          \\\n+    \"Connection: close\\r\\n\"                           \\\n+    \"Sec-WebSocket-Version: \"                         \\\n+    QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION             \\\n+    \"\\r\\n\"\n+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR \\\n+    \"HTTP/1.1 500 Internal Server Error\\r\\n\"         \\\n+    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON         \\\n+    \"Connection: close\\r\\n\"                          \\\n+    \"\\r\\n\"\n+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE  \\\n+    \"HTTP/1.1 403 Request Entity Too Large\\r\\n\"      \\\n+    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON         \\\n+    \"Connection: close\\r\\n\"                          \\\n+    \"\\r\\n\"\n #define QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM \"\\r\\n\"\n #define QIO_CHANNEL_WEBSOCK_HANDSHAKE_END \"\\r\\n\\r\\n\"\n #define QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION \"13\"\n@@ -123,8 +150,44 @@ enum {\n     QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA\n };\n \n+static void qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc,\n+                                                   const char *resmsg,\n+                                                   ...)\n+{\n+    va_list vargs;\n+    char *response;\n+    size_t responselen;\n+\n+    va_start(vargs, resmsg);\n+    response = g_strdup_vprintf(resmsg, vargs);\n+    responselen = strlen(response);\n+    buffer_reserve(&ioc->encoutput, responselen);\n+    buffer_append(&ioc->encoutput, response, responselen);\n+    va_end(vargs);\n+}\n+\n+static gchar *qio_channel_websock_date_str(void)\n+{\n+    GTimeZone *utc = g_time_zone_new_utc();\n+    GDateTime *now = g_date_time_new_now(utc);\n+    gchar *ret = g_date_time_format(now, \"%a, %d %b %Y %H:%M:%S GMT\");\n+    g_assert(ret);\n+    g_date_time_unref(now);\n+    g_time_zone_unref(utc);\n+    return ret;\n+}\n+\n+static void qio_channel_websock_handshake_send_res_err(QIOChannelWebsock *ioc,\n+                                                       const char *resdata)\n+{\n+    char *date = qio_channel_websock_date_str();\n+    qio_channel_websock_handshake_send_res(ioc, resdata, date);\n+    g_free(date);\n+}\n+\n static size_t\n-qio_channel_websock_extract_headers(char *buffer,\n+qio_channel_websock_extract_headers(QIOChannelWebsock *ioc,\n+                                    char *buffer,\n                                     QIOChannelWebsockHTTPHeader *hdrs,\n                                     size_t nhdrsalloc,\n                                     Error **errp)\n@@ -145,7 +208,7 @@ qio_channel_websock_extract_headers(char *buffer,\n     nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);\n     if (!nl) {\n         error_setg(errp, \"Missing HTTP header delimiter\");\n-        return 0;\n+        goto bad_request;\n     }\n     *nl = '\\0';\n \n@@ -158,18 +221,20 @@ qio_channel_websock_extract_headers(char *buffer,\n \n     if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_METHOD)) {\n         error_setg(errp, \"Unsupported HTTP method %s\", buffer);\n-        return 0;\n+        goto bad_request;\n     }\n \n     buffer = tmp + 1;\n     tmp = strchr(buffer, ' ');\n     if (!tmp) {\n         error_setg(errp, \"Missing HTTP version delimiter\");\n-        return 0;\n+        goto bad_request;\n     }\n     *tmp = '\\0';\n \n     if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_PATH)) {\n+        qio_channel_websock_handshake_send_res_err(\n+            ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND);\n         error_setg(errp, \"Unexpected HTTP path %s\", buffer);\n         return 0;\n     }\n@@ -178,7 +243,7 @@ qio_channel_websock_extract_headers(char *buffer,\n \n     if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_VERSION)) {\n         error_setg(errp, \"Unsupported HTTP version %s\", buffer);\n-        return 0;\n+        goto bad_request;\n     }\n \n     buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);\n@@ -203,7 +268,7 @@ qio_channel_websock_extract_headers(char *buffer,\n         sep = strchr(buffer, ':');\n         if (!sep) {\n             error_setg(errp, \"Malformed HTTP header\");\n-            return 0;\n+            goto bad_request;\n         }\n         *sep = '\\0';\n         sep++;\n@@ -213,7 +278,7 @@ qio_channel_websock_extract_headers(char *buffer,\n \n         if (nhdrs >= nhdrsalloc) {\n             error_setg(errp, \"Too many HTTP headers\");\n-            return 0;\n+            goto bad_request;\n         }\n \n         hdr = &hdrs[nhdrs++];\n@@ -231,6 +296,11 @@ qio_channel_websock_extract_headers(char *buffer,\n     } while (nl != NULL);\n \n     return nhdrs;\n+\n+ bad_request:\n+    qio_channel_websock_handshake_send_res_err(\n+        ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);\n+    return 0;\n }\n \n static const char *\n@@ -250,14 +320,14 @@ qio_channel_websock_find_header(QIOChannelWebsockHTTPHeader *hdrs,\n }\n \n \n-static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,\n-                                                       const char *key,\n-                                                       Error **errp)\n+static void qio_channel_websock_handshake_send_res_ok(QIOChannelWebsock *ioc,\n+                                                      const char *key,\n+                                                      Error **errp)\n {\n     char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +\n                       QIO_CHANNEL_WEBSOCK_GUID_LEN + 1];\n-    char *accept = NULL, *response = NULL;\n-    size_t responselen;\n+    char *accept = NULL;\n+    char *date = qio_channel_websock_date_str();\n \n     g_strlcpy(combined_key, key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + 1);\n     g_strlcat(combined_key, QIO_CHANNEL_WEBSOCK_GUID,\n@@ -271,105 +341,108 @@ static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,\n                             QIO_CHANNEL_WEBSOCK_GUID_LEN,\n                             &accept,\n                             errp) < 0) {\n-        return -1;\n+        qio_channel_websock_handshake_send_res_err(\n+            ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR);\n+        return;\n     }\n \n-    response = g_strdup_printf(QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE, accept);\n-    responselen = strlen(response);\n-    buffer_reserve(&ioc->encoutput, responselen);\n-    buffer_append(&ioc->encoutput, response, responselen);\n+    qio_channel_websock_handshake_send_res(\n+        ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK, date, accept);\n \n+    g_free(date);\n     g_free(accept);\n-    g_free(response);\n-\n-    return 0;\n }\n \n-static int qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,\n-                                                 char *buffer,\n-                                                 Error **errp)\n+static void qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,\n+                                                  char *buffer,\n+                                                  Error **errp)\n {\n     QIOChannelWebsockHTTPHeader hdrs[32];\n     size_t nhdrs = G_N_ELEMENTS(hdrs);\n     const char *protocols = NULL, *version = NULL, *key = NULL,\n         *host = NULL, *connection = NULL, *upgrade = NULL;\n \n-    nhdrs = qio_channel_websock_extract_headers(buffer, hdrs, nhdrs, errp);\n+    nhdrs = qio_channel_websock_extract_headers(ioc, buffer, hdrs, nhdrs, errp);\n     if (!nhdrs) {\n-        return -1;\n+        return;\n     }\n \n     protocols = qio_channel_websock_find_header(\n         hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL);\n     if (!protocols) {\n         error_setg(errp, \"Missing websocket protocol header data\");\n-        return -1;\n+        goto bad_request;\n     }\n \n     version = qio_channel_websock_find_header(\n         hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_VERSION);\n     if (!version) {\n         error_setg(errp, \"Missing websocket version header data\");\n-        return -1;\n+        goto bad_request;\n     }\n \n     key = qio_channel_websock_find_header(\n         hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_KEY);\n     if (!key) {\n         error_setg(errp, \"Missing websocket key header data\");\n-        return -1;\n+        goto bad_request;\n     }\n \n     host = qio_channel_websock_find_header(\n         hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_HOST);\n     if (!host) {\n         error_setg(errp, \"Missing websocket host header data\");\n-        return -1;\n+        goto bad_request;\n     }\n \n     connection = qio_channel_websock_find_header(\n         hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION);\n     if (!connection) {\n         error_setg(errp, \"Missing websocket connection header data\");\n-        return -1;\n+        goto bad_request;\n     }\n \n     upgrade = qio_channel_websock_find_header(\n         hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE);\n     if (!upgrade) {\n         error_setg(errp, \"Missing websocket upgrade header data\");\n-        return -1;\n+        goto bad_request;\n     }\n \n     if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) {\n         error_setg(errp, \"No '%s' protocol is supported by client '%s'\",\n                    QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols);\n-        return -1;\n+        goto bad_request;\n     }\n \n     if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) {\n         error_setg(errp, \"Version '%s' is not supported by client '%s'\",\n                    QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION, version);\n-        return -1;\n+        goto bad_request;\n     }\n \n     if (strlen(key) != QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN) {\n         error_setg(errp, \"Key length '%zu' was not as expected '%d'\",\n                    strlen(key), QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN);\n-        return -1;\n+        goto bad_request;\n     }\n \n     if (!g_strrstr(connection, QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE)) {\n         error_setg(errp, \"No connection upgrade requested '%s'\", connection);\n-        return -1;\n+        goto bad_request;\n     }\n \n     if (!g_str_equal(upgrade, QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET)) {\n         error_setg(errp, \"Incorrect upgrade method '%s'\", upgrade);\n-        return -1;\n+        goto bad_request;\n     }\n \n-    return qio_channel_websock_handshake_send_response(ioc, key, errp);\n+    qio_channel_websock_handshake_send_res_ok(ioc, key, errp);\n+    return;\n+\n+ bad_request:\n+    qio_channel_websock_handshake_send_res_err(\n+        ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);\n }\n \n static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,\n@@ -393,20 +466,20 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,\n                                  QIO_CHANNEL_WEBSOCK_HANDSHAKE_END);\n     if (!handshake_end) {\n         if (ioc->encinput.offset >= 4096) {\n+            qio_channel_websock_handshake_send_res_err(\n+                ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE);\n             error_setg(errp,\n                        \"End of headers not found in first 4096 bytes\");\n-            return -1;\n+            return 1;\n         } else {\n             return 0;\n         }\n     }\n     *handshake_end = '\\0';\n \n-    if (qio_channel_websock_handshake_process(ioc,\n-                                              (char *)ioc->encinput.buffer,\n-                                              errp) < 0) {\n-        return -1;\n-    }\n+    qio_channel_websock_handshake_process(ioc,\n+                                          (char *)ioc->encinput.buffer,\n+                                          errp);\n \n     buffer_advance(&ioc->encinput,\n                    handshake_end - (char *)ioc->encinput.buffer +\n@@ -438,8 +511,15 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,\n \n     buffer_advance(&wioc->encoutput, ret);\n     if (wioc->encoutput.offset == 0) {\n-        trace_qio_channel_websock_handshake_complete(ioc);\n-        qio_task_complete(task);\n+        if (wioc->io_err) {\n+            trace_qio_channel_websock_handshake_fail(ioc);\n+            qio_task_set_error(task, wioc->io_err);\n+            wioc->io_err = NULL;\n+            qio_task_complete(task);\n+        } else {\n+            trace_qio_channel_websock_handshake_complete(ioc);\n+            qio_task_complete(task);\n+        }\n         return FALSE;\n     }\n     trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT);\n@@ -458,6 +538,11 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,\n \n     ret = qio_channel_websock_handshake_read(wioc, &err);\n     if (ret < 0) {\n+        /*\n+         * We only take this path on a fatal I/O error reading from\n+         * client connection, as most of the time we have an\n+         * HTTP 4xx err response to send instead\n+         */\n         trace_qio_channel_websock_handshake_fail(ioc);\n         qio_task_set_error(task, err);\n         qio_task_complete(task);\n@@ -469,6 +554,10 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,\n         return TRUE;\n     }\n \n+    if (err) {\n+        error_propagate(&wioc->io_err, err);\n+    }\n+\n     trace_qio_channel_websock_handshake_reply(ioc);\n     qio_channel_add_watch(\n         wioc->master,\n",
    "prefixes": [
        "v2",
        "1/3"
    ]
}