Exemplo n.º 1
0
h2o_websocket_conn_t *h2o_upgrade_to_websocket(h2o_req_t *req, const char *client_key, void *data, h2o_websocket_msg_callback cb)
{
    h2o_websocket_conn_t *conn = h2o_mem_alloc(sizeof(*conn));
    char accept_key[29];

    /* only for http1 connection */
    assert(req->version < 0x200);

    /* setup the context */
    memset(conn, 0, sizeof(*conn));
    // conn->sock = sock; set by on_complete
    conn->ws_callbacks.recv_callback = recv_callback;
    conn->ws_callbacks.send_callback = send_callback;
    conn->ws_callbacks.on_msg_recv_callback = on_msg_callback;
    conn->data = data;
    conn->cb = cb;

    wslay_event_context_server_init(&conn->ws_ctx, &conn->ws_callbacks, conn);

    /* build response */
    create_accept_key(accept_key, client_key);
    req->res.status = 101;
    req->res.reason = "Switching Protocols";
    h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, NULL, H2O_STRLIT("websocket"));
    h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("sec-websocket-accept"), 0, NULL, accept_key,
                          strlen(accept_key));

    /* send */
    h2o_http1_upgrade(req, NULL, 0, on_complete, conn);

    return conn;
}
Exemplo n.º 2
0
WebSocket::WebSocket(const SocketClient::SharedPtr& client)
    : mClient(client)
{
    wslay_event_callbacks callbacks = {
        wslayRecvCallback,
        wslaySendCallback,
        0, // genmask_callback
        0, // on_frame_recv_start_callback
        0, // on_frame_recv_callback
        0, // on_frame_recv_end_callback
        wslayOnMsgRecvCallback
    };
    wslay_event_context_server_init(&mCtx, &callbacks, this);

    client->readyRead().connect([this](const SocketClient::SharedPtr& client, Buffer&& buf) {
            if (buf.isEmpty())
                return;
            mBuffers.push_back(std::move(buf));
            if (wslay_event_recv(mCtx) < 0) {
                // close socket
                client->close();
                mClient.reset();
                mError(this);
            }
        });
    client->disconnected().connect([this](const SocketClient::SharedPtr& client) {
            mClient.reset();
            mDisconnected(this);
        });
}
Exemplo n.º 3
0
/*
 * if websocket server is initiated from http(s), you just can call this function.
 * see the comment of websocket_server_open to know what is different.
 */
