OSDN Git Service

911e4e85b52f277702edbd858f446a6ff34387fa
[iptd/iPTd.git] / src / net / HTTPDaemon.cpp
1 //\r
2 //  HTTPDaemon.cpp\r
3 //\r
4 \r
5 #define DBG_LEVEL 0\r
6 #include <Raym/Log.h>\r
7 #include <Raym/Raym.h>\r
8 #include "net/HTTPDaemon.h"\r
9 \r
10 using namespace Raym;\r
11 \r
12 namespace NET\r
13 {\r
14 \r
15 HTTPDaemon::HTTPDaemon()\r
16 {\r
17     DebugLog2("HTTPDaemon::HTTPDaemon()");\r
18 \r
19     _port = -1;\r
20     _backlog = -1;\r
21     _state = ST_IDLE;\r
22     _rootPath = NULL;\r
23     _delegate = NULL;\r
24     _sockets = NULL;\r
25 \r
26 }\r
27 \r
28 HTTPDaemon::~HTTPDaemon()\r
29 {\r
30     stop();\r
31 \r
32     RELEASE(_rootPath);\r
33     RELEASE(_sockets);\r
34 \r
35     DebugLog2("HTTPDaemon::~HTTPDaemon()");\r
36 }\r
37 \r
38 HTTPDaemon *HTTPDaemon::alloc()\r
39 {\r
40     return new HTTPDaemon();\r
41 }\r
42 \r
43 HTTPDaemon *HTTPDaemon::initWithPort(int port, int backlog)\r
44 {\r
45     DebugLog2("HTTPDaemon::initWithPort()");\r
46 \r
47     _port = port;\r
48     _backlog = backlog;\r
49     _sockets = Array::alloc()->initWithCapacity(0);\r
50     return this;\r
51 }\r
52 \r
53 void HTTPDaemon::setDelegate(HTTPDaemonDelegate *delegate)\r
54 {\r
55     DebugLog2("HTTPDaemon::setDelegate()");\r
56 \r
57     EnterCriticalSection(&_cs);\r
58     _delegate = delegate;\r
59     LeaveCriticalSection(&_cs);\r
60 }\r
61 \r
62 void HTTPDaemon::setRootPath(String *path)\r
63 {\r
64     DebugLog2("HTTPDaemon::setRootPath()");\r
65 \r
66     RELEASE(_rootPath);\r
67     if (path != NULL)\r
68     {\r
69         _rootPath = path;\r
70         _rootPath->retain();\r
71     }\r
72 }\r
73 \r
74 String *HTTPDaemon::rootPath()\r
75 {\r
76     return _rootPath;\r
77 }\r
78 \r
79 HTTPResponse *HTTPDaemon::responseWithReason(String *reason, int status, String *version)\r
80 {\r
81     DebugLog2("HTTPDaemon::responseWithReason()");\r
82 \r
83     HTTPResponse *resp = HTTPResponse::alloc()->init();\r
84     resp->setVersion(version);\r
85     resp->setReason(reason);\r
86     resp->setStatus(status);\r
87 \r
88     // header & body\r
89     InternetTextMessageHeader * header = NULL;\r
90     InternetTextMessageBody *   body   = NULL;\r
91     switch (status)\r
92     {\r
93     case HTTP_STATUS_NO_CONTENT:\r
94         {\r
95             header = InternetTextMessageHeader::alloc()->init();\r
96             header->setFieldBodyWithName(String::stringWithUTF8String("close"), String::stringWithUTF8String("Connection"));\r
97         }\r
98         break;\r
99 \r
100     case HTTP_STATUS_INTERNAL_SERVER_ERROR:\r
101         {\r
102             // header\r
103             header = InternetTextMessageHeader::alloc()->init();\r
104             header->setFieldBodyWithName(String::stringWithUTF8String("close"), String::stringWithUTF8String("Connection"));\r
105 \r
106             // body\r
107             char html[1024];\r
108             sprintf_s(html, 1024, "<html><head><title>%s</title></head><body>%s</body></html>", reason->cString(), reason->cString());\r
109             body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(html));\r
110         }\r
111         break;\r
112 \r
113     default:\r
114         break;\r
115     }\r
116 \r
117     // Contet-Length\r
118     header->setFieldBodyWithName(String::stringWithFormat("%I64u", body->body()->length()), "Content-Length");\r
119 \r
120     // message\r
121     InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
122     RELEASE(header);\r
123     RELEASE(body);\r
124 \r
125     resp->setMessage(message);\r
126     resp->autorelease();\r
127     RELEASE(message);\r
128 \r
129     return resp;\r
130 }\r
131 \r
132 HTTPResponse *HTTPDaemon::responseWithPath(String *path, HTTPRequest *request)\r
133 {\r
134     DebugLog2("HTTPDaemon::responseWithPath()");\r
135 \r
136     HTTPResponse *result = NULL;\r
137     if ((path != NULL) && (request != NULL))\r
138     {\r
139         Data *bodyData = Data::alloc()->initWithContentsOfFile(path);\r
140         if (bodyData != NULL)\r
141         {\r
142             // header\r
143             InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
144             // Date\r
145             // Server\r
146             // Content-Encoding\r
147             // Last-Modified\r
148             // Content-Type\r
149             String *ext = path->pathExtension()->lowercaseString();\r
150             if (ext->isEqualToString("htm") || ext->isEqualToString("html"))\r
151             {\r
152                 header->setFieldBodyWithName("text/html", "Content-Type");\r
153             }\r
154             else if (ext->isEqualToString("jpg"))\r
155             {\r
156                 header->setFieldBodyWithName("image/jpeg", "Content-Type");\r
157             }\r
158             else if (ext->isEqualToString("png"))\r
159             {\r
160                 header->setFieldBodyWithName("image/png", "Content-Type");\r
161             }\r
162             else if (ext->isEqualToString("gif"))\r
163             {\r
164                 header->setFieldBodyWithName("image/gif", "Content-Type");\r
165             }\r
166             else if (ext->isEqualToString("js"))\r
167             {\r
168                 header->setFieldBodyWithName("text/javascript", "Content-Type");\r
169             }\r
170             else if (ext->isEqualToString("css"))\r
171             {\r
172                 header->setFieldBodyWithName("text/css", "Content-Type");\r
173             }\r
174             else if (ext->isEqualToString("log"))\r
175             {\r
176                 header->setFieldBodyWithName("text/plane", "Content-Type");\r
177             }\r
178             else if (ext->isEqualToString("m3u8"))\r
179             {\r
180                 header->setFieldBodyWithName("application/x-mpegURL", "Content-Type");\r
181             }\r
182             else\r
183             {\r
184                 header->setFieldBodyWithName("application/octet-stream", "Content-Type");\r
185             }\r
186             // Connection\r
187             // Transfer-Encoding\r
188             // Content-Length\r
189             header->setFieldBodyWithName(String::stringWithFormat("%I64u", bodyData->length()), "Content-Length");\r
190 \r
191             // body\r
192             InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithData(bodyData);\r
193             RELEASE(bodyData);\r
194 \r
195             // message\r
196             InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
197             RELEASE(header);\r
198             RELEASE(body);\r
199             if (message != NULL)\r
200             {\r
201 //                result = HTTPResponse::response();\r
202                 result = HTTPResponse::alloc()->init();\r
203                 result->setVersion(request->version());\r
204                 result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_OK));\r
205                 result->setStatus(HTTP_STATUS_OK);\r
206                 result->setMessage(message);\r
207                 result->autorelease();\r
208                 RELEASE(message);\r
209             }\r
210         }\r
211         else\r
212         {\r
213             DebugLog3("HTTPDaemon::responseWithPath(): date read error.");\r
214         }\r
215     }\r
216     return result;\r
217 }\r
218 \r
219 String *HTTPDaemon::reasonForStatus(int status)\r
220 {\r
221     DebugLog2("HTTPDaemon::reasonForStatus()");\r
222 \r
223     const char *result = NULL;\r
224     switch (status)\r
225     {\r
226     // Informational 1xx\r
227     case 100:   result = "Continue";                            break;\r
228     case 101:   result = "Swithing Protocols";                  break;\r
229     // Successful 2xx\r
230     case 200:   result = "OK";                                  break;\r
231     case 201:   result = "Created";                             break;\r
232     case 202:   result = "Accepted";                            break;\r
233     case 203:   result = "Non-Authoritative Information";       break;\r
234     case 204:   result = "No Content";                          break;\r
235     case 205:   result = "Reset Content";                       break;\r
236     case 206:   result = "Partial Content";                     break;\r
237     // Redirection 3xx\r
238     case 300:   result = "Multiple Choices";                    break;\r
239     case 301:   result = "Moved Permanently";                   break;\r
240     case 302:   result = "Found";                               break;\r
241     case 303:   result = "See Other";                           break;\r
242     case 304:   result = "Not Modified";                        break;\r
243     case 305:   result = "Use Proxy";                           break;\r
244     case 306:   result = "(Unused)";                            break;\r
245     case 307:   result = "Temporary Redirect";                  break;\r
246     // Client Error 4xx\r
247     case 400:   result = "Bad Request";                         break;\r
248     case 401:   result = "Unauthorized";                        break;\r
249     case 402:   result = "Payment Required";                    break;\r
250     case 403:   result = "Forbidden";                           break;\r
251     case 404:   result = "Not Found";                           break;\r
252     case 405:   result = "Method Not Allowed";                  break;\r
253     case 406:   result = "Not Acceptable";                      break;\r
254     case 407:   result = "Proxy Authentication Required";       break;\r
255     case 408:   result = "Request Timeout";                     break;\r
256     case 409:   result = "Conflict";                            break;\r
257     case 410:   result = "Gone";                                break;\r
258     case 411:   result = "Length Required";                     break;\r
259     case 412:   result = "Precondition Failed";                 break;\r
260     case 413:   result = "Request Entity Too Large";            break;\r
261     case 414:   result = "Request-URI Too Long";                break;\r
262     case 415:   result = "Unsupported Media Type";              break;\r
263     case 416:   result = "Requested Range Not Satisfiable";     break;\r
264     case 417:   result = "Expectation Failed";                  break;\r
265     // Server Error 5xx\r
266     case 500:   result = "Internal Server Error";               break;\r
267     case 501:   result = "Not Implemented";                     break;\r
268     case 502:   result = "Bad Gateway";                         break;\r
269     case 503:   result = "Service Unavailable";                 break;\r
270     case 504:   result = "Gateway Timeout";                     break;\r
271     case 505:   result = "HTTP Version Not Supported";          break;\r
272     default:                                                    break;\r
273     }\r
274     return String::stringWithUTF8String(result);\r
275 }\r
276 \r
277 unsigned __stdcall HTTPDaemon_session(void *arg)\r
278 {\r
279     HTTPDaemonSessionArgs *session = (HTTPDaemonSessionArgs *)arg;\r
280     session->_daemon->session(session->_sock, &session->_client);\r
281     closesocket(session->_sock);\r
282     delete session;\r
283     return 0;\r
284 }\r
285 \r
286 void HTTPDaemon::session(SOCKET sock, struct sockaddr_in *client)\r
287 {\r
288     DebugLog2("HTTPDaemon::session()");\r
289 \r
290     Number *num_sock = Number::alloc()->initWithInt((int)sock);\r
291 \r
292     EnterCriticalSection(&_cs);\r
293     _sockets->addObject(num_sock);\r
294     LeaveCriticalSection(&_cs);\r
295 \r
296     while (true)\r
297     {\r
298         AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
299         bool done;\r
300 \r
301         HTTPResponse *response = NULL;\r
302         DebugLog3("before request()");\r
303         HTTPRequest *request = HTTPRequest::requestWithSocket(sock);\r
304         DebugLog3("after request()");\r
305         if (request != NULL)\r
306         {\r
307             if (_delegate != NULL)\r
308             {\r
309                 response = _delegate->request(request, client);\r
310             }\r
311 \r
312 //            if ((response == NULL) && request->method()->isEqualToString(String::stringWithUTF8String("GET")))\r
313             if ((response == NULL) && (request->method()->isEqualToString("GET") || request->method()->isEqualToString("HEAD")))\r
314             {\r
315                 if (_rootPath != NULL)\r
316                 {\r
317                     String *path = _rootPath->stringByAppendingPathComponent(request->URI());\r
318                     //path = path->stringByAbbreviatingWithTildeInPath();\r
319                     //path = path->stringByStandardizingPath();\r
320                     if (path->hasPrefix(_rootPath))\r
321                     {\r
322                         FileManager *fm = FileManager::defaultManager();\r
323                         bool isDir = false;\r
324                         if (fm->fileExistsAtPath(path, &isDir))\r
325                         {\r
326                             if ((!isDir) && !(request->URI()->hasSuffix("/")))\r
327                             {\r
328                                 String *ext = path->pathExtension()->lowercaseString();\r
329                                 if (ext->isEqualToString("htm") ||\r
330                                     ext->isEqualToString("html") ||\r
331                                     ext->isEqualToString("jpg") ||\r
332                                     ext->isEqualToString("png") ||\r
333                                     ext->isEqualToString("gif") ||\r
334                                     ext->isEqualToString("js") ||\r
335                                     ext->isEqualToString("manifest") ||\r
336                                     ext->isEqualToString("gtpl") ||\r
337                                     ext->isEqualToString("css"))\r
338                                 {\r
339                                     response = responseWithPath(path, request);\r
340                                     if (response == NULL)\r
341                                     {\r
342                                         DebugLog2("error: %s\n", path->cString());\r
343                                     }\r
344                                 }\r
345                                 else\r
346                                 {\r
347                                     DebugLog2("unsupported type: %s\n", ext->cString());\r
348                                 }\r
349                             }\r
350                         }\r
351                         else\r
352                         {\r
353                             DebugLog2("not exists: %s\n", path->cString());\r
354                         }\r
355                     }\r
356                 }\r
357             }\r
358 \r
359             if (response == NULL)\r
360             {\r
361                 DebugLog3("response == NULL");\r
362                 String *ver;\r
363                 if (request == NULL)\r
364                 {\r
365                     ver = String::stringWithUTF8String("HTTP/1.1");\r
366                 }\r
367                 else\r
368                 {\r
369                     ver = request->version();\r
370                 }\r
371 \r
372                 int status;\r
373                 if (request == NULL)\r
374                 {\r
375                 DebugLog3("request == NULL");\r
376                 }\r
377                 else if (request->method() == NULL)\r
378                 {\r
379                 DebugLog3("method == NULL");\r
380                 }\r
381                 if (request->method()->isEqualToString("GET"))\r
382                 {\r
383                     status = HTTP_STATUS_INTERNAL_SERVER_ERROR;\r
384                 }\r
385                 else if (request->method()->isEqualToString("POST"))\r
386                 {\r
387                     status = HTTP_STATUS_NO_CONTENT;\r
388                 }\r
389                 response = responseWithReason(reasonForStatus(status), status, ver);\r
390             }\r
391 \r
392             // status line\r
393             char statusLine[256];\r
394             sprintf_s(statusLine, sizeof(statusLine), "%s %03d %s\r\n", response->version()->cString(), response->status(), response->reason()->cString());\r
395             send(sock, statusLine, (int)strlen(statusLine), 0);\r
396 \r
397             InternetTextMessage *message = response->message();\r
398             if (message != NULL)\r
399             {\r
400                 // response header\r
401                 InternetTextMessageHeader *header = message->header();\r
402                 if (header != NULL)\r
403                 {\r
404                     Array *fieldNames = header->fieldNames();\r
405                     for (uint i = 0; i < fieldNames->count(); ++i)\r
406                     {\r
407                         String *name = (String *)fieldNames->objectAtIndex(i);\r
408                         char field[16384];\r
409                         sprintf_s(field, sizeof(field), "%s: %s\r\n", name->cString(), header->fieldBodyForName(name)->cString());\r
410 \r
411                         DebugLog2("send response header");\r
412                         send(sock, field, (int)strlen(field), 0);\r
413                     }\r
414                 }\r
415 \r
416                 send(sock, "\r\n", 2, 0);\r
417 \r
418                 if (!request->method()->isEqualToString("HEAD"))\r
419                 {\r
420                     // response entity\r
421                     InternetTextMessageBody *body = message->body();\r
422                     if (body != NULL)\r
423                     {\r
424                         const char *ptr = NULL;\r
425                         UInteger length;\r
426                         Data *data = body->data();\r
427                         if (data != NULL)\r
428                         {\r
429                             ptr = (const char *)data->bytes();\r
430                             length = data->length();\r
431                         }\r
432                         else if (body->body() != NULL)\r
433                         {\r
434                             String *str = body->body();\r
435                             ptr = str->cString();\r
436                             length = str->length();\r
437                         }\r
438                         if (ptr != NULL)\r
439                         {\r
440                             UInteger offset = 0;\r
441                             while (offset < length)\r
442                             {\r
443                                 int len = ((length - offset) > 16384) ? 16384 : (int)(length - offset);\r
444                                 DebugLog2("send response entity");\r
445                                 send(sock, &ptr[offset], len, 0);\r
446                                 offset += len;\r
447                             }\r
448                         }\r
449                     }\r
450                 }\r
451             }\r
452 \r
453             if ((response->status() / 100) != 2)\r
454             {\r
455                 DebugLog2("done. response is not OK.");\r
456                 done = true;\r
457             }\r
458             else if (request != NULL)\r
459             {\r
460                 if (message != NULL)\r
461                 {\r
462                     InternetTextMessageHeader *header = message->header();\r
463                     if (header != NULL)\r
464                     {\r
465                         String *fieldBody = header->fieldBodyForName(String::stringWithUTF8String("Connection"));\r
466                         if (fieldBody != NULL)\r
467                         {\r
468                             if (strstr(fieldBody->cString(), "close") != NULL)\r
469                             {\r
470                                 DebugLog2("done. request connection is close.");\r
471                                 done = true;\r
472                             }\r
473                         }\r
474                     }\r
475                 }\r
476             }\r
477         }\r
478         else\r
479         {\r
480             DebugLog3("reauest is null");\r
481             done = true;\r
482         }\r
483 \r
484         pool->release();\r
485         if (done)\r
486         {\r
487             break;\r
488         }\r
489     }\r
490 \r
491     EnterCriticalSection(&_cs);\r
492     _sockets->removeObject(num_sock);\r
493     LeaveCriticalSection(&_cs);\r
494     num_sock->release();\r
495 }\r
496 \r
497 unsigned __stdcall HTTPDaemon_run(void *arg)\r
498 {\r
499     ((HTTPDaemon *)arg)->run();\r
500     return 0;\r
501 }\r
502 \r
503 void HTTPDaemon::run()\r
504 {\r
505     DebugLog2("%s()\n", __FUNCTION__);\r
506 \r
507     AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
508 \r
509     EnterCriticalSection(&_cs);\r
510 \r
511     while (_state == ST_READY)\r
512     {\r
513 /*\r
514 使用側でコールしておくこと\r
515         WSADATA wsaData;\r
516         WSAStartup(MAKEWORD(2,0), &wsaData);\r
517 */\r
518         SOCKET httpd = INVALID_SOCKET;\r
519 \r
520         // ready socket\r
521         httpd = socket(AF_INET, SOCK_STREAM, 0);\r
522         if (httpd == INVALID_SOCKET)\r
523         {\r
524             DebugLog3("error: socket() %d\n", WSAGetLastError());\r
525             break;\r
526         }\r
527 \r
528         //\r
529         BOOL yes = 1;\r
530         setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));\r
531 \r
532         // bind\r
533         struct sockaddr_in own_addr;\r
534         own_addr.sin_family = AF_INET;\r
535         own_addr.sin_port = htons(_port);\r
536         own_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r
537         if (bind(httpd, (struct sockaddr *)&own_addr, sizeof(own_addr)) != 0)\r
538         {\r
539             DebugLog3("error: bind() %d\n", WSAGetLastError());\r
540             closesocket(httpd);\r
541             break;\r
542         }\r
543 \r
544         // listen\r
545         if (listen(httpd, _backlog) != 0)\r
546         {\r
547             DebugLog3("error: listen() %d\n", WSAGetLastError());\r
548             closesocket(httpd);\r
549             break;\r
550         }\r
551 \r
552         // state change\r
553         _state = ST_RUN;\r
554 \r
555         LeaveCriticalSection(&_cs);\r
556 \r
557         bool done = false;\r
558         while (!done)\r
559         {\r
560             fd_set fdset;\r
561             FD_ZERO(&fdset);\r
562             FD_SET(httpd, &fdset);\r
563             struct timeval timeout = {1, 0};\r
564             \r
565             if (select(0, &fdset, NULL, NULL, &timeout) == SOCKET_ERROR)\r
566             {\r
567                 int err = WSAGetLastError();\r
568                 if (err != WSAEINTR)\r
569                 {\r
570                     DebugLog3("error: select() %d\n", err);\r
571                     break;\r
572                 }\r
573             }\r
574 \r
575             if (FD_ISSET(httpd, &fdset))\r
576             {\r
577                 // accept\r
578                 struct sockaddr_in acc_addr;\r
579                 int sock_len = sizeof(acc_addr);\r
580                 SOCKET newsock;\r
581                 if ((newsock = accept(httpd, (struct sockaddr *)&acc_addr, &sock_len)) != INVALID_SOCKET)\r
582                 {\r
583                     HTTPDaemonSessionArgs *args = new HTTPDaemonSessionArgs();\r
584                     args->_daemon = this;\r
585                     args->_sock = newsock;\r
586                     memcpy(&args->_client, &acc_addr, sizeof(acc_addr));\r
587 \r
588                     HANDLE h;\r
589                     unsigned int uiThreadId;\r
590                     h = (HANDLE)_beginthreadex(NULL,\r
591                                                0,\r
592                                                HTTPDaemon_session,\r
593                                                args,\r
594                                                0,\r
595                                                &uiThreadId);\r
596                     if (h == NULL)\r
597                     {\r
598                         DebugLog3("error: _beginthreades()\n");\r
599                         closesocket(newsock);\r
600                         delete args;\r
601                     }\r
602                 }\r
603                 else\r
604                 {\r
605                     DebugLog3("error: accept() %d\n", WSAGetLastError());\r
606                 }\r
607             }\r
608 \r
609             // state check\r
610             EnterCriticalSection(&_cs);\r
611             done = (_state == ST_DONE);\r
612             LeaveCriticalSection(&_cs);\r
613         }\r
614 \r
615         // socket close\r
616         closesocket(httpd);\r
617 \r
618 //        WSACleanup();\r
619 \r
620         // state change\r
621         EnterCriticalSection(&_cs);\r
622         break;\r
623     }\r
624 \r
625     DebugLog2("HTTPDaemon::run() loop done.");\r
626 \r
627     // セッションの終了待ち\r
628     if (_sockets->count() > 0)\r
629     {\r
630         // 残存セッションを終了させる為、ソケットを閉じる\r
631         for (uint i = 0; i < _sockets->count(); ++i)\r
632         {\r
633             SOCKET s = (SOCKET)((Number *)_sockets->objectAtIndex(i))->intValue();\r
634             closesocket(s);\r
635         }\r
636         // セッションが終了して_socketからオブジェクトを削除するのを待つ\r
637         while (_sockets->count() > 0)\r
638         {\r
639             LeaveCriticalSection(&_cs);\r
640             Sleep(20);\r
641             EnterCriticalSection(&_cs);\r
642         }\r
643     }\r
644 \r
645     DebugLog2("HTTPDaemon::run() session close done.");\r
646 \r
647     _state = ST_IDLE;\r
648 \r
649     LeaveCriticalSection(&_cs);\r
650 \r
651     pool->release();\r
652 \r
653     DebugLog2("%s() done.\n", __FUNCTION__);\r
654 }\r
655 \r
656 bool HTTPDaemon::start()\r
657 {\r
658     DebugLog2("%s()\n", __FUNCTION__);\r
659     \r
660     bool result = false;\r
661     \r
662     EnterCriticalSection(&_cs);\r
663     \r
664     if (_state == ST_IDLE)\r
665     {\r
666         HANDLE h;\r
667         unsigned int uiThreadId;\r
668         \r
669         h = (HANDLE)_beginthreadex(NULL,\r
670                                    0,\r
671                                    HTTPDaemon_run,\r
672                                    this,\r
673                                    0,\r
674                                    &uiThreadId);\r
675         if (h != NULL)\r
676         {\r
677             _state = ST_READY;\r
678 \r
679             LeaveCriticalSection(&_cs);\r
680 \r
681             bool done = false;\r
682             while (!done)\r
683             {\r
684                 bool needSleep = false;\r
685 \r
686                 EnterCriticalSection(&_cs);\r
687 \r
688                 if (_state == ST_IDLE)\r
689                 {\r
690                     done = true;\r
691                 }\r
692                 else if (_state == ST_RUN)\r
693                 {\r
694                     done = true;\r
695                     result = true;\r
696                 }\r
697                 else if (_state == ST_READY)\r
698                 {\r
699                     needSleep = true;\r
700                 }\r
701                 LeaveCriticalSection(&_cs);\r
702 \r
703                 if (needSleep)\r
704                 {\r
705                     ::Sleep(100); // 100 ms\r
706                 }\r
707             }\r
708 \r
709             EnterCriticalSection(&_cs);\r
710         }\r
711     }\r
712 \r
713     LeaveCriticalSection(&_cs);\r
714 \r
715     return result;\r
716 }\r
717 \r
718 void HTTPDaemon::stop()\r
719 {\r
720     DebugLog2("HTTPDaemon::stop()", __FUNCTION__);\r
721 \r
722     EnterCriticalSection(&_cs);\r
723     if (_state == ST_RUN)\r
724     {\r
725         _state = ST_DONE;\r
726     }\r
727     LeaveCriticalSection(&_cs);\r
728 \r
729     wait();\r
730 }\r
731 \r
732 void HTTPDaemon::wait()\r
733 {\r
734     bool done = false;\r
735     while (!done)\r
736     {\r
737         EnterCriticalSection(&_cs);\r
738         done = (_state == ST_IDLE);\r
739         LeaveCriticalSection(&_cs);\r
740         if (!done)\r
741         {\r
742             ::Sleep(100);\r
743         }\r
744     }\r
745 }\r
746 \r
747 const char *HTTPDaemon::className()\r
748 {\r
749     return "NET::HTTPDaemon";\r
750 }\r
751 \r
752 } // NET\r