/* Get native file descriptor. */ static MVMint64 mvm_fileno(MVMThreadContext *tc, MVMOSHandle *h) { MVMIOSyncPipeData *data = (MVMIOSyncPipeData *)h->body.data; uv_os_fd_t fd; if (uv_fileno((uv_handle_t *)data->ss.handle, &fd) >= 0) return (MVMint64)fd; return -1; }
static bool connection_tls_handshake(Client *client) { uv_os_fd_t fd; struct tls *context = NULL; int ret = TLS_READ_AGAIN; if(uv_fileno((uv_handle_t *)client->handle, &fd) != 0) { return false; } while((ret = tls_accept_socket(CurrentConnectionState.ServerContext, &context, fd)) != 0) { if(ret == -1) { return false; } } client->TlsContext = context; tls_get_cert_fingerprint(client->TlsContext, client->CertificateFp, sizeof(client->CertificateFp)); return true; }
void connection_start_read(Client *client) { if(client == NULL) { return; } if(client->TlsContext != NULL) { uv_os_fd_t fd; uv_poll_t *handle = Malloc(sizeof(uv_poll_t)); handle->data = client; uv_fileno((uv_handle_t *)client->handle, &fd); uv_poll_init(serverstate_get_event_loop(), handle, fd); if(uv_poll_start(handle, UV_READABLE, connection_poll_callback) < 0) { client_free(client); } } else { if(uv_read_start((uv_stream_t *)client->handle, connection_allocate_buffer_callback, connection_on_read_callback) < 0) { client_free(client); } } }
int SocketAccept(uv_stream_t *const sstream, struct tls *const ssecure, SocketRef *const out) { SocketRef socket = calloc(1, sizeof(struct Socket)); if(!socket) return UV_ENOMEM; int rc = uv_tcp_init(async_loop, socket->stream); if(rc < 0) goto cleanup; rc = uv_accept(sstream, (uv_stream_t *)socket->stream); if(rc < 0) goto cleanup; if(ssecure) { uv_os_fd_t fd; rc = uv_fileno((uv_handle_t *)socket->stream, &fd); if(rc < 0) goto cleanup; for(;;) { int event = tls_accept_socket(ssecure, &socket->secure, fd); if(0 == event) break; rc = tls_poll((uv_stream_t *)socket->stream, event); if(rc < 0) goto cleanup; } } socket->rdmem = NULL; *socket->rd = uv_buf_init(NULL, 0); *socket->wr = uv_buf_init(NULL, 0); *out = socket; socket = NULL; cleanup: SocketFree(&socket); return rc; }
static int luv_fileno(lua_State* L) { uv_handle_t* handle = luv_check_handle(L, 1); uv_os_fd_t fd; int ret = uv_fileno(handle, &fd); if (ret < 0) return luv_error(L, ret); lua_pushinteger(L, (LUA_INTEGER)fd); return 1; }
static int set_tcp_option(uv_handle_t *handle, int option, int val) { uv_os_fd_t fd = 0; if (uv_fileno(handle, &fd) == 0) { return setsockopt(fd, IPPROTO_TCP, option, &val, sizeof(val)); } return 0; /* N/A */ }
void WebSocket::onReadable(uv_poll_t *p, int status, int events) { SocketData *socketData = (SocketData *) p->data; // this one is not needed, read will do this! if (status < 0) { WebSocket(p).close(true, 1006); return; } char *src = socketData->server->recvBuffer; memcpy(src, socketData->spill, socketData->spillLength); uv_os_fd_t fd; uv_fileno((uv_handle_t *) p, &fd); ssize_t received; if (socketData->ssl) { received = SSL_read(socketData->ssl, src + socketData->spillLength, Server::LARGE_BUFFER_SIZE - socketData->spillLength); } else { received = recv(fd, src + socketData->spillLength, Server::LARGE_BUFFER_SIZE - socketData->spillLength, 0); } if (received == -1 || received == 0) { // do we have a close frame in our buffer, and did we already set the state as CLOSING? if (socketData->state == CLOSING && socketData->controlBuffer.length()) { std::tuple<unsigned short, char *, size_t> closeFrame = Parser::parseCloseFrame(socketData->controlBuffer); if (!std::get<0>(closeFrame)) { std::get<0>(closeFrame) = 1006; } WebSocket(p).close(true, std::get<0>(closeFrame), std::get<1>(closeFrame), std::get<2>(closeFrame)); } else { WebSocket(p).close(true, 1006); } return; } // do not parse any data once in closing state if (socketData->state == CLOSING) { return; } // cork sends into one large package #ifdef __linux int cork = 1; setsockopt(fd, IPPROTO_TCP, TCP_CORK, &cork, sizeof(int)); #endif Parser::consume(socketData->spillLength + received, src, socketData, p); #ifdef __linux cork = 0; setsockopt(fd, IPPROTO_TCP, TCP_CORK, &cork, sizeof(int)); #endif }
void Connection::on_connect(Connector* connector) { Connection* connection = static_cast<Connection*>(connector->data()); if (!connection->connect_timer_.is_running()) { return; // Timed out } if (connector->status() == 0) { LOG_DEBUG("Connected to host %s on connection(%p)", connection->host_->address_string().c_str(), static_cast<void*>(connection)); #ifdef HAVE_NOSIGPIPE // This must be done after connection for the socket file descriptor to be // valid. uv_os_fd_t fd = 0; int enabled = 1; if (uv_fileno(copy_cast<uv_tcp_t*, uv_handle_t*>(&connection->socket_), &fd) != 0 || setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&enabled, sizeof(int)) != 0) { LOG_WARN("Unable to set socket option SO_NOSIGPIPE for host %s", connection->host_->address_string().c_str()); } #endif if (connection->ssl_session_) { uv_read_start(copy_cast<uv_tcp_t*, uv_stream_t*>(&connection->socket_), Connection::alloc_buffer_ssl, Connection::on_read_ssl); } else { uv_read_start(copy_cast<uv_tcp_t*, uv_stream_t*>(&connection->socket_), Connection::alloc_buffer, Connection::on_read); } connection->set_state(CONNECTION_STATE_CONNECTED); if (connection->ssl_session_) { connection->ssl_handshake(); } else { connection->on_connected(); } } else { connection->notify_error("Connect error '" + std::string(UV_ERRSTR(connector->status(), connection->loop_)) + "'"); } }
/* * TTY control */ static void tty_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { /* Set output streams */ FILE *out = stdout, *outerr = stderr; uv_os_fd_t stream_fd = 0; uv_fileno((uv_handle_t *)stream, &stream_fd); if (stream_fd != STDIN_FILENO) { if (nread <= 0) { /* Close if disconnected */ uv_close((uv_handle_t *)stream, (uv_close_cb) free); return; } uv_os_fd_t dup_fd = dup(stream_fd); if (dup_fd >= 0) { out = outerr = fdopen(dup_fd, "w"); } } /* Execute */ if (stream && buf && nread > 0) { char *cmd = buf->base; if (cmd[nread - 1] == '\n') { cmd[nread - 1] = '\0'; } struct engine *engine = stream->data; lua_State *L = engine->L; int ret = engine_cmd(engine, cmd); const char *message = ""; if (lua_gettop(L) > 0) { message = lua_tostring(L, -1); } if (stream_fd != STDIN_FILENO) { fprintf(stdout, "%s\n", cmd); /* Duplicate command to logs */ fprintf(out, "%s\n> ", message); /* Duplicate output to sender */ } fprintf(ret ? stderr : stdout, "%s\n> ", message); lua_settop(L, 0); free(buf->base); } fflush(out); /* Close if redirected */ if (stream_fd != STDIN_FILENO) { fclose(out); /* outerr is the same */ } }
static PyObject * Stream_func_fileno(Stream *self) { int err; uv_os_fd_t fd; RAISE_IF_HANDLE_NOT_INITIALIZED(self, NULL); RAISE_IF_HANDLE_CLOSED(self, PyExc_HandleClosedError, NULL); err = uv_fileno(UV_HANDLE(self), &fd); if (err < 0) { RAISE_STREAM_EXCEPTION(err, UV_HANDLE(self)); return NULL; } /* us_os_fd_t is a HANDLE on Windows which is a 64-bit data type but which * is guaranteed to contain only values < 2^24. * For more information, see: http://www.viva64.com/en/k/0005/ */ return PyInt_FromLong((long) fd); }
WebSocket::Address WebSocket::getAddress() { uv_os_fd_t fd; uv_fileno((uv_handle_t *) p, &fd); sockaddr_storage addr; socklen_t addrLength = sizeof(addr); getpeername(fd, (sockaddr *) &addr, &addrLength); static __thread char buf[INET6_ADDRSTRLEN]; if (addr.ss_family == AF_INET) { sockaddr_in *ipv4 = (sockaddr_in *) &addr; inet_ntop(AF_INET, &ipv4->sin_addr, buf, sizeof(buf)); return {ntohs(ipv4->sin_port), buf, "IPv4"}; } else { sockaddr_in6 *ipv6 = (sockaddr_in6 *) &addr; inet_ntop(AF_INET6, &ipv6->sin6_addr, buf, sizeof(buf)); return {ntohs(ipv6->sin6_port), buf, "IPv6"}; } }
int uv__getsockpeername(const uv_handle_t* handle, uv__peersockfunc func, struct sockaddr* name, int* namelen, int delayed_error) { int result; uv_os_fd_t fd; result = uv_fileno(handle, &fd); if (result != 0) return result; if (delayed_error) return uv_translate_sys_error(delayed_error); result = func((SOCKET) fd, name, namelen); if (result != 0) return uv_translate_sys_error(WSAGetLastError()); return 0; }
static int luv_spawn(lua_State* L) { uv_process_t* handle; uv_process_options_t options; size_t i, len = 0; int ret; memset(&options, 0, sizeof(options)); options.exit_cb = exit_cb; options.file = luaL_checkstring(L, 1); options.flags = 0; // Make sure the 2nd argument is a table luaL_checktype(L, 2, LUA_TTABLE); // get the args list lua_getfield(L, 2, "args"); // +1 for inserted command at front if (lua_type(L, -1) == LUA_TTABLE) { len = 1 + lua_rawlen(L, -1); } else if (lua_type(L, -1) != LUA_TNIL) { luv_clean_options(&options); return luaL_argerror(L, 3, "args option must be table"); } else { len = 1; } // +1 for null terminator at end options.args = malloc((len + 1) * sizeof(*options.args)); if (!options.args) { luv_clean_options(&options); return luaL_error(L, "Problem allocating args"); } options.args[0] = (char*)options.file; for (i = 1; i < len; ++i) { lua_rawgeti(L, -1, i); options.args[i] = (char*)lua_tostring(L, -1); lua_pop(L, 1); } options.args[len] = NULL; lua_pop(L, 1); // get the stdio list lua_getfield(L, 2, "stdio"); if (lua_type(L, -1) == LUA_TTABLE) { options.stdio_count = len = lua_rawlen(L, -1); options.stdio = malloc(len * sizeof(*options.stdio)); if (!options.stdio) { luv_clean_options(&options); return luaL_error(L, "Problem allocating stdio"); } for (i = 0; i < len; ++i) { lua_rawgeti(L, -1, i + 1); // integers are assumed to be file descripters if (lua_type(L, -1) == LUA_TNUMBER) { options.stdio[i].flags = UV_INHERIT_FD; options.stdio[i].data.fd = lua_tointeger(L, -1); } // userdata is assumed to be a uv_stream_t instance else if (lua_type(L, -1) == LUA_TUSERDATA) { uv_os_fd_t fd; uv_stream_t* stream = luv_check_stream(L, -1); int err = uv_fileno((uv_handle_t*)stream, &fd); if (err == UV_EINVAL || err == UV_EBADF) { options.stdio[i].flags = UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE; } else { options.stdio[i].flags = UV_INHERIT_STREAM; } options.stdio[i].data.stream = stream; } else if (lua_type(L, -1) == LUA_TNIL) { options.stdio[i].flags = UV_IGNORE; } else { luv_clean_options(&options); return luaL_argerror(L, 2, "stdio table entries must be nil, uv_stream_t, or integer"); } lua_pop(L, 1); } } else if (lua_type(L, -1) != LUA_TNIL) { luv_clean_options(&options); return luaL_argerror(L, 2, "stdio option must be table"); } lua_pop(L, 1); // Get the env lua_getfield(L, 2, "env"); if (lua_type(L, -1) == LUA_TTABLE) { len = lua_rawlen(L, -1); options.env = malloc((len + 1) * sizeof(*options.env)); if (!options.env) { luv_clean_options(&options); return luaL_error(L, "Problem allocating env"); } for (i = 0; i < len; ++i) { lua_rawgeti(L, -1, i + 1); options.env[i] = (char*)lua_tostring(L, -1); lua_pop(L, 1); } options.env[len] = NULL; } else if (lua_type(L, -1) != LUA_TNIL) { luv_clean_options(&options); return luaL_argerror(L, 2, "env option must be table"); } lua_pop(L, 1); // Get the cwd lua_getfield(L, 2, "cwd"); if (lua_type(L, -1) == LUA_TSTRING) { options.cwd = (char*)lua_tostring(L, -1); } else if (lua_type(L, -1) != LUA_TNIL) { luv_clean_options(&options); return luaL_argerror(L, 2, "cwd option must be string"); } lua_pop(L, 1); // Check for uid lua_getfield(L, 2, "uid"); if (lua_type(L, -1) == LUA_TNUMBER) { options.uid = lua_tointeger(L, -1); options.flags |= UV_PROCESS_SETUID; } else if (lua_type(L, -1) != LUA_TNIL) { luv_clean_options(&options); return luaL_argerror(L, 2, "uid option must be number"); } lua_pop(L, 1); // Check for gid lua_getfield(L, 2, "gid"); if (lua_type(L, -1) == LUA_TNUMBER) { options.gid = lua_tointeger(L, -1); options.flags |= UV_PROCESS_SETGID; } else if (lua_type(L, -1) != LUA_TNIL) { luv_clean_options(&options); return luaL_argerror(L, 2, "gid option must be number"); } lua_pop(L, 1); // Check for the boolean flags lua_getfield(L, 2, "verbatim"); if (lua_toboolean(L, -1)) { options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; } lua_pop(L, 1); lua_getfield(L, 2, "detached"); if (lua_toboolean(L, -1)) { options.flags |= UV_PROCESS_DETACHED; } lua_pop(L, 1); lua_getfield(L, 2, "hide"); if (lua_toboolean(L, -1)) { options.flags |= UV_PROCESS_WINDOWS_HIDE; } lua_pop(L, 1); handle = lua_newuserdata(L, sizeof(*handle)); handle->type = UV_PROCESS; handle->data = luv_setup_handle(L); if (!lua_isnoneornil(L, 3)) { luv_check_callback(L, handle->data, LUV_EXIT, 3); } ret = uv_spawn(luv_loop(L), handle, &options); luv_clean_options(&options); if (ret < 0) { /* The async callback is required here because luajit GC may reclaim the * luv handle before libuv is done closing it down. */ uv_close((uv_handle_t*)handle, luv_spawn_close_cb); return luv_error(L, ret); } lua_pushinteger(L, handle->pid); return 2; }
void WebSocket::close(bool force, unsigned short code, char *data, size_t length) { uv_os_fd_t fd; uv_fileno((uv_handle_t *) p, &fd); SocketData *socketData = (SocketData *) p->data; if (socketData->state != CLOSING) { socketData->state = CLOSING; if (socketData->prev == socketData->next) { socketData->server->clients = nullptr; } else { if (socketData->prev) { ((SocketData *) socketData->prev->data)->next = socketData->next; } else { socketData->server->clients = socketData->next; } if (socketData->next) { ((SocketData *) socketData->next->data)->prev = socketData->prev; } } // reuse prev as timer, mark no timer set socketData->prev = nullptr; // call disconnection callback on first close (graceful or force) socketData->server->disconnectionCallback(p, code, data, length); } else if (!force) { std::cerr << "WARNING: Already gracefully closed: " << p << std::endl; return; } if (force) { // delete all messages in queue while (!socketData->messageQueue.empty()) { SocketData::Queue::Message *message = socketData->messageQueue.front(); if (message->callback) { message->callback(nullptr, message->callbackData, true); } socketData->messageQueue.pop(); } uv_poll_stop(p); uv_close((uv_handle_t *) p, [](uv_handle_t *handle) { delete (uv_poll_t *) handle; }); ::close(fd); SSL_free(socketData->ssl); socketData->controlBuffer.clear(); // cancel force close timer if (socketData->prev) { uv_timer_stop((uv_timer_t *) socketData->prev); uv_close((uv_handle_t *) socketData->prev, [](uv_handle_t *handle) { delete (uv_timer_t *) handle; }); } delete socketData->pmd; delete socketData; } else { // force close after 15 seconds socketData->prev = (uv_poll_t *) new uv_timer_t; uv_timer_init(socketData->server->loop, (uv_timer_t *) socketData->prev); ((uv_timer_t *) socketData->prev)->data = p; uv_timer_start((uv_timer_t *) socketData->prev, [](uv_timer_t *timer) { WebSocket((uv_poll_t *) timer->data).close(true, 1006); }, 15000, 0); char *sendBuffer = socketData->server->sendBuffer; if (code) { length = std::min<size_t>(1024, length) + 2; *((uint16_t *) &sendBuffer[length + 2]) = htons(code); memcpy(&sendBuffer[length + 4], data, length - 2); } write(sendBuffer, formatMessage(sendBuffer, &sendBuffer[length + 2], length, CLOSE, length), false, [](WebSocket webSocket, void *data, bool cancelled) { uv_os_fd_t fd; uv_fileno((uv_handle_t *) webSocket.p, &fd); SocketData *socketData = (SocketData *) webSocket.p->data; if (socketData->ssl) { SSL_shutdown(socketData->ssl); } shutdown(fd, SHUT_WR); }); } }
// async Unix send (has a Message struct in the start if transferOwnership) void WebSocket::write(char *data, size_t length, bool transferOwnership, void(*callback)(WebSocket webSocket, void *data, bool cancelled), void *callbackData) { uv_os_fd_t fd; uv_fileno((uv_handle_t *) p, &fd); ssize_t sent = 0; SocketData *socketData = (SocketData *) p->data; if (!socketData->messageQueue.empty()) { goto queueIt; } if (socketData->ssl) { sent = SSL_write(socketData->ssl, data, length); } else { sent = ::send(fd, data, length, MSG_NOSIGNAL); } if (sent == (int) length) { // everything was sent in one go! if (transferOwnership) { delete [] (data - sizeof(SocketData::Queue::Message)); } if (callback) { callback(p, callbackData, false); } } else { // not everything was sent if (sent == -1) { // check to see if any error occurred if (socketData->ssl) { int error = SSL_get_error(socketData->ssl, sent); if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) { goto queueIt; } } else { #ifdef _WIN32 if (WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEWOULDBLOCK) { #else if (errno == EAGAIN || errno == EWOULDBLOCK) { #endif goto queueIt; } } // error sending! if (transferOwnership) { delete [] (data - sizeof(SocketData::Queue::Message)); } return; } else { queueIt: sent = std::max<ssize_t>(sent, 0); // queue the rest of the message! SocketData::Queue::Message *messagePtr; if (transferOwnership) { messagePtr = (SocketData::Queue::Message *) (data - sizeof(SocketData::Queue::Message)); messagePtr->data = data + sent; messagePtr->length = length - sent; messagePtr->nextMessage = nullptr; } else { // we need to copy the buffer messagePtr = (SocketData::Queue::Message *) new char[sizeof(SocketData::Queue::Message) + length - sent]; messagePtr->length = length - sent; messagePtr->data = ((char *) messagePtr) + sizeof(SocketData::Queue::Message); messagePtr->nextMessage = nullptr; memcpy(messagePtr->data, data + sent, messagePtr->length); } messagePtr->callback = callback; messagePtr->callbackData = callbackData; ((SocketData *) p->data)->messageQueue.push(messagePtr); // only start this if we just broke the 0 queue size! uv_poll_start(p, UV_WRITABLE | UV_READABLE, [](uv_poll_t *handle, int status, int events) { // handle all poll errors with forced disconnection if (status < 0) { WebSocket(handle).close(true, 1006); return; } // handle reads if available if (events & UV_READABLE) { onReadable(handle, status, events); if (!(events & UV_WRITABLE)) { return; } } SocketData *socketData = (SocketData *) handle->data; if (socketData->state == CLOSING) { if (uv_is_closing((uv_handle_t *) handle)) { return; } else { uv_poll_start(handle, UV_READABLE, onReadable); } } uv_os_fd_t fd; uv_fileno((uv_handle_t *) handle, &fd); do { SocketData::Queue::Message *messagePtr = socketData->messageQueue.front(); ssize_t sent; if (socketData->ssl) { sent = SSL_write(socketData->ssl, messagePtr->data, messagePtr->length); } else { sent = ::send(fd, messagePtr->data, messagePtr->length, MSG_NOSIGNAL); } if (sent == (int) messagePtr->length) { if (messagePtr->callback) { messagePtr->callback(handle, messagePtr->callbackData, false); } socketData->messageQueue.pop(); } else { if (sent == -1) { // check to see if any error occurred if (socketData->ssl) { int error = SSL_get_error(socketData->ssl, sent); if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) { return; } } else { #ifdef _WIN32 if (WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEWOULDBLOCK) { #else if (errno == EAGAIN || errno == EWOULDBLOCK) { #endif return; } } // error sending! uv_poll_start(handle, UV_READABLE, onReadable); return; } else { // update the Message messagePtr->data += sent; messagePtr->length -= sent; return; } } } while (!socketData->messageQueue.empty()); // only receive when we have fully sent everything uv_poll_start(handle, UV_READABLE, onReadable); }); } }
void uv_custom_poll_cb(uv_poll_t *req, int status, int events) { /* * Make sure we execute in the main thread */ const uv_thread_t pth_cur_id = uv_thread_self(); assert(uv_thread_equal(&pth_main_id, &pth_cur_id)); struct uv_custom_poll_t *custom_poll_data = NULL; struct iobuf_t *send_io = NULL; char buffer[BUFFER_SIZE]; uv_os_fd_t fd = 0; long int fromlen = 0; int r = 0, n = 0; custom_poll_data = req->data; if(custom_poll_data == NULL) { uv_poll_stop(req); return; } /* * Status == -9: Socket is unreachable * Events == 0: Client-end got disconnected */ r = uv_fileno((uv_handle_t *)req, &fd); if(status < 0 || events == 0) { if(status == -9) { logprintf(LOG_ERR, "uv_custom_poll_cb: socket not responding"); } else { logprintf(LOG_ERR, "uv_custom_poll_cb: %s", uv_strerror(status)); } if(custom_poll_data->close_cb != NULL) { custom_poll_data->close_cb(req); } if(!uv_is_closing((uv_handle_t *)req)) { uv_poll_stop(req); } if(fd > 0) { close(fd); } return; } custom_poll_data->started = 1; send_io = &custom_poll_data->send_iobuf; memset(&buffer, 0, BUFFER_SIZE); if(uv_is_closing((uv_handle_t *)req)) { return; } r = uv_fileno((uv_handle_t *)req, &fd); if(r != 0) { logprintf(LOG_ERR, "uv_fileno: %s", uv_strerror(r)); return; } if(custom_poll_data->is_ssl == 1 && custom_poll_data->ssl.init == 0) { custom_poll_data->ssl.init = 1; struct mbedtls_ssl_config *ssl_conf = &ssl_client_conf; if(custom_poll_data->is_server == 1) { custom_poll_data->ssl.handshake = 1; ssl_conf = &ssl_server_conf; } if((r = mbedtls_ssl_setup(&custom_poll_data->ssl.ctx, ssl_conf)) < 0) { mbedtls_strerror(r, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_ERR, "mbedtls_ssl_setup: %s", buffer); FREE(req); return; } if((r = mbedtls_ssl_session_reset(&custom_poll_data->ssl.ctx)) < 0) { mbedtls_strerror(r, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_ERR, "mbedtls_ssl_session_reset: %s", buffer); FREE(req); return; } // mbedtls_debug_set_threshold(2); mbedtls_ssl_set_bio(&custom_poll_data->ssl.ctx, &fd, mbedtls_net_send, mbedtls_net_recv, NULL); mbedtls_ssl_conf_dbg(ssl_conf, my_debug, stdout); if(custom_poll_data->host != NULL) { mbedtls_ssl_set_hostname(&custom_poll_data->ssl.ctx, custom_poll_data->host); } } if(custom_poll_data->is_ssl == 1 && custom_poll_data->ssl.handshake == 0) { n = mbedtls_ssl_handshake(&custom_poll_data->ssl.ctx); if(n == MBEDTLS_ERR_SSL_WANT_READ) { custom_poll_data->doread = 1; custom_poll_data->dowrite = 0; goto end; } else if(n == MBEDTLS_ERR_SSL_WANT_WRITE) { /*LCOV_EXCL_START*/ custom_poll_data->dowrite = 1; custom_poll_data->doread = 0; goto end; /*LCOV_EXCL_STOP*/ } if(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { } else if(n < 0) { /*LCOV_EXCL_START*/ mbedtls_strerror(n, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_NOTICE, "mbedtls_ssl_handshake: %s", buffer); uv_poll_stop(req); return; /*LCOV_EXCL_STOP*/ } else { custom_poll_data->ssl.handshake = 1; } custom_poll_data->dowrite = 1; goto end; } if(events & UV_WRITABLE) { if(send_io->len > 0) { if(custom_poll_data->is_ssl == 1) { n = mbedtls_ssl_write(&custom_poll_data->ssl.ctx, (unsigned char *)send_io->buf, send_io->len); if(n == MBEDTLS_ERR_SSL_WANT_READ) { /*LCOV_EXCL_START*/ custom_poll_data->doread = 1; custom_poll_data->dowrite = 0; goto end; /*LCOV_EXCL_STOP*/ } else if(n == MBEDTLS_ERR_SSL_WANT_WRITE) { /*LCOV_EXCL_START*/ custom_poll_data->dowrite = 1; custom_poll_data->doread = 0; goto end; /*LCOV_EXCL_STOP*/ } if(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { } else if(n < 0) { /*LCOV_EXCL_START*/ mbedtls_strerror(n, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_NOTICE, "mbedtls_ssl_handshake: %s", buffer); uv_poll_stop(req); return; /*LCOV_EXCL_STOP*/ } } else { n = (int)send((unsigned int)fd, send_io->buf, send_io->len, 0); } if(n > 0) { iobuf_remove(send_io, n); if(send_io->len > 0) { custom_poll_data->dowrite = 1; } else { custom_poll_data->dowrite = 0; if(custom_poll_data->doclose == 1 && send_io->len == 0) { custom_poll_data->doread = 0; goto end; } else { custom_poll_data->dowrite = 0; if(custom_poll_data->write_cb != NULL) { custom_poll_data->write_cb(req); } } } } else if(n == 0) { } else if(custom_poll_data->is_ssl == 0 && n < 0 && errno != EAGAIN && errno != EINTR) { if(errno == ECONNRESET) { uv_poll_stop(req); return; } else { uv_poll_stop(req); return; } } } else { custom_poll_data->dowrite = 0; if(custom_poll_data->doclose == 1 && send_io->len == 0) { custom_poll_data->doread = 0; goto end; } else { custom_poll_data->dowrite = 0; if(custom_poll_data->write_cb != NULL) { custom_poll_data->write_cb(req); } } } } if(send_io->len > 0) { custom_poll_data->dowrite = 1; } if(events & UV_READABLE) { if(custom_poll_data->is_ssl == 1) { n = mbedtls_ssl_read(&custom_poll_data->ssl.ctx, (unsigned char *)buffer, BUFFER_SIZE); if(n == MBEDTLS_ERR_SSL_WANT_READ) { custom_poll_data->doread = 1; custom_poll_data->dowrite = 0; goto end; } else if(n == MBEDTLS_ERR_SSL_WANT_WRITE) { /*LCOV_EXCL_START*/ custom_poll_data->dowrite = 1; custom_poll_data->doread = 0; goto end; /*LCOV_EXCL_STOP*/ } else if(n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { custom_poll_data->doread = 0; if(custom_poll_data->read_cb != NULL) { custom_poll_data->read_cb(req, &custom_poll_data->recv_iobuf.len, custom_poll_data->recv_iobuf.buf); } } if(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { } else if(n < 0) { if(n == MBEDTLS_ERR_NET_RECV_FAILED) { /* * FIXME: New client not yet accepted */ if(custom_poll_data->read_cb != NULL) { one = 1; custom_poll_data->read_cb(req, &one, NULL); } } else if(n != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { mbedtls_strerror(n, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_NOTICE, "mbedtls_ssl_handshake: %s", buffer); uv_poll_stop(req); } return; } } else { if(custom_poll_data->custom_recv == 0) { if(custom_poll_data->is_udp == 1) { n = (int)recv((unsigned int)fd, buffer, BUFFER_SIZE, 0); } else { #ifdef _WIN32 n = recvfrom((SOCKET)fd, buffer, BUFFER_SIZE, 0, NULL, (socklen_t *)&fromlen); #else n = recvfrom(fd, buffer, BUFFER_SIZE, 0, NULL, (socklen_t *)&fromlen); #endif } } } if(custom_poll_data->custom_recv == 0) { if(n > 0) { iobuf_append(&custom_poll_data->recv_iobuf, buffer, n); custom_poll_data->doread = 0; if(custom_poll_data->read_cb != NULL) { custom_poll_data->read_cb(req, &custom_poll_data->recv_iobuf.len, custom_poll_data->recv_iobuf.buf); } } else if(n < 0 && errno != EINTR) { #ifdef _WIN32 switch(WSAGetLastError()) { case WSAENOTCONN: if(custom_poll_data->read_cb != NULL) { one = 1; custom_poll_data->read_cb(req, &one, NULL); } break; case WSAEWOULDBLOCK: #else switch(errno) { case ENOTCONN: if(custom_poll_data->read_cb != NULL) { one = 1; custom_poll_data->read_cb(req, &one, NULL); } break; #if defined EAGAIN case EAGAIN: #endif #if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif #endif custom_poll_data->doread = 1; break; default: break; } /* * Client was disconnected */ } else if(n == 0) { custom_poll_data->doclose = 1; custom_poll_data->doread = 0; goto end; } } else { custom_poll_data->doread = 0; if(custom_poll_data->read_cb != NULL) { zero = 0; custom_poll_data->read_cb(req, &zero, NULL); } } }
void dfk_http(dfk_coro_t* coro, dfk_tcp_socket_t* sock, dfk_http_t* http) { DFK_UNUSED(coro); assert(sock); assert(http); dfk_t* dfk = coro->dfk; { uv_os_fd_t fd; uv_fileno((uv_handle_t*) &sock->_socket, &fd); struct linger l = { .l_onoff = 1, .l_linger = 10 }; setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); } /* Arena for per-connection data */ dfk_arena_t connection_arena; dfk_arena_init(&connection_arena, http->dfk); DFK_DBG(dfk, "{%p} initialize connection arena %p", (void*) sock, (void*) &connection_arena); /* Requests processed within this connection */ ssize_t nrequests = 0; int keepalive = 1; while (keepalive) { /* Arena for per-request data */ dfk_arena_t request_arena; dfk_arena_init(&request_arena, http->dfk); DFK_DBG(dfk, "{%p} initialize request arena %p", (void*) sock, (void*) &request_arena); dfk_http_request_t req; /** @todo check return value */ dfk_http_request_init(&req, http, &request_arena, &connection_arena, sock); int err = dfk_http_request_read_headers(&req); if (err != dfk_err_ok) { DFK_ERROR(dfk, "{%p} dfk_http_request_read_headers failed with %s", (void*) http, dfk_strerr(dfk, err)); keepalive = 0; goto cleanup; } DFK_DBG(http->dfk, "{%p} request parsing done, " "%s %.*s HTTP/%hu.%hu \"%.*s\"", (void*) http, http_method_str((enum http_method) req.method), (int) req.url.size, req.url.data, req.major_version, req.minor_version, (int) req.user_agent.size, req.user_agent.data); /* Determine requested connection type */ dfk_buf_t connection = dfk_strmap_get(&req.headers, DFK_HTTP_CONNECTION, sizeof(DFK_HTTP_CONNECTION) - 1); if (connection.size) { if (!strncmp(connection.data, "close", DFK_MIN(connection.size, 5))) { keepalive = 0; } if (!strncmp(connection.data, "Keep-Alive", DFK_MIN(connection.size, 10))) { keepalive = 1; } } else { keepalive = !(req.major_version == 1 && req.minor_version == 0); } DFK_DBG(http->dfk, "{%p} client requested %skeepalive connection", (void*) http, keepalive ? "" : "not "); dfk_http_response_t resp; /** @todo check return value */ dfk_http_response_init(&resp, &req, &request_arena, &connection_arena, sock, keepalive); DFK_DBG(http->dfk, "{%p} run request handler", (void*) http); int hres = http->_handler(http->_handler_ud, http, &req, &resp); DFK_INFO(http->dfk, "{%p} http handler returned %s", (void*) http, dfk_strerr(http->dfk, hres)); if (hres != dfk_err_ok) { resp.status = DFK_HTTP_INTERNAL_SERVER_ERROR; } /* Fix request handler possible protocol violations */ if (req.major_version < resp.major_version) { DFK_WARNING(http->dfk, "{%p} request handler attempted HTTP version upgrade from %d.%d to %d.%d", (void*) http, req.major_version, req.minor_version, resp.major_version, resp.minor_version); resp.major_version = req.major_version; resp.minor_version = req.minor_version; } else if (req.minor_version < resp.minor_version) { DFK_WARNING(http->dfk, "{%p} request handler attempted HTTP version upgrade from %d.%d to %d.%d", (void*) http, req.major_version, req.minor_version, resp.major_version, resp.minor_version); resp.minor_version = req.minor_version; } if (!keepalive && resp.keepalive) { DFK_WARNING(http->dfk, "{%p} request handler enabled keepalive, although " "client have not requested", (void*) http); } /* * Keep connection alive only if it was requested by the client and request * handler did not refuse. */ keepalive = keepalive && resp.keepalive; if (keepalive && http->keepalive_requests >= 0 && nrequests + 1 >= http->keepalive_requests) { DFK_INFO(http->dfk, "{%p} maximum number of keepalive requests (%llu) " "for connection {%p} has reached, close connection", (void*) http, (unsigned long long) http->keepalive_requests, (void*) sock); keepalive = 0; } #if DFK_DEBUG { dfk_buf_t connection = dfk_strmap_get(&req.headers, DFK_HTTP_CONNECTION, sizeof(DFK_HTTP_CONNECTION) - 1); if (connection.size) { DFK_WARNING(http->dfk, "{%p} manually set header \"" DFK_HTTP_CONNECTION " : %.*s\" will be overwritten" " use dfk_http_response_t.keepalive instead", (void*) http, (int) connection.size, connection.data); } } #endif if (!keepalive) { dfk_http_response_set(&resp, DFK_HTTP_CONNECTION, sizeof(DFK_HTTP_CONNECTION) - 1, "close", 5); } else { dfk_http_response_set(&resp, DFK_HTTP_CONNECTION, sizeof(DFK_HTTP_CONNECTION) - 1, "Keep-Alive", 10); } /** @todo remove kludge */ dfk_http_response_set(&resp, "Content-Length", 14, "0", 1); dfk_http_response_flush_headers(&resp); /* * If request handler hasn't read all bytes of the body, we have to * skip them at this point. */ if (req.content_length > 0 && req._body_nread < req.content_length) { size_t bytesremain = req.content_length - req._body_nread; DFK_DBG(dfk, "{%p} bytes read by handler %llu, need to flush %llu", (void*) http, (unsigned long long ) req._body_nread, (unsigned long long) bytesremain); char buf[1024]; while (bytesremain) { ssize_t nread = dfk_http_request_read(&req, buf, DFK_MIN(bytesremain, sizeof(buf))); if (nread < 0) { DFK_ERROR(dfk, "{%p} failed to flush request body", (void*) http); keepalive = 0; goto cleanup; } bytesremain -= nread; } } ++nrequests; cleanup: DFK_DBG(dfk, "{%p} cleaup per-request resources", (void*) http); /** @todo check for return value */ dfk_http_response_free(&resp); dfk_http_request_free(&req); dfk_arena_free(&request_arena); } dfk_arena_free(&connection_arena); DFK_DBG(dfk, "{%p} wait for client to close connection", (void*) http); DFK_CALL_RVOID(dfk, dfk_tcp_socket_shutdown(sock)); int err = dfk_tcp_socket_wait_disconnect(sock, 1000); if (err == dfk_err_timeout) { DFK_WARNING(dfk, "{%p} client has not closed connection, force close", (void*) http); } if (err != dfk_err_ok) { DFK_ERROR(dfk, "{%p} dfk_tcp_socket_wait_disconnect returned %s", (void*) http, dfk_strerr(dfk, err)); } DFK_DBG(http->dfk, "{%p} close socket", (void*) http); DFK_CALL_RVOID(http->dfk, dfk_tcp_socket_close(sock)); }