websocket_return_t websocket_server_init(websocket_t *server)
{
	int r = WEBSOCKET_SUCCESS;
	struct websocket_info_t *socket_data = NULL;

	if (server == NULL) {
		WEBSOCKET_DEBUG("NULL parameter\n");
		return WEBSOCKET_ALLOCATION_ERROR;
	}

	socket_data = calloc(1, sizeof(struct websocket_info_t));
	if (socket_data == NULL) {
		WEBSOCKET_DEBUG("fail to allocate memory\n");
		r = WEBSOCKET_ALLOCATION_ERROR;
		goto EXIT_SERVER_INIT;
	}
	socket_data->data = server;

	if (wslay_event_context_server_init(&(server->ctx), server->cb, socket_data) != WEBSOCKET_SUCCESS) {
		WEBSOCKET_DEBUG("fail to initiate websocket server\n");
		r = WEBSOCKET_INIT_ERROR;
		goto EXIT_SERVER_INIT;
	}

	if (websocket_config_socket(server->fd) != WEBSOCKET_SUCCESS) {
		r = WEBSOCKET_SOCKET_ERROR;
		goto EXIT_SERVER_INIT;
	}

	WEBSOCKET_DEBUG("start websocket server handling loop\n");
	r = websocket_handler(server);

EXIT_SERVER_INIT:
	WEBSOCKET_CLOSE(server->fd);

	if (server->ctx) {
		wslay_event_context_free(server->ctx);
		server->ctx = NULL;
	}

	if (server->tls_enabled) {
		mbedtls_net_free(&(server->tls_net));
		mbedtls_ssl_free(server->tls_ssl);
		WEBSOCKET_FREE(server->tls_ssl);
	}

	websocket_update_state(server, WEBSOCKET_STOP);

	return r;
}
Exemplo n.º 4
0
static mrb_value
mrb_wslay_event_context_server_init(mrb_state *mrb, mrb_value self)
{
  mrb_value callbacks_obj;

  mrb_get_args(mrb, "o", &callbacks_obj);

  mrb_value recv_callback, send_callback, on_msg_recv_callback;
  recv_callback = mrb_iv_get(mrb, callbacks_obj, mrb_intern_lit(mrb, "@recv_callback"));
  if (mrb_type(recv_callback) != MRB_TT_PROC) {
    mrb_raise(mrb, E_ARGUMENT_ERROR, "recv_callback missing");
  }
  send_callback = mrb_iv_get(mrb, callbacks_obj, mrb_intern_lit(mrb, "@send_callback"));
  if (mrb_type(send_callback) != MRB_TT_PROC) {
    mrb_raise(mrb, E_ARGUMENT_ERROR, "send_callback missing");
  }
  on_msg_recv_callback = mrb_iv_get(mrb, callbacks_obj, mrb_intern_lit(mrb, "@on_msg_recv_callback"));
  if (mrb_type(on_msg_recv_callback) != MRB_TT_PROC) {
    mrb_raise(mrb, E_ARGUMENT_ERROR, "on_msg_recv_callback missing");
  }
  mrb_iv_set(mrb, self, mrb_intern_lit(mrb, "callbacks"), callbacks_obj);
  mrb_wslay_user_data *data = (mrb_wslay_user_data *) mrb_malloc(mrb, sizeof(mrb_wslay_user_data));
  mrb_data_init(self, data, &mrb_wslay_user_data_type);
  data->mrb = mrb;
  data->recv_callback = recv_callback;
  data->send_callback = send_callback;
  data->on_msg_recv_callback = on_msg_recv_callback;
  struct wslay_event_callbacks server_callbacks = {
    mrb_wslay_event_recv_callback,
    mrb_wslay_event_send_callback,
    NULL,
    NULL,
    NULL,
    NULL,
    mrb_wslay_event_on_msg_recv_callback
  };

  int err = wslay_event_context_server_init(&data->ctx, &server_callbacks, data);
  if (err == WSLAY_ERR_NOMEM) {
    mrb_sys_fail(mrb, "wslay_event_context_server_init");
  }
  else if (err != 0) {
    return MRB_WSLAY_ERROR(mrb_fixnum_value(err));
  }

  return self;
}
Exemplo n.º 5
0
/*
 * Communicate with the client. This function performs HTTP handshake
 * and WebSocket data transfer until close handshake is done or an
 * error occurs. *fd* is the file descriptor of the connection to the
 * client. This function returns 0 if it succeeds, or returns 0.
 */
int communicate(int fd)
{
  wslay_event_context_ptr ctx;
  struct wslay_event_callbacks callbacks = {
    recv_callback,
    send_callback,
    NULL,
    NULL,
    NULL,
    NULL,
    on_msg_recv_callback
  };
  struct Session session = { fd };
  int val = 1;
  struct pollfd event;
  int res = 0;

  if(http_handshake(fd) == -1) {
    return -1;
  }
  if(make_non_block(fd) == -1) {
    return -1;
  }
  if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val))
     == -1) {
    perror("setsockopt: TCP_NODELAY");
    return -1;
  }
  memset(&event, 0, sizeof(struct pollfd));
  event.fd = fd;
  event.events = POLLIN;
  wslay_event_context_server_init(&ctx, &callbacks, &session);
  /*
   * Event loop: basically loop until both wslay_event_want_read(ctx)
   * and wslay_event_want_write(ctx) return 0.
   */
  while(wslay_event_want_read(ctx) || wslay_event_want_write(ctx)) {
    int r;
    while((r = poll(&event, 1, -1)) == -1 && errno == EINTR);
    if(r == -1) {
      perror("poll");
      res = -1;
      break;
    }
    if(((event.revents & POLLIN) && wslay_event_recv(ctx) != 0) ||
       ((event.revents & POLLOUT) && wslay_event_send(ctx) != 0) ||
       (event.revents & (POLLERR | POLLHUP | POLLNVAL))) {
      /*
       * If either wslay_event_recv() or wslay_event_send() return
       * non-zero value, it means serious error which prevents wslay
       * library from processing further data, so WebSocket connection
       * must be closed.
       */
      res = -1;
      break;
    }
    event.events = 0;
    if(wslay_event_want_read(ctx)) {
      event.events |= POLLIN;
    }
    if(wslay_event_want_write(ctx)) {
      event.events |= POLLOUT;
    }
  }
  return res;
}
Exemplo n.º 6
0
/**
 * \brief The function with websocket infinite loop
 */
