static int tcp_bind_finalize(uv_handle_t *handle) { /* TCP_FASTOPEN enables 1 RTT connection resumptions. */ #ifdef TCP_FASTOPEN # ifdef __linux__ (void) set_tcp_option(handle, TCP_FASTOPEN, 16); /* Accepts queue length hint */ # else (void) set_tcp_option(handle, TCP_FASTOPEN, 1); /* Accepts on/off */ # endif #endif handle->data = NULL; return 0; }
static int _tcp_bind(uv_tcp_t *handle, struct sockaddr *addr, uv_connection_cb connection) { unsigned flags = 0; if (addr->sa_family == AF_INET6) { flags |= UV_TCP_IPV6ONLY; } int ret = uv_tcp_bind(handle, addr, flags); if (ret != 0) { return ret; } /* TCP_DEFER_ACCEPT delays accepting connections until there is readable data. */ #ifdef TCP_DEFER_ACCEPT if (set_tcp_option((uv_handle_t *)handle, TCP_DEFER_ACCEPT, KR_CONN_RTT_MAX/1000) != 0) { kr_log_info("[ io ] tcp_bind (defer_accept): %s\n", strerror(errno)); } #endif ret = uv_listen((uv_stream_t *)handle, 16, connection); if (ret != 0) { return ret; } return tcp_bind_finalize((uv_handle_t *)handle); }
void eStreamClient::notifier(int what) { if (!(what & eSocketNotifier::Read)) return; ePtr<eStreamClient> ref = this; char buf[512]; int len; if ((len = singleRead(streamFd, buf, sizeof(buf))) <= 0) { rsn->stop(); stop(); parent->connectionLost(this); return; } request.append(buf, len); if (running || (request.find('\n') == std::string::npos)) return; if (request.substr(0, 5) == "GET /") { size_t pos; size_t posdur; if (eConfigManager::getConfigBoolValue("config.streaming.authentication")) { bool authenticated = false; if ((pos = request.find("Authorization: Basic ")) != std::string::npos) { std::string authentication, username, password; std::string hash = request.substr(pos + 21); pos = hash.find('\r'); hash = hash.substr(0, pos); authentication = base64decode(hash); pos = authentication.find(':'); if (pos != std::string::npos) { char *buffer = (char*)malloc(4096); if (buffer) { struct passwd pwd; struct passwd *pwdresult = NULL; std::string crypt; username = authentication.substr(0, pos); password = authentication.substr(pos + 1); getpwnam_r(username.c_str(), &pwd, buffer, 4096, &pwdresult); if (pwdresult) { struct crypt_data cryptdata; char *cryptresult = NULL; cryptdata.initialized = 0; crypt = pwd.pw_passwd; if (crypt == "*" || crypt == "x") { struct spwd spwd; struct spwd *spwdresult = NULL; getspnam_r(username.c_str(), &spwd, buffer, 4096, &spwdresult); if (spwdresult) { crypt = spwd.sp_pwdp; } } cryptresult = crypt_r(password.c_str(), crypt.c_str(), &cryptdata); authenticated = cryptresult && cryptresult == crypt; } free(buffer); } } } if (!authenticated) { const char *reply = "HTTP/1.0 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"streamserver\"\r\n\r\n"; writeAll(streamFd, reply, strlen(reply)); rsn->stop(); parent->connectionLost(this); return; } } pos = request.find(' ', 5); if (pos != std::string::npos) { std::string serviceref = urlDecode(request.substr(5, pos - 5)); if (!serviceref.empty()) { const char *reply = "HTTP/1.0 200 OK\r\nConnection: Close\r\nContent-Type: video/mpeg\r\nServer: streamserver\r\n\r\n"; writeAll(streamFd, reply, strlen(reply)); /* We don't expect any incoming data, so set a tiny buffer */ set_socket_option(streamFd, SO_RCVBUF, 1 * 1024); /* We like 188k packets, so set the TCP window size to that */ set_socket_option(streamFd, SO_SNDBUF, 188 * 1024); /* activate keepalive */ set_socket_option(streamFd, SO_KEEPALIVE, 1); /* configure keepalive */ set_tcp_option(streamFd, TCP_KEEPINTVL, 10); // every 10 seconds set_tcp_option(streamFd, TCP_KEEPIDLE, 1); // after 1 second of idle set_tcp_option(streamFd, TCP_KEEPCNT, 2); // drop connection after second miss /* also set 10 seconds data push timeout */ set_tcp_option(streamFd, TCP_USER_TIMEOUT, 10 * 1000); if (serviceref.substr(0, 10) == "file?file=") /* convert openwebif stream reqeust back to serviceref */ serviceref = "1:0:1:0:0:0:0:0:0:0:" + serviceref.substr(10); /* Strip session ID from URL if it exists, PLi streaming can not handle it */ pos = serviceref.find("&sessionid="); if (pos != std::string::npos) { serviceref.erase(pos, std::string::npos); } pos = serviceref.find("?sessionid="); if (pos != std::string::npos) { serviceref.erase(pos, std::string::npos); } pos = serviceref.find('?'); if (pos == std::string::npos) { eDebug("[eDVBServiceStream] stream ref: %s", serviceref.c_str()); if (eDVBServiceStream::start(serviceref.c_str(), streamFd) >= 0) { running = true; m_serviceref = serviceref; m_useencoder = false; } } else { request = serviceref.substr(pos); serviceref = serviceref.substr(0, pos); pos = request.find("?bitrate="); posdur = request.find("?duration="); eDebug("[eDVBServiceStream] stream ref: %s", serviceref.c_str()); if (posdur != std::string::npos) { if (eDVBServiceStream::start(serviceref.c_str(), streamFd) >= 0) { running = true; m_serviceref = serviceref; m_useencoder = false; } int timeout = 0; sscanf(request.substr(posdur).c_str(), "?duration=%d", &timeout); eDebug("[eDVBServiceStream] duration: %d seconds", timeout); if (timeout) { m_timeout->startLongTimer(timeout); } } else if (pos != std::string::npos) { /* we need to stream transcoded data */ int bitrate = 1024 * 1024; int width = 720; int height = 576; int framerate = 25000; int interlaced = 0; int aspectratio = 0; sscanf(request.substr(pos).c_str(), "?bitrate=%d", &bitrate); pos = request.find("?width="); if (pos != std::string::npos) sscanf(request.substr(pos).c_str(), "?width=%d", &width); pos = request.find("?height="); if (pos != std::string::npos) sscanf(request.substr(pos).c_str(), "?height=%d", &height); pos = request.find("?framerate="); if (pos != std::string::npos) sscanf(request.substr(pos).c_str(), "?framerate=%d", &framerate); pos = request.find("?interlaced="); if (pos != std::string::npos) sscanf(request.substr(pos).c_str(), "?interlaced=%d", &interlaced); pos = request.find("?aspectratio="); if (pos != std::string::npos) sscanf(request.substr(pos).c_str(), "?aspectratio=%d", &aspectratio); encoderFd = -1; if (eEncoder::getInstance()) encoderFd = eEncoder::getInstance()->allocateEncoder(serviceref, bitrate, width, height, framerate, !!interlaced, aspectratio); if (encoderFd >= 0) { running = true; streamThread = new eDVBRecordStreamThread(188); if (streamThread) { streamThread->setTargetFD(streamFd); streamThread->start(encoderFd); } m_serviceref = serviceref; m_useencoder = true; } } } } } } if (!running) { const char *reply = "HTTP/1.0 400 Bad Request\r\n\r\n"; writeAll(streamFd, reply, strlen(reply)); rsn->stop(); parent->connectionLost(this); return; } request.clear(); }