/src/prod/src/Management/HttpTransport/HttpServerWebSocket.cpp

https://github.com/Microsoft/service-fabric · C++ · 382 lines · 320 code · 58 blank · 4 comment · 21 complexity · 1d693d871f810fd4fb3b9a343b251075 MD5 · raw file

  1. // ------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
  4. // ------------------------------------------------------------
  5. #include "stdafx.h"
  6. #if NTDDI_VERSION >= NTDDI_WIN8
  7. using namespace std;
  8. using namespace Common;
  9. using namespace HttpCommon;
  10. using namespace HttpServer;
  11. StringLiteral const TraceType("HttpServerWebSocket");
  12. HttpServerWebSocket::HttpServerWebSocket(
  13. __in IRequestMessageContext &clientRequest,
  14. __in KStringViewA const &supportedSubProtocols,
  15. __in_opt ULONG receiveBufferSize,
  16. __in_opt ULONG sendBufferSize,
  17. __in_opt ULONGLONG openHandshakeTimeoutMilliSeconds,
  18. __in_opt ULONGLONG gracefulCloseTimeoutMilliSeconds,
  19. __in_opt ULONGLONG pongKeepAlivePeriodMilliSeconds,
  20. __in_opt ULONGLONG pingIntervalMilliSeconds,
  21. __in_opt ULONGLONG pongTimeoutMilliSeconds)
  22. : WebSocketHandler(TraceType, clientRequest.GetUrl())
  23. , clientRequest_(clientRequest)
  24. , supportedSubProtocols_(supportedSubProtocols)
  25. , receiveBufferSize_(receiveBufferSize)
  26. , sendBufferSize_(sendBufferSize)
  27. , openHandshakeTimeoutMilliSeconds_(openHandshakeTimeoutMilliSeconds)
  28. , gracefulCloseTimeoutMilliSeconds_(gracefulCloseTimeoutMilliSeconds)
  29. , pongKeepAlivePeriodMilliSeconds_(pongKeepAlivePeriodMilliSeconds)
  30. , pingIntervalMilliSeconds_(pingIntervalMilliSeconds)
  31. , pongTimeoutMilliSeconds_(pongTimeoutMilliSeconds)
  32. {}
  33. class HttpServerWebSocket::OpenAsyncOperation : public AsyncOperation
  34. {
  35. public:
  36. OpenAsyncOperation(
  37. HttpServerWebSocket &owner,
  38. WebSocketCloseReceivedCallback const &closeReceivedCallback,
  39. Common::AsyncCallback const &callback,
  40. Common::AsyncOperationSPtr const &parent)
  41. : AsyncOperation(callback, parent)
  42. , owner_(owner)
  43. {
  44. owner.closeReceivedCallback_ = closeReceivedCallback;
  45. }
  46. static ErrorCode End(__in AsyncOperationSPtr const& operation)
  47. {
  48. auto thisPtr = AsyncOperation::End<OpenAsyncOperation>(operation);
  49. return thisPtr->Error;
  50. }
  51. protected:
  52. void OnStart(AsyncOperationSPtr const &thisSPtr);
  53. private:
  54. void OnOpenComplete(
  55. KAsyncContextBase* const ,
  56. KAsyncServiceBase& ,
  57. NTSTATUS openStatus)
  58. {
  59. if (!NT_SUCCESS(openStatus))
  60. {
  61. WriteWarning(
  62. TraceType,
  63. "WebSocket open failed with NTSTATUS - {0}, for request url {1}",
  64. owner_.clientRequest_.GetUrl(),
  65. openStatus);
  66. }
  67. TryComplete(thisSPtr_, ErrorCode::FromNtStatus(openStatus));
  68. thisSPtr_.reset();
  69. }
  70. HttpServerWebSocket &owner_;
  71. AsyncOperationSPtr thisSPtr_;
  72. };
  73. void HttpServerWebSocket::OpenAsyncOperation::OnStart(AsyncOperationSPtr const &thisSPtr)
  74. {
  75. auto status = KHttpServerWebSocket::Create(owner_.kWebSocket_, owner_.clientRequest_.GetThisAllocator());
  76. if (!NT_SUCCESS(status))
  77. {
  78. WriteWarning(
  79. TraceType,
  80. "KHttpServerWebSocket create failed with NTSTATUS - {0}, for request url {1}",
  81. owner_.clientRequest_.GetUrl(),
  82. status);
  83. TryComplete(thisSPtr, ErrorCode::FromNtStatus(status));
  84. return;
  85. }
  86. status = owner_.kWebSocket_->CreateReceiveFragmentOperation(owner_.receiveFragmentKtlOperation_);
  87. if (!NT_SUCCESS(status))
  88. {
  89. WriteWarning(
  90. TraceType,
  91. "create receive fragment operation failed with NTSTATUS - {0}, for request url {1}",
  92. owner_.clientRequest_.GetUrl(),
  93. status);
  94. TryComplete(thisSPtr, ErrorCode::FromNtStatus(status));
  95. return;
  96. }
  97. status = owner_.kWebSocket_->CreateSendFragmentOperation(owner_.sendFragmentKtlOperation_);
  98. if (!NT_SUCCESS(status))
  99. {
  100. WriteWarning(
  101. TraceType,
  102. "Create send fragment operation failed with NTSTATUS - {0}, for request url {1}",
  103. owner_.clientRequest_.GetUrl(),
  104. status);
  105. TryComplete(thisSPtr, ErrorCode::FromNtStatus(status));
  106. return;
  107. }
  108. auto error = owner_.SetTimingConstants();
  109. if (!error.IsSuccess())
  110. {
  111. TryComplete(thisSPtr, error);
  112. return;
  113. }
  114. KStringA::SPtr ktlSubProtocolAnsiString;
  115. status = KStringA::Create(ktlSubProtocolAnsiString, HttpUtil::GetKtlAllocator(), owner_.supportedSubProtocols_, TRUE);
  116. if (!NT_SUCCESS(status))
  117. {
  118. WriteWarning(
  119. TraceType,
  120. "Getting subprotocol string failed with NTSTATUS - {0}, for request url {1}",
  121. owner_.clientRequest_.GetUrl(),
  122. status);
  123. TryComplete(thisSPtr, ErrorCode::FromNtStatus(status));
  124. return;
  125. }
  126. thisSPtr_ = shared_from_this();
  127. status = owner_.kWebSocket_->StartOpenWebSocket(
  128. *(static_cast<RequestMessageContext&>(owner_.clientRequest_)).GetInnerRequest(),
  129. KWebSocket::WebSocketCloseReceivedCallback(&owner_, &WebSocketHandler::CloseReceived),
  130. nullptr,
  131. KAsyncServiceBase::OpenCompletionCallback(this, &HttpServerWebSocket::OpenAsyncOperation::OnOpenComplete),
  132. owner_.receiveBufferSize_,
  133. owner_.sendBufferSize_,
  134. ktlSubProtocolAnsiString);
  135. if (!NT_SUCCESS(status))
  136. {
  137. WriteWarning(
  138. TraceType,
  139. "Starting websocket open failed with NTSTATUS - {0}, for request url {1}",
  140. owner_.clientRequest_.GetUrl(),
  141. status);
  142. thisSPtr_.reset();
  143. TryComplete(thisSPtr, ErrorCode::FromNtStatus(status));
  144. return;
  145. }
  146. }
  147. class HttpServerWebSocket::CloseAsyncOperation : public AsyncOperation
  148. {
  149. public:
  150. CloseAsyncOperation(
  151. HttpServerWebSocket &owner,
  152. __in KWebSocket::CloseStatusCode &statusCode,
  153. __in KSharedBufferStringA::SPtr &statusReason,
  154. AsyncCallback const &callback,
  155. AsyncOperationSPtr const &parent)
  156. : AsyncOperation(callback, parent)
  157. , owner_(owner)
  158. , statusCode_(statusCode)
  159. , statusReason_(statusReason)
  160. {
  161. }
  162. static ErrorCode End(AsyncOperationSPtr const& operation)
  163. {
  164. auto thisPtr = AsyncOperation::End<CloseAsyncOperation>(operation);
  165. return thisPtr->Error;
  166. }
  167. protected:
  168. void OnStart(AsyncOperationSPtr const &thisSPtr);
  169. private:
  170. VOID
  171. OnCloseComplete(
  172. KAsyncContextBase* const,
  173. KAsyncServiceBase&,
  174. NTSTATUS closeStatus)
  175. {
  176. if (!NT_SUCCESS(closeStatus))
  177. {
  178. WriteWarning(
  179. TraceType,
  180. "Close complete for request url {0} with status {1}",
  181. owner_.clientRequest_.GetUrl(),
  182. closeStatus);
  183. }
  184. TryComplete(thisSPtr_, ErrorCode::FromNtStatus(closeStatus));
  185. thisSPtr_.reset();
  186. }
  187. HttpServerWebSocket &owner_;
  188. AsyncOperationSPtr thisSPtr_;
  189. KWebSocket::CloseStatusCode statusCode_;
  190. KSharedBufferStringA::SPtr statusReason_;
  191. };
  192. void HttpServerWebSocket::CloseAsyncOperation::OnStart(AsyncOperationSPtr const &thisSPtr)
  193. {
  194. thisSPtr_ = thisSPtr;
  195. if (owner_.kWebSocket_->GetConnectionStatus() == KWebSocket::ConnectionStatus::CloseInitiated ||
  196. owner_.kWebSocket_->GetConnectionStatus() == KWebSocket::ConnectionStatus::CloseCompleting ||
  197. owner_.kWebSocket_->GetConnectionStatus() == KWebSocket::ConnectionStatus::Closed)
  198. {
  199. TryComplete(thisSPtr, ErrorCodeValue::ObjectClosed);
  200. return;
  201. }
  202. owner_.kWebSocket_->StartCloseWebSocket(
  203. nullptr,
  204. KAsyncServiceBase::CloseCompletionCallback(this, &HttpServerWebSocket::CloseAsyncOperation::OnCloseComplete),
  205. statusCode_,
  206. statusReason_);
  207. }
  208. AsyncOperationSPtr HttpServerWebSocket::BeginOpen(
  209. WebSocketCloseReceivedCallback const &closeReceivedCallback,
  210. AsyncCallback const &callback,
  211. AsyncOperationSPtr const &parent)
  212. {
  213. return AsyncOperation::CreateAndStart<OpenAsyncOperation>(
  214. *this,
  215. closeReceivedCallback,
  216. callback,
  217. parent);
  218. }
  219. ErrorCode HttpServerWebSocket::EndOpen(
  220. __in AsyncOperationSPtr const &operation)
  221. {
  222. return OpenAsyncOperation::End(operation);
  223. }
  224. AsyncOperationSPtr HttpServerWebSocket::BeginClose(
  225. __in KWebSocket::CloseStatusCode &statusCode,
  226. __in KSharedBufferStringA::SPtr &statusReason,
  227. AsyncCallback const &callback,
  228. AsyncOperationSPtr const &parent)
  229. {
  230. return AsyncOperation::CreateAndStart<CloseAsyncOperation>(
  231. *this,
  232. statusCode,
  233. statusReason,
  234. callback,
  235. parent);
  236. }
  237. ErrorCode HttpServerWebSocket::EndClose(
  238. AsyncOperationSPtr const &operation)
  239. {
  240. return CloseAsyncOperation::End(operation);
  241. }
  242. ErrorCode HttpServerWebSocket::GetRemoteCloseStatus(
  243. __out KWebSocket::CloseStatusCode &statusCode,
  244. __out KSharedBufferStringA::SPtr &statusReason)
  245. {
  246. if (kWebSocket_->GetConnectionStatus() == KWebSocket::ConnectionStatus::CloseReceived ||
  247. kWebSocket_->GetConnectionStatus() == KWebSocket::ConnectionStatus::CloseCompleting ||
  248. kWebSocket_->GetConnectionStatus() == KWebSocket::ConnectionStatus::Closed)
  249. {
  250. statusCode = kWebSocket_->GetRemoteWebSocketCloseStatusCode();
  251. statusReason = kWebSocket_->GetRemoteCloseReason();
  252. return ErrorCodeValue::Success;
  253. }
  254. return ErrorCodeValue::NotReady;
  255. }
  256. ErrorCode HttpServerWebSocket::SetTimingConstants()
  257. {
  258. auto status = kWebSocket_->SetTimingConstant(
  259. KWebSocket::TimingConstant::OpenTimeoutMs,
  260. static_cast<KWebSocket::TimingConstantValue>(openHandshakeTimeoutMilliSeconds_));
  261. if (!NT_SUCCESS(status))
  262. {
  263. WriteWarning(
  264. TraceType,
  265. "Set OpenTimeout failed with NTSTATUS - {0}, for request url {1}",
  266. status,
  267. requestUri_);
  268. return ErrorCode::FromNtStatus(status);
  269. }
  270. status = kWebSocket_->SetTimingConstant(
  271. KWebSocket::TimingConstant::CloseTimeoutMs,
  272. static_cast<KWebSocket::TimingConstantValue>(gracefulCloseTimeoutMilliSeconds_));
  273. if (!NT_SUCCESS(status))
  274. {
  275. WriteWarning(
  276. TraceType,
  277. "Set CloseTimeout failed with NTSTATUS - {0}, for request url {1}",
  278. status,
  279. requestUri_);
  280. return ErrorCode::FromNtStatus(status);
  281. }
  282. status = kWebSocket_->SetTimingConstant(
  283. KWebSocket::TimingConstant::PingQuietChannelPeriodMs,
  284. static_cast<KWebSocket::TimingConstantValue>(pingIntervalMilliSeconds_));
  285. if (!NT_SUCCESS(status))
  286. {
  287. WriteWarning(
  288. TraceType,
  289. "Set PingQuietChannelPeriodMs failed with NTSTATUS - {0}, for request url {1}",
  290. status,
  291. requestUri_);
  292. return ErrorCode::FromNtStatus(status);
  293. }
  294. status = kWebSocket_->SetTimingConstant(
  295. KWebSocket::TimingConstant::PongTimeoutMs,
  296. static_cast<KWebSocket::TimingConstantValue>(pongTimeoutMilliSeconds_));
  297. if (!NT_SUCCESS(status))
  298. {
  299. WriteWarning(
  300. TraceType,
  301. "Set PongTimeoutMs failed with NTSTATUS - {0}, for request url {1}",
  302. status,
  303. requestUri_);
  304. return ErrorCode::FromNtStatus(status);
  305. }
  306. status = kWebSocket_->SetTimingConstant(
  307. KWebSocket::TimingConstant::PongKeepalivePeriodMs,
  308. static_cast<KWebSocket::TimingConstantValue>(pongKeepAlivePeriodMilliSeconds_));
  309. if (!NT_SUCCESS(status))
  310. {
  311. WriteWarning(
  312. TraceType,
  313. "Set PongKeepalivePeriodMs failed with NTSTATUS - {0}, for request url {1}",
  314. status,
  315. requestUri_);
  316. return ErrorCode::FromNtStatus(status);
  317. }
  318. return ErrorCodeValue::Success;
  319. }
  320. KWebSocket::ConnectionStatus HttpServerWebSocket::GetConnectionStatus()
  321. {
  322. return kWebSocket_->GetConnectionStatus();
  323. }
  324. #endif