void *vs_websocket_loop(void *arg)
{
	/* The vContext is passed as *user_data* in callback functions. */
	struct vContext *C = (struct vContext*)arg;
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VStreamConn *stream_conn = CTX_current_stream_conn(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VMessage *r_message=NULL, *s_message=NULL;
	wslay_event_context_ptr wslay_ctx;
	fd_set read_set, write_set;
	struct timeval tv;
	int ret, flags;
	unsigned int int_size;

	struct wslay_event_callbacks callbacks = {
			vs_recv_ws_callback_data,
			vs_send_ws_callback_data,
			NULL,
			NULL,
			NULL,
			NULL,
			vs_ws_recv_msg_callback
	};

	/* Set socket blocking */
	flags = fcntl(io_ctx->sockfd, F_GETFL, 0);
	fcntl(io_ctx->sockfd, F_SETFL, flags & ~O_NONBLOCK);

	http_handshake(io_ctx->sockfd);

	/* Try to get size of TCP buffer */
	int_size = sizeof(int_size);
	getsockopt(io_ctx->sockfd, SOL_SOCKET, SO_RCVBUF,
			(void *)&stream_conn->socket_buffer_size, &int_size);

	r_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));
	s_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));
	CTX_r_message_set(C, r_message);
	CTX_s_message_set(C, s_message);

	stream_conn->host_state = TCP_SERVER_STATE_RESPOND_METHODS;

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: RESPOND_methods\n");
		printf("%c[%dm", 27, 0);
	}

	/* Set non-blocking */
	flags = fcntl(io_ctx->sockfd, F_GETFL, 0);
	fcntl(io_ctx->sockfd, F_SETFL, flags | O_NONBLOCK);

	wslay_event_context_server_init(&wslay_ctx, &callbacks, C);

	/* "Never ending" loop */
	while(vsession->stream_conn->host_state != TCP_SERVER_STATE_CLOSED)
	{
		if(vs_ctx->state != SERVER_STATE_READY) {
			vsession->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;
			/* Try to close connection with websocket client */
			wslay_event_queue_close(wslay_ctx,
					WSLAY_CODE_GOING_AWAY,
					(uint8_t*)"Server shutdown",	/* Close message */
					15);	/* The length of close message s*/
		}

		/* Initialize read set */
		FD_ZERO(&read_set);
		FD_SET(io_ctx->sockfd, &read_set);

		/* Initialize write set */
		FD_ZERO(&write_set);
		FD_SET(io_ctx->sockfd, &write_set);

		/* Set timeout for select() */
		if(stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) {
			/* Use negotiated FPS */
			tv.tv_sec = 0;
			tv.tv_usec = 1000000/vsession->fps_host;
		} else {
			/* User have to send something in 30 seconds */
			tv.tv_sec = VRS_TIMEOUT;
			tv.tv_usec = 0;
		}

		if( (ret = select(io_ctx->sockfd+1,
				&read_set,
				&write_set,
				NULL,			/* Exception*/
				&tv)) == -1) {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "%s:%s():%d select(): %s\n",
					__FILE__, __FUNCTION__,  __LINE__, strerror(errno));
			goto end;
			/* Was event on the listen socket */
		} else if(ret>0){
			if(FD_ISSET(io_ctx->sockfd, &read_set)) {
				if( wslay_event_recv(wslay_ctx) != 0 ) {
					goto end;
				}
			} else if (FD_ISSET(io_ctx->sockfd, &write_set)) {
				if( wslay_event_send(wslay_ctx) != 0 ) {
					goto end;
				}
			}
		}

		if(stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) {
			/* Check if there is any command in outgouing queue
			 * and eventually pack these commands to buffer */
			if((ret = v_STREAM_pack_message(C)) == 0 ) {
				goto end;
			}

			/* When at least one command was packed to buffer, then
			 * queue this buffer to WebSocket layer */
			if(ret == 1) {
				struct wslay_event_msg msgarg;
			    msgarg.opcode = WSLAY_BINARY_FRAME;
			    msgarg.msg = (uint8_t*)io_ctx->buf;
			    msgarg.msg_length = io_ctx->buf_size;
			    wslay_event_queue_msg(wslay_ctx, &msgarg);
			}
		}
	}

