ServerDlg.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. // ServerDlg.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "Server.h"
  5. #include "ServerDlg.h"
  6. #include "afxdialogex.h"
  7. // CServerDlg dialog
  8. const LPCTSTR CServerDlg::ADDRESS = _T("0.0.0.0");
  9. const LPCTSTR CServerDlg::HTTP_NAME = _T("http");
  10. const LPCTSTR CServerDlg::HTTPS_NAME = _T("https");
  11. const USHORT CServerDlg::HTTP_PORT = 8080;
  12. const USHORT CServerDlg::HTTPS_PORT = 8443;
  13. CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)
  14. : CDialogEx(CServerDlg::IDD, pParent)
  15. , m_HttpListener(this)
  16. , m_HttpsListener(this)
  17. , m_HttpServer(&m_HttpListener)
  18. , m_HttpsServer(&m_HttpsListener)
  19. {
  20. m_HttpListener.SetServer(HTTP_NAME, (IHttpServer*)&m_HttpServer);
  21. m_HttpsListener.SetServer(HTTPS_NAME, (IHttpServer*)&m_HttpsServer);
  22. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  23. }
  24. void CServerDlg::DoDataExchange(CDataExchange* pDX)
  25. {
  26. CDialogEx::DoDataExchange(pDX);
  27. DDX_Control(pDX, IDC_INFO, m_Info);
  28. DDX_Control(pDX, IDC_START, m_Start);
  29. DDX_Control(pDX, IDC_STOP, m_Stop);
  30. DDX_Control(pDX, IDC_CONN_ID, m_ConnID);
  31. DDX_Control(pDX, IDC_DISCONNECT, m_DisConn);
  32. DDX_Control(pDX, IDC_RELEASE, m_Release);
  33. DDX_Control(pDX, IDC_HTTP, m_RadioHttp);
  34. DDX_Control(pDX, IDC_HTTPS, m_RadioHttps);
  35. }
  36. BEGIN_MESSAGE_MAP(CServerDlg, CDialogEx)
  37. ON_WM_PAINT()
  38. ON_WM_VKEYTOITEM()
  39. ON_WM_QUERYDRAGICON()
  40. ON_MESSAGE(USER_INFO_MSG, OnUserInfoMsg)
  41. ON_BN_CLICKED(IDC_START, &CServerDlg::OnBnClickedStart)
  42. ON_BN_CLICKED(IDC_STOP, &CServerDlg::OnBnClickedStop)
  43. ON_BN_CLICKED(IDC_DISCONNECT, &CServerDlg::OnBnClickedDisconnect)
  44. ON_EN_CHANGE(IDC_CONN_ID, &CServerDlg::OnEnChangeConnId)
  45. ON_BN_CLICKED(IDC_RELEASE, &CServerDlg::OnBnClickedRelease)
  46. END_MESSAGE_MAP()
  47. // CServerDlg message handlers
  48. BOOL CServerDlg::OnInitDialog()
  49. {
  50. CDialogEx::OnInitDialog();
  51. // Set the icon for this dialog. The framework does this automatically
  52. // when the application's main window is not a dialog
  53. SetIcon(m_hIcon, TRUE); // Set big icon
  54. SetIcon(m_hIcon, FALSE); // Set small icon
  55. // TODO: Add extra initialization here
  56. CString strTitle;
  57. CString strOriginTitle;
  58. GetWindowText(strOriginTitle);
  59. strTitle.Format(_T("%s - (%s:%d/%d)"), strOriginTitle, ADDRESS, HTTP_PORT, HTTPS_PORT);
  60. SetWindowText(strTitle);
  61. m_RadioHttp.SetCheck(BST_CHECKED);
  62. ::SetMainWnd(this);
  63. ::SetInfoList(&m_Info);
  64. SetAppState(ST_STOPPED);
  65. return TRUE; // return TRUE unless you set the focus to a control
  66. }
  67. // If you add a minimize button to your dialog, you will need the code below
  68. // to draw the icon. For MFC applications using the document/view model,
  69. // this is automatically done for you by the framework.
  70. void CServerDlg::OnPaint()
  71. {
  72. if (IsIconic())
  73. {
  74. CPaintDC dc(this); // device context for painting
  75. SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
  76. // Center icon in client rectangle
  77. int cxIcon = GetSystemMetrics(SM_CXICON);
  78. int cyIcon = GetSystemMetrics(SM_CYICON);
  79. CRect rect;
  80. GetClientRect(&rect);
  81. int x = (rect.Width() - cxIcon + 1) / 2;
  82. int y = (rect.Height() - cyIcon + 1) / 2;
  83. // Draw the icon
  84. dc.DrawIcon(x, y, m_hIcon);
  85. }
  86. else
  87. {
  88. CDialogEx::OnPaint();
  89. }
  90. }
  91. // The system calls this function to obtain the cursor to display while the user drags
  92. // the minimized window.
  93. HCURSOR CServerDlg::OnQueryDragIcon()
  94. {
  95. return static_cast<HCURSOR>(m_hIcon);
  96. }
  97. BOOL CServerDlg::PreTranslateMessage(MSG* pMsg)
  98. {
  99. if (
  100. pMsg->message == WM_KEYDOWN
  101. &&( pMsg->wParam == VK_ESCAPE
  102. || pMsg->wParam == VK_CANCEL
  103. || pMsg->wParam == VK_RETURN
  104. ))
  105. return TRUE;
  106. return CDialog::PreTranslateMessage(pMsg);
  107. }
  108. void CServerDlg::SetAppState(EnAppState state)
  109. {
  110. m_enState = state;
  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_RadioHttp.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  116. m_RadioHttps.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  117. m_DisConn.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  118. m_Release.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  119. }
  120. void CServerDlg::OnBnClickedStart()
  121. {
  122. SetAppState(ST_STARTING);
  123. g_SSL.Cleanup();
  124. if(!g_SSL.Initialize(SSL_SM_SERVER, SSL_VM_NONE, g_s_lpszPemCertFile, g_s_lpszPemKeyFile, g_s_lpszKeyPasswod, g_s_lpszCAPemCertFileOrPath))
  125. {
  126. ::LogServerStartFail(::GetLastError(), _T("initialize SSL env fail"));
  127. SetAppState(ST_STOPPED);
  128. return;
  129. }
  130. if(m_HttpServer.Start(ADDRESS, HTTP_PORT))
  131. {
  132. ::LogServerStart(ADDRESS, HTTP_PORT, HTTP_NAME);
  133. if(m_HttpsServer.Start(ADDRESS, HTTPS_PORT))
  134. {
  135. ::LogServerStart(ADDRESS, HTTPS_PORT, HTTPS_NAME);
  136. SetAppState(ST_STARTED);
  137. }
  138. else
  139. {
  140. m_HttpServer.Stop();
  141. ::LogServerStartFail(m_HttpsServer.GetLastError(), m_HttpsServer.GetLastErrorDesc(), HTTPS_NAME);
  142. SetAppState(ST_STOPPED);
  143. }
  144. }
  145. else
  146. {
  147. ::LogServerStartFail(m_HttpServer.GetLastError(), m_HttpServer.GetLastErrorDesc(), HTTP_NAME);
  148. SetAppState(ST_STOPPED);
  149. }
  150. }
  151. void CServerDlg::OnBnClickedStop()
  152. {
  153. SetAppState(ST_STOPPING);
  154. if(!m_HttpsServer.Stop())
  155. ASSERT(FALSE);
  156. ::LogServerStop(HTTPS_NAME);
  157. if(!m_HttpServer.Stop())
  158. ASSERT(FALSE);
  159. ::LogServerStop(HTTP_NAME);
  160. SetAppState(ST_STOPPED);
  161. }
  162. void CServerDlg::OnBnClickedDisconnect()
  163. {
  164. CString strConnID;
  165. m_ConnID.GetWindowText(strConnID);
  166. CONNID dwConnID = (CONNID)_ttoi(strConnID);
  167. BOOL bHttp = m_RadioHttp.GetCheck() == BST_CHECKED;
  168. LPCTSTR lpszName = bHttp ? HTTP_NAME : HTTPS_NAME;
  169. IHttpServer* pServer = bHttp ? (IHttpServer*)&m_HttpServer : (IHttpServer*)&m_HttpsServer;
  170. if(pServer->Disconnect(dwConnID))
  171. ::LogDisconnect(dwConnID, lpszName);
  172. else
  173. ::LogDisconnectFail(dwConnID, lpszName);
  174. }
  175. void CServerDlg::OnBnClickedRelease()
  176. {
  177. CString strConnID;
  178. m_ConnID.GetWindowText(strConnID);
  179. CONNID dwConnID = (CONNID)_ttoi(strConnID);
  180. BOOL bHttp = m_RadioHttp.GetCheck() == BST_CHECKED;
  181. LPCTSTR lpszName = bHttp ? HTTP_NAME : HTTPS_NAME;
  182. IHttpServer* pServer = bHttp ? (IHttpServer*)&m_HttpServer : (IHttpServer*)&m_HttpsServer;
  183. if(pServer->Release(dwConnID))
  184. ::LogRelease(dwConnID, lpszName);
  185. else
  186. ::LogReleaseFail(dwConnID, lpszName);
  187. }
  188. void CServerDlg::OnEnChangeConnId()
  189. {
  190. m_RadioHttp.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  191. m_RadioHttps.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  192. m_DisConn.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  193. m_Release.EnableWindow(m_enState == ST_STARTED && m_ConnID.GetWindowTextLength() > 0);
  194. }
  195. int CServerDlg::OnVKeyToItem(UINT nKey, CListBox* pListBox, UINT nIndex)
  196. {
  197. if(nKey == 'C')
  198. pListBox->ResetContent();
  199. return __super::OnVKeyToItem(nKey, pListBox, nIndex);
  200. }
  201. LRESULT CServerDlg::OnUserInfoMsg(WPARAM wp, LPARAM lp)
  202. {
  203. info_msg* msg = (info_msg*)wp;
  204. ::LogInfoMsg(msg);
  205. return 0;
  206. }
  207. // ------------------------------------------------------------------------------------------------------------- //
  208. EnHandleResult CHttpServerListenerImpl::OnPrepareListen(SOCKET soListen)
  209. {
  210. TCHAR szAddress[40];
  211. int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);
  212. USHORT usPort;
  213. m_pServer->GetListenAddress(szAddress, iAddressLen, usPort);
  214. ::PostOnPrepareListen(szAddress, usPort, m_strName);
  215. return HR_OK;
  216. }
  217. EnHandleResult CHttpServerListenerImpl::OnAccept(CONNID dwConnID, SOCKET soClient)
  218. {
  219. BOOL bPass = TRUE;
  220. TCHAR szAddress[40];
  221. int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);
  222. USHORT usPort;
  223. m_pServer->GetRemoteAddress(dwConnID, szAddress, iAddressLen, usPort);
  224. ::PostOnAccept(dwConnID, szAddress, usPort, bPass, m_strName);
  225. return bPass ? HR_OK : HR_ERROR;
  226. }
  227. EnHandleResult CHttpServerListenerImpl::OnHandShake(CONNID dwConnID)
  228. {
  229. ::PostOnHandShake(dwConnID, m_strName);
  230. return HR_OK;
  231. }
  232. EnHandleResult CHttpServerListenerImpl::OnReceive(CONNID dwConnID, const BYTE* pData, int iLength)
  233. {
  234. ::PostOnReceive(dwConnID, pData, iLength, m_strName);
  235. return HR_OK;
  236. }
  237. EnHandleResult CHttpServerListenerImpl::OnSend(CONNID dwConnID, const BYTE* pData, int iLength)
  238. {
  239. ::PostOnSend(dwConnID, pData, iLength, m_strName);
  240. return HR_OK;
  241. }
  242. EnHandleResult CHttpServerListenerImpl::OnClose(CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
  243. {
  244. iErrorCode == SE_OK ? ::PostOnClose(dwConnID, m_strName) :
  245. ::PostOnError(dwConnID, enOperation, iErrorCode, m_strName) ;
  246. return HR_OK;
  247. }
  248. EnHandleResult CHttpServerListenerImpl::OnShutdown()
  249. {
  250. ::PostOnShutdown(m_strName);
  251. return HR_OK;
  252. }
  253. // ------------------------------------------------------------------------------------------------------------- //
  254. EnHttpParseResult CHttpServerListenerImpl::OnMessageBegin(CONNID dwConnID)
  255. {
  256. ::PostOnMessageBegin(dwConnID, m_strName);
  257. return HPR_OK;
  258. }
  259. EnHttpParseResult CHttpServerListenerImpl::OnRequestLine(CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
  260. {
  261. ::PostOnRequestLine(dwConnID, lpszMethod, m_pServer->GetUrlFieldSet(dwConnID), lpszUrl, m_strName);
  262. return HPR_OK;
  263. }
  264. EnHttpParseResult CHttpServerListenerImpl::OnHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
  265. {
  266. ::PostOnHeader(dwConnID, lpszName, lpszValue, m_strName);
  267. return HPR_OK;
  268. }
  269. EnHttpParseResult CHttpServerListenerImpl::OnHeadersComplete(CONNID dwConnID)
  270. {
  271. CStringA strSummary = GetHeaderSummary(dwConnID, " ", 0, TRUE);
  272. ::PostOnHeadersComplete(dwConnID, strSummary, m_strName);
  273. return HPR_OK;
  274. }
  275. EnHttpParseResult CHttpServerListenerImpl::OnBody(CONNID dwConnID, const BYTE* pData, int iLength)
  276. {
  277. ::PostOnBody(dwConnID, pData, iLength, m_strName);
  278. return HPR_OK;
  279. }
  280. EnHttpParseResult CHttpServerListenerImpl::OnChunkHeader(CONNID dwConnID, int iLength)
  281. {
  282. ::PostOnChunkHeader(dwConnID, iLength, m_strName);
  283. return HPR_OK;
  284. }
  285. EnHttpParseResult CHttpServerListenerImpl::OnChunkComplete(CONNID dwConnID)
  286. {
  287. ::PostOnChunkComplete(dwConnID, m_strName);
  288. return HPR_OK;
  289. }
  290. EnHttpParseResult CHttpServerListenerImpl::OnMessageComplete(CONNID dwConnID)
  291. {
  292. ::PostOnMessageComplete(dwConnID, m_strName);
  293. if(m_pServer->IsUpgrade(dwConnID))
  294. return HPR_OK;
  295. CStringA strBody = GetHeaderSummary(dwConnID, " ", 0, FALSE);
  296. int iBodyLength = strBody.GetLength();
  297. BOOL bSkipBody = FALSE;
  298. if(strcmp(m_pServer->GetMethod(dwConnID), HTTP_METHOD_HEAD) == 0)
  299. bSkipBody = TRUE;
  300. CStringA strContentLength;
  301. strContentLength.Format("%u", iBodyLength);
  302. DWORD dwSeq = 1;
  303. LPCSTR lpszReqSequence = nullptr;
  304. if(m_pServer->GetCookie(dwConnID, "__reqSequence", &lpszReqSequence))
  305. dwSeq += atoi(lpszReqSequence);
  306. CStringA strSeqCookie;
  307. strSeqCookie.Format("__reqSequence=%u; path=/", dwSeq);
  308. THeader header[] = {{"Content-Type", "text/plain"}, {"Content-Length", strContentLength}, {"Set-Cookie", strSeqCookie}};
  309. int iHeaderCount = sizeof(header) / sizeof(THeader);
  310. if(bSkipBody)
  311. {
  312. strBody.Empty();
  313. iBodyLength = 0;
  314. }
  315. m_pServer->SendResponse(dwConnID,
  316. HSC_OK,
  317. "HP Http Server OK",
  318. header, iHeaderCount,
  319. (const BYTE*)(LPCSTR)strBody,
  320. iBodyLength);
  321. if(!m_pServer->IsKeepAlive(dwConnID))
  322. m_pServer->Release(dwConnID);
  323. return HPR_OK;
  324. }
  325. EnHttpParseResult CHttpServerListenerImpl::OnUpgrade(CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
  326. {
  327. ::PostOnUpgrade(dwConnID, enUpgradeType, m_strName);
  328. if(enUpgradeType == HUT_HTTP_TUNNEL)
  329. {
  330. m_pServer->SendResponse(dwConnID, HSC_OK, "Connection Established");
  331. }
  332. else if(enUpgradeType == HUT_WEB_SOCKET)
  333. {
  334. THeader header[] = {{"Connection", UPGRADE_HEADER},
  335. {UPGRADE_HEADER, WEB_SOCKET_HEADER_VALUE},
  336. {"Sec-WebSocket-Accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="},
  337. {"Sec-WebSocket-Protocol", "chat"}};
  338. int iHeaderCount = sizeof(header) / sizeof(THeader);
  339. m_pServer->SendResponse(dwConnID, HSC_SWITCHING_PROTOCOLS, nullptr, header, iHeaderCount);
  340. }
  341. else
  342. ASSERT(FALSE);
  343. return HPR_OK;
  344. }
  345. EnHttpParseResult CHttpServerListenerImpl::OnParseError(CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
  346. {
  347. ::PostOnParseError(dwConnID, iErrorCode, lpszErrorDesc, m_strName);
  348. return HPR_OK;
  349. }
  350. CStringA CHttpServerListenerImpl::GetHeaderSummary(CONNID dwConnID, LPCSTR lpszSep, int iSepCount, BOOL bWithContentLength)
  351. {
  352. CStringA SEP1;
  353. for(int i = 0; i < iSepCount; i++)
  354. SEP1 += lpszSep;
  355. CStringA SEP2(SEP1);
  356. SEP2 += lpszSep;
  357. CStringA strResult;
  358. USHORT usUrlFieldSet = m_pServer->GetUrlFieldSet(dwConnID);
  359. strResult.AppendFormat("%s[URL Fields]%s", SEP1, CRLF);
  360. strResult.AppendFormat("%s%8s: %s%s", SEP2, "SCHEMA", m_pServer->GetUrlField(dwConnID, HUF_SCHEMA), CRLF);
  361. strResult.AppendFormat("%s%8s: %s%s", SEP2, "HOST", m_pServer->GetUrlField(dwConnID, HUF_HOST), CRLF);
  362. strResult.AppendFormat("%s%8s: %s%s", SEP2, "PORT", m_pServer->GetUrlField(dwConnID, HUF_PORT), CRLF);
  363. strResult.AppendFormat("%s%8s: %s%s", SEP2, "PATH", m_pServer->GetUrlField(dwConnID, HUF_PATH), CRLF);
  364. strResult.AppendFormat("%s%8s: %s%s", SEP2, "QUERY", m_pServer->GetUrlField(dwConnID, HUF_QUERY), CRLF);
  365. strResult.AppendFormat("%s%8s: %s%s", SEP2, "FRAGMENT", m_pServer->GetUrlField(dwConnID, HUF_FRAGMENT), CRLF);
  366. strResult.AppendFormat("%s%8s: %s%s", SEP2, "USERINFO", m_pServer->GetUrlField(dwConnID, HUF_USERINFO), CRLF);
  367. DWORD dwHeaderCount = 0;
  368. m_pServer->GetAllHeaders(dwConnID, nullptr, dwHeaderCount);
  369. strResult.AppendFormat("%s[Request Headers]%s", SEP1, CRLF);
  370. if(dwHeaderCount == 0)
  371. strResult.AppendFormat("%s(no header)%s", SEP2, CRLF);
  372. else
  373. {
  374. unique_ptr<THeader[]> headers(new THeader[dwHeaderCount]);
  375. VERIFY(m_pServer->GetAllHeaders(dwConnID, headers.get(), dwHeaderCount));
  376. for(DWORD i = 0; i < dwHeaderCount; i++)
  377. strResult.AppendFormat("%s%s: %s%s", SEP2, headers[i].name, headers[i].value, CRLF);
  378. }
  379. DWORD dwCookieCount = 0;
  380. m_pServer->GetAllCookies(dwConnID, nullptr, dwCookieCount);
  381. strResult.AppendFormat("%s[Cookies]%s", SEP1, CRLF);
  382. if(dwCookieCount == 0)
  383. strResult.AppendFormat("%s(no cookie)%s", SEP2, CRLF);
  384. else
  385. {
  386. unique_ptr<TCookie[]> cookies(new TCookie[dwCookieCount]);
  387. VERIFY(m_pServer->GetAllCookies(dwConnID, cookies.get(), dwCookieCount));
  388. for(DWORD i = 0; i < dwCookieCount; i++)
  389. strResult.AppendFormat("%s%s: %s%s", SEP2, cookies[i].name, cookies[i].value, CRLF);
  390. }
  391. CStringA strVersion;
  392. ::GetHttpVersionStr((EnHttpVersion)m_pServer->GetVersion(dwConnID), strVersion);
  393. EnHttpUpgradeType enUpgType = m_pServer->GetUpgradeType(dwConnID);
  394. LPCSTR lpszUpgrade = enUpgType != HUT_NONE ? "true" : "false";
  395. LPCSTR lpszKeepAlive = m_pServer->IsKeepAlive(dwConnID) ? "true" : "false";
  396. strResult.AppendFormat("%s[Basic Info]%s", SEP1, CRLF);
  397. strResult.AppendFormat("%s%13s: %s%s", SEP2, "Version", strVersion, CRLF);
  398. strResult.AppendFormat("%s%13s: %s%s", SEP2, "Method", m_pServer->GetMethod(dwConnID), CRLF);
  399. strResult.AppendFormat("%s%13s: %s%s", SEP2, "IsUpgrade", lpszUpgrade, CRLF);
  400. if(enUpgType != HUT_NONE)
  401. strResult.AppendFormat("%s%13s: %d%s", SEP2, "UpgradeType", enUpgType, CRLF);
  402. strResult.AppendFormat("%s%13s: %s%s", SEP2, "IsKeepAlive", lpszKeepAlive, CRLF);
  403. if(bWithContentLength)
  404. strResult.AppendFormat("%s%13s: %lld%s", SEP2, "ContentLength", m_pServer->GetContentLength(dwConnID), CRLF);
  405. strResult.AppendFormat("%s%13s: %s%s", SEP2, "ContentType", m_pServer->GetContentType(dwConnID), CRLF);
  406. return strResult;
  407. }