1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "common/log_wrapper.h"
17 #include "websocket/define.h"
18 #include "websocket/network.h"
19 #include "websocket/websocket_base.h"
20
21 namespace OHOS::ArkCompiler::Toolchain {
22 // if the data is too large, it will be split into multiple frames, the first frame will be marked as 0x0
23 // and the last frame will be marked as 0x1.
24 // we just add the 'isLast' parameter to indicate whether it is the last frame.
SendReply(const std::string & message,FrameType frameType,bool isLast) const25 bool WebSocketBase::SendReply(const std::string& message, FrameType frameType, bool isLast) const
26 {
27 if (socketState_ != SocketState::CONNECTED) {
28 LOGE("SendReply failed, websocket not connected");
29 return false;
30 }
31
32 auto frame = CreateFrame(isLast, frameType, message);
33 if (!Send(connectionFd_, frame, 0)) {
34 LOGE("SendReply: send failed");
35 return false;
36 }
37 return true;
38 }
39
40 /**
41 * The wired format of this data transmission section is described in detail through ABNFRFC5234.
42 * When receive the message, we should decode it according the spec. The structure is as follows:
43 * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
44 * +-+-+-+-+-------+-+-------------+-------------------------------+
45 * |F|R|R|R| opcode|M| Payload len | Extended payload length |
46 * |I|S|S|S| (4) |A| (7) | (16/64) |
47 * |N|V|V|V| |S| | (if payload len==126/127) |
48 * | |1|2|3| |K| | |
49 * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
50 * | Extended payload length continued, if payload len == 127 |
51 * + - - - - - - - - - - - - - - - +-------------------------------+
52 * | |Masking-key, if MASK set to 1 |
53 * +-------------------------------+-------------------------------+
54 * | Masking-key (continued) | Payload Data |
55 * +-------------------------------- - - - - - - - - - - - - - - - +
56 * : Payload Data continued ... :
57 * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
58 * | Payload Data continued ... |
59 * +---------------------------------------------------------------+
60 */
61
ReadPayload(WebSocketFrame & wsFrame)62 bool WebSocketBase::ReadPayload(WebSocketFrame& wsFrame)
63 {
64 if (wsFrame.payloadLen == WebSocketFrame::TWO_BYTES_LENTH_ENC) {
65 uint8_t recvbuf[WebSocketFrame::TWO_BYTES_LENTH] = {0};
66 if (!Recv(connectionFd_, recvbuf, WebSocketFrame::TWO_BYTES_LENTH, 0)) {
67 LOGE("ReadPayload: Recv payloadLen == 126 failed");
68 return false;
69 }
70 wsFrame.payloadLen = NetToHostLongLong(recvbuf, WebSocketFrame::TWO_BYTES_LENTH);
71 } else if (wsFrame.payloadLen == WebSocketFrame::EIGHT_BYTES_LENTH_ENC) {
72 uint8_t recvbuf[WebSocketFrame::EIGHT_BYTES_LENTH] = {0};
73 if (!Recv(connectionFd_, recvbuf, WebSocketFrame::EIGHT_BYTES_LENTH, 0)) {
74 LOGE("ReadPayload: Recv payloadLen == 127 failed");
75 return false;
76 }
77 wsFrame.payloadLen = NetToHostLongLong(recvbuf, WebSocketFrame::EIGHT_BYTES_LENTH);
78 }
79 return DecodeMessage(wsFrame);
80 }
81
HandleDataFrame(WebSocketFrame & wsFrame)82 bool WebSocketBase::HandleDataFrame(WebSocketFrame& wsFrame)
83 {
84 if (wsFrame.opcode == EnumToNumber(FrameType::TEXT)) {
85 return ReadPayload(wsFrame);
86 } else {
87 LOGW("Received unsupported data frame, opcode = %{public}d", wsFrame.opcode);
88 }
89 return true;
90 }
91
HandleControlFrame(WebSocketFrame & wsFrame)92 bool WebSocketBase::HandleControlFrame(WebSocketFrame& wsFrame)
93 {
94 if (wsFrame.opcode == EnumToNumber(FrameType::PING)) {
95 // A Pong frame sent in response to a Ping frame must have identical
96 // "Application data" as found in the message body of the Ping frame
97 // being replied to.
98 // https://www.rfc-editor.org/rfc/rfc6455#section-5.5.3
99 if (!ReadPayload(wsFrame)) {
100 LOGE("Failed to read ping frame payload");
101 return false;
102 }
103 SendPongFrame(wsFrame.payload);
104 } else if (wsFrame.opcode == EnumToNumber(FrameType::CLOSE)) {
105 // might read payload to response by echoing the status code
106 CloseConnection(CloseStatusCode::NO_STATUS_CODE, SocketState::INITED);
107 }
108 return true;
109 }
110
Decode()111 std::string WebSocketBase::Decode()
112 {
113 if (socketState_ != SocketState::CONNECTED) {
114 LOGE("Decode failed, websocket not connected!");
115 return "";
116 }
117
118 uint8_t recvbuf[WebSocketFrame::HEADER_LEN] = {0};
119 if (!Recv(connectionFd_, recvbuf, WebSocketFrame::HEADER_LEN, 0)) {
120 LOGE("Decode failed, client websocket disconnect");
121 CloseConnection(CloseStatusCode::UNEXPECTED_ERROR, SocketState::INITED);
122 return std::string(DECODE_DISCONNECT_MSG);
123 }
124 WebSocketFrame wsFrame(recvbuf);
125 if (!ValidateIncomingFrame(wsFrame)) {
126 LOGE("Received websocket frame is invalid - header is %02x%02x", recvbuf[0], recvbuf[1]);
127 CloseConnection(CloseStatusCode::PROTOCOL_ERROR, SocketState::INITED);
128 }
129
130 if (IsControlFrame(wsFrame.opcode)) {
131 if (HandleControlFrame(wsFrame)) {
132 return wsFrame.payload;
133 }
134 } else if (HandleDataFrame(wsFrame)) {
135 return wsFrame.payload;
136 }
137 return std::string(DECODE_DISCONNECT_MSG);
138 }
139
IsConnected()140 bool WebSocketBase::IsConnected()
141 {
142 return socketState_ == SocketState::CONNECTED;
143 }
144
SetCloseConnectionCallback(CloseConnectionCallback cb)145 void WebSocketBase::SetCloseConnectionCallback(CloseConnectionCallback cb)
146 {
147 closeCb_ = std::move(cb);
148 }
149
SetFailConnectionCallback(FailConnectionCallback cb)150 void WebSocketBase::SetFailConnectionCallback(FailConnectionCallback cb)
151 {
152 failCb_ = std::move(cb);
153 }
154
CloseConnectionSocket(ConnectionCloseReason status,SocketState newSocketState)155 void WebSocketBase::CloseConnectionSocket(ConnectionCloseReason status, SocketState newSocketState)
156 {
157 #if defined(OHOS_PLATFORM)
158 shutdown(connectionFd_, SHUT_RDWR);
159 #endif
160 close(connectionFd_);
161 connectionFd_ = -1;
162 socketState_ = newSocketState;
163
164 if (status == ConnectionCloseReason::FAIL) {
165 if (failCb_) {
166 failCb_();
167 }
168 } else if (status == ConnectionCloseReason::CLOSE) {
169 if (closeCb_) {
170 closeCb_();
171 }
172 }
173 }
174
SendPongFrame(std::string payload)175 void WebSocketBase::SendPongFrame(std::string payload)
176 {
177 auto frame = CreateFrame(true, FrameType::PONG, std::move(payload));
178 if (!Send(connectionFd_, frame, 0)) {
179 LOGE("Decode: Send pong frame failed");
180 }
181 }
182
SendCloseFrame(CloseStatusCode status)183 void WebSocketBase::SendCloseFrame(CloseStatusCode status)
184 {
185 auto frame = CreateFrame(true, FrameType::CLOSE, ToString(status));
186 if (!Send(connectionFd_, frame, 0)) {
187 LOGE("SendCloseFrame: Send close frame failed");
188 }
189 }
190
CloseConnection(CloseStatusCode status,SocketState newSocketState)191 void WebSocketBase::CloseConnection(CloseStatusCode status, SocketState newSocketState)
192 {
193 LOGI("Close connection, status = %{public}d", static_cast<int>(status));
194 // can close connection right after sending back close frame.
195 CloseConnectionSocket(ConnectionCloseReason::CLOSE, newSocketState);
196 }
197
198 /* static */
IsDecodeDisconnectMsg(const std::string & message)199 bool WebSocketBase::IsDecodeDisconnectMsg(const std::string& message)
200 {
201 return message == DECODE_DISCONNECT_MSG;
202 }
203
204 #if !defined(OHOS_PLATFORM)
205 /* static */
SetWebSocketTimeOut(int32_t fd,uint32_t timeoutLimit)206 bool WebSocketBase::SetWebSocketTimeOut(int32_t fd, uint32_t timeoutLimit)
207 {
208 if (timeoutLimit > 0) {
209 struct timeval timeout = {timeoutLimit, 0};
210 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
211 reinterpret_cast<char *>(&timeout), sizeof(timeout)) != SOCKET_SUCCESS) {
212 LOGE("SetWebSocketTimeOut setsockopt SO_SNDTIMEO failed, errno = %{public}d", errno);
213 return false;
214 }
215 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
216 reinterpret_cast<char *>(&timeout), sizeof(timeout)) != SOCKET_SUCCESS) {
217 LOGE("SetWebSocketTimeOut setsockopt SO_RCVTIMEO failed, errno = %{public}d", errno);
218 return false;
219 }
220 }
221 return true;
222 }
223 #else
224 /* static */
SetWebSocketTimeOut(int32_t fd,uint32_t timeoutLimit)225 bool WebSocketBase::SetWebSocketTimeOut(int32_t fd, uint32_t timeoutLimit)
226 {
227 if (timeoutLimit > 0) {
228 struct timeval timeout = {timeoutLimit, 0};
229 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != SOCKET_SUCCESS) {
230 LOGE("SetWebSocketTimeOut setsockopt SO_SNDTIMEO failed, errno = %{public}d", errno);
231 return false;
232 }
233 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != SOCKET_SUCCESS) {
234 LOGE("SetWebSocketTimeOut setsockopt SO_RCVTIMEO failed, errno = %{public}d", errno);
235 return false;
236 }
237 }
238 return true;
239 }
240 #endif
241 } // namespace OHOS::ArkCompiler::Toolchain
242