end:
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSING\n");
		printf("%c[%dm", 27, 0);
	}

	/* Set up TCP CLOSING state (non-blocking) */
	vs_CLOSING(C);

	/* Receive and Send messages are not neccessary any more */
	if(r_message!=NULL) {
		free(r_message);
		r_message = NULL;
		CTX_r_message_set(C, NULL);
	}
	if(s_message!=NULL) {
		free(s_message);
		s_message = NULL;
		CTX_s_message_set(C, NULL);
	}

	/* TCP connection is considered as CLOSED, but it is not possible to use
	 * this connection for other client */
	stream_conn->host_state = TCP_SERVER_STATE_CLOSED;

	/* NULL pointer at stream connection */
	CTX_current_stream_conn_set(C, NULL);

	/* Set TCP connection to CLOSED */
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSED\n");
		printf("%c[%dm", 27, 0);
	}


	pthread_mutex_lock(&vs_ctx->data.mutex);
	/* Unsubscribe this session (this avatar) from all nodes */
	vs_node_free_avatar_reference(vs_ctx, vsession);
	/* Try to destroy avatar node */
	vs_node_destroy_avatar_node(vs_ctx, vsession);
	pthread_mutex_unlock(&vs_ctx->data.mutex);

	/* This session could be used again for authentication */
	stream_conn->host_state=TCP_SERVER_STATE_LISTEN;

	/* Clear session flags */
	vsession->flags = 0;

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: LISTEN\n");
		printf("%c[%dm", 27, 0);
	}

	free(C);
	C = NULL;

	pthread_exit(NULL);
	return NULL;
}
Exemplo n.º 7
0
int broker_handshake_handle_ws(Broker *broker,
                               Client *client,
                               const char *dsId,
                               const char *auth,
                               const char *wsAccept) {

    ref_t *oldDsId = NULL;
    ref_t *ref = dslink_map_remove_get(&broker->client_connecting,
                                       (char *) dsId);
    if (!ref) {
        return 1;
    }
    RemoteDSLink *link = ref->data;
    dslink_decref(ref);
    if (link->name) {
        dslink_map_remove(&broker->client_connecting,
                          (char *) link->name);
    }
    if (!(auth && link->auth->pubKey)) {
        return 1;
    }

    uv_timer_t *ping_timer = NULL;
    int ret = 0;
    { // Perform auth check
        char expectedAuth[90];
        if (dslink_handshake_gen_auth_key(&link->auth->tempKey,
                                          link->auth->pubKey,
                                          link->auth->salt,
                                          (unsigned char *) expectedAuth,
                                          sizeof(expectedAuth)) != 0) {
            ret = 1;
            goto exit;
        }

        if (strcmp(expectedAuth, auth) != 0) {
            ret = 1;
            goto exit;
        }
    }

    DownstreamNode *node = NULL;
    int pendingUpdateList = 0;
    { // Handle retrieval of the downstream node
        ref = dslink_map_get(broker->downstream->children,
                                    (char *) link->name);
        if (!ref) {
            node = broker_init_downstream_node(broker->downstream, link->name);
            if (!node) {
                ret = 1;
                goto exit;
            }
            oldDsId = dslink_ref(dslink_strdup(dsId), dslink_free);
            if (broker->downstream->list_stream) {
                pendingUpdateList = 1;
            }
            broker_downstream_nodes_changed(broker);
        } else {
            node = ref->data;
            oldDsId = node->dsId;
        }
    }

    if (node->link) {
        Client *c = node->link->client;
        broker_close_link(node->link);
        uv_poll_t *poll = c->poll;
        dslink_socket_free(c->sock);
        dslink_free(c);
        uv_close((uv_handle_t *) poll, broker_free_handle);
    }
    
    // add permission group to link
    json_t *group = json_object_get(node->meta, "$$group");
    permission_groups_load(&link->permission_groups, dsId, json_string_value(group));

    link->client = client;
    link->dsId = oldDsId;
    link->node = node;
    node->dsId = oldDsId;
    client->sock_data = link;
    json_object_set_new(node->meta, "$$dsId", json_string_nocheck(dsId));

    wslay_event_context_ptr ws;
    if (wslay_event_context_server_init(&ws,
                                        broker_ws_callbacks(),
                                        link) != 0) {
        ret = 1;
        goto exit;
    }
    link->ws = ws;
    broker_ws_send_init(client->sock, wsAccept);

    ping_timer = dslink_malloc(sizeof(uv_timer_t));
    ping_timer->data = link;
    uv_timer_init(link->client->poll->loop, ping_timer);
    uv_timer_start(ping_timer, dslink_handle_ping, 1000, 30000);
    link->pingTimerHandle = ping_timer;

    // set the ->link and update all existing stream
    broker_dslink_connect(node, link);

    if (pendingUpdateList) {
        update_list_child(broker->downstream,
                          broker->downstream->list_stream,
                          link->name);
    }

    log_info("DSLink `%s` has connected\n", dsId);
exit:
    mbedtls_ecdh_free(&link->auth->tempKey);
    dslink_free((void *) link->auth->pubKey);
    dslink_free(link->auth);
    link->auth = NULL;
    if (ret != 0) {
        dslink_map_free(&link->requester_streams);
        dslink_map_free(&link->responder_streams);
        dslink_free((char *)link->path);
        dslink_free(link);

        if (ping_timer) {
            uv_timer_stop(ping_timer);
            uv_close((uv_handle_t *) ping_timer, broker_free_handle);
        }
    }

    return ret;
}
Exemplo n.º 8
0
HIDDEN int ws_start_channel(struct transaction_t *txn, const char *protocol,
                            int (*data_cb)(struct buf *inbuf, struct buf *outbuf,
                                           struct buf *logbuf, void **rock))
{
    int r;
    const char **hdr, *accept = NULL;
    wslay_event_context_ptr ev;
    struct ws_context *ctx;
    struct wslay_event_callbacks callbacks = {
        recv_cb,
        send_cb,
        NULL,
        NULL,
        NULL,
        NULL,
        on_msg_recv_cb
    };

