• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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