fastdo  0.6.8
eiennet_websocket.hpp
浏览该文件的文档.
1 #pragma once
2 
3 namespace eiennet
4 {
5 namespace old_v1
6 {
8 namespace ws
9 {
33  /*
34  一个为未分片的消息(FIN为1,opcode非0)
35  一个分片的消息由起始帧:
36  (FIN为0,opcode非0),
37  若干(0个或多个)帧(FIN为0,opcode为0),
38  结束帧(FIN为1,opcode为0)
39  */
40  /*
41  HTTP升级为websocket连接的请求
42  GET /xxx HTTP/1.1
43  Host: hostname[:port]
44  Upgrade: websocket
45  Connection: Upgrade
46  Sec-WebSocket-Key: 随机生成的16字节的值用BASE64编码后的字符串
47  Origin: 其值为请求发起页面的scheme://hostname (因为中间可能经过代理才到达服务器,服务器可以根据该字段选择是否和客户端建立连接)
48  Sec-WebSocket-Version: 13
49  [Sec-WebSocket-Protocol: 其值为由逗号分隔的子协议的名字,按优先度排序,每个名字必须唯一]
50  [Sec-WebSocket-Extension: 表示协议级别的扩展]
51 
52  连接建立的合法响应
53  HTTP/1.1 101 xxx
54  Upgrade: websocket
55  Connection: Upgrade
56  Sec-WebSocket-Accept: 其值为 Base64( SHA1( RequestHeader{Sec-WebSocket-Key} + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ) )
57  [Sec-WebSocket-Protocol: 其值为请求发来的子协议中的一个或空]
58  */
59 
62 {
63  dataContinued = 0x0U,
64  dataText = 0x1U,
65  dataBinary = 0x2U,
66  dataRsv3 = 0x3U,
67  dataRsv4 = 0x4U,
68  dataRsv5 = 0x5U,
69  dataRsv6 = 0x6U,
70  dataRsv7 = 0x7U,
71  ctrlClose = 0x8U,
72  ctrlPing = 0x9U,
73  ctrlPong = 0xAU,
74  ctrlRsvB = 0xBU,
75  ctrlRsvC = 0xCU,
76  ctrlRsvD = 0xDU,
77  ctrlRsvE = 0xEU,
78  ctrlRsvF = 0xFU,
79 };
80 
83 {
86  //errCnnUpgradeFail,
87 };
88 
91 {
92  ftBasic = 0b0010,
93  ftBasicMasked = 0b0011,
94  ftExtended16 = 0b0100,
96  ftExtended64 = 0b1000,
98 };
99 
102 {
107 };
108 
109 #pragma pack(push, 1)
110 
112 struct FrameBase
113 {
119 
122 
124  {
125  memset( this, 0, sizeof(*this) );
126  }
127 };
128 
129 // WebSocket数据帧根据Payload-len来决定帧头部大小
130 // Payload-len < 126: 2bytes, 16bits
131 // Payload-len ==126: 4bytes, 32bits
132 // Payload-len ==127: 10bytes, 80bits
133 // 除此之外,如果mask位为1,则包含32bits的掩码Key,即
134 // Payload-len < 126: 6bytes, 48bits
135 // Payload-len ==126: 8bytes, 64bits
136 // Payload-len ==127: 14bytes, 112bits
137 
138 // 基本帧 payload len < 126, mask == 0
140 {
142  {
143  memset( this, 0, sizeof(*this) );
144  }
145 
146  template < typename _Ty >
147  _Ty * data()
148  {
149  return reinterpret_cast<_Ty*>( this + 1 );
150  }
151 };
152 #pragma pack(pop)
153 
154 #pragma pack(push, 2)
155 
156 // 基本掩码帧 payload len < 126, mask == 1
158 {
159  winux::uint8 maskingKey[4];
160 
162  {
163  memset( this, 0, sizeof(*this) );
164  }
165 
166  template < typename _Ty >
167  _Ty * data()
168  {
169  return reinterpret_cast<_Ty*>( this + 1 );
170  }
171 };
172 
173 // 16位数据扩展帧 payload len == 126, mask == 0
175 {
177 
179  {
180  memset( this, 0, sizeof(*this) );
181  }
182 
183  template < typename _Ty >
184  _Ty * data()
185  {
186  return reinterpret_cast<_Ty*>( this + 1 );
187  }
188 };
189 
190 // 16位数据扩展掩码帧 payload len == 126, mask == 1
192 {
194  winux::uint8 maskingKey[4];
195 
197  {
198  memset( this, 0, sizeof(*this) );
199  }
200 
201  template < typename _Ty >
202  _Ty * data()
203  {
204  return reinterpret_cast<_Ty*>( this + 1 );
205  }
206 };
207 
208 // 64位数据扩展帧 payload len == 127, mask == 0
210 {
212 
214  {
215  memset( this, 0, sizeof(*this) );
216  }
217 
218  template < typename _Ty >
219  _Ty * data()
220  {
221  return reinterpret_cast<_Ty*>( this + 1 );
222  }
223 };
224 
225 // 64位数据扩展掩码帧 payload len == 127, mask == 1
227 {
229  winux::uint8 maskingKey[4];
230 
232  {
233  memset( this, 0, sizeof(*this) );
234  }
235 
236  template < typename _Ty >
237  _Ty * data()
238  {
239  return reinterpret_cast<_Ty*>( this + 1 );
240  }
241 };
242 
243 // 关闭帧载荷数据
245 {
247  template < typename _Ty >
248  _Ty * data()
249  {
250  return reinterpret_cast<_Ty*>( this + 1 );
251  }
252 };
253 
254 
255 #pragma pack(pop)
256 
257 // 发送WebSocket帧
258 EIENNET_FUNC_DECL(bool) SendWebSocketFrame( eiennet::Socket * sock, OpCode opcode, bool fin, bool mask, winux::byte * payloadData = nullptr, size_t payloadDataSize = 0 );
259 // 发送WebSocket数据
260 EIENNET_FUNC_DECL(bool) SendWebSocketBuffer( eiennet::Socket * sock, OpCode opcode, bool mask, winux::Buffer payloadData = winux::Buffer(), size_t perFrameMaxPayloadSize = -1 );
261 // 发送WebSocket数据
262 EIENNET_FUNC_DECL(bool) SendWebSocketAnsi( eiennet::Socket * sock, OpCode opcode, bool mask, winux::AnsiString payloadData = winux::AnsiString(), size_t perFrameMaxPayloadSize = -1 );
263 
264 
266 
268 {
269  WebSocketWrapper( ClientCtx * clientCtx ) : clientCtx(clientCtx)
270  {
271  }
273  winux::AnsiString messageBuf; // websocket消息缓冲区,用于接收多帧数据(一条消息可能分片)
274  int messageType = 0; // 消息类型,1文本;2二进制
275  ws::ReadyState state = ws::stateConnecting; // 连接状态
276 
277  // 发生close帧
278  bool close( winux::uint16 errCode = -1, winux::AnsiString const & errStr = "" )
279  {
280  winux::GrowBuffer data;
281  if ( errCode != (winux::uint16)-1 )
282  {
283  errCode = htont(errCode);
284  data.append( &errCode, sizeof(errCode) );
285  data.append(errStr);
286  }
287  if ( !SendWebSocketBuffer( clientCtx->clientSockPtr.get(), ws::ctrlClose, false, std::move(data) ) ) return false;
288 
289  if ( this->state == ws::stateClosing )
290  this->state = ws::stateClosed;
291  else
292  this->state = ws::stateClosing;
293  return true;
294  }
295 
296  bool send( winux::AnsiString const & data, ws::OpCode opcode = ws::dataText )
297  {
298  return SendWebSocketAnsi( clientCtx->clientSockPtr.get(), opcode, false, data, 65535 );
299  }
300 };
301 
304 {
305 public:
307  : ClientCtx( clientId, clientEpStr, clientSockPtr ), url(http::Url::urlSimple), config(nullptr), isWebSocketWrapper(false), websocket(this)
308  {
309  }
311  {
312  if ( !this->config || this->config->outputVerbose )
313  ColorOutputLine( winux::fgPurple, "Client[", clientId, "]客户场景`", clientEpStr, "`析构" );
314  }
315  DataRecvSendCtx forClient; // 数据收发场景
316  winux::AnsiString requestHeaderStr; // 请求头字符串
318  winux::GrowBuffer requestBody; // 请求体数据
319  http::Url url; // URL
320  HttpServerConfig * config; // 配置参数
321 
322  bool isWebSocketWrapper; // 是否启用websocket包装
324 };
325 
327 template < class _ClientCtx = WsHttpClientCtx >
328 class WsHttpServer : public Server<_ClientCtx>
329 {
330 public:
332 
333  using OpenHandlerFunction = std::function< void( ClientCtxSharedPointer clientCtxPtr ) >;
334  using MessageHandlerFunction = std::function< void( ClientCtxSharedPointer clientCtxPtr, winux::AnsiString const & data, int messageType ) >;
335  using CloseHandlerFunction = std::function< void( ClientCtxSharedPointer clientCtxPtr, winux::uint16 errCode, winux::AnsiString const & errStr ) >;
336  using ErrorHandlerFunction = std::function< void( ClientCtxSharedPointer clientCtxPtr, WebSocketErrorCode ec ) >;
337 
338  using ResponseHandlerFunction = std::function< void ( ClientCtxSharedPointer & clientCtxPtr, http::Header const & reqHdr, http::Url const & url, http::Header & rspHdr, std::ostream & rspOut ) >;
339 
341 
342  WsHttpServer( HttpServerConfig const & confObj ) :
343  Server<_ClientCtx>( ip::EndPoint( confObj.serverIp, confObj.serverPort ), confObj.threadCount, confObj.listenBacklog ),
344  config(confObj),
345  _cache(confObj.cacheLifeTime)
346  {
347  }
348 
349  WsHttpServer( winux::String const & serverIp, winux::ushort port, int threadCount = 10, int listenBacklog = 10, double durationSec = 0.1 ) :
350  Server<_ClientCtx>( ip::EndPoint( serverIp, port ), threadCount, listenBacklog ),
351  config( serverIp, port, threadCount, listenBacklog, durationSec ),
352  _cache(config.cacheLifeTime)
353  {
354  }
356  void setHandler( winux::String const & urlPath, ResponseHandlerFunction handler ) { _handlers[urlPath] = handler; }
357 
359  void onOpenHandler( OpenHandlerFunction handler ) { _openHandler = handler; }
361  void onMessageHandler( MessageHandlerFunction handler ) { _messageHandler = handler; }
363  void onCloseHandler( CloseHandlerFunction handler ) { _closeHandler = handler; }
365  void onErrorHandler( ErrorHandlerFunction handler ) { _errorHandler = handler; }
366 
367 protected:
368  // 业务启动函数
369  virtual void onStartup( ClientCtxSharedPointer clientCtxPtr ) override
370  {
371  clientCtxPtr->config = &this->config;
372  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgYellow, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`到来" );
373  this->_pool.task( &WsHttpServer::_doRecvRequestHeaderTask, this, clientCtxPtr ).post();
374  }
375 
376  // 尝试接收一个请求头的任务
378  {
379  int rcWait;
380  int rc = clientCtxPtr->clientSockPtr->recvWaitUntilTarget(
381  "\r\n\r\n",
382  &clientCtxPtr->forClient.data,
383  &clientCtxPtr->forClient.extraData,
384  &clientCtxPtr->forClient.hadBytes,
385  &clientCtxPtr->forClient.startpos,
386  &clientCtxPtr->forClient.pos,
387  this->config.durationSec,
388  &rcWait,
389  [&clientCtxPtr] ( int had, void* ) {
390  clientCtxPtr->forClient.retryCount = 0; // 重置次数
391  }
392  );
393 
394  if ( clientCtxPtr->forClient.pos != -1 ) // 接收成功
395  {
396  // 解析请求头
397  clientCtxPtr->requestHeaderStr = clientCtxPtr->forClient.data.toAnsi();
398  clientCtxPtr->requestHeader.clear();
399  clientCtxPtr->requestHeader.parse(clientCtxPtr->requestHeaderStr);
400 
401  clientCtxPtr->forClient.resetStatus(); // 重置收发状态
402  clientCtxPtr->forClient.data = std::move(clientCtxPtr->forClient.extraData); // 把额外接收的数据当作请求体数据
403  // 从Content-Length获取请求体大小
404  int contentLength = static_cast<http::Header&>(clientCtxPtr->requestHeader).getHeader<int>("Content-Length");
405  clientCtxPtr->forClient.targetBytes = contentLength - clientCtxPtr->forClient.data.getSize(); // 需要接收的目标大小
406  if ( clientCtxPtr->forClient.targetBytes > 0 ) // 需要完全接收请求体
407  {
408  // 投递尝试接收请求体
409  this->_pool.task( &WsHttpServer::_doRecvRequestBodyTask, this, clientCtxPtr ).post();
410  }
411  else // 不需要接收或已经接收完全请求体
412  {
413  // 保存请求体数据
414  clientCtxPtr->requestBody = std::move(clientCtxPtr->forClient.data);
415 
416  // 投递处理请求任务
417  this->_pool.task( &WsHttpServer::_doRequestTask, this, clientCtxPtr ).post();
418  }
419  }
420  else if ( rcWait == 0 )
421  {
422  clientCtxPtr->forClient.retryCount++;
423  if ( (int)clientCtxPtr->forClient.retryCount < this->config.retryCount )
424  {
425  // 重投尝试接收请求头
426  this->_pool.task( &WsHttpServer::_doRecvRequestHeaderTask, this, clientCtxPtr ).post();
427  }
428  else
429  {
430  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]从客户`", clientCtxPtr->clientEpStr, "`接收一个请求头时长时间没有数据到来" );
431  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
432  this->removeClient(clientCtxPtr->clientId);
433  }
434  }
435  else
436  {
437  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]从客户`", clientCtxPtr->clientEpStr, "`接收头部出错" );
438  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
439  this->removeClient(clientCtxPtr->clientId);
440  }
441  }
442 
443  // 尝试接收一个请求体的任务
445  {
446  int rcWait;
447  int rc = clientCtxPtr->clientSockPtr->recvWaitUntilSize(
448  clientCtxPtr->forClient.targetBytes,
449  &clientCtxPtr->forClient.data,
450  &clientCtxPtr->forClient.hadBytes,
451  this->config.durationSec,
452  &rcWait,
453  [&clientCtxPtr] ( int had, void* ) {
454  clientCtxPtr->forClient.retryCount = 0;
455  }
456  );
457 
458  if ( clientCtxPtr->forClient.hadBytes == clientCtxPtr->forClient.targetBytes ) // 接收完成
459  {
460  // 保存请求体数据
461  clientCtxPtr->requestBody = std::move(clientCtxPtr->forClient.data);
462  clientCtxPtr->forClient.resetStatus(); // 重置收发状态
463 
464  // 投递处理请求任务
465  this->_pool.task( &WsHttpServer::_doRequestTask, this, clientCtxPtr ).post();
466  }
467  else if ( rcWait == 0 )
468  {
469  clientCtxPtr->forClient.retryCount++;
470  if ( (int)clientCtxPtr->forClient.retryCount < this->config.retryCount )
471  {
472  // 重投尝试接收请求体
473  this->_pool.task( &WsHttpServer::_doRecvRequestBodyTask, this, clientCtxPtr ).post();
474  }
475  else
476  {
477  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]从客户`", clientCtxPtr->clientEpStr, "`接收一个请求体时长时间没有数据到来" );
478  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
479  this->removeClient(clientCtxPtr->clientId);
480  }
481  }
482  else
483  {
484  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]从客户`", clientCtxPtr->clientEpStr, "`接收请求体出错" );
485  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
486  this->removeClient(clientCtxPtr->clientId);
487  }
488  }
489 
490  // 处理一个请求的任务
492  {
493  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgGreen, "Client[", clientCtxPtr->clientId, "]从客户`", clientCtxPtr->clientEpStr, "`接收请求头(bytes:", clientCtxPtr->requestHeaderStr.size(), ")" );
494  if ( this->config.outputVerbose ) std::cout << clientCtxPtr->requestHeaderStr;
495  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgAtrovirens, "Client[", clientCtxPtr->clientId, "]从客户`", clientCtxPtr->clientEpStr, "`接收请求体(bytes:", clientCtxPtr->requestBody.getSize(), ")\n" );
496 
497  // 构造响应
498  std::stringbuf rspBuf;
499  std::ostream rspOut(&rspBuf);
500  http::Header rspHdr;
501  rspHdr.setResponseLine( "HTTP/1.1 200 OK", false );
502  rspHdr["Content-Type"] = "text/html";
503 
504  clientCtxPtr->url.clear();
505  clientCtxPtr->url.parse( clientCtxPtr->requestHeader.getUrl() );
506 
507  // 调用具体HTTP业务处理
508  this->_httpProcess( clientCtxPtr, clientCtxPtr->requestHeader, clientCtxPtr->url, rspHdr, rspOut );
509 
510  // 输出响应
511  winux::AnsiString rspStr = rspBuf.str();
512  rspHdr("Content-Length") << rspStr.size();
513  winux::AnsiString rspHdrStr = rspHdr.toString();
514 
515  // 发送响应
516  if ( clientCtxPtr->clientSockPtr->sendUntil( rspHdrStr + rspStr ) )
517  {
518  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgGreen, "Client[", clientCtxPtr->clientId, "]服务器发送给客户`", clientCtxPtr->clientEpStr, "`响应(bytes:", rspHdrStr.size() + rspStr.size(), ")" );
519 
520  // 如果不保活就删除连接,否则继续投请求任务
521  if ( winux::StrLower( clientCtxPtr->requestHeader["Connection"] ) == "keep-alive" )
522  {
523  // 尝试接收下一个请求头
524  this->_pool.task( &WsHttpServer::_doRecvRequestHeaderTask, this, clientCtxPtr ).post();
525  }
526  else if ( clientCtxPtr->isWebSocketWrapper ) // 是否启用websocket包装,投递websocket处理任务
527  {
528  // 已经成功建立websocket连接
529  clientCtxPtr->websocket.state = ws::stateOpen;
530  // 调用onopen事件虚函数
531  this->onOpen(clientCtxPtr);
532 
533  // 投递接收websocket帧任务
534  this->_pool.task( &WsHttpServer::_doRecvWebSocketFrameTask, this, clientCtxPtr ).post();
535  }
536  else
537  {
538  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgAqua, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`不保活,通信完毕即关闭" );
539  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
540  this->removeClient(clientCtxPtr->clientId);
541  }
542  }
543  else // 发送响应失败
544  {
545  if ( clientCtxPtr->isWebSocketWrapper )
546  {
547  // websocket出错,连接意外失效
548  this->onError( clientCtxPtr, errCnnUnexpectedInvalid );
549  }
550  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`发送响应失败" );
551  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
552  this->removeClient(clientCtxPtr->clientId);
553  }
554  }
555 
556  // 具体HTTP业务处理
557  void _httpProcess( ClientCtxSharedPointer & clientCtxPtr, http::Header const & reqHdr, http::Url const & url, http::Header & rspHdr, std::ostream & rspOut )
558  {
559  // 是否升级为websocket连接
560  if (
561  reqHdr.hasHeader("Upgrade") && reqHdr.getHeader("Upgrade") == "websocket" &&
562  reqHdr.hasHeader("Connection") && reqHdr.getHeader("Connection") == "Upgrade" &&
563  reqHdr.hasHeader("Sec-WebSocket-Key")
564  )
565  {
566  rspHdr.clear();
567  rspHdr.setResponseLine( "HTTP/1.1 101 Switching Protocols", false );
568  rspHdr["Upgrade"] = "websocket";
569  rspHdr["Connection"] = "Upgrade";
570  winux::String secWebsocketAccept = Base64Encode( winux::Sha1( reqHdr.getHeader("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ) );
571  rspHdr["Sec-WebSocket-Accept"] = secWebsocketAccept;
572 
573  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgAqua, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`升级为websocket连接" );
574  if ( this->config.outputVerbose ) std::cout << rspHdr.toString();
575 
576  // 正在升级为websocket
577  clientCtxPtr->websocket.state = ws::stateConnecting;
578 
579  // 启用WebSocket包装
580  clientCtxPtr->isWebSocketWrapper = true;
581  }
582  else
583  {
584  this->_webProcess( clientCtxPtr, reqHdr, url, rspHdr, rspOut );
585  }
586  }
587 
588  // Web处理
589  void _webProcess( ClientCtxSharedPointer & clientCtxPtr, http::Header const & reqHdr, http::Url const & url, http::Header & rspHdr, std::ostream & rspOut )
590  {
591  winux::String urlPath = url.getPath();
592 
593  if ( winux::isset( _handlers, urlPath ) ) // 有响应处理
594  {
595  _handlers[urlPath].operator()( clientCtxPtr, clientCtxPtr->requestHeader, url, rspHdr, rspOut );
596 
597  if ( this->config.outputVerbose )
598  ColorOutputLine( winux::fgAqua, "Client[", clientCtxPtr->clientId, "] `", clientCtxPtr->clientEpStr, "` → request_uri: ", url.toString() );
599  }
600  else // 无响应处理,可以处理静态文件
601  {
602  winux::String filePath;
603  if ( urlPath.empty() )
604  {
605  winux::String fileName = this->config.documentIndex;
606  filePath = winux::CombinePath( this->config.documentRoot, fileName );
607  }
608  else
609  {
610  bool isDir = false;
611  filePath = winux::CombinePath( this->config.documentRoot, urlPath );
612  if ( winux::DetectPath( filePath, &isDir ) )
613  {
614  if ( isDir )
615  {
616  winux::String fileName = this->config.documentIndex;
617  filePath = winux::CombinePath( filePath, fileName );
618  }
619  }
620  else
621  {
622  goto ERR_NOT_FOUND;
623  }
624  }
625 
626  if ( _cache.hasCache(filePath) ) // 是否存在缓存
627  {
628  StaticFileMemoryCache::CacheItem const & cacheItem = _cache.get(filePath);
629  rspHdr["Content-Type"] = cacheItem.mime;
630  rspOut.write( cacheItem.content.getBuf<char const>(), cacheItem.content.getSize() );
631  if ( this->config.outputVerbose )
632  ColorOutputLine( winux::fgAqua, "Client[", clientCtxPtr->clientId, "] `", clientCtxPtr->clientEpStr, "` 读取到了缓存`",filePath,"`" );
633  }
634  else if ( winux::DetectPath(filePath) ) // 静态文件是否存在
635  {
636  winux::String extName;
637  winux::FileTitle( filePath, &extName );
638  StaticFileMemoryCache::CacheItem & cacheItem = _cache.writeCache( filePath, this->config.getMime(extName), winux::FileGetContentsEx( filePath, false ) );
639  rspHdr["Content-Type"] = cacheItem.mime;
640  rspOut.write( cacheItem.content.getBuf<char const>(), cacheItem.content.getSize() );
641  if ( this->config.outputVerbose )
642  ColorOutputLine( winux::fgAqua, "Client[", clientCtxPtr->clientId, "] `", clientCtxPtr->clientEpStr, "` 读取到了静态文件`",filePath,"`" );
643  }
644  else
645  {
646  ERR_NOT_FOUND:
647  // 响应
648  rspOut << "<h1>HTTP 404</h1><strong>URLpath: `" << filePath << "` is not found!</strong>";
649  rspHdr.setStatusCode("404").setStatusStr("Not found");
650  }
651 
652  if ( this->config.outputVerbose )
653  ColorOutputLine( winux::fgAqua, "Client[", clientCtxPtr->clientId, "] `", clientCtxPtr->clientEpStr, "` → request_uri: ", url.toString(), ", filepath: ", filePath );
654  }
655  }
656 
657  // 接收一个WebSocket帧的任务
659  {
660  winux::GrowBuffer frameBuf;
661  ws::FrameBase * frame0;
662  winux::int64 wantBytes = 0;
663  winux::uint8 frameType = 0;
664 
665  // 先读取2字节的帧基础
666  if ( !clientCtxPtr->clientSockPtr->recvUntilSize( sizeof(ws::FrameBase), &frameBuf ) )
667  {
668  // websocket出错,连接意外失效
669  this->onError( clientCtxPtr, errCnnUnexpectedInvalid );
670  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`读取帧基础失败" );
671  goto RecvFrameFailure;
672  }
673 
674  frame0 = frameBuf.getBuf<ws::FrameBase>();
675 
676  // 判断并读取载荷数据长度
677  if ( frame0->payloadLen < 126 )
678  {
679  frameType |= ws::ftBasic;
680  auto frame1 = frameBuf.getBuf<ws::FrameBasic>();
681  wantBytes += frame1->payloadLen;
682  }
683  else if ( frame0->payloadLen == 126 )
684  {
685  frameType |= ws::ftExtended16;
686  if ( !clientCtxPtr->clientSockPtr->recvUntilSize( sizeof(ws::FrameExtended16::extendedPayloadLen), &frameBuf ) )
687  {
688  // websocket出错,连接意外失效
689  this->onError( clientCtxPtr, errCnnUnexpectedInvalid );
690  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`读取16位帧载荷长度失败" );
691  goto RecvFrameFailure;
692  }
693  auto len = ntoht( frameBuf.getBuf<ws::FrameExtended16>()->extendedPayloadLen ); // `extendedPayloadLen` is network byte order
694  wantBytes += len;
695  }
696  else // 127
697  {
698  frameType |= ws::ftExtended64;
699  if ( !clientCtxPtr->clientSockPtr->recvUntilSize( sizeof(ws::FrameExtended64::extendedPayloadLen), &frameBuf ) )
700  {
701  // websocket出错,连接意外失效
702  this->onError( clientCtxPtr, errCnnUnexpectedInvalid );
703  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`读取64位帧载荷长度失败" );
704  goto RecvFrameFailure;
705  }
706  auto len = ntoht( frameBuf.getBuf<ws::FrameExtended64>()->extendedPayloadLen ); // `extendedPayloadLen` is network byte order
707  wantBytes += len;
708  }
709 
710  // 是否有掩码
711  if ( frame0->mask )
712  {
713  frameType |= 1;
714  // 读取掩码
715  if ( !clientCtxPtr->clientSockPtr->recvUntilSize( 4, &frameBuf ) )
716  {
717  // websocket出错,连接意外失效
718  this->onError( clientCtxPtr, errCnnUnexpectedInvalid );
719  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`读取帧掩码失败" );
720  goto RecvFrameFailure;
721  }
722  }
723 
724  // 读取载荷数据
725  if ( !clientCtxPtr->clientSockPtr->recvUntilSize( (int)wantBytes, &frameBuf ) )
726  {
727  // websocket出错,连接意外失效
728  this->onError( clientCtxPtr, errCnnUnexpectedInvalid );
729  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`读取载荷数据失败" );
730  goto RecvFrameFailure;
731  }
732 
733  // 进入帧处理
734  switch ( frameType )
735  {
736  case ws::ftBasic:
737  {
738  auto frame = frameBuf.getBuf<ws::FrameBasic>();
739  // 得到数据
740  char * data = frame->data<char>();
741  size_t len = frame->payloadLen;
742 
743  // 投递处理WebSocket帧任务
744  this->_pool.task( &WsHttpServer::_doProcessWebSocketFrameTask, this, clientCtxPtr, (bool)frame->fin, (winux::uint)frame->opcode, winux::Buffer( data, len ) ).post();
745  }
746  break;
747  case ws::ftBasicMasked:
748  {
749  auto frame = frameBuf.getBuf<ws::FrameBasicMasked>();
750  // 得到数据
751  char * data = frame->data<char>();
752  int len = frame->payloadLen;
753 
754  // 掩码算法
755  for ( int i = 0; i < len; ++i )
756  data[i] ^= frame->maskingKey[ i % 4 ];
757 
758  // 投递处理WebSocket帧任务
759  this->_pool.task( &WsHttpServer::_doProcessWebSocketFrameTask, this, clientCtxPtr, (bool)frame->fin, (winux::uint)frame->opcode, winux::Buffer( data, len ) ).post();
760  }
761  break;
762  case ws::ftExtended16:
763  {
764  auto frame = frameBuf.getBuf<ws::FrameExtended16>();
765  // 得到数据
766  char * data = frame->data<char>();
767  int len = ntoht(frame->extendedPayloadLen);
768 
769  // 投递处理WebSocket帧任务
770  this->_pool.task( &WsHttpServer::_doProcessWebSocketFrameTask, this, clientCtxPtr, (bool)frame->fin, (winux::uint)frame->opcode, winux::Buffer( data, len ) ).post();
771  }
772  break;
774  {
775  auto frame = frameBuf.getBuf<ws::FrameExtended16Masked>();
776  // 得到数据
777  char * data = frame->data<char>();
778  int len = ntoht(frame->extendedPayloadLen);
779 
780  // 掩码算法
781  for ( int i = 0; i < len; ++i )
782  data[i] ^= frame->maskingKey[ i % 4 ];
783 
784  // 投递处理WebSocket帧任务
785  this->_pool.task( &WsHttpServer::_doProcessWebSocketFrameTask, this, clientCtxPtr, (bool)frame->fin, (winux::uint)frame->opcode, winux::Buffer( data, (winux::uint)len ) ).post();
786  }
787  break;
788  case ws::ftExtended64:
789  {
790  auto frame = frameBuf.getBuf<ws::FrameExtended64>();
791  // 得到数据
792  char * data = frame->data<char>();
793  winux::int64 len = ntoht(frame->extendedPayloadLen);
794 
795  // 投递处理WebSocket帧任务
796  this->_pool.task( &WsHttpServer::_doProcessWebSocketFrameTask, this, clientCtxPtr, (bool)frame->fin, (winux::uint)frame->opcode, winux::Buffer( data, (winux::uint)len ) ).post();
797  }
798  break;
800  {
801  auto frame = frameBuf.getBuf<ws::FrameExtended64Masked>();
802  // 得到数据
803  char * data = frame->data<char>();
804  winux::int64 len = ntoht(frame->extendedPayloadLen);
805 
806  // 掩码算法
807  for ( winux::int64 i = 0; i < len; ++i )
808  data[i] ^= frame->maskingKey[ i % 4 ];
809 
810  // 投递处理WebSocket帧任务
811  this->_pool.task( &WsHttpServer::_doProcessWebSocketFrameTask, this, clientCtxPtr, (bool)frame->fin, (winux::uint)frame->opcode, winux::Buffer( data, (winux::uint)len ) ).post();
812  }
813  break;
814  }
815 
816  return;
817 
818  RecvFrameFailure:
819  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
820  this->removeClient(clientCtxPtr->clientId);
821  }
822 
823  // 处理WebSocket帧的任务
824  void _doProcessWebSocketFrameTask( ClientCtxSharedPointer clientCtxPtr, bool fin, winux::uint opcode, winux::Buffer & payloadData )
825  {
826  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgGreen, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`收到一个帧{fin:", (winux::uint)fin, ",opcode:", opcode, ",datasize:", payloadData.getSize(), "}" );
827 
828  switch ( opcode )
829  {
830  case ws::dataContinued:
831  {
832  if ( fin )
833  {
834  clientCtxPtr->websocket.messageBuf.append( payloadData.getBuf<char>(), payloadData.getSize() );
835  this->onMessage( clientCtxPtr, clientCtxPtr->websocket.messageBuf, clientCtxPtr->websocket.messageType );
836  clientCtxPtr->websocket.messageBuf.clear();
837  clientCtxPtr->websocket.messageType = 0;
838  }
839  else
840  {
841  clientCtxPtr->websocket.messageBuf.append( payloadData.getBuf<char>(), payloadData.getSize() );
842  }
843 
844  // 投递接收下一个websocket帧的任务
845  this->_pool.task( &WsHttpServer::_doRecvWebSocketFrameTask, this, clientCtxPtr ).post();
846  }
847  break;
848  case ws::dataText:
849  case ws::dataBinary:
850  case ws::dataRsv3:
851  case ws::dataRsv4:
852  case ws::dataRsv5:
853  case ws::dataRsv6:
854  case ws::dataRsv7:
855  {
856  if ( fin )
857  {
858  clientCtxPtr->websocket.messageBuf.append( payloadData.getBuf<char>(), payloadData.getSize() );
859  clientCtxPtr->websocket.messageType = opcode;
860  this->onMessage( clientCtxPtr, clientCtxPtr->websocket.messageBuf, clientCtxPtr->websocket.messageType );
861  clientCtxPtr->websocket.messageBuf.clear();
862  clientCtxPtr->websocket.messageType = 0;
863  }
864  else
865  {
866  clientCtxPtr->websocket.messageBuf.append( payloadData.getBuf<char>(), payloadData.getSize() );
867  clientCtxPtr->websocket.messageType = opcode;
868  }
869 
870  // 投递接收下一个websocket帧的任务
871  this->_pool.task( &WsHttpServer::_doRecvWebSocketFrameTask, this, clientCtxPtr ).post();
872  }
873  break;
874  case ws::ctrlClose:
875  {
876  // 收到一个close帧
877  // 如果有载荷
878  winux::uint16 errCode = 0;
879  winux::AnsiString errStr;
880  if ( payloadData.getSize() > 0 )
881  {
882  auto closeData = payloadData.getBuf<ws::CloseFramePayloadData>();
883  errCode = ntoht(closeData->code);
884  errStr.assign( closeData->data<char>(), payloadData.getSize() - sizeof(ws::CloseFramePayloadData::code) );
885  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgMaroon, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`收到一个Close帧{errcode:", errCode, ",errstr:", errStr, "}" );
886  }
887  else // 没有载荷数据
888  {
889  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgMaroon, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`收到一个Close帧" );
890  }
891 
892  // 收到一个close帧,如果处在closing状态,应立即关闭连接并改为closed状态;如果不处在closing状态则改为closing,发送一个close帧到对方后改为closed。
893  if ( clientCtxPtr->websocket.state == ws::stateClosing )
894  {
895  clientCtxPtr->clientSockPtr->close();
896  clientCtxPtr->websocket.state = ws::stateClosed;
897 
898  this->onClose( clientCtxPtr, errCode, errStr ); // 处理onclose事件
899 
900  // 删除场景
901  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
902  this->removeClient(clientCtxPtr->clientId);
903  }
904  else
905  {
906  clientCtxPtr->websocket.state = ws::stateClosing;
907 
908  if ( clientCtxPtr->websocket.close( errCode, errStr ) )
909  {
910  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgAqua, "服务器回了一个close帧" );
911  clientCtxPtr->clientSockPtr->close();
912  clientCtxPtr->websocket.state = ws::stateClosed;
913  }
914 
915  this->onClose( clientCtxPtr, errCode, errStr ); // 处理onclose事件
916 
917  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
918  this->removeClient(clientCtxPtr->clientId);
919  }
920  }
921  break;
922  case ws::ctrlPing:
923  {
924  // 收到一个ping帧,回一个pong帧
925  SendWebSocketAnsi( clientCtxPtr->clientSockPtr.get(), ws::ctrlPong, false );
926 
927  // 投递接收下一个websocket帧任务
928  this->_pool.task( &WsHttpServer::_doRecvWebSocketFrameTask, this, clientCtxPtr ).post();
929  }
930  break;
931  case ws::ctrlPong:
932  {
933  // 收到pong帧,无视
934 
935  // 投递接收下一个websocket帧任务
936  this->_pool.task( &WsHttpServer::_doRecvWebSocketFrameTask, this, clientCtxPtr ).post();
937  }
938  break;
939  default:
940  // 意外帧
941  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgRed, "Client[", clientCtxPtr->clientId, "]客户`", clientCtxPtr->clientEpStr, "`收到一个意外帧{fin:",(winux::uint)fin,",opcode:",opcode,",datasize:",payloadData.getSize(),"}" );
942  if ( this->config.outputVerbose ) ColorOutputLine( winux::fgFuchsia, "Client[", clientCtxPtr->clientId, "]移除客户`", clientCtxPtr->clientEpStr, "`,还剩", this->getClientsCount() - 1, "客户" );
943  this->removeClient(clientCtxPtr->clientId);
944  break;
945  }
946  }
947 
948  virtual void onOpen( ClientCtxSharedPointer clientCtxPtr )
949  {
950  if ( _openHandler ) _openHandler(clientCtxPtr);
951  }
952 
953  virtual void onMessage( ClientCtxSharedPointer clientCtxPtr, winux::AnsiString const & data, int messageType )
954  {
955  if ( _messageHandler ) _messageHandler( clientCtxPtr, data, messageType );
956  }
957 
958  virtual void onClose( ClientCtxSharedPointer clientCtxPtr, winux::uint16 errCode, winux::AnsiString const & errStr )
959  {
960  if ( _closeHandler ) _closeHandler( clientCtxPtr, errCode, errStr );
961  }
962 
963  virtual void onError( ClientCtxSharedPointer clientCtxPtr, WebSocketErrorCode ec )
964  {
965  if ( _errorHandler ) _errorHandler( clientCtxPtr, ec );
966  }
967 
972 
973  std::map< winux::String, ResponseHandlerFunction > _handlers; // 响应处理函数
974  StaticFileMemoryCache _cache; // 静态文件内存缓存
975 
976 };
977 
978 } // namespace ws
979 
980 } // namespace old_v1
981 
982 } // namespace eiennet
XString< char > AnsiString
Definition: utilities.hpp:212
#define EIENNET_FUNC_DECL(ret)
__int64 int64
Definition: utilities.hpp:187
void append(void const *data, size_t size)
添加数据:C语言缓冲区
代表HTTP头部
Definition: http_misc.hpp:10
void * getBuf() const
暴露缓冲区指针
Definition: utilities.hpp:585
typename Server< _ClientCtx >::ClientCtxSharedPointer ClientCtxSharedPointer
void _doRecvWebSocketFrameTask(ClientCtxSharedPointer clientCtxPtr)
winux::SharedPointer< ip::tcp::Socket > clientSockPtr
void clear()
清空
virtual void onClose(ClientCtxSharedPointer clientCtxPtr, winux::uint16 errCode, winux::AnsiString const &errStr)
winux::String getMime(winux::String const &extName) const
void _doRecvRequestHeaderTask(ClientCtxSharedPointer clientCtxPtr)
FrameType
数据帧类型
winux::String getHeader(winux::String const &name, winux::String const &defval=winux::String()) const
Definition: http_misc.hpp:82
virtual void onOpen(ClientCtxSharedPointer clientCtxPtr)
winux::String toString(bool isAddCrlfAtEnd=true) const
转为字符串,isAddCrlfAtEnd指示末尾是否添加\r\n
static void ColorOutputLine(winux::ConsoleAttr const &ca, _ArgType &&...arg)
Definition: console.hpp:181
void _doProcessWebSocketFrameTask(ClientCtxSharedPointer clientCtxPtr, bool fin, winux::uint opcode, winux::Buffer &payloadData)
XString< tchar > String
Definition: utilities.hpp:216
std::function< void(ClientCtxSharedPointer clientCtxPtr, winux::uint16 errCode, winux::AnsiString const &errStr) > CloseHandlerFunction
Header & setStatusCode(winux::String const &statusCode)
状态码. For a response: required.
Definition: http_misc.hpp:155
bool send(winux::AnsiString const &data, ws::OpCode opcode=ws::dataText)
端点基类(套接字地址对象基类)
Buffer Sha1(void const *buf, size_t size)
将数据进行sha1编码,返回二进制数据
http协议的相关简单类封装
Definition: http.hpp:32
std::function< void(ClientCtxSharedPointer clientCtxPtr, winux::AnsiString const &data, int messageType) > MessageHandlerFunction
size_t getSize() const
获取数据大小
Definition: utilities.hpp:613
URL类
Definition: http_url.hpp:8
bool SendWebSocketBuffer(eiennet::Socket *sock, OpCode opcode, bool mask, winux::Buffer payloadData=winux::Buffer(), size_t perFrameMaxPayloadSize=-1)
void setHandler(winux::String const &urlPath, ResponseHandlerFunction handler)
设置动态页面处理
WsHttpClientCtx(winux::uint64 clientId, winux::String clientEpStr, winux::SharedPointer< ip::tcp::Socket > clientSockPtr)
静态文件内存缓存(带互斥锁)
WsHttpServer(HttpServerConfig const &confObj)
void onOpenHandler(OpenHandlerFunction handler)
设置WebSocket打开事件处理
套接字基础类
void _webProcess(ClientCtxSharedPointer &clientCtxPtr, http::Header const &reqHdr, http::Url const &url, http::Header &rspHdr, std::ostream &rspOut)
缓冲区,表示内存中一块二进制数据(利用malloc/realloc进行内存分配)
Definition: utilities.hpp:528
数据收发场景,存放数据收发过程中的一些变量
bool SendWebSocketAnsi(eiennet::Socket *sock, OpCode opcode, bool mask, winux::AnsiString payloadData=winux::AnsiString(), size_t perFrameMaxPayloadSize=-1)
String CombinePath(String const &dirPath, String const &fileName)
把一个目录路径和一个文件名组合成一个新路径
String Base64Encode(void const *buf, size_t size)
Base64编码
bool DetectPath(String const &path, bool *isDir=NULL)
探测一个路径是存在还是不存在,是目录还是文件
void _doRecvRequestBodyTask(ClientCtxSharedPointer clientCtxPtr)
bool hasHeader(winux::String const &name) const
Definition: http_misc.hpp:91
bool isset(_MAP const &m, _KEY const &k)
检测map中是否有该键的值
Definition: utilities.hpp:268
客户场景类基础
unsigned int uint
Definition: utilities.hpp:170
void setResponseLine(winux::String const &responseLine, bool setStatus=true)
设置响应行 格式: HttpVersion StatusCode StatusStr eg. HTTP/1.1 200 OK
void onCloseHandler(CloseHandlerFunction handler)
设置WebSocket关闭事件处理
String FileTitle(String const &fileName, String *extName=NULL)
获取文件标题
WebSocketErrorCode
WebSocket错误码
unsigned char byte
Definition: utilities.hpp:204
网络通信库
virtual void onError(ClientCtxSharedPointer clientCtxPtr, WebSocketErrorCode ec)
std::function< void(ClientCtxSharedPointer clientCtxPtr, WebSocketErrorCode ec) > ErrorHandlerFunction
高效的可增长缓冲区,1.33倍冗余量
Definition: utilities.hpp:659
void onErrorHandler(ErrorHandlerFunction handler)
设置WebSocket出错事件处理
std::function< void(ClientCtxSharedPointer &clientCtxPtr, http::Header const &reqHdr, http::Url const &url, http::Header &rspHdr, std::ostream &rspOut) > ResponseHandlerFunction
std::map< winux::String, ResponseHandlerFunction > _handlers
virtual void onStartup(ClientCtxSharedPointer clientCtxPtr) override
AnsiString StrLower(AnsiString str)
使字符串小写
Definition: strings.hpp:238
_Ty ntoht(_Ty v)
virtual void onMessage(ClientCtxSharedPointer clientCtxPtr, winux::AnsiString const &data, int messageType)
unsigned short ushort
Definition: utilities.hpp:173
bool close(winux::uint16 errCode=-1, winux::AnsiString const &errStr="")
MessageHandlerFunction _messageHandler
unsigned short uint16
Definition: utilities.hpp:173
_Ty htont(_Ty v)
bool SendWebSocketFrame(eiennet::Socket *sock, OpCode opcode, bool fin, bool mask, winux::byte *payloadData=nullptr, size_t payloadDataSize=0)
winux::String toString() const
根据flags和存储的信息,组装成整个URL串.
winux::String getPath() const
获取路径。不以&#39;/&#39;开头
WsHttpServer(winux::String const &serverIp, winux::ushort port, int threadCount=10, int listenBacklog=10, double durationSec=0.1)
服务器类基础
unsigned __int64 uint64
Definition: utilities.hpp:185
void onMessageHandler(MessageHandlerFunction handler)
设置WebSocket消息到达事件处理
Header & setStatusStr(winux::String const &statusStr)
状态文本. For a response: Not required
Definition: http_misc.hpp:157
void _doRequestTask(ClientCtxSharedPointer clientCtxPtr)
unsigned char uint8
Definition: utilities.hpp:175
void _httpProcess(ClientCtxSharedPointer &clientCtxPtr, http::Header const &reqHdr, http::Url const &url, http::Header &rspHdr, std::ostream &rspOut)
std::function< void(ClientCtxSharedPointer clientCtxPtr) > OpenHandlerFunction
Buffer FileGetContentsEx(String const &filename, bool textMode)
载入文件内容为一个Buffer,textMode表示是否为文本模式