6 #include <Raym/Log.h>
\r
7 #include <Raym/Raym.h>
\r
8 #include "net/HTTPDaemon.h"
\r
10 using namespace Raym;
\r
15 HTTPDaemon::HTTPDaemon()
\r
17 DebugLog2("HTTPDaemon::HTTPDaemon()");
\r
28 HTTPDaemon::~HTTPDaemon()
\r
35 DebugLog2("HTTPDaemon::~HTTPDaemon()");
\r
38 HTTPDaemon *HTTPDaemon::alloc()
\r
40 return new HTTPDaemon();
\r
43 HTTPDaemon *HTTPDaemon::initWithPort(int port, int backlog)
\r
45 DebugLog2("HTTPDaemon::initWithPort()");
\r
49 _sockets = Array::alloc()->initWithCapacity(0);
\r
53 void HTTPDaemon::setDelegate(HTTPDaemonDelegate *delegate)
\r
55 DebugLog2("HTTPDaemon::setDelegate()");
\r
57 EnterCriticalSection(&_cs);
\r
58 _delegate = delegate;
\r
59 LeaveCriticalSection(&_cs);
\r
62 void HTTPDaemon::setRootPath(String *path)
\r
64 DebugLog2("HTTPDaemon::setRootPath()");
\r
70 _rootPath->retain();
\r
74 String *HTTPDaemon::rootPath()
\r
79 HTTPResponse *HTTPDaemon::responseWithReason(String *reason, int status, String *version)
\r
81 DebugLog2("HTTPDaemon::responseWithReason()");
\r
83 HTTPResponse *resp = HTTPResponse::alloc()->init();
\r
84 resp->setVersion(version);
\r
85 resp->setReason(reason);
\r
86 resp->setStatus(status);
\r
89 InternetTextMessageHeader * header = NULL;
\r
90 InternetTextMessageBody * body = NULL;
\r
93 case HTTP_STATUS_NO_CONTENT:
\r
95 header = InternetTextMessageHeader::alloc()->init();
\r
96 header->setFieldBodyWithName(String::stringWithUTF8String("close"), String::stringWithUTF8String("Connection"));
\r
100 case HTTP_STATUS_INTERNAL_SERVER_ERROR:
\r
103 header = InternetTextMessageHeader::alloc()->init();
\r
104 header->setFieldBodyWithName(String::stringWithUTF8String("close"), String::stringWithUTF8String("Connection"));
\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
118 header->setFieldBodyWithName(String::stringWithFormat("%I64u", body->body()->length()), "Content-Length");
\r
121 InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);
\r
125 resp->setMessage(message);
\r
126 resp->autorelease();
\r
132 HTTPResponse *HTTPDaemon::responseWithPath(String *path, HTTPRequest *request)
\r
134 DebugLog2("HTTPDaemon::responseWithPath()");
\r
136 HTTPResponse *result = NULL;
\r
137 if ((path != NULL) && (request != NULL))
\r
139 Data *bodyData = Data::alloc()->initWithContentsOfFile(path);
\r
140 if (bodyData != NULL)
\r
143 InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();
\r
146 // Content-Encoding
\r
149 String *ext = path->pathExtension()->lowercaseString();
\r
150 if (ext->isEqualToString("htm") || ext->isEqualToString("html"))
\r
152 header->setFieldBodyWithName("text/html", "Content-Type");
\r
154 else if (ext->isEqualToString("jpg"))
\r
156 header->setFieldBodyWithName("image/jpeg", "Content-Type");
\r
158 else if (ext->isEqualToString("png"))
\r
160 header->setFieldBodyWithName("image/png", "Content-Type");
\r
162 else if (ext->isEqualToString("gif"))
\r
164 header->setFieldBodyWithName("image/gif", "Content-Type");
\r
166 else if (ext->isEqualToString("js"))
\r
168 header->setFieldBodyWithName("text/javascript", "Content-Type");
\r
170 else if (ext->isEqualToString("css"))
\r
172 header->setFieldBodyWithName("text/css", "Content-Type");
\r
174 else if (ext->isEqualToString("log"))
\r
176 header->setFieldBodyWithName("text/plane", "Content-Type");
\r
178 else if (ext->isEqualToString("m3u8"))
\r
180 header->setFieldBodyWithName("application/x-mpegURL", "Content-Type");
\r
184 header->setFieldBodyWithName("application/octet-stream", "Content-Type");
\r
187 // Transfer-Encoding
\r
189 header->setFieldBodyWithName(String::stringWithFormat("%I64u", bodyData->length()), "Content-Length");
\r
192 InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithData(bodyData);
\r
196 InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);
\r
199 if (message != NULL)
\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
213 DebugLog3("HTTPDaemon::responseWithPath(): date read error.");
\r
219 String *HTTPDaemon::reasonForStatus(int status)
\r
221 DebugLog2("HTTPDaemon::reasonForStatus()");
\r
223 const char *result = NULL;
\r
226 // Informational 1xx
\r
227 case 100: result = "Continue"; break;
\r
228 case 101: result = "Swithing Protocols"; break;
\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
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
274 return String::stringWithUTF8String(result);
\r
277 unsigned __stdcall HTTPDaemon_session(void *arg)
\r
279 HTTPDaemonSessionArgs *session = (HTTPDaemonSessionArgs *)arg;
\r
280 session->_daemon->session(session->_sock, &session->_client);
\r
281 closesocket(session->_sock);
\r
286 void HTTPDaemon::session(SOCKET sock, struct sockaddr_in *client)
\r
288 DebugLog2("HTTPDaemon::session()");
\r
290 Number *num_sock = Number::alloc()->initWithInt((int)sock);
\r
292 EnterCriticalSection(&_cs);
\r
293 _sockets->addObject(num_sock);
\r
294 LeaveCriticalSection(&_cs);
\r
298 AutoreleasePool *pool = AutoreleasePool::alloc()->init();
\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
307 if (_delegate != NULL)
\r
309 response = _delegate->request(request, client);
\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
315 if (_rootPath != NULL)
\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
322 FileManager *fm = FileManager::defaultManager();
\r
323 bool isDir = false;
\r
324 if (fm->fileExistsAtPath(path, &isDir))
\r
326 if ((!isDir) && !(request->URI()->hasSuffix("/")))
\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
339 response = responseWithPath(path, request);
\r
340 if (response == NULL)
\r
342 DebugLog2("error: %s\n", path->cString());
\r
347 DebugLog2("unsupported type: %s\n", ext->cString());
\r
353 DebugLog2("not exists: %s\n", path->cString());
\r
359 if (response == NULL)
\r
361 DebugLog3("response == NULL");
\r
363 if (request == NULL)
\r
365 ver = String::stringWithUTF8String("HTTP/1.1");
\r
369 ver = request->version();
\r
373 if (request == NULL)
\r
375 DebugLog3("request == NULL");
\r
377 else if (request->method() == NULL)
\r
379 DebugLog3("method == NULL");
\r
381 if (request->method()->isEqualToString("GET"))
\r
383 status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
\r
385 else if (request->method()->isEqualToString("POST"))
\r
387 status = HTTP_STATUS_NO_CONTENT;
\r
389 response = responseWithReason(reasonForStatus(status), status, ver);
\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
397 InternetTextMessage *message = response->message();
\r
398 if (message != NULL)
\r
401 InternetTextMessageHeader *header = message->header();
\r
402 if (header != NULL)
\r
404 Array *fieldNames = header->fieldNames();
\r
405 for (uint i = 0; i < fieldNames->count(); ++i)
\r
407 String *name = (String *)fieldNames->objectAtIndex(i);
\r
409 sprintf_s(field, sizeof(field), "%s: %s\r\n", name->cString(), header->fieldBodyForName(name)->cString());
\r
411 DebugLog2("send response header");
\r
412 send(sock, field, (int)strlen(field), 0);
\r
416 send(sock, "\r\n", 2, 0);
\r
418 if (!request->method()->isEqualToString("HEAD"))
\r
421 InternetTextMessageBody *body = message->body();
\r
424 const char *ptr = NULL;
\r
426 Data *data = body->data();
\r
429 ptr = (const char *)data->bytes();
\r
430 length = data->length();
\r
432 else if (body->body() != NULL)
\r
434 String *str = body->body();
\r
435 ptr = str->cString();
\r
436 length = str->length();
\r
440 UInteger offset = 0;
\r
441 while (offset < length)
\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
453 if ((response->status() / 100) != 2)
\r
455 DebugLog2("done. response is not OK.");
\r
458 else if (request != NULL)
\r
460 if (message != NULL)
\r
462 InternetTextMessageHeader *header = message->header();
\r
463 if (header != NULL)
\r
465 String *fieldBody = header->fieldBodyForName(String::stringWithUTF8String("Connection"));
\r
466 if (fieldBody != NULL)
\r
468 if (strstr(fieldBody->cString(), "close") != NULL)
\r
470 DebugLog2("done. request connection is close.");
\r
480 DebugLog3("reauest is null");
\r
491 EnterCriticalSection(&_cs);
\r
492 _sockets->removeObject(num_sock);
\r
493 LeaveCriticalSection(&_cs);
\r
494 num_sock->release();
\r
497 unsigned __stdcall HTTPDaemon_run(void *arg)
\r
499 ((HTTPDaemon *)arg)->run();
\r
503 void HTTPDaemon::run()
\r
505 DebugLog2("%s()\n", __FUNCTION__);
\r
507 AutoreleasePool *pool = AutoreleasePool::alloc()->init();
\r
509 EnterCriticalSection(&_cs);
\r
511 while (_state == ST_READY)
\r
516 WSAStartup(MAKEWORD(2,0), &wsaData);
\r
518 SOCKET httpd = INVALID_SOCKET;
\r
521 httpd = socket(AF_INET, SOCK_STREAM, 0);
\r
522 if (httpd == INVALID_SOCKET)
\r
524 DebugLog3("error: socket() %d\n", WSAGetLastError());
\r
530 setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
\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
539 DebugLog3("error: bind() %d\n", WSAGetLastError());
\r
540 closesocket(httpd);
\r
545 if (listen(httpd, _backlog) != 0)
\r
547 DebugLog3("error: listen() %d\n", WSAGetLastError());
\r
548 closesocket(httpd);
\r
555 LeaveCriticalSection(&_cs);
\r
562 FD_SET(httpd, &fdset);
\r
563 struct timeval timeout = {1, 0};
\r
565 if (select(0, &fdset, NULL, NULL, &timeout) == SOCKET_ERROR)
\r
567 int err = WSAGetLastError();
\r
568 if (err != WSAEINTR)
\r
570 DebugLog3("error: select() %d\n", err);
\r
575 if (FD_ISSET(httpd, &fdset))
\r
578 struct sockaddr_in acc_addr;
\r
579 int sock_len = sizeof(acc_addr);
\r
581 if ((newsock = accept(httpd, (struct sockaddr *)&acc_addr, &sock_len)) != INVALID_SOCKET)
\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
589 unsigned int uiThreadId;
\r
590 h = (HANDLE)_beginthreadex(NULL,
\r
592 HTTPDaemon_session,
\r
598 DebugLog3("error: _beginthreades()\n");
\r
599 closesocket(newsock);
\r
605 DebugLog3("error: accept() %d\n", WSAGetLastError());
\r
610 EnterCriticalSection(&_cs);
\r
611 done = (_state == ST_DONE);
\r
612 LeaveCriticalSection(&_cs);
\r
616 closesocket(httpd);
\r
621 EnterCriticalSection(&_cs);
\r
625 DebugLog2("HTTPDaemon::run() loop done.");
\r
628 if (_sockets->count() > 0)
\r
630 // 残存セッションを終了させる為、ソケットを閉じる
\r
631 for (uint i = 0; i < _sockets->count(); ++i)
\r
633 SOCKET s = (SOCKET)((Number *)_sockets->objectAtIndex(i))->intValue();
\r
636 // セッションが終了して_socketからオブジェクトを削除するのを待つ
\r
637 while (_sockets->count() > 0)
\r
639 LeaveCriticalSection(&_cs);
\r
641 EnterCriticalSection(&_cs);
\r
645 DebugLog2("HTTPDaemon::run() session close done.");
\r
649 LeaveCriticalSection(&_cs);
\r
653 DebugLog2("%s() done.\n", __FUNCTION__);
\r
656 bool HTTPDaemon::start()
\r
658 DebugLog2("%s()\n", __FUNCTION__);
\r
660 bool result = false;
\r
662 EnterCriticalSection(&_cs);
\r
664 if (_state == ST_IDLE)
\r
667 unsigned int uiThreadId;
\r
669 h = (HANDLE)_beginthreadex(NULL,
\r
679 LeaveCriticalSection(&_cs);
\r
684 bool needSleep = false;
\r
686 EnterCriticalSection(&_cs);
\r
688 if (_state == ST_IDLE)
\r
692 else if (_state == ST_RUN)
\r
697 else if (_state == ST_READY)
\r
701 LeaveCriticalSection(&_cs);
\r
705 ::Sleep(100); // 100 ms
\r
709 EnterCriticalSection(&_cs);
\r
713 LeaveCriticalSection(&_cs);
\r
718 void HTTPDaemon::stop()
\r
720 DebugLog2("HTTPDaemon::stop()", __FUNCTION__);
\r
722 EnterCriticalSection(&_cs);
\r
723 if (_state == ST_RUN)
\r
727 LeaveCriticalSection(&_cs);
\r
732 void HTTPDaemon::wait()
\r
737 EnterCriticalSection(&_cs);
\r
738 done = (_state == ST_IDLE);
\r
739 LeaveCriticalSection(&_cs);
\r
747 const char *HTTPDaemon::className()
\r
749 return "NET::HTTPDaemon";
\r