#include "io/channel-websock.h"
#include "crypto/hash.h"
#include "trace.h"
+#include "qemu/iov.h"
#include <time.h>
QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA
};
-static void qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc,
- const char *resmsg,
- ...)
+static void GCC_FMT_ATTR(2, 3)
+qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc,
+ const char *resmsg,
+ ...)
{
va_list vargs;
char *response;
goto bad_request;
}
*nl = '\0';
+ trace_qio_channel_websock_http_greeting(ioc, buffer);
tmp = strchr(buffer, ' ');
if (!tmp) {
char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
QIO_CHANNEL_WEBSOCK_GUID_LEN + 1];
char *accept = NULL;
- char *date = qio_channel_websock_date_str();
+ char *date = NULL;
g_strlcpy(combined_key, key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + 1);
g_strlcat(combined_key, QIO_CHANNEL_WEBSOCK_GUID,
return;
}
+ date = qio_channel_websock_date_str();
qio_channel_websock_handshake_send_res(
ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK, date, accept);
size_t nhdrs = G_N_ELEMENTS(hdrs);
const char *protocols = NULL, *version = NULL, *key = NULL,
*host = NULL, *connection = NULL, *upgrade = NULL;
+ char **connectionv;
+ bool upgraded = false;
+ size_t i;
nhdrs = qio_channel_websock_extract_headers(ioc, buffer, hdrs, nhdrs, errp);
if (!nhdrs) {
goto bad_request;
}
+ trace_qio_channel_websock_http_request(ioc, protocols, version,
+ host, connection, upgrade, key);
+
if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) {
error_setg(errp, "No '%s' protocol is supported by client '%s'",
QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols);
goto bad_request;
}
- if (strcasecmp(connection, QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE) != 0) {
+ connectionv = g_strsplit(connection, ",", 0);
+ for (i = 0; connectionv != NULL && connectionv[i] != NULL; i++) {
+ g_strstrip(connectionv[i]);
+ if (strcasecmp(connectionv[i],
+ QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE) == 0) {
+ upgraded = true;
+ }
+ }
+ g_strfreev(connectionv);
+ if (!upgraded) {
error_setg(errp, "No connection upgrade requested '%s'", connection);
goto bad_request;
}
static void qio_channel_websock_write_close(QIOChannelWebsock *ioc,
uint16_t code, const char *reason)
{
- struct iovec iov;
- buffer_reserve(&ioc->rawoutput, 2 + (reason ? strlen(reason) : 0));
- *(uint16_t *)(ioc->rawoutput.buffer + ioc->rawoutput.offset) =
- cpu_to_be16(code);
- ioc->rawoutput.offset += 2;
+ struct iovec iov[2] = {
+ { .iov_base = &code, .iov_len = sizeof(code) },
+ };
+ size_t niov = 1;
+ size_t size = iov[0].iov_len;
+
+ cpu_to_be16s(&code);
+
if (reason) {
- buffer_append(&ioc->rawoutput, reason, strlen(reason));
+ iov[1].iov_base = (void *)reason;
+ iov[1].iov_len = strlen(reason);
+ size += iov[1].iov_len;
+ niov++;
}
- iov.iov_base = ioc->rawoutput.buffer;
- iov.iov_len = ioc->rawoutput.offset;
qio_channel_websock_encode(ioc, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE,
- &iov, 1, iov.iov_len);
- buffer_reset(&ioc->rawoutput);
+ iov, niov, size);
qio_channel_websock_write_wire(ioc, NULL);
qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
}
buffer_free(&ioc->encinput);
buffer_free(&ioc->encoutput);
buffer_free(&ioc->rawinput);
- buffer_free(&ioc->rawoutput);
object_unref(OBJECT(ioc->master));
if (ioc->io_tag) {
g_source_remove(ioc->io_tag);
Error **errp)
{
QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
- size_t i;
- ssize_t done = 0;
+ ssize_t want = iov_size(iov, niov);
+ ssize_t avail;
ssize_t ret;
if (wioc->io_err) {
return -1;
}
- for (i = 0; i < niov; i++) {
- size_t want = iov[i].iov_len;
- if ((want + wioc->rawoutput.offset) > QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
- want = (QIO_CHANNEL_WEBSOCK_MAX_BUFFER - wioc->rawoutput.offset);
- }
- if (want == 0) {
- goto done;
- }
-
- buffer_reserve(&wioc->rawoutput, want);
- buffer_append(&wioc->rawoutput, iov[i].iov_base, want);
- done += want;
- if (want < iov[i].iov_len) {
- break;
- }
+ avail = wioc->encoutput.offset >= QIO_CHANNEL_WEBSOCK_MAX_BUFFER ?
+ 0 : (QIO_CHANNEL_WEBSOCK_MAX_BUFFER - wioc->encoutput.offset);
+ if (want > avail) {
+ want = avail;
}
- done:
- if (wioc->rawoutput.offset) {
- struct iovec iov = { .iov_base = wioc->rawoutput.buffer,
- .iov_len = wioc->rawoutput.offset };
+ if (want) {
qio_channel_websock_encode(wioc,
QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME,
- &iov, 1, iov.iov_len);
- buffer_reset(&wioc->rawoutput);
+ iov, niov, want);
}
+
+ /* Even if want == 0, we'll try write_wire in case there's
+ * pending data we could usefully flush out
+ */
ret = qio_channel_websock_write_wire(wioc, errp);
if (ret < 0 &&
ret != QIO_CHANNEL_ERR_BLOCK) {
qio_channel_websock_set_watch(wioc);
- if (done == 0) {
+ if (want == 0) {
return QIO_CHANNEL_ERR_BLOCK;
}
- return done;
+ return want;
}
static int qio_channel_websock_set_blocking(QIOChannel *ioc,