struct libwebsocket *__libwebsocket_client_connect_2( struct libwebsocket_context *context, struct libwebsocket *wsi ) { struct pollfd pfd; struct hostent *server_hostent; struct sockaddr_in server_addr; int n; int plen = 0; const char *ads; lwsl_client("__libwebsocket_client_connect_2\n"); /* * proxy? */ if (context->http_proxy_port) { plen = sprintf((char *)context->service_buffer, "CONNECT %s:%u HTTP/1.0\x0d\x0a" "User-agent: libwebsockets\x0d\x0a" /*Proxy-authorization: basic aGVsbG86d29ybGQ= */ "\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), wsi->u.hdr.ah->c_port); /* OK from now on we talk via the proxy, so connect to that */ /* * (will overwrite existing pointer, * leaving old string/frag there but unreferenced) */ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, context->http_proxy_address)) goto oom4; wsi->u.hdr.ah->c_port = context->http_proxy_port; } /* * prepare the actual connection (to the proxy, if any) */ ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); lwsl_client("__libwebsocket_client_connect_2: address %s\n", ads); server_hostent = gethostbyname(ads); if (server_hostent == NULL) { lwsl_err("Unable to get host name from %s\n", ads); goto oom4; } wsi->sock = socket(AF_INET, SOCK_STREAM, 0); if (wsi->sock < 0) { lwsl_warn("Unable to open socket\n"); goto oom4; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(wsi->u.hdr.ah->c_port); server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr); bzero(&server_addr.sin_zero, 8); if (connect(wsi->sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { lwsl_debug("Connect failed\n"); compatible_close(wsi->sock); goto oom4; } lwsl_client("connected\n"); if (lws_set_socket_options(context, wsi->sock)) { lwsl_err("Failed to set wsi socket options\n"); close(wsi->sock); goto oom4; } insert_wsi_socket_into_fds(context, wsi); /* we are connected to server, or proxy */ if (context->http_proxy_port) { n = send(wsi->sock, context->service_buffer, plen, 0); if (n < 0) { compatible_close(wsi->sock); lwsl_debug("ERROR writing to proxy socket\n"); goto oom4; } libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, AWAITING_TIMEOUT); wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY; return wsi; } /* * provoke service to issue the handshake directly * we need to do it this way because in the proxy case, this is the * next state and executed only if and when we get a good proxy * response inside the state machine... but notice in SSL case this * may not have sent anything yet with 0 return, and won't until some * many retries from main loop. To stop that becoming endless, * cover with a timeout. */ libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, AWAITING_TIMEOUT); wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE; pfd.fd = wsi->sock; pfd.revents = POLLIN; n = libwebsocket_service_fd(context, &pfd); if (n < 0) goto oom4; if (n) /* returns 1 on failure after closing wsi */ return NULL; return wsi; oom4: free(wsi->u.hdr.ah); free(wsi); return NULL; }
struct libwebsocket *libwebsocket_client_connect_2( struct libwebsocket_context *context, struct libwebsocket *wsi ) { struct pollfd pfd; #ifdef LWS_USE_IPV6 struct sockaddr_in6 server_addr6; struct sockaddr_in6 client_addr6; struct addrinfo hints, *result; #endif struct sockaddr_in server_addr4; struct sockaddr_in client_addr4; struct hostent *server_hostent; struct sockaddr *v; int n; int plen = 0; const char *ads; lwsl_client("libwebsocket_client_connect_2\n"); /* * proxy? */ if (context->http_proxy_port) { plen = sprintf((char *)context->service_buffer, "CONNECT %s:%u HTTP/1.0\x0d\x0a" "User-agent: libwebsockets\x0d\x0a" /*Proxy-authorization: basic aGVsbG86d29ybGQ= */ "\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), wsi->u.hdr.ah->c_port); ads = context->http_proxy_address; #ifdef LWS_USE_IPV6 if (LWS_IPV6_ENABLED(context)) server_addr6.sin6_port = htons(context->http_proxy_port); else #endif server_addr4.sin_port = htons(context->http_proxy_port); } else { ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); #ifdef LWS_WITH_IPV6 if (LWS_IPV6_ENABLED(context)) server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port); else #endif server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port); } /* * prepare the actual connection (to the proxy, if any) */ lwsl_client("libwebsocket_client_connect_2: address %s\n", ads); #ifdef LWS_USE_IPV6 if (LWS_IPV6_ENABLED(context)) { memset(&hints, 0, sizeof(struct addrinfo)); n = getaddrinfo(ads, NULL, &hints, &result); if (n) { lwsl_err("getaddrinfo: %s\n", gai_strerror(n)); goto oom4; } server_addr6.sin6_family = AF_INET6; switch (result->ai_family) { case AF_INET: /* map IPv4 to IPv6 */ bzero((char *)&server_addr6.sin6_addr, sizeof(struct in6_addr)); server_addr6.sin6_addr.s6_addr16[5] = 0xffff; bcopy(&((struct sockaddr_in *)result->ai_addr)->sin_addr, &server_addr6.sin6_addr.s6_addr16[6], sizeof(struct in_addr)); break; case AF_INET6: memcpy(&server_addr6.sin6_addr, &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, sizeof(struct in6_addr)); break; default: lwsl_err("Unknown address family\n"); freeaddrinfo(result); goto oom4; } freeaddrinfo(result); } else #endif { server_hostent = gethostbyname(ads); if (!server_hostent) { lwsl_err("Unable to get host name from %s\n", ads); goto oom4; } server_addr4.sin_family = AF_INET; server_addr4.sin_addr = *((struct in_addr *)server_hostent->h_addr); bzero(&server_addr4.sin_zero, 8); } if (wsi->sock < 0) { #ifdef LWS_USE_IPV6 if (LWS_IPV6_ENABLED(context)) wsi->sock = socket(AF_INET6, SOCK_STREAM, 0); else #endif wsi->sock = socket(AF_INET, SOCK_STREAM, 0); if (wsi->sock < 0) { lwsl_warn("Unable to open socket\n"); goto oom4; } if (lws_set_socket_options(context, wsi->sock)) { lwsl_err("Failed to set wsi socket options\n"); compatible_close(wsi->sock); goto oom4; } wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT; insert_wsi_socket_into_fds(context, wsi); libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, AWAITING_TIMEOUT); #ifdef LWS_USE_IPV6 if (LWS_IPV6_ENABLED(context)) { v = (struct sockaddr *)&client_addr6; n = sizeof(client_addr6); bzero((char *)v, n); client_addr6.sin6_family = AF_INET6; } else #endif { v = (struct sockaddr *)&client_addr4; n = sizeof(client_addr4); bzero((char *)v, n); client_addr4.sin_family = AF_INET; } if (context->iface) { if (interface_to_sa(context, context->iface, (struct sockaddr_in *)v, n) < 0) { lwsl_err("Unable to find interface %s\n", context->iface); compatible_close(wsi->sock); goto failed; } if (bind(wsi->sock, v, n) < 0) { lwsl_err("Error binding to interface %s", context->iface); compatible_close(wsi->sock); goto failed; } } } #ifdef LWS_USE_IPV6 if (LWS_IPV6_ENABLED(context)) { v = (struct sockaddr *)&server_addr6; n = sizeof(struct sockaddr_in6); } else #endif { v = (struct sockaddr *)&server_addr4; n = sizeof(struct sockaddr); } if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) { if (LWS_ERRNO == LWS_EALREADY || LWS_ERRNO == LWS_EINPROGRESS) { lwsl_client("nonblocking connect retry\n"); /* * must do specifically a POLLOUT poll to hear * about the connect completion */ lws_change_pollfd(wsi, 0, POLLOUT); return wsi; } if (LWS_ERRNO != LWS_EISCONN) { lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO); goto failed; } } lwsl_client("connected\n"); /* we are connected to server, or proxy */ if (context->http_proxy_port) { /* OK from now on we talk via the proxy, so connect to that */ /* * (will overwrite existing pointer, * leaving old string/frag there but unreferenced) */ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, context->http_proxy_address)) goto failed; wsi->u.hdr.ah->c_port = context->http_proxy_port; n = send(wsi->sock, context->service_buffer, plen, MSG_NOSIGNAL); if (n < 0) { lwsl_debug("ERROR writing to proxy socket\n"); goto failed; } libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, AWAITING_TIMEOUT); wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY; return wsi; } /* * provoke service to issue the handshake directly * we need to do it this way because in the proxy case, this is the * next state and executed only if and when we get a good proxy * response inside the state machine... but notice in SSL case this * may not have sent anything yet with 0 return, and won't until some * many retries from main loop. To stop that becoming endless, * cover with a timeout. */ libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, AWAITING_TIMEOUT); wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE; pfd.fd = wsi->sock; pfd.revents = POLLIN; n = libwebsocket_service_fd(context, &pfd); if (n < 0) goto failed; if (n) /* returns 1 on failure after closing wsi */ return NULL; return wsi; oom4: free(wsi->u.hdr.ah); free(wsi); return NULL; failed: libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return NULL; }
void webui_start() { char cert_path[1024]; char key_path[1024]; int n = 0; int use_ssl = 0; int opts = 0; char interface_name[128] = ""; const char *iface = NULL; #ifndef WIN32 int syslog_options = LOG_PID | LOG_PERROR; #endif struct lws_context_creation_info info; int debug_level = 7; #ifndef LWS_NO_DAEMONIZE int daemonize = 0; #endif memset(&info, 0, sizeof info); info.port = 5678; const string path = WEBUI_PATH; const char* resource_path = path.c_str(); #ifndef WIN32 /* we will only try to log things according to our debug_level */ setlogmask(LOG_UPTO (LOG_DEBUG)); openlog("lwsts", syslog_options, LOG_DAEMON); #endif /* tell the library what debug level to emit and to send it to syslog */ lws_set_log_level(debug_level, lwsl_emit_syslog); lwsl_notice("libwebsockets test server - " "(C) Copyright 2010-2013 Andy Green <*****@*****.**> - " "licensed under LGPL2.1\n"); #ifdef EXTERNAL_POLL max_poll_elements = getdtablesize(); pollfds = malloc(max_poll_elements * sizeof (struct pollfd)); fd_lookup = malloc(max_poll_elements * sizeof (int)); if (pollfds == NULL || fd_lookup == NULL) { lwsl_err("Out of memory pollfds=%d\n", max_poll_elements); return -1; } #endif info.iface = iface; info.protocols = protocols; #ifndef LWS_NO_EXTENSIONS info.extensions = libwebsocket_get_internal_extensions(); #endif if (!use_ssl) { info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; } else { if (strlen(resource_path) > sizeof(cert_path) - 32) { lwsl_err("resource path too long\n"); return ; } sprintf(cert_path, "%s/libwebsockets-test-server.pem", resource_path); if (strlen(resource_path) > sizeof(key_path) - 32) { lwsl_err("resource path too long\n"); return; } sprintf(key_path, "%s/libwebsockets-test-server.key.pem", resource_path); info.ssl_cert_filepath = cert_path; info.ssl_private_key_filepath = key_path; } info.gid = -1; info.uid = -1; info.options = opts; context = libwebsocket_create_context(&info); if (context == NULL) { lwsl_err("libwebsocket init failed\n"); return ; } n = 0; double old_time = 0; while (n >= 0 && !force_exit) { double time = os_GetSeconds(); /* * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every * live websocket connection using the DUMB_INCREMENT protocol, * as soon as it can take more packets (usually immediately) */ if ((time - old_time ) > 0.050) { libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]); old_time = time; } #ifdef EXTERNAL_POLL /* * this represents an existing server's single poll action * which also includes libwebsocket sockets */ n = poll(pollfds, count_pollfds, 50); if (n < 0) continue; if (n) for (n = 0; n < count_pollfds; n++) if (pollfds[n].revents) /* * returns immediately if the fd does not * match anything under libwebsockets * control */ if (libwebsocket_service_fd(context, &pollfds[n]) < 0) goto done; #else /* * If libwebsockets sockets are all we care about, * you can use this api which takes care of the poll() * and looping through finding who needed service. * * If no socket needs service, it'll return anyway after * the number of ms in the second argument. */ n = libwebsocket_service(context, 50); #endif } #ifdef EXTERNAL_POLL done: #endif libwebsocket_context_destroy(context); lwsl_notice("libwebsockets-test-server exited cleanly\n"); #ifndef WIN32 closelog(); #endif return; }
LWS_VISIBLE int lws_plat_service(struct libwebsocket_context *context, int timeout_ms) { int n; int m; char buf; #ifdef LWS_OPENSSL_SUPPORT struct libwebsocket *wsi, *wsi_next; #endif /* stay dead once we are dead */ if (!context) return 1; lws_libev_run(context); context->service_tid = context->protocols[0].callback(context, NULL, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); #ifdef LWS_OPENSSL_SUPPORT /* if we know we have non-network pending data, do not wait in poll */ if (lws_ssl_anybody_has_buffered_read(context)) timeout_ms = 0; #endif n = poll(context->fds, context->fds_count, timeout_ms); context->service_tid = 0; #ifdef LWS_OPENSSL_SUPPORT if (!lws_ssl_anybody_has_buffered_read(context) && n == 0) { #else if (n == 0) /* poll timeout */ { #endif libwebsocket_service_fd(context, NULL); return 0; } if (n < 0) { if (LWS_ERRNO != LWS_EINTR) return -1; return 0; } #ifdef LWS_OPENSSL_SUPPORT /* * For all guys with buffered SSL read data already saved up, if they * are not flowcontrolled, fake their POLLIN status so they'll get * service to use up the buffered incoming data, even though their * network socket may have nothing */ wsi = context->pending_read_list; while (wsi) { wsi_next = wsi->pending_read_list_next; context->fds[wsi->sock].revents |= context->fds[wsi->sock].events & POLLIN; if (context->fds[wsi->sock].revents & POLLIN) { /* * he's going to get serviced now, take him off the * list of guys with buffered SSL. If he still has some * at the end of the service, he'll get put back on the * list then. */ lws_ssl_remove_wsi_from_buffered_list(context, wsi); } wsi = wsi_next; } #endif /* any socket with events to service? */ for (n = 0; n < context->fds_count; n++) { if (!context->fds[n].revents) continue; if (context->fds[n].fd == context->dummy_pipe_fds[0]) { if (read(context->fds[n].fd, &buf, 1) != 1) lwsl_err("Cannot read from dummy pipe."); continue; } m = libwebsocket_service_fd(context, &context->fds[n]); if (m < 0) return -1; /* if something closed, retry this slot */ if (m) n--; } return 0; } LWS_VISIBLE int lws_plat_set_socket_options(struct libwebsocket_context *context, int fd) { int optval = 1; socklen_t optlen = sizeof(optval); #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ defined(__OpenBSD__) struct protoent *tcp_proto; #endif if (context->ka_time) { /* enable keepalive on this socket */ optval = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&optval, optlen) < 0) return 1; #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ defined(__CYGWIN__) || defined(__OpenBSD__) /* * didn't find a way to set these per-socket, need to * tune kernel systemwide values */ #else /* set the keepalive conditions we want on it too */ optval = context->ka_time; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (const void *)&optval, optlen) < 0) return 1; optval = context->ka_interval; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (const void *)&optval, optlen) < 0) return 1; optval = context->ka_probes; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (const void *)&optval, optlen) < 0) return 1; #endif } /* Disable Nagle */ optval = 1; #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \ !defined(__OpenBSD__) if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) return 1; #else tcp_proto = getprotobyname("TCP"); if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0) return 1; #endif /* We are nonblocking... */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) return 1; return 0; }
int main(int argc, char **argv) { char cert_path[1024]; char key_path[1024]; int n = 0; int use_ssl = 0; struct libwebsocket_context *context; int opts = 0; char interface_name[128] = ""; const char *iface = NULL; #ifndef WIN32 int syslog_options = LOG_PID | LOG_PERROR; #endif unsigned int oldus = 0; struct lws_context_creation_info info; int debug_level = 7; #ifndef LWS_NO_DAEMONIZE int daemonize = 0; #endif memset(&info, 0, sizeof info); info.port = 7681; while (n >= 0) { n = getopt_long(argc, argv, "ci:hsap:d:Dr:", options, NULL); if (n < 0) continue; switch (n) { #ifndef LWS_NO_DAEMONIZE case 'D': daemonize = 1; #ifndef WIN32 syslog_options &= ~LOG_PERROR; #endif break; #endif case 'd': debug_level = atoi(optarg); break; case 's': use_ssl = 1; break; case 'a': opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; break; case 'p': info.port = atoi(optarg); break; case 'i': strncpy(interface_name, optarg, sizeof interface_name); interface_name[(sizeof interface_name) - 1] = '\0'; iface = interface_name; break; case 'c': close_testing = 1; fprintf(stderr, " Close testing mode -- closes on " "client after 50 dumb increments" "and suppresses lws_mirror spam\n"); break; case 'r': resource_path = optarg; printf("Setting resource path to \"%s\"\n", resource_path); break; case 'h': fprintf(stderr, "Usage: test-server " "[--port=<p>] [--ssl] " "[-d <log bitfield>] " "[--resource_path <path>]\n"); exit(1); } } #if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32) /* * normally lock path would be /var/lock/lwsts or similar, to * simplify getting started without having to take care about * permissions or running as root, set to /tmp/.lwsts-lock */ if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { fprintf(stderr, "Failed to daemonize\n"); return 1; } #endif signal(SIGINT, sighandler); #ifndef WIN32 /* we will only try to log things according to our debug_level */ setlogmask(LOG_UPTO (LOG_DEBUG)); openlog("lwsts", syslog_options, LOG_DAEMON); #endif /* tell the library what debug level to emit and to send it to syslog */ lws_set_log_level(debug_level, lwsl_emit_syslog); lwsl_notice("libwebsockets test server - " "(C) Copyright 2010-2013 Andy Green <*****@*****.**> - " "licensed under LGPL2.1\n"); #ifdef EXTERNAL_POLL max_poll_elements = getdtablesize(); pollfds = malloc(max_poll_elements * sizeof (struct pollfd)); fd_lookup = malloc(max_poll_elements * sizeof (int)); if (pollfds == NULL || fd_lookup == NULL) { lwsl_err("Out of memory pollfds=%d\n", max_poll_elements); return -1; } #endif info.iface = iface; info.protocols = protocols; #ifndef LWS_NO_EXTENSIONS info.extensions = libwebsocket_get_internal_extensions(); #endif if (!use_ssl) { info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; } else { if (strlen(resource_path) > sizeof(cert_path) - 32) { lwsl_err("resource path too long\n"); return -1; } sprintf(cert_path, "%s/libwebsockets-test-server.pem", resource_path); if (strlen(resource_path) > sizeof(key_path) - 32) { lwsl_err("resource path too long\n"); return -1; } sprintf(key_path, "%s/libwebsockets-test-server.key.pem", resource_path); info.ssl_cert_filepath = cert_path; info.ssl_private_key_filepath = key_path; } info.gid = -1; info.uid = -1; info.options = opts; context = libwebsocket_create_context(&info); if (context == NULL) { lwsl_err("libwebsocket init failed\n"); return -1; } n = 0; while (n >= 0 && !force_exit) { struct timeval tv; gettimeofday(&tv, NULL); /* * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every * live websocket connection using the DUMB_INCREMENT protocol, * as soon as it can take more packets (usually immediately) */ if (((unsigned int)tv.tv_usec - oldus) > 50000) { libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]); oldus = tv.tv_usec; } #ifdef EXTERNAL_POLL /* * this represents an existing server's single poll action * which also includes libwebsocket sockets */ n = poll(pollfds, count_pollfds, 50); if (n < 0) continue; if (n) for (n = 0; n < count_pollfds; n++) if (pollfds[n].revents) /* * returns immediately if the fd does not * match anything under libwebsockets * control */ if (libwebsocket_service_fd(context, &pollfds[n]) < 0) goto done; #else /* * If libwebsockets sockets are all we care about, * you can use this api which takes care of the poll() * and looping through finding who needed service. * * If no socket needs service, it'll return anyway after * the number of ms in the second argument. */ n = libwebsocket_service(context, 50); #endif } #ifdef EXTERNAL_POLL done: #endif libwebsocket_context_destroy(context); lwsl_notice("libwebsockets-test-server exited cleanly\n"); #ifndef WIN32 closelog(); #endif return 0; }
LWS_VISIBLE int lws_plat_service(struct libwebsocket_context *context, int timeout_ms) { int n; int i; DWORD ev; WSANETWORKEVENTS networkevents; struct libwebsocket_pollfd *pfd; /* stay dead once we are dead */ if (context == NULL) return 1; context->service_tid = context->protocols[0].callback(context, NULL, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); for (i = 0; i < context->fds_count; ++i) { pfd = &context->fds[i]; if (pfd->fd == context->listen_service_fd) continue; if (pfd->events & LWS_POLLOUT) { if (context->lws_lookup[pfd->fd]->sock_send_blocking) continue; pfd->revents = LWS_POLLOUT; n = libwebsocket_service_fd(context, pfd); if (n < 0) return n; } } ev = WSAWaitForMultipleEvents(context->fds_count + 1, context->events, FALSE, timeout_ms, FALSE); context->service_tid = 0; if (ev == WSA_WAIT_TIMEOUT) { libwebsocket_service_fd(context, NULL); return 0; } if (ev == WSA_WAIT_EVENT_0) { WSAResetEvent(context->events[0]); return 0; } if (ev < WSA_WAIT_EVENT_0 || ev > WSA_WAIT_EVENT_0 + context->fds_count) return -1; pfd = &context->fds[ev - WSA_WAIT_EVENT_0 - 1]; if (WSAEnumNetworkEvents(pfd->fd, context->events[ev - WSA_WAIT_EVENT_0], &networkevents) == SOCKET_ERROR) { lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO); return -1; } pfd->revents = networkevents.lNetworkEvents; if (pfd->revents & LWS_POLLOUT) context->lws_lookup[pfd->fd]->sock_send_blocking = FALSE; return libwebsocket_service_fd(context, pfd); }
struct libwebsocket * __libwebsocket_client_connect_2( struct libwebsocket_context *context, struct libwebsocket *wsi ) { struct pollfd pfd; struct timeval tv; struct hostent *server_hostent; struct sockaddr_in server_addr; int n; int plen = 0; char pkt[512]; int opt = 1; #if defined(__APPLE__) struct protoent* tcp_proto; #endif fprintf(stderr, "__libwebsocket_client_connect_2\n"); wsi->candidate_children_list = NULL; /* * proxy? */ if (context->http_proxy_port) { plen = sprintf(pkt, "CONNECT %s:%u HTTP/1.0\x0d\x0a" "User-agent: libwebsockets\x0d\x0a" /*Proxy-authorization: basic aGVsbG86d29ybGQ= */ "\x0d\x0a", wsi->c_address, wsi->c_port); /* OK from now on we talk via the proxy */ free(wsi->c_address); wsi->c_address = strdup(context->http_proxy_address); wsi->c_port = context->http_proxy_port; } /* * prepare the actual connection (to the proxy, if any) */ fprintf(stderr, "__libwebsocket_client_connect_2: address %s", wsi->c_address); server_hostent = gethostbyname(wsi->c_address); if (server_hostent == NULL) { fprintf(stderr, "Unable to get host name from %s\n", wsi->c_address); goto oom4; } wsi->sock = socket(AF_INET, SOCK_STREAM, 0); if (wsi->sock < 0) { fprintf(stderr, "Unable to open socket\n"); goto oom4; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(wsi->c_port); server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr); bzero(&server_addr.sin_zero, 8); /* Disable Nagle */ #if !defined(__APPLE__) setsockopt(wsi->sock, SOL_TCP, TCP_NODELAY, (const void *)&opt, sizeof(opt)); #else tcp_proto = getprotobyname("TCP"); setsockopt(wsi->sock, tcp_proto->p_proto, TCP_NODELAY, &opt, sizeof(opt)); #endif /* Set receiving timeout */ tv.tv_sec = 0; tv.tv_usec = 100 * 1000; setsockopt(wsi->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv); if (connect(wsi->sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { fprintf(stderr, "Connect failed\n"); goto oom4; } fprintf(stderr, "connected\n"); /* into fd -> wsi hashtable */ insert_wsi(context, wsi); /* into internal poll list */ context->fds[context->fds_count].fd = wsi->sock; context->fds[context->fds_count].revents = 0; context->fds[context->fds_count++].events = POLLIN; /* external POLL support via protocol 0 */ context->protocols[0].callback(context, wsi, LWS_CALLBACK_ADD_POLL_FD, (void *)(long)wsi->sock, NULL, POLLIN); /* we are connected to server, or proxy */ if (context->http_proxy_port) { n = send(wsi->sock, pkt, plen, 0); if (n < 0) { #ifdef WIN32 closesocket(wsi->sock); #else close(wsi->sock); #endif fprintf(stderr, "ERROR writing to proxy socket\n"); goto bail1; } libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, 5); wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY; return wsi; } /* * provoke service to issue the handshake directly * we need to do it this way because in the proxy case, this is the * next state and executed only if and when we get a good proxy * response inside the state machine */ wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE; pfd.fd = wsi->sock; pfd.revents = POLLIN; libwebsocket_service_fd(context, &pfd); return wsi; oom4: if (wsi->c_protocol) free(wsi->c_protocol); if (wsi->c_origin) free(wsi->c_origin); free(wsi->c_host); free(wsi->c_path); bail1: free(wsi); return NULL; }
int main(const int argc, const char *argv[]) { assert(argc == 2); const char *id = argv[1]; llog_init(); subscriptions_new(&SUBSCRIPTIONS); SUBSCRIPTIONS.unsubscribe_cb = client_unsubscribed; #ifdef EXTERNAL_POLL max_poll_elements = getdtablesize(); pollfds = malloc(max_poll_elements * sizeof(struct pollfd)); fd_lookup = malloc(max_poll_elements * sizeof(int)); if (pollfds == NULL || fd_lookup == NULL) { lwsl_err("Out of memory pollfds=%d\n", max_poll_elements); return -1; } #endif struct lws_context_creation_info info; WEBSOCKETS = initialize_lws_context(&info); if (!WEBSOCKETS) { llog(LOG_ERR, "Server Starting\n"); return -1; } MessageQueue queue; if (!MessageQueue_new(&queue, id)) llog(LOG_ERR, "Failed to create the MessageQueue.\n"); queue.on_message = my_message_callback; queue.on_connect = my_connect_callback; queue.on_log = my_log_callback; queue.on_subscribe = my_subscribe_callback; MessageQueue_connect(&queue); MOSQUITTO = queue.queue; llog(LOG_INFO, "Starting server\n"); for (;; ) { #ifdef EXTERNAL_POLL int n = 0; /* * this represents an existing server's single poll action * which also includes libwebsocket sockets */ n = poll(pollfds, count_pollfds, 50); if (n < 0) continue; if (n) for (n = 0; n < count_pollfds; n++) if (pollfds[n].revents) /* * returns immediately if the fd does not * match anything under libwebsockets * control */ if (libwebsocket_service_fd(WEBSOCKETS, &pollfds[n]) < 0) lwsl_notice("No socket needs LWS service"); #else libwebsocket_service(WEBSOCKETS, EVENT_LOOP_TIMEOUT); #endif MessageQueue_run(&queue, EVENT_LOOP_TIMEOUT); } libwebsocket_context_destroy(WEBSOCKETS); lwsl_notice("Server Shutdown down\n"); MessageQueue_free(&queue); subscriptions_destroy(&SUBSCRIPTIONS); return 0; }
LWS_VISIBLE int libwebsocket_service_fd(struct libwebsocket_context *context, struct libwebsocket_pollfd *pollfd) { struct libwebsocket *wsi; int n, m; lws_sockfd_type mfd; #if LWS_POSIX int listen_socket_fds_index = 0; #endif time_t now; int timed_out = 0; lws_sockfd_type our_fd = 0; char draining_flow = 0; int more; struct lws_tokens eff_buf; unsigned int pending = 0; #if LWS_POSIX if (context->listen_service_fd) listen_socket_fds_index = wsi_from_fd(context,context->listen_service_fd)->position_in_fds_table; #endif /* * you can call us with pollfd = NULL to just allow the once-per-second * global timeout checks; if less than a second since the last check * it returns immediately then. */ time(&now); /* TODO: if using libev, we should probably use timeout watchers... */ if (context->last_timeout_check_s != now) { context->last_timeout_check_s = now; lws_plat_service_periodic(context); /* global timeout check once per second */ if (pollfd) our_fd = pollfd->fd; for (n = 0; n < context->fds_count; n++) { mfd = context->fds[n].fd; wsi = wsi_from_fd(context, mfd); if (!wsi) continue; if (libwebsocket_service_timeout_check(context, wsi, now)) /* he did time out... */ if (mfd == our_fd) { /* it was the guy we came to service! */ timed_out = 1; /* he's gone, no need to mark as handled */ } } } /* the socket we came to service timed out, nothing to do */ if (timed_out) return 0; /* just here for timeout management? */ if (pollfd == NULL) return 0; /* no, here to service a socket descriptor */ wsi = wsi_from_fd(context, pollfd->fd); if (wsi == NULL) /* not lws connection ... leave revents alone and return */ return 0; /* * so that caller can tell we handled, past here we need to * zero down pollfd->revents after handling */ #if LWS_POSIX /* * deal with listen service piggybacking * every listen_service_modulo services of other fds, we * sneak one in to service the listen socket if there's anything waiting * * To handle connection storms, as found in ab, if we previously saw a * pending connection here, it causes us to check again next time. */ if (context->listen_service_fd && pollfd != &context->fds[listen_socket_fds_index]) { context->listen_service_count++; if (context->listen_service_extraseen || context->listen_service_count == context->listen_service_modulo) { context->listen_service_count = 0; m = 1; if (context->listen_service_extraseen > 5) m = 2; while (m--) { /* * even with extpoll, we prepared this * internal fds for listen */ n = lws_poll_listen_fd(&context->fds[listen_socket_fds_index]); if (n > 0) { /* there's a conn waiting for us */ libwebsocket_service_fd(context, &context-> fds[listen_socket_fds_index]); context->listen_service_extraseen++; } else { if (context->listen_service_extraseen) context-> listen_service_extraseen--; break; } } } } /* handle session socket closed */ if ((!(pollfd->revents & LWS_POLLIN)) && (pollfd->revents & LWS_POLLHUP)) { lwsl_debug("Session Socket %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); goto close_and_handled; } #endif /* okay, what we came here to do... */ switch (wsi->mode) { case LWS_CONNMODE_HTTP_SERVING: case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: case LWS_CONNMODE_SERVER_LISTENER: case LWS_CONNMODE_SSL_ACK_PENDING: n = lws_server_socket_service(context, wsi, pollfd); if (n < 0) goto close_and_handled; goto handled; case LWS_CONNMODE_WS_SERVING: case LWS_CONNMODE_WS_CLIENT: case LWS_CONNMODE_HTTP2_SERVING: /* the guy requested a callback when it was OK to write */ if ((pollfd->revents & LWS_POLLOUT) && (wsi->state == WSI_STATE_ESTABLISHED || wsi->state == WSI_STATE_HTTP2_ESTABLISHED || wsi->state == WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS || wsi->state == WSI_STATE_RETURNED_CLOSE_ALREADY || wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) && lws_handle_POLLOUT_event(context, wsi, pollfd)) { if (wsi->state == WSI_STATE_RETURNED_CLOSE_ALREADY) wsi->state = WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE; lwsl_info("libwebsocket_service_fd: closing\n"); goto close_and_handled; } if (wsi->rxflow_buffer && (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) { lwsl_info("draining rxflow\n"); /* well, drain it */ eff_buf.token = (char *)wsi->rxflow_buffer + wsi->rxflow_pos; eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos; draining_flow = 1; goto drain; } /* any incoming data ready? */ if (!(pollfd->revents & LWS_POLLIN)) break; read: eff_buf.token_len = lws_ssl_capable_read(context, wsi, context->service_buffer, pending?pending:sizeof(context->service_buffer)); switch (eff_buf.token_len) { case 0: lwsl_info("service_fd: closing due to 0 length read\n"); goto close_and_handled; case LWS_SSL_CAPABLE_MORE_SERVICE: lwsl_info("SSL Capable more service\n"); n = 0; goto handled; case LWS_SSL_CAPABLE_ERROR: lwsl_info("Closing when error\n"); goto close_and_handled; } /* * give any active extensions a chance to munge the buffer * before parse. We pass in a pointer to an lws_tokens struct * prepared with the default buffer and content length that's in * there. Rather than rewrite the default buffer, extensions * that expect to grow the buffer can adapt .token to * point to their own per-connection buffer in the extension * user allocation. By default with no extensions or no * extension callback handling, just the normal input buffer is * used then so it is efficient. */ eff_buf.token = (char *)context->service_buffer; drain: do { more = 0; m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_PACKET_RX_PREPARSE, &eff_buf, 0); if (m < 0) goto close_and_handled; if (m) more = 1; /* service incoming data */ if (eff_buf.token_len) { n = libwebsocket_read(context, wsi, (unsigned char *)eff_buf.token, eff_buf.token_len); if (n < 0) { /* we closed wsi */ n = 0; goto handled; } } eff_buf.token = NULL; eff_buf.token_len = 0; } while (more); pending = lws_ssl_pending(wsi); if (pending) { pending = pending > sizeof(context->service_buffer)? sizeof(context->service_buffer):pending; goto read; } if (draining_flow && wsi->rxflow_buffer && wsi->rxflow_pos == wsi->rxflow_len) { lwsl_info("flow buffer: drained\n"); lws_free2(wsi->rxflow_buffer); /* having drained the rxflow buffer, can rearm POLLIN */ #ifdef LWS_NO_SERVER n = #endif _libwebsocket_rx_flow_control(wsi); /* n ignored, needed for NO_SERVER case */ } break; default: #ifdef LWS_NO_CLIENT break; #else n = lws_client_socket_service(context, wsi, pollfd); goto handled; #endif } n = 0; goto handled; close_and_handled: lwsl_debug("Close and handled\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); // pollfd points to something else after the close return 1; handled: pollfd->revents = 0; return n; }