+static void
+conn_plain_write_sendq(rb_fde_t *fd, void *data)
+{
+ conn_t *conn = data;
+ int retlen;
+
+ if(IsDead(conn))
+ return;
+
+ while((retlen = rb_linebuf_flush(fd, &conn->plainbuf_out)) > 0)
+ conn->plain_out += retlen;
+
+ if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
+ {
+ close_conn(data, NO_WAIT, NULL);
+ return;
+ }
+
+ if(rb_linebuf_alloclen(&conn->plainbuf_out) > 0)
+ rb_setselect(conn->plain_fd, RB_SELECT_WRITE, conn_plain_write_sendq, conn);
+ else
+ rb_setselect(conn->plain_fd, RB_SELECT_WRITE, NULL, NULL);
+}
+
+static void
+conn_mod_write_sendq(rb_fde_t *fd, void *data)
+{
+ conn_t *conn = data;
+ const char *err;
+ int retlen;
+
+ if(IsDead(conn))
+ return;
+
+ while((retlen = rb_rawbuf_flush(conn->modbuf_out, fd)) > 0)
+ conn->mod_out += retlen;
+
+ if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
+ {
+ if(retlen == 0)
+ close_conn(conn, WAIT_PLAIN, "%s", remote_closed);
+ err = strerror(errno);
+ close_conn(conn, WAIT_PLAIN, "Write error: %s", err);
+ return;
+ }
+
+ if(rb_rawbuf_length(conn->modbuf_out) > 0)
+ rb_setselect(conn->mod_fd, RB_SELECT_WRITE, conn_mod_write_sendq, conn);
+ else
+ rb_setselect(conn->mod_fd, RB_SELECT_WRITE, NULL, NULL);
+
+ if(IsCork(conn) && rb_rawbuf_length(conn->modbuf_out) == 0)
+ {
+ ClearCork(conn);
+ conn_plain_read_cb(conn->plain_fd, conn);
+ }
+}
+
+static void
+conn_mod_write(conn_t * conn, void *data, size_t len)
+{
+ if(IsDead(conn)) /* no point in queueing to a dead man */
+ return;
+ rb_rawbuf_append(conn->modbuf_out, data, len);
+}
+
+static void
+conn_mod_write_short_frame(conn_t * conn, void *data, int len)
+{
+ ws_frame_hdr_t hdr = WEBSOCKET_FRAME_HDR_INIT;
+
+ ws_frame_set_opcode(&hdr, WEBSOCKET_OPCODE_TEXT_FRAME);
+ ws_frame_set_fin(&hdr, 1);
+ hdr.payload_length_mask = (len + 2) & 0x7f;
+
+ conn_mod_write(conn, &hdr, sizeof(hdr));
+ conn_mod_write(conn, data, len);
+ conn_mod_write(conn, "\r\n", 2);
+}
+
+static void
+conn_mod_write_long_frame(conn_t * conn, void *data, int len)
+{
+ ws_frame_ext_t hdr = WEBSOCKET_FRAME_EXT_INIT;
+
+ ws_frame_set_opcode(&hdr.header, WEBSOCKET_OPCODE_TEXT_FRAME);
+ ws_frame_set_fin(&hdr.header, 1);
+ hdr.header.payload_length_mask = 126;
+ hdr.payload_length_extended = htons(len + 2);
+
+ conn_mod_write(conn, &hdr, sizeof(hdr));
+ conn_mod_write(conn, data, len);
+ conn_mod_write(conn, "\r\n", 2);
+}
+
+static void
+conn_mod_write_frame(conn_t *conn, void *data, int len)
+{
+ if(IsDead(conn)) /* no point in queueing to a dead man */
+ return;
+
+ if (len < 123)
+ {
+ conn_mod_write_short_frame(conn, data, len);
+ return;
+ }
+
+ conn_mod_write_long_frame(conn, data, len);
+}
+