From nobody Sat Feb 7 07:42:47 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1535395431799861.8621067140348; Mon, 27 Aug 2018 11:43:51 -0700 (PDT) Received: from localhost ([::1]:34689 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuMUO-0004TB-U4 for importer@patchew.org; Mon, 27 Aug 2018 14:43:48 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57571) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuMSR-00015V-Ne for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fuMS9-0007TG-9Z for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:43 -0400 Received: from smtp58.i.mail.ru ([217.69.128.38]:44942) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fuMS8-0007OK-Jt for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:29 -0400 Received: by smtp58.i.mail.ru with esmtpa (envelope-from ) id 1fuMS6-00033W-Hu; Mon, 27 Aug 2018 21:41:26 +0300 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=f3lVU5F9A+SqvcBiyU50Fb537Q0tVf1VCuxUDYKBqfE=; b=NK5vVHvZ0yYCUDheIblZJ8nwZMBBIzNTJ9MXnsW7aXY+Ck6zdw5FMxSC5QYmRZtpFuS/ecHnvWB9qSY7PZzoKVfF5vypXAiDdScjZjvT4dVxgq8bgngUwv+jv+mWpZDwxSlWGiwN4ywQg5sK6+02trv0D4LnGzGGmW+XEacKZVc=; To: qemu-devel@nongnu.org Date: Mon, 27 Aug 2018 21:41:02 +0300 Message-Id: <20180827184103.32190-2-jusual@mail.ru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180827184103.32190-1-jusual@mail.ru> References: <20180827184103.32190-1-jusual@mail.ru> Authentication-Results: smtp58.i.mail.ru; auth=pass smtp.auth=jusual@mail.ru smtp.mailfrom=jusual@mail.ru X-7FA49CB5: 0D63561A33F958A5C1BA94FE51B3A637028798E0FEBB25D60D9903CC397465C58941B15DA834481FA18204E546F3947CB861051D4BA689FCF6B57BC7E64490618DEB871D839B7333395957E7521B51C2545D4CF71C94A83E9FA2833FD35BB23D27C277FBC8AE2E8B974A882099E279BDA471835C12D1D977C4224003CC8364767815B9869FA544D8090A508E0FED62991661749BA6B97735D94E105876FE7799B3661434B16C20ACE7DDDDC251EA7DABAAAE862A0553A39223F8577A6DFFEA7CF26DBECE9D91448F7AD949B7F7BF34B5EFF80C71ABB335746BA297DBC24807EA27F269C8F02392CD5E72292F11C3671E27F269C8F02392CD5571747095F342E88FB05168BE4CE3AF X-Mailru-Sender: 7766D515518070DE138AAC7428EA760D1E5583EEC6F8F87CB2E38ADA9C1E2CC9114698FBA59F30EE7C4160E8B47E48163DDE9B364B0DF2898CB68AF7A628805D594FB4C9F0DBF412AE208404248635DF X-Mras: OK X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 217.69.128.38 Subject: [Qemu-devel] [PATCH v2 1/2] chardev: Add websocket support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Julia Suvorova via Qemu-devel Reply-To: Julia Suvorova Cc: Jim Mussared , =?UTF-8?q?Steffen=20G=C3=B6rtz?= , Stefan Hajnoczi , Julia Suvorova , Joel Stanley , Stefan Hajnoczi , marcandre.lureau@redhat.com, Paolo Bonzini Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" New option "websock" added to allow using websocket protocol for chardev socket backend. Example: -chardev socket,websock,id=3D... Signed-off-by: Julia Suvorova --- chardev/char-socket.c | 124 +++++++++++++++++++++++++++++++----------- chardev/char.c | 8 ++- qapi/char.json | 3 + 3 files changed, 103 insertions(+), 32 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index efbad6ee7c..1d3c14d85d 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -26,6 +26,7 @@ #include "chardev/char.h" #include "io/channel-socket.h" #include "io/channel-tls.h" +#include "io/channel-websock.h" #include "io/net-listener.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -69,6 +70,8 @@ typedef struct { GSource *telnet_source; TCPChardevTelnetInit *telnet_init; =20 + bool is_websock; + GSource *reconnect_timer; int64_t reconnect_time; bool connect_err_reported; @@ -385,30 +388,37 @@ static void tcp_chr_free_connection(Chardev *chr) s->connected =3D 0; } =20 -static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr, - bool is_listen, bool is_telnet) +static const char *qemu_chr_socket_protocol(SocketChardev *s) +{ + if (s->is_telnet) { + return "telnet"; + } + return s->is_websock ? "websock" : "tcp"; +} + +static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix) { - switch (addr->type) { + switch (s->addr->type) { case SOCKET_ADDRESS_TYPE_INET: return g_strdup_printf("%s%s:%s:%s%s", prefix, - is_telnet ? "telnet" : "tcp", - addr->u.inet.host, - addr->u.inet.port, - is_listen ? ",server" : ""); + qemu_chr_socket_protocol(s), + s->addr->u.inet.host, + s->addr->u.inet.port, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_UNIX: return g_strdup_printf("%sunix:%s%s", prefix, - addr->u.q_unix.path, - is_listen ? ",server" : ""); + s->addr->u.q_unix.path, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.str, - is_listen ? ",server" : ""); + return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_VSOCK: return g_strdup_printf("%svsock:%s:%s", prefix, - addr->u.vsock.cid, - addr->u.vsock.port); + s->addr->u.vsock.cid, + s->addr->u.vsock.port); default: abort(); } @@ -419,8 +429,7 @@ static void update_disconnected_filename(SocketChardev = *s) Chardev *chr =3D CHARDEV(s); =20 g_free(chr->filename); - chr->filename =3D SocketAddress_to_str("disconnected:", s->addr, - s->is_listen, s->is_telnet); + chr->filename =3D qemu_chr_socket_address(s, "disconnected:"); } =20 /* NB may be called even if tcp_chr_connect has not been @@ -506,10 +515,12 @@ static int tcp_chr_sync_read(Chardev *chr, const uint= 8_t *buf, int len) return size; } =20 -static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len, - struct sockaddr_storage *ps, socklen_t ps_len, - bool is_listen, bool is_telnet) +static char *qemu_chr_compute_filename(SocketChardev *s) { + struct sockaddr_storage *ss =3D &s->sioc->localAddr; + struct sockaddr_storage *ps =3D &s->sioc->remoteAddr; + socklen_t ss_len =3D s->sioc->localAddrLen; + socklen_t ps_len =3D s->sioc->remoteAddrLen; char shost[NI_MAXHOST], sserv[NI_MAXSERV]; char phost[NI_MAXHOST], pserv[NI_MAXSERV]; const char *left =3D "", *right =3D ""; @@ -519,7 +530,7 @@ static char *sockaddr_to_str(struct sockaddr_storage *s= s, socklen_t ss_len, case AF_UNIX: return g_strdup_printf("unix:%s%s", ((struct sockaddr_un *)(ss))->sun_path, - is_listen ? ",server" : ""); + s->is_listen ? ",server" : ""); #endif case AF_INET6: left =3D "["; @@ -531,9 +542,9 @@ static char *sockaddr_to_str(struct sockaddr_storage *s= s, socklen_t ss_len, getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost), pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV); return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s", - is_telnet ? "telnet" : "tcp", + qemu_chr_socket_protocol(s), left, shost, right, sserv, - is_listen ? ",server" : "", + s->is_listen ? ",server" : "", left, phost, right, pserv); =20 default: @@ -547,10 +558,7 @@ static void tcp_chr_connect(void *opaque) SocketChardev *s =3D SOCKET_CHARDEV(opaque); =20 g_free(chr->filename); - chr->filename =3D sockaddr_to_str( - &s->sioc->localAddr, s->sioc->localAddrLen, - &s->sioc->remoteAddr, s->sioc->remoteAddrLen, - s->is_listen, s->is_telnet); + chr->filename =3D qemu_chr_compute_filename(s); =20 s->connected =3D 1; chr->gsource =3D io_add_watch_poll(chr, s->ioc, @@ -699,6 +707,48 @@ cont: } =20 =20 +static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data) +{ + Chardev *chr =3D user_data; + SocketChardev *s =3D user_data; + + if (qio_task_propagate_error(task, NULL)) { + tcp_chr_disconnect(chr); + } else { + if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); + } else { + tcp_chr_connect(chr); + } + } +} + + +static void tcp_chr_websock_init(Chardev *chr) +{ + SocketChardev *s =3D SOCKET_CHARDEV(chr); + QIOChannelWebsock *wioc =3D NULL; + gchar *name; + + if (s->is_listen) { + wioc =3D qio_channel_websock_new_server(s->ioc); + }; + + if (wioc =3D=3D NULL) { + tcp_chr_disconnect(chr); + return; + } + + name =3D g_strdup_printf("chardev-websock-server-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(wioc), name); + g_free(name); + object_unref(OBJECT(s->ioc)); + s->ioc =3D QIO_CHANNEL(wioc); + + qio_channel_websock_handshake(wioc, tcp_chr_websock_handshake, chr, NU= LL); +} + + static void tcp_chr_tls_handshake(QIOTask *task, gpointer user_data) { @@ -708,7 +758,9 @@ static void tcp_chr_tls_handshake(QIOTask *task, if (qio_task_propagate_error(task, NULL)) { tcp_chr_disconnect(chr); } else { - if (s->do_telnetopt) { + if (s->is_websock) { + tcp_chr_websock_init(chr); + } else if (s->do_telnetopt) { tcp_chr_telnet_init(chr); } else { tcp_chr_connect(chr); @@ -799,12 +851,12 @@ static int tcp_chr_new_client(Chardev *chr, QIOChanne= lSocket *sioc) =20 if (s->tls_creds) { tcp_chr_tls_init(chr); + } else if (s->is_websock) { + tcp_chr_websock_init(chr); + } else if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); } else { - if (s->do_telnetopt) { - tcp_chr_telnet_init(chr); - } else { - tcp_chr_connect(chr); - } + tcp_chr_connect(chr); } =20 return 0; @@ -948,14 +1000,21 @@ static void qmp_chardev_open_socket(Chardev *chr, bool is_listen =3D sock->has_server ? sock->server : true; bool is_telnet =3D sock->has_telnet ? sock->telnet : false; bool is_tn3270 =3D sock->has_tn3270 ? sock->tn3270 : false; + bool is_websock =3D sock->has_websock ? sock->websock : false; bool is_waitconnect =3D sock->has_wait ? sock->wait : false; int64_t reconnect =3D sock->has_reconnect ? sock->reconnect : 0; QIOChannelSocket *sioc =3D NULL; SocketAddress *addr; =20 + if (!is_listen && is_websock) { + error_setg(errp, "%s", "Websocket client is not implemented"); + goto error; + } + s->is_listen =3D is_listen; s->is_telnet =3D is_telnet; s->is_tn3270 =3D is_tn3270; + s->is_websock =3D is_websock; s->do_nodelay =3D do_nodelay; if (sock->tls_creds) { Object *creds; @@ -1061,6 +1120,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, Cha= rdevBackend *backend, bool is_waitconnect =3D is_listen && qemu_opt_get_bool(opts, "wait", t= rue); bool is_telnet =3D qemu_opt_get_bool(opts, "telnet", false); bool is_tn3270 =3D qemu_opt_get_bool(opts, "tn3270", false); + bool is_websock =3D qemu_opt_get_bool(opts, "websock", false); bool do_nodelay =3D !qemu_opt_get_bool(opts, "delay", true); int64_t reconnect =3D qemu_opt_get_number(opts, "reconnect", 0); const char *path =3D qemu_opt_get(opts, "path"); @@ -1109,6 +1169,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, Cha= rdevBackend *backend, sock->telnet =3D is_telnet; sock->has_tn3270 =3D true; sock->tn3270 =3D is_tn3270; + sock->has_websock =3D true; + sock->websock =3D is_websock; sock->has_wait =3D true; sock->wait =3D is_waitconnect; sock->has_reconnect =3D true; diff --git a/chardev/char.c b/chardev/char.c index 1ab80e0f68..c045b8183b 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -404,7 +404,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, cons= t char *filename) } if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p) || - strstart(filename, "tn3270:", &p)) { + strstart(filename, "tn3270:", &p) || + strstart(filename, "websock:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { host[0] =3D 0; if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) @@ -424,6 +425,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, cons= t char *filename) qemu_opt_set(opts, "telnet", "on", &error_abort); } else if (strstart(filename, "tn3270:", &p)) { qemu_opt_set(opts, "tn3270", "on", &error_abort); + } else if (strstart(filename, "websock:", &p)) { + qemu_opt_set(opts, "websock", "on", &error_abort); } return opts; } @@ -837,6 +840,9 @@ QemuOptsList qemu_chardev_opts =3D { },{ .name =3D "tls-creds", .type =3D QEMU_OPT_STRING, + },{ + .name =3D "websock", + .type =3D QEMU_OPT_BOOL, },{ .name =3D "width", .type =3D QEMU_OPT_NUMBER, diff --git a/qapi/char.json b/qapi/char.json index b7b2a05766..aa48a150a8 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -251,6 +251,8 @@ # sockets (default: false) # @tn3270: enable tn3270 protocol on server # sockets (default: false) (Since: 2.10) +# @websock: enable websocket protocol on server +# sockets (default: false) (Since: 3.1) # @reconnect: For a client socket, if a socket is disconnected, # then attempt a reconnect after the given number of seconds. # Setting this to zero disables this function. (default: 0) @@ -265,6 +267,7 @@ '*nodelay' : 'bool', '*telnet' : 'bool', '*tn3270' : 'bool', + '*websock' : 'bool', '*reconnect' : 'int' }, 'base': 'ChardevCommon' } =20 --=20 2.17.1 From nobody Sat Feb 7 07:42:47 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 15353954733511022.9620909162571; Mon, 27 Aug 2018 11:44:33 -0700 (PDT) Received: from localhost ([::1]:34691 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuMV5-0004vu-UD for importer@patchew.org; Mon, 27 Aug 2018 14:44:32 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57555) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuMSR-000153-Ld for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:57 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fuMSE-0007dl-Ls for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:45 -0400 Received: from smtp58.i.mail.ru ([217.69.128.38]:45136) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fuMSE-0007aN-7x for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:34 -0400 Received: by smtp58.i.mail.ru with esmtpa (envelope-from ) id 1fuMSC-00033W-Gc; Mon, 27 Aug 2018 21:41:32 +0300 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=5WiHEgDe+nlXUpxYTuVIKhqBPqE/Dwu8P+t3qEkBaYU=; b=Uwr4EBrM9bPmjfcEDHSDAvZggxuLe2p3wdIj9w7IRFzInDO1zhG9DEyMJebdGWoi4+82ts6hoFiffpHcAEIcJsadcrh5rCAO+oQ+VGv1Zty9MHBkQoUpDyXjMEHc55s30zywGarzeRxCYC7aVdXEscVFjPAZvsO+Tm2afzf7YFc=; To: qemu-devel@nongnu.org Date: Mon, 27 Aug 2018 21:41:03 +0300 Message-Id: <20180827184103.32190-3-jusual@mail.ru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180827184103.32190-1-jusual@mail.ru> References: <20180827184103.32190-1-jusual@mail.ru> Authentication-Results: smtp58.i.mail.ru; auth=pass smtp.auth=jusual@mail.ru smtp.mailfrom=jusual@mail.ru X-7FA49CB5: 0D63561A33F958A5738C1335A0D70092028798E0FEBB25D65FFF28DEED61885B8941B15DA834481FA18204E546F3947CCF7CD7A0D5AA5F25F6B57BC7E64490618DEB871D839B7333395957E7521B51C2545D4CF71C94A83E9FA2833FD35BB23D27C277FBC8AE2E8B55B19328CBC4F849A471835C12D1D977C4224003CC8364767815B9869FA544D8090A508E0FED62991661749BA6B97735D94E105876FE7799B3661434B16C20ACE7DDDDC251EA7DABAAAE862A0553A39223F8577A6DFFEA7C0E4470344BC355C1B27F8605DD8BE795EFF80C71ABB335746BA297DBC24807EA27F269C8F02392CDC1A41FEE7B8D90AE3C9F3DD0FB1AF5EB4E70A05D1297E1BBCB5012B2E24CD356 X-Mailru-Sender: 7766D515518070DE138AAC7428EA760D9852942E7AF893E702149C96F778D4D03C72AD0B1D2E52B07C4160E8B47E48163DDE9B364B0DF2898CB68AF7A628805D594FB4C9F0DBF412AE208404248635DF X-Mras: OK X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 217.69.128.38 Subject: [Qemu-devel] [PATCH v2 2/2] tests/test-char: Check websocket chardev functionality X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Julia Suvorova via Qemu-devel Reply-To: Julia Suvorova Cc: Jim Mussared , =?UTF-8?q?Steffen=20G=C3=B6rtz?= , Stefan Hajnoczi , Julia Suvorova , Joel Stanley , Stefan Hajnoczi , marcandre.lureau@redhat.com, Paolo Bonzini Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Test order: Creating server websocket chardev Creating usual tcp chardev client Sending handshake message from client Receiving handshake reply Sending ping frame with "hello" payload Receiving pong reply Sending binary data "world" Checking the received data on server side Checking of closing handshake Signed-off-by: Julia Suvorova Reviewed-by: Stefan Hajnoczi --- tests/test-char.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/tests/test-char.c b/tests/test-char.c index 5905d31441..deba699b25 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -406,6 +406,130 @@ static void char_socket_fdpass_test(void) } =20 =20 +static void websock_server_read(void *opaque, const uint8_t *buf, int size) +{ + g_assert(size =3D=3D 5); + g_assert(strncmp((char *) buf, "world", size) =3D=3D 0); + quit =3D true; +} + + +static int websock_server_can_read(void *opaque) +{ + return 10; +} + + +static bool websock_check_http_headers(char *buf, int size) +{ + int i; + const char *ans[] =3D { "HTTP/1.1 101 Switching Protocols\r\n", + "Server: QEMU VNC\r\n", + "Upgrade: websocket\r\n", + "Connection: Upgrade\r\n", + "Sec-WebSocket-Accept:", + "Sec-WebSocket-Protocol: binary\r\n" }; + + for (i =3D 0; i < 6; i++) { + if (g_strstr_len(buf, size, ans[i]) =3D=3D NULL) { + return false; + } + } + + return true; +} + + +static void websock_client_read(void *opaque, const uint8_t *buf, int size) +{ + const uint8_t ping[] =3D { 0x89, 0x85, /* Ping header= */ + 0x07, 0x77, 0x9e, 0xf9, /* Masking key */ + 0x6f, 0x12, 0xf2, 0x95, 0x68 /* "hello" */ }; + + const uint8_t binary[] =3D { 0x82, 0x85, /* Binary he= ader */ + 0x74, 0x90, 0xb9, 0xdf, /* Masking key= */ + 0x03, 0xff, 0xcb, 0xb3, 0x10 /* "world" */ = }; + Chardev *chr_client =3D opaque; + + if (websock_check_http_headers((char *) buf, size)) { + qemu_chr_fe_write(chr_client->be, ping, sizeof(ping)); + } else if (buf[0] =3D=3D 0x8a && buf[1] =3D=3D 0x05) { + g_assert(strncmp((char *) buf + 2, "hello", 5) =3D=3D 0); + qemu_chr_fe_write(chr_client->be, binary, sizeof(binary)); + } else { + g_assert(buf[0] =3D=3D 0x88 && buf[1] =3D=3D 0x16); + g_assert(strncmp((char *) buf + 4, "peer requested close", 10) =3D= =3D 0); + quit =3D true; + } +} + + +static int websock_client_can_read(void *opaque) +{ + return 4096; +} + + +static void char_websock_test(void) +{ + QObject *addr; + QDict *qdict; + const char *port; + char *tmp; + char *handshake_port; + CharBackend be; + CharBackend client_be; + Chardev *chr_client; + Chardev *chr =3D qemu_chr_new("server", + "websock:127.0.0.1:0,server,nowait"); + const char handshake[] =3D "GET / HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Host: localhost:%s\r\n" + "Origin: http://localhost:%s\r\n" + "Sec-WebSocket-Key: o9JHNiS3/0/0zYE1wa3yIw=3D= =3D\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Protocol: binary\r\n\r\n"; + const uint8_t close[] =3D { 0x88, 0x82, /* Close header */ + 0xef, 0xaa, 0xc5, 0x97, /* Masking key */ + 0xec, 0x42 /* Status code */ }; + + addr =3D object_property_get_qobject(OBJECT(chr), "addr", &error_abort= ); + qdict =3D qobject_to(QDict, addr); + port =3D qdict_get_str(qdict, "port"); + tmp =3D g_strdup_printf("tcp:127.0.0.1:%s", port); + handshake_port =3D g_strdup_printf(handshake, port, port); + qobject_unref(qdict); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_handlers(&be, websock_server_can_read, websock_server_= read, + NULL, NULL, chr, NULL, true); + + chr_client =3D qemu_chr_new("client", tmp); + qemu_chr_fe_init(&client_be, chr_client, &error_abort); + qemu_chr_fe_set_handlers(&client_be, websock_client_can_read, + websock_client_read, + NULL, NULL, chr_client, NULL, true); + g_free(tmp); + + qemu_chr_write_all(chr_client, + (uint8_t *) handshake_port, + strlen(handshake_port)); + g_free(handshake_port); + main_loop(); + + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abo= rt)); + g_assert(object_property_get_bool(OBJECT(chr_client), + "connected", &error_abort)); + + qemu_chr_write_all(chr_client, close, sizeof(close)); + main_loop(); + + object_unparent(OBJECT(chr_client)); + object_unparent(OBJECT(chr)); +} + + #ifndef _WIN32 static void char_pipe_test(void) { @@ -829,6 +953,7 @@ int main(int argc, char **argv) g_test_add_func("/char/serial", char_serial_test); #endif g_test_add_func("/char/hotswap", char_hotswap_test); + g_test_add_func("/char/websocket", char_websock_test); =20 return g_test_run(); } --=20 2.17.1