/* * Copyright 2018 Alex Hultman and contributors. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef WEBSOCKET_H #define WEBSOCKET_H #include "WebSocketData.h" #include "WebSocketProtocol.h" #include "AsyncSocket.h" #include namespace uWS { template struct WebSocket : AsyncSocket { template friend struct TemplatedApp; private: typedef AsyncSocket Super; using SOCKET_TYPE = typename StaticDispatch::SOCKET_TYPE; using StaticDispatch::static_dispatch; void *init(bool perMessageDeflate, bool slidingCompression) { new (static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this)) WebSocketData(perMessageDeflate, slidingCompression); return this; } public: /* Returns pointer to the per socket user data */ //template void *getUserData() { WebSocketData *webSocketData = (WebSocketData *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this); return nullptr; } /* See AsyncSocket */ using Super::getBufferedAmount; /* Send or buffer a WebSocket frame, compressed or not. Returns false on increased user space backpressure. */ bool send(std::string_view message, uWS::OpCode opCode = uWS::OpCode::BINARY, bool compress = false) { /* Transform the message to compressed domain if requested */ if (compress) { WebSocketData *webSocketData = (WebSocketData *) Super::getExt(); /* Check and correct the compress hint */ if (opCode < 3 && webSocketData->compressionStatus == WebSocketData::ENABLED) { LoopData *loopData = Super::getLoopData(); /* Compress using either shared or dedicated deflationStream */ if (webSocketData->deflationStream) { message = webSocketData->deflationStream->deflate(loopData->zlibContext, message, false); } else { message = loopData->deflationStream->deflate(loopData->zlibContext, message, true); } } else { compress = false; } } /* Get size, alloate size, write if needed */ size_t messageFrameSize = WebSocketProtocol>::messageFrameSize(message.length()); auto[sendBuffer, requiresWrite] = Super::getSendBuffer(messageFrameSize); WebSocketProtocol>::formatMessage(sendBuffer, message.data(), message.length(), opCode, message.length(), compress); if (requiresWrite) { auto[written, failed] = Super::write(sendBuffer, messageFrameSize); /* For now, we are slow here (fix!) */ free(sendBuffer); /* Return true for success */ return !failed; } /* Return success */ return true; } /* Emit close event, stat passive timeout */ void close(int code, std::string_view message = {}) { // closing should trigger close event! /*if (code == 1001) { std::cout << "Going away" << std::endl; } std::cout << "Closing websocket: " << code << " = " << message << std::endl;*/ static const int MAX_CLOSE_PAYLOAD = 123; int length = std::min(MAX_CLOSE_PAYLOAD, message.length()); // here we start a timeout and handle it accordingly in the timeout handler //WebSocketData *webSocketData = (WebSocketData *) us_socket_ext((us_socket *) this); WebSocketData *webSocketData = (WebSocketData *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this); webSocketData->isShuttingDown = true; /* Format and send the close frame */ char closePayload[MAX_CLOSE_PAYLOAD + 2]; int closePayloadLength = (int) WebSocketProtocol>::formatClosePayload(closePayload, code, message.data(), length); send(std::string_view(closePayload, closePayloadLength), OpCode::CLOSE); // why should we fin here? //us_socket_shutdown((us_socket *) this); } }; } #endif // WEBSOCKET_H