    /* Check for supported WebSocket version */
    hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Version");
    if (!hdr) {
        txn->error.desc = "Missing WebSocket version";
        return HTTP_BAD_REQUEST;
    }
    else if (hdr[1]) {
        txn->error.desc = "Multiple WebSocket versions";
        return HTTP_BAD_REQUEST;
    }
    else if (strcmp(hdr[0], WS_VERSION)) {
        txn->error.desc = "Unsupported WebSocket version";
        return HTTP_UPGRADE;
    }

    if (protocol) {
        /* Check for supported WebSocket subprotocol */
        int i, found = 0;

        hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Protocol");
        if (!hdr) {
            txn->error.desc = "Missing WebSocket protocol";
            return HTTP_BAD_REQUEST;
        }

        for (i = 0; !found && hdr[i]; i++) {
            tok_t tok = TOK_INITIALIZER(hdr[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT);
            char *token;

            while ((token = tok_next(&tok))) {
                if (!strcmp(token, protocol)) {
                    found = 1;
                    break;
                }
            }
            tok_fini(&tok);
        }
        if (!found) {
            txn->error.desc = "Unsupported WebSocket protocol";
            return HTTP_BAD_REQUEST;
        }
    }

    if (txn->flags.ver == VER_1_1) {
        unsigned char sha1buf[SHA1_DIGEST_LENGTH];

        /* Check for WebSocket client key */
        hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Key");
        if (!hdr) {
            txn->error.desc = "Missing WebSocket client key";
            return HTTP_BAD_REQUEST;
        }
        else if (hdr[1]) {
            txn->error.desc = "Multiple WebSocket client keys";
            return HTTP_BAD_REQUEST;
        }
        else if (strlen(hdr[0]) != WS_CKEY_LEN) {
            txn->error.desc = "Invalid WebSocket client key";
            return HTTP_BAD_REQUEST;
        }

        /* Create WebSocket accept key */
        buf_setcstr(&txn->buf, hdr[0]);
        buf_appendcstr(&txn->buf, WS_GUID);
        xsha1((u_char *) buf_base(&txn->buf), buf_len(&txn->buf), sha1buf);

        buf_ensure(&txn->buf, WS_AKEY_LEN+1);
        accept = buf_base(&txn->buf);

        r = sasl_encode64((char *) sha1buf, SHA1_DIGEST_LENGTH,
                          (char *) accept, WS_AKEY_LEN+1, NULL);
        if (r != SASL_OK) syslog(LOG_WARNING, "sasl_encode64: %d", r);
    }

    /* Create server context */
    r = wslay_event_context_server_init(&ev, &callbacks, txn);
    if (r) {
        syslog(LOG_WARNING,
               "wslay_event_context_init: %s", wslay_strerror(r));
        return HTTP_SERVER_ERROR;
    }

    /* Create channel context */
    ctx = xzmalloc(sizeof(struct ws_context));
    ctx->event = ev;
    ctx->accept = accept;
    ctx->protocol = protocol;
    ctx->data_cb = data_cb;
    txn->ws_ctx = ctx;

    /* Check for supported WebSocket extensions */
    parse_extensions(txn);

    /* Prepare log buffer */

    /* Add client data */
    buf_printf(&ctx->log, "%s", txn->conn->clienthost);
    if (httpd_userid) buf_printf(&ctx->log, " as \"%s\"", httpd_userid);
    if ((hdr = spool_getheader(txn->req_hdrs, "User-Agent"))) {
        buf_printf(&ctx->log, " with \"%s\"", hdr[0]);
        if ((hdr = spool_getheader(txn->req_hdrs, "X-Client")))
            buf_printf(&ctx->log, " by \"%s\"", hdr[0]);
        else if ((hdr = spool_getheader(txn->req_hdrs, "X-Requested-With")))
            buf_printf(&ctx->log, " by \"%s\"", hdr[0]);
    }

    /* Add request-line */
    buf_printf(&ctx->log, "; \"WebSocket/%s via %s\"",
               protocol ? protocol : "echo" , txn->req_line.ver);
    ctx->log_tail = buf_len(&ctx->log);

    /* Tell client that WebSocket negotiation has succeeded */
    if (txn->conn->sess_ctx) {
        /* Treat WS data as chunked response */
        txn->flags.te = TE_CHUNKED;

        response_header(HTTP_OK, txn);

        /* Force the response to the client immediately */
        prot_flush(httpd_out);
    }
    else response_header(HTTP_SWITCH_PROT, txn);

    /* Set connection as non-blocking */
    prot_NONBLOCK(txn->conn->pin);

    /* Don't do telemetry logging in prot layer */
    prot_setlog(txn->conn->pin, PROT_NO_FD);
    prot_setlog(txn->conn->pout, PROT_NO_FD);

    return 0;
}