开源的socket服务端客户端,支持C# C++

ClientDlg.cpp 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. // ClientDlg.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "Client.h"
  5. #include "ClientDlg.h"
  6. #include "afxdialogex.h"
  7. // CClientDlg dialog
  8. #define DEFAULT_PATH _T("req/path?p1=v1&p2=v2")
  9. #define DEFAULT_ADDRESS _T("127.0.0.1")
  10. #define DEFAULT_PORT _T("8080")
  11. CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/)
  12. : CDialogEx(CClientDlg::IDD, pParent)
  13. {
  14. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  15. }
  16. void CClientDlg::DoDataExchange(CDataExchange* pDX)
  17. {
  18. CDialogEx::DoDataExchange(pDX);
  19. DDX_Control(pDX, IDC_SEND, m_Send);
  20. DDX_Control(pDX, IDC_INFO, m_Info);
  21. DDX_Control(pDX, IDC_ADDRESS, m_Address);
  22. DDX_Control(pDX, IDC_PORT, m_Port);
  23. DDX_Control(pDX, IDC_START, m_Start);
  24. DDX_Control(pDX, IDC_STOP, m_Stop);
  25. DDX_Control(pDX, IDC_METHOD, m_Method);
  26. DDX_Control(pDX, IDC_SCHEMA, m_Schema);
  27. DDX_Control(pDX, IDC_PATH, m_Path);
  28. DDX_Control(pDX, IDC_HEADER_NAME, m_HeaderName);
  29. DDX_Control(pDX, IDC_HEADER_VALUE, m_HeaderValue);
  30. DDX_Control(pDX, IDC_HEADER_ADD, m_HeaderAdd);
  31. DDX_Control(pDX, IDC_HEADERS, m_Headers);
  32. DDX_Control(pDX, IDC_BODY, m_Body);
  33. }
  34. BEGIN_MESSAGE_MAP(CClientDlg, CDialogEx)
  35. ON_WM_PAINT()
  36. ON_WM_QUERYDRAGICON()
  37. ON_BN_CLICKED(IDC_SEND, &CClientDlg::OnBnClickedSend)
  38. ON_BN_CLICKED(IDC_START, &CClientDlg::OnBnClickedStart)
  39. ON_BN_CLICKED(IDC_STOP, &CClientDlg::OnBnClickedStop)
  40. ON_BN_CLICKED(IDC_HEADER_ADD, &CClientDlg::OnBnClickedHeaderAdd)
  41. ON_CBN_SELCHANGE(IDC_METHOD, &CClientDlg::OnCbnSelchangeMethod)
  42. ON_MESSAGE(USER_INFO_MSG, OnUserInfoMsg)
  43. ON_WM_VKEYTOITEM()
  44. END_MESSAGE_MAP()
  45. // CClientDlg message handlers
  46. BOOL CClientDlg::OnInitDialog()
  47. {
  48. CDialogEx::OnInitDialog();
  49. // Set the icon for this dialog. The framework does this automatically
  50. // when the application's main window is not a dialog
  51. SetIcon(m_hIcon, TRUE); // Set big icon
  52. SetIcon(m_hIcon, FALSE); // Set small icon
  53. // TODO: Add extra initialization here
  54. m_Method.SetCurSel(3);
  55. m_Schema.SetCurSel(0);
  56. m_Path.SetWindowText(DEFAULT_PATH);
  57. m_Address.SetWindowText(DEFAULT_ADDRESS);
  58. m_Port.SetWindowText(DEFAULT_PORT);
  59. ::SetMainWnd(this);
  60. ::SetInfoList(&m_Info);
  61. SetAppState(ST_STOPPED);
  62. m_bWebSocket = FALSE;
  63. return TRUE; // return TRUE unless you set the focus to a control
  64. }
  65. // If you add a minimize button to your dialog, you will need the code below
  66. // to draw the icon. For MFC applications using the document/view model,
  67. // this is automatically done for you by the framework.
  68. void CClientDlg::OnPaint()
  69. {
  70. if (IsIconic())
  71. {
  72. CPaintDC dc(this); // device context for painting
  73. SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
  74. // Center icon in client rectangle
  75. int cxIcon = GetSystemMetrics(SM_CXICON);
  76. int cyIcon = GetSystemMetrics(SM_CYICON);
  77. CRect rect;
  78. GetClientRect(&rect);
  79. int x = (rect.Width() - cxIcon + 1) / 2;
  80. int y = (rect.Height() - cyIcon + 1) / 2;
  81. // Draw the icon
  82. dc.DrawIcon(x, y, m_hIcon);
  83. }
  84. else
  85. {
  86. CDialogEx::OnPaint();
  87. }
  88. }
  89. // The system calls this function to obtain the cursor to display while the user drags
  90. // the minimized window.
  91. HCURSOR CClientDlg::OnQueryDragIcon()
  92. {
  93. return static_cast<HCURSOR>(m_hIcon);
  94. }
  95. BOOL CClientDlg::PreTranslateMessage(MSG* pMsg)
  96. {
  97. if (
  98. pMsg->message == WM_KEYDOWN
  99. &&( pMsg->wParam == VK_ESCAPE
  100. || pMsg->wParam == VK_CANCEL
  101. || (pMsg->wParam == VK_RETURN && pMsg->hwnd == this->GetSafeHwnd())
  102. ))
  103. return TRUE;
  104. return CDialog::PreTranslateMessage(pMsg);
  105. }
  106. void CClientDlg::SetAppState(EnAppState state)
  107. {
  108. m_enState = state;
  109. if(m_enState == ST_STOPPED)
  110. m_bWebSocket = FALSE;
  111. if(this->GetSafeHwnd() == nullptr)
  112. return;
  113. m_Start.EnableWindow(m_enState == ST_STOPPED);
  114. m_Stop.EnableWindow(m_enState == ST_STARTED);
  115. m_Send.EnableWindow(m_enState == ST_STARTED);
  116. m_Schema.EnableWindow(m_enState == ST_STOPPED);
  117. m_Address.EnableWindow(m_enState == ST_STOPPED);
  118. m_Port.EnableWindow(m_enState == ST_STOPPED);
  119. m_Body.EnableWindow(m_Method.GetCurSel() <= 2 || m_bWebSocket);
  120. m_Path.EnableWindow(m_Method.GetCurSel() != 8);
  121. }
  122. void CClientDlg::OnBnClickedHeaderAdd()
  123. {
  124. CString strName;
  125. CString strValue;
  126. m_HeaderName.GetWindowText(strName);
  127. m_HeaderValue.GetWindowText(strValue);
  128. strName.Trim();
  129. strValue.Trim();
  130. if(!strName.IsEmpty())
  131. {
  132. if(strName == _T("Content-Length"))
  133. {
  134. MessageBox(_T("'Content-Length' header can't be specified!"), _T("Add Header"), MB_OK | MB_ICONINFORMATION);
  135. m_HeaderName.SetFocus();
  136. return;
  137. }
  138. CString strHeader;
  139. strHeader.Format(_T("%s: %s"), strName, strValue);
  140. m_Headers.AddString(strHeader);
  141. m_HeaderName.SetWindowText(_T(""));
  142. m_HeaderValue.SetWindowText(_T(""));
  143. }
  144. }
  145. void CClientDlg::OnBnClickedSend()
  146. {
  147. m_bWebSocket ? SendWebSocket() : SendHttp();
  148. }
  149. void CClientDlg::SendHttp()
  150. {
  151. USES_CONVERSION;
  152. if(!CheckStarted(TRUE))
  153. return;
  154. CString strMethod;
  155. CString strSchema;
  156. CString strAddress;
  157. CString strPort;
  158. CString strPath;
  159. m_Method.GetWindowText(strMethod);
  160. m_Schema.GetWindowText(strSchema);
  161. m_Address.GetWindowText(strAddress);
  162. m_Port.GetWindowText(strPort);
  163. if(m_Method.GetCurSel() != 8)
  164. {
  165. m_Path.GetWindowText(strPath);
  166. strPath.Trim();
  167. if(strPath.IsEmpty() || strPath.GetAt(0) != '/')
  168. strPath.Insert(0, '/');
  169. }
  170. THttpHeaderMap headers;
  171. int iCount = m_Headers.GetCount();
  172. for(int i = 0; i < iCount; i++)
  173. {
  174. CString strHeader;
  175. m_Headers.GetText(i, strHeader);
  176. int j = 0;
  177. CString strName = strHeader.Tokenize(_T(": "), j);
  178. CString strValue = strHeader.Mid(j + 1);
  179. headers.emplace(THttpHeaderMap::value_type(T2A(strName), T2A(strValue)));
  180. }
  181. CStringA strBodyA;
  182. CStringA strPathA;
  183. if(m_Method.GetCurSel() <= 2)
  184. {
  185. CString strBody;
  186. m_Body.GetWindowText(strBody);
  187. strBodyA = T2A(strBody);
  188. }
  189. else if(m_Method.GetCurSel() == 8)
  190. {
  191. THttpHeaderMapCI it = headers.find("Host");
  192. if(it != headers.end() && !it->second.IsEmpty())
  193. strPathA = it->second;
  194. else
  195. {
  196. CString strHost;
  197. strHost.Format(_T("%s:%s"), strAddress, strPort);
  198. strPathA = strHost;
  199. }
  200. }
  201. if(strPathA.IsEmpty())
  202. strPathA = T2A(strPath);
  203. DWORD dwIndex = 0;
  204. DWORD dwSize = (DWORD)headers.size();
  205. unique_ptr<THeader[]> szHeaders(new THeader[dwSize]);
  206. for(THttpHeaderMapCI it = headers.begin(), end = headers.end(); it != end; ++it, ++dwIndex)
  207. {
  208. szHeaders[dwIndex].name = it->first;
  209. szHeaders[dwIndex].value = it->second;
  210. }
  211. CONNID dwConnID = m_pClient->GetConnectionID();
  212. if(m_pClient->SendRequest(T2A(strMethod), strPathA, szHeaders.get(), dwSize, (const BYTE*)(LPCSTR)strBodyA, strBodyA.GetLength()))
  213. {
  214. CString strContent;
  215. strContent.Format(_T("[%s] %s://%s:%s%s"), strMethod, strSchema, strAddress, strPort, strPath);
  216. ::LogSend(dwConnID, strContent);
  217. CheckSetCookie(m_pClient.get());
  218. CStringA strSummary = GetHeaderSummary(m_pClient.get(), " ", 0, TRUE);
  219. ::PostOnHeadersComplete(dwConnID, strSummary);
  220. LPCBYTE pData = nullptr;
  221. int iLength = 0;
  222. m_pClient->GetResponseBody(&pData, &iLength);
  223. if(iLength > 0)
  224. {
  225. ::PostOnBody(dwConnID, pData, iLength);
  226. LPCSTR lpszEnc = m_pClient->GetContentEncoding();
  227. if(lpszEnc && ::StrStrIA(lpszEnc, "gzip"))
  228. {
  229. int rs = 0;
  230. DWORD dwLen = ::GZipGuessUncompressBound(pData, iLength);
  231. if(dwLen == 0 || dwLen > 5 * 1024 * 1024)
  232. rs = -10;
  233. else
  234. {
  235. CBufferPtr szBuff(dwLen);
  236. rs = ::GZipUncompress(pData, iLength, szBuff, dwLen);
  237. }
  238. if(rs == 0)
  239. ::PostUncompressBody(dwConnID, dwLen);
  240. else
  241. {
  242. ::PostUncompressBodyFail(dwConnID, rs);
  243. ::PostOnMessageComplete(dwConnID);
  244. OnBnClickedStop();
  245. return;
  246. }
  247. }
  248. }
  249. ::PostOnMessageComplete(dwConnID);
  250. EnHttpUpgradeType enUpgrade = m_pClient->GetUpgradeType();
  251. if(enUpgrade == HUT_WEB_SOCKET)
  252. {
  253. ::PostOnUpgrade(dwConnID, enUpgrade);
  254. m_bWebSocket = TRUE;
  255. OnCbnSelchangeMethod();
  256. }
  257. }
  258. else
  259. {
  260. ::LogSendFail(dwConnID, ::GetLastError(), ::GetSocketErrorDesc(SE_DATA_SEND));
  261. SetAppState(ST_STOPPED);
  262. }
  263. m_pClient->CleanupRequestResult();
  264. }
  265. void CClientDlg::SendWebSocket()
  266. {
  267. static LPCSTR CLOSE_FLAG = "$close";
  268. static const BYTE MASK_KEY[] = {0x1, 0x2, 0x3, 0x4};
  269. USES_CONVERSION;
  270. if(!CheckStarted(FALSE))
  271. return;
  272. CString strBody;
  273. m_Body.GetWindowText(strBody);
  274. CStringA strBodyA = T2A(strBody);
  275. BYTE* pData = (BYTE*)(LPCSTR)strBodyA;
  276. int iLength = strBodyA.GetLength();
  277. CONNID dwConnID = m_pClient->GetConnectionID();
  278. if(strBodyA.CompareNoCase(CLOSE_FLAG) != 0)
  279. {
  280. if(m_pClient->SendWSMessage(TRUE, 0, 0x1, MASK_KEY, pData, iLength))
  281. {
  282. CString strContent;
  283. strContent.Format(_T("[WebSocket] (len: %d)"), iLength);
  284. ::LogSend(dwConnID, strContent);
  285. BOOL bFinal;
  286. BYTE iReserved;
  287. BYTE iOperationCode;
  288. LPCBYTE lpszMask;
  289. ULONGLONG ullBodyLen;
  290. VERIFY(m_pClient->GetWSMessageState(&bFinal, &iReserved, &iOperationCode, &lpszMask, &ullBodyLen, nullptr));
  291. ::PostOnWSMessageHeader(dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);
  292. if(ullBodyLen > 0)
  293. {
  294. m_pClient->GetResponseBody((LPCBYTE*)&pData, &iLength);
  295. ::PostOnWSMessageBody(dwConnID, pData, iLength);
  296. }
  297. ::PostOnWSMessageComplete(dwConnID);
  298. if(iOperationCode == 0x8)
  299. OnBnClickedStop();
  300. }
  301. else
  302. {
  303. ::LogSendFail(dwConnID, ::GetLastError(), ::GetSocketErrorDesc(SE_DATA_SEND));
  304. SetAppState(ST_STOPPED);
  305. }
  306. }
  307. else
  308. {
  309. if(m_pClient->SendWSMessage(TRUE, 0, 0x8, MASK_KEY, nullptr, 0))
  310. {
  311. ::LogSend(dwConnID, _T("[WebSocket] (OP: close)"));
  312. }
  313. m_Body.SetWindowText(_T(""));
  314. OnBnClickedStop();
  315. }
  316. m_pClient->CleanupRequestResult();
  317. }
  318. void CClientDlg::OnBnClickedStart()
  319. {
  320. SetAppState(ST_STARTING);
  321. g_SSL.Cleanup();
  322. if(!g_SSL.Initialize(SSL_SM_CLIENT, SSL_VM_NONE, g_c_lpszPemCertFile, g_c_lpszPemKeyFile, g_c_lpszKeyPasswod, g_c_lpszCAPemCertFileOrPath))
  323. {
  324. ::LogClientStartFail(::GetLastError(), _T("initialize SSL env fail"));
  325. SetAppState(ST_STOPPED);
  326. return;
  327. }
  328. CString strAddress;
  329. CString strPort;
  330. m_Address.GetWindowText(strAddress);
  331. m_Port.GetWindowText(strPort);
  332. USHORT usPort = (USHORT)_ttoi(strPort);
  333. BOOL isHttp = m_Schema.GetCurSel() == 0;
  334. if(isHttp)
  335. m_pClient.reset((IHttpSyncClient*)(new CHttpSyncClient()));
  336. else
  337. m_pClient.reset((IHttpSyncClient*)(new CHttpsSyncClient()));
  338. ::LogClientStarting(strAddress, usPort);
  339. if(m_pClient->Start(strAddress, usPort))
  340. {
  341. ::LogOnConnect3(m_pClient->GetConnectionID(), strAddress, usPort);
  342. m_pClient->AddCookie("__reqSequence", "1");
  343. SetAppState(ST_STARTED);
  344. }
  345. else
  346. {
  347. ::LogClientStartFail(m_pClient->GetLastError(), m_pClient->GetLastErrorDesc());
  348. SetAppState(ST_STOPPED);
  349. }
  350. }
  351. void CClientDlg::OnBnClickedStop()
  352. {
  353. SetAppState(ST_STOPPING);
  354. ::LogClientStopping(m_pClient->GetConnectionID());
  355. m_pClient->Stop();
  356. while(m_pClient->GetState() != SS_STOPPED)
  357. ::Sleep(50);
  358. ::PostOnClose(m_pClient->GetConnectionID());
  359. SetAppState(ST_STOPPED);
  360. }
  361. void CClientDlg::OnCbnSelchangeMethod()
  362. {
  363. m_Body.EnableWindow(m_Method.GetCurSel() <= 2 || m_bWebSocket);
  364. m_Path.EnableWindow(m_Method.GetCurSel() != 8);
  365. }
  366. int CClientDlg::OnVKeyToItem(UINT nKey, CListBox* pListBox, UINT nIndex)
  367. {
  368. if(nKey == 'C')
  369. pListBox->ResetContent();
  370. else if(nKey == 'D' && pListBox == &m_Headers)
  371. {
  372. int index = pListBox->GetCurSel();
  373. if(index != LB_ERR)
  374. {
  375. pListBox->DeleteString(index);
  376. int count = pListBox->GetCount();
  377. if(index < count)
  378. pListBox->SetCurSel(index);
  379. else if(index > 0)
  380. pListBox->SetCurSel(index - 1);
  381. }
  382. }
  383. return __super::OnVKeyToItem(nKey, pListBox, nIndex);
  384. }
  385. LRESULT CClientDlg::OnUserInfoMsg(WPARAM wp, LPARAM lp)
  386. {
  387. info_msg* msg = (info_msg*)wp;
  388. ::LogInfoMsg(msg);
  389. return 0;
  390. }
  391. // ------------------------------------------------------------------------------------------------------------- //
  392. BOOL CClientDlg::CheckStarted(BOOL bRestart)
  393. {
  394. if(m_pClient == nullptr)
  395. return FALSE;
  396. EnServiceState state = m_pClient->GetState();
  397. if(state == SS_STARTED)
  398. return TRUE;
  399. else if(state == SS_STARTING)
  400. {
  401. do
  402. {
  403. ::Sleep(50);
  404. state = m_pClient->GetState();
  405. } while(state != SS_STARTED && state != SS_STOPPED);
  406. }
  407. else
  408. {
  409. SetAppState(bRestart ? ST_STARTING : ST_STOPPING);
  410. while(state != SS_STOPPED)
  411. {
  412. ::Sleep(50);
  413. state = m_pClient->GetState();
  414. }
  415. if(bRestart)
  416. {
  417. ::LogOnClose(m_pClient->GetConnectionID());
  418. OnBnClickedStart();
  419. }
  420. state = m_pClient->GetState();
  421. }
  422. if(state == SS_STOPPED)
  423. {
  424. ::PostOnClose(m_pClient->GetConnectionID());
  425. SetAppState(ST_STOPPED);
  426. return FALSE;
  427. }
  428. return TRUE;
  429. }
  430. void CClientDlg::CheckSetCookie(IHttpSyncClient* pHttpClient)
  431. {
  432. DWORD dwHeaderCount = 0;
  433. pHttpClient->GetHeaders("Set-Cookie", nullptr, dwHeaderCount);
  434. if(dwHeaderCount == 0)
  435. return;
  436. unique_ptr<LPCSTR[]> values(new LPCSTR[dwHeaderCount]);
  437. VERIFY(pHttpClient->GetHeaders("Set-Cookie", values.get(), dwHeaderCount));
  438. for(DWORD i = 0; i < dwHeaderCount; i++)
  439. {
  440. CStringA strValue = values[i];
  441. int j = 0;
  442. CStringA strItem = strValue.Tokenize("; ", j);
  443. if(j <= 0)
  444. continue;
  445. int k = strItem.Find('=');
  446. if(k <= 0)
  447. continue;
  448. pHttpClient->AddCookie(strItem.Left(k), strItem.Mid(k + 1));
  449. }
  450. }
  451. CStringA CClientDlg::GetHeaderSummary(IHttpSyncClient* pHttpClient, LPCSTR lpszSep, int iSepCount, BOOL bWithContentLength)
  452. {
  453. CStringA SEP1;
  454. for(int i = 0; i < iSepCount; i++)
  455. SEP1 += lpszSep;
  456. CStringA SEP2(SEP1);
  457. SEP2 += lpszSep;
  458. CStringA strResult;
  459. strResult.AppendFormat("%s[Status Fields]%s", SEP1, CRLF);
  460. strResult.AppendFormat("%s%13s: %u%s", SEP2, "Status Code", pHttpClient->GetStatusCode(), CRLF);
  461. DWORD dwHeaderCount = 0;
  462. pHttpClient->GetAllHeaders(nullptr, dwHeaderCount);
  463. strResult.AppendFormat("%s[Response Headers]%s", SEP1, CRLF);
  464. if(dwHeaderCount == 0)
  465. strResult.AppendFormat("%s(no header)%s", SEP2, CRLF);
  466. else
  467. {
  468. unique_ptr<THeader[]> headers(new THeader[dwHeaderCount]);
  469. VERIFY(pHttpClient->GetAllHeaders(headers.get(), dwHeaderCount));
  470. for(DWORD i = 0; i < dwHeaderCount; i++)
  471. strResult.AppendFormat("%s%s: %s%s", SEP2, headers[i].name, headers[i].value, CRLF);
  472. }
  473. DWORD dwCookieCount = 0;
  474. pHttpClient->GetAllCookies(nullptr, dwCookieCount);
  475. strResult.AppendFormat("%s[Cookies]%s", SEP1, CRLF);
  476. if(dwCookieCount == 0)
  477. strResult.AppendFormat("%s(no cookie)%s", SEP2, CRLF);
  478. else
  479. {
  480. unique_ptr<TCookie[]> cookies(new TCookie[dwCookieCount]);
  481. VERIFY(pHttpClient->GetAllCookies(cookies.get(), dwCookieCount));
  482. for(DWORD i = 0; i < dwCookieCount; i++)
  483. strResult.AppendFormat("%s%s: %s%s", SEP2, cookies[i].name, cookies[i].value, CRLF);
  484. }
  485. CStringA strVersion;
  486. ::HttpVersionToString((EnHttpVersion)pHttpClient->GetVersion(), strVersion);
  487. EnHttpUpgradeType enUpgType = pHttpClient->GetUpgradeType();
  488. LPCSTR lpszUpgrade = enUpgType != HUT_NONE ? "true" : "false";
  489. LPCSTR lpszKeepAlive = pHttpClient->IsKeepAlive() ? "true" : "false";
  490. strResult.AppendFormat("%s[Basic Info]%s", SEP1, CRLF);
  491. strResult.AppendFormat("%s%13s: %s%s", SEP2, "Version", strVersion, CRLF);
  492. strResult.AppendFormat("%s%13s: %u%s", SEP2, "Status Code", pHttpClient->GetStatusCode(), CRLF);
  493. strResult.AppendFormat("%s%13s: %s%s", SEP2, "IsUpgrade", lpszUpgrade, CRLF);
  494. if(enUpgType != HUT_NONE)
  495. strResult.AppendFormat("%s%13s: %d%s", SEP2, "UpgradeType", enUpgType, CRLF);
  496. strResult.AppendFormat("%s%13s: %s%s", SEP2, "IsKeepAlive", lpszKeepAlive, CRLF);
  497. if(bWithContentLength)
  498. strResult.AppendFormat("%s%13s: %lld%s", SEP2, "ContentLength", pHttpClient->GetContentLength(), CRLF);
  499. strResult.AppendFormat("%s%13s: %s%s", SEP2, "ContentType", pHttpClient->GetContentType(), CRLF);
  500. return strResult;
  501. }