{"action":"created","issue":{"url":"https://api.github.com/repos/warmcat/libwebsockets/issues/447","repository_url":"https://api.github.com/repos/warmcat/libwebsockets","labels_url":"https://api.github.com/repos/warmcat/libwebsockets/issues/447/labels{/name}","comments_url":"https://api.github.com/repos/warmcat/libwebsockets/issues/447/comments","events_url":"https://api.github.com/repos/warmcat/libwebsockets/issues/447/events","html_url":"https://github.com/warmcat/libwebsockets/issues/447","id":137659244,"number":447,"title":"SIGPIPE during upgrade to SSL connection","user":{"login":"bdkjones","id":926566,"avatar_url":"https://avatars.githubusercontent.com/u/926566?v=3","gravatar_id":"","url":"https://api.github.com/users/bdkjones","html_url":"https://github.com/bdkjones","followers_url":"https://api.github.com/users/bdkjones/followers","following_url":"https://api.github.com/users/bdkjones/following{/other_user}","gists_url":"https://api.github.com/users/bdkjones/gists{/gist_id}","starred_url":"https://api.github.com/users/bdkjones/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bdkjones/subscriptions","organizations_url":"https://api.github.com/users/bdkjones/orgs","repos_url":"https://api.github.com/users/bdkjones/repos","events_url":"https://api.github.com/users/bdkjones/events{/privacy}","received_events_url":"https://api.github.com/users/bdkjones/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":9,"created_at":"2016-03-01T19:10:36Z","updated_at":"2016-03-02T03:20:42Z","closed_at":null,"body":"I have a Mac app that runs a websocket server using libwebsockets. Here's how I'm creating the server:\r\n\r\n```\r\n- (void) startListeningOnPort:(int)port withProtocolName:(NSString *)protocolName andCompletionBlock:(void (^)(NSError *))completionBlock\r\n{\r\n    if (self.isRunning) {\r\n        NSLog(@\"Attempt was made to start LPWebSocketServer, but the server is already running. Aborted.\");\r\n        return;\r\n    }\r\n  \r\n    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),\r\n    ^{\r\n        lws_set_log_level(LLL_INFO|LLL_DEBUG|LLL_NOTICE|LLL_ERR|LLL_WARN|LLL_HEADER|LLL_EXT|LLL_PARSER|LLL_LATENCY|LLL_COUNT|LLL_CLIENT, logger);\r\n        \r\n        struct lws_protocols protocols[] =\r\n        {\r\n            // first protocol must always be HTTP handler\r\n            {\r\n                \"http-only\",\r\n                callback_http,\r\n                0,                              // Per-session data size\r\n                0                               // Max frame size\r\n            },\r\n            {\r\n                [protocolName cStringUsingEncoding:NSASCIIStringEncoding],\r\n                callback_websockets,\r\n                sizeof(NSUInteger),            // the session is identified by an id\r\n                0,\r\n                0\r\n            },\r\n            {NULL, NULL, 0, 0}   // End of list\r\n        };\r\n        \r\n        \r\n        int options = LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED|LWS_SERVER_OPTION_VALIDATE_UTF8|LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME;\r\n        \r\n        struct lws_context_creation_info creationInfo = {0};    // Intialize everything in the struct to 0.\r\n        creationInfo.port = 5758;\r\n        creationInfo.iface = NULL;\r\n        creationInfo.protocols = protocols;\r\n        creationInfo.extensions = lws_get_internal_extensions();\r\n        creationInfo.token_limits = NULL;\r\n        creationInfo.ssl_cert_filepath = (_sslCertificatePath) ? [_sslCertificatePath cStringUsingEncoding:NSUTF8StringEncoding] : NULL;\r\n        creationInfo.ssl_private_key_filepath = (_sslKeyPath) ? [_sslKeyPath cStringUsingEncoding:NSUTF8StringEncoding] : NULL;\r\n        creationInfo.ssl_private_key_password = NULL;\r\n        creationInfo.ssl_ca_filepath = NULL;\r\n        creationInfo.ssl_cipher_list = NULL;\r\n        creationInfo.http_proxy_address = NULL;\r\n        creationInfo.http_proxy_port = 0;\r\n        creationInfo.gid = -1;\r\n        creationInfo.uid = -1;\r\n        creationInfo.options = options;\r\n        creationInfo.user = (__bridge void *)(self);           // Pointer to objective-c class/object that holds the socket server.\r\n        creationInfo.ka_time = 0;\r\n        creationInfo.ka_probes = 0;\r\n        creationInfo.ka_interval = 0;\r\n        \r\n        self.context = lws_create_context(&creationInfo);\r\n        \r\n        NSError *error = nil;\r\n        if (self.context == NULL) {\r\n            error = [NSError errorWithDomain:errorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@\"Couldn't create the libwebsockets context.\", @\"\")}];\r\n        }\r\n        \r\n        dispatch_async(dispatch_get_main_queue(), ^{\r\n            completionBlock(error);\r\n        });\r\n        \r\n        if (!error)\r\n        {\r\n            self.isRunning = YES;\r\n            \r\n            // For now infinite loop which proceses events and wait for n ms.\r\n            while (!self.stopServer)\r\n            {\r\n                @autoreleasepool\r\n                {\r\n                    lws_service(self.context, 0);\r\n                    \r\n                    if (self.messagesCount > 0) {\r\n                        lws_callback_on_writable_all_protocol(self.context, &protocols[1]);\r\n                    }\r\n                }\r\n                usleep(pollingInterval);\r\n            }\r\n            \r\n            // Once we get here (past the loop), we've been told to stop running the server. Cleanup.\r\n            self.isRunning = NO;\r\n            self.stopServer = NO;\r\n            lws_context_destroy(self.context);\r\n            self.context = NULL;\r\n            [self.messageQueues removeAllObjects];\r\n            self.nextAvailableSessionID = 1;\r\n            self.messagesCount = 0;\r\n            \r\n            dispatch_async(dispatch_get_main_queue(), ^{\r\n                self.serverStoppedCompletionBlock();\r\n                self.serverStoppedCompletionBlock = nil;\r\n            });\r\n        }\r\n    });\r\n}\r\n```\r\n\r\nMy HTTP callback function is very simple. Right now, it just logs the reason for the callback:\r\n\r\n```\r\nstatic int callback_http(struct lws *wsi,\r\n                         enum lws_callback_reasons reason,\r\n                         void *user,\r\n                         void *in,\r\n                         size_t len)\r\n{\r\n    switch (reason)\r\n    {\r\n        case LWS_CALLBACK_HTTP_WRITEABLE:\r\n        {\r\n            // We have nothing to write over HTTP.\r\n            NSLog(@\"HTTP - CALLBACK_HTTP_WRITEABLE\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_GET_THREAD_ID:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_GET_THREAD_ID\");\r\n            uint64_t tid;\r\n            pthread_threadid_np(NULL, &tid);\r\n            return (int)tid;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_HTTP:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_HTTP\");\r\n            \r\n            char *requested_uri = (char *) in;\r\n            printf(\"callback HTTP: requested URI: %s\\n\", requested_uri);\r\n            \r\n            if (strcmp(requested_uri, \"/\") == 0) {\r\n                void *universal_response = \"Hello, World!\";\r\n                lws_write(wsi, universal_response, strlen(universal_response), LWS_WRITE_HTTP);\r\n            }\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_CLOSED_HTTP:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_CLOSED_HTTP\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_CHANGE_MODE_POLL_FD:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_CHANGE_MODE_POLL_FD\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_UNLOCK_POLL:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_UNLOCK_POLL\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_LOCK_POLL:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_LOCK_POLL\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_ADD_POLL_FD:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_ADD_POLL_FD\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_DEL_POLL_FD:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_DEL_POLL_FD\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_SERVER_NEW_CLIENT_INSANTITATED\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_WSI_CREATE:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_WSI_CREATE\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_FILTER_NETWORK_CONNECTION\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS\");\r\n            return 0;\r\n            break;\r\n        }\r\n        case LWS_CALLBACK_PROTOCOL_INIT:\r\n        {\r\n            NSLog(@\"HTTP - CALLBACK_PROTOCOL_INIT\");\r\n            return 0;\r\n            break;\r\n        }\r\n            \r\n        default:\r\n        {\r\n            NSLog(@\"callback_http for reason: %i\", reason);\r\n            return 0;\r\n            break;\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n\r\n# SSL Info\r\n\r\nMy app runs an HTTPS server on port 5757. That server is secured using the same certificate that I'm using in libwebsockets, above. When the user loads my server address in the browser (https://localhost:5757), they see the \"certificate not trusted\" warning and must accept the certificate before proceeding. This means that by the time the browser calls the `wss://` link to set up the socket, the user has already manually trusted the certificate, so the call should succeed. \r\n\r\nHowever, what I'm seeing is that the call to the `wss://` link triggers a bunch of callbacks to my HTTP handler (above) and then I get a SIGPIPE and the app crashes. I believe this is because the client (the browser) is severing the handshake because it doesn't like the self-signed cert. That's fine. But shouldn't libwebsockets be able to handle that gracefully instead of crashing?\r\n\r\n\r\n# Output\r\n\r\nWhen the websocket context is first created, before any client attempts to connect to it, here's the logged output I get:\r\n\r\n```\r\nInitial logging level 1023\r\nLibwebsockets version: 1.7.3 bdkjones@VDK.local-\r\nIPV6 not compiled in\r\nlibev support not compiled in\r\n Threads: 1 each 10240 fds\r\n mem: platform fd map: 81920 bytes\r\n mem: per-conn:          400 bytes + protocol rx buf\r\n Compiled with OpenSSL support\r\n Using SSL mode\r\n SSL ECDH curve 'prime256v1'\r\n2016-03-01 10:49:12.908 CodeKit[18612:4108710] HTTP - CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS\r\n2016-03-01 10:49:12.909 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 10:49:12.909 CodeKit[18612:4108710] HTTP - CALLBACK_ADD_POLL_FD\r\n2016-03-01 10:49:12.909 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n Listening on port 5758\r\n2016-03-01 10:49:12.909 CodeKit[18612:4108710] HTTP - CALLBACK_PROTOCOL_INIT\r\n2016-03-01 10:49:12.909 CodeKit[18612:4108710] -----callback_websockets called with reason: 27.\r\n2016-03-01 10:49:12.909 CodeKit[18612:4108710] HTTP - CALLBACK_GET_THREAD_ID\r\n```\r\n\r\nAnd here's the output that occurs when I visit `https://localhost:5757` in my browser, which triggers an attempt to connect to `wss://localhost:5758`:\r\n\r\n```\r\n2016-03-01 11:07:33.649 CodeKit[18612:4108710] HTTP - CALLBACK_FILTER_NETWORK_CONNECTION\r\n2016-03-01 11:07:33.649 CodeKit[18612:4108710] HTTP - CALLBACK_WSI_CREATE\r\n2016-03-01 11:07:33.649 CodeKit[18612:4108710] HTTP - CALLBACK_SERVER_NEW_CLIENT_INSANTITATED\r\n2016-03-01 11:07:33.649 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.650 CodeKit[18612:4108710] HTTP - CALLBACK_ADD_POLL_FD\r\n2016-03-01 11:07:33.650 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.650 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.650 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.650 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.652 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.652 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.652 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.674 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.674 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.674 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_FILTER_NETWORK_CONNECTION\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_WSI_CREATE\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_SERVER_NEW_CLIENT_INSANTITATED\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_ADD_POLL_FD\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.696 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.698 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.698 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.698 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.698 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.698 CodeKit[18612:4108710] HTTP - CALLBACK_CLOSED_HTTP\r\n2016-03-01 11:07:33.699 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.699 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.699 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n2016-03-01 11:07:33.720 CodeKit[18612:4108710] HTTP - CALLBACK_LOCK_POLL\r\n2016-03-01 11:07:33.720 CodeKit[18612:4108710] HTTP - CALLBACK_DEL_POLL_FD\r\n2016-03-01 11:07:33.720 CodeKit[18612:4108710] HTTP - CALLBACK_CHANGE_MODE_POLL_FD\r\n2016-03-01 11:07:33.720 CodeKit[18612:4108710] HTTP - CALLBACK_UNLOCK_POLL\r\n(App terminates with SIGPIPE)\r\n```\r\n\r\nIn Chrome, the Console shows this error message:\r\n\r\n```\r\nWebSocket connection to 'wss://localhost:5758/' failed: WebSocket opening handshake was canceled\r\n```\r\n\r\nThat's what's leading me to believe Chrome is rejecting the connection once the process gets underway and libwebsockets is then attempting to write to a closed pipe, which generates the SIGPIPE. \r\n\r\nHave I done something wrong? I certainly wouldn't rule that out. Note that if I don't use SSL, everything works just fine. \r\n\r\n"},"comment":{"url":"https://api.github.com/repos/warmcat/libwebsockets/issues/comments/191035663","html_url":"https://github.com/warmcat/libwebsockets/issues/447#issuecomment-191035663","issue_url":"https://api.github.com/repos/warmcat/libwebsockets/issues/447","id":191035663,"user":{"login":"lws-team","id":15154525,"avatar_url":"https://avatars.githubusercontent.com/u/15154525?v=3","gravatar_id":"","url":"https://api.github.com/users/lws-team","html_url":"https://github.com/lws-team","followers_url":"https://api.github.com/users/lws-team/followers","following_url":"https://api.github.com/users/lws-team/following{/other_user}","gists_url":"https://api.github.com/users/lws-team/gists{/gist_id}","starred_url":"https://api.github.com/users/lws-team/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lws-team/subscriptions","organizations_url":"https://api.github.com/users/lws-team/orgs","repos_url":"https://api.github.com/users/lws-team/repos","events_url":"https://api.github.com/users/lws-team/events{/privacy}","received_events_url":"https://api.github.com/users/lws-team/received_events","type":"User","site_admin":false},"created_at":"2016-03-02T03:20:42Z","updated_at":"2016-03-02T03:20:42Z","body":"I think the write() is OK because there may not be a way to discover this situation without writing to it.\r\n\r\nActually lws captures and ignores SIGPIPE early in the lws_create_context() code.  After that you shouldn't get any SIGPIPE from the whole process, he'll discover it later as an error state on poll().  So there is something different in your code to the test server with regards to this."}}