OSDN Git Service

httpd
authorosx86_pt1 <rmitachi@ta2.so-net.ne.jp>
Mon, 7 Mar 2016 13:37:10 +0000 (22:37 +0900)
committerosx86_pt1 <rmitachi@ta2.so-net.ne.jp>
Mon, 7 Mar 2016 13:37:10 +0000 (22:37 +0900)
14 files changed:
src/net/HTTPDaemon.cpp [new file with mode: 0644]
src/net/HTTPDaemon.h [new file with mode: 0644]
src/net/HTTPRequest.cpp [new file with mode: 0644]
src/net/HTTPRequest.h [new file with mode: 0644]
src/net/HTTPResponse.cpp [new file with mode: 0644]
src/net/HTTPResponse.h [new file with mode: 0644]
src/net/InternetTextMessage.cpp [new file with mode: 0644]
src/net/InternetTextMessage.h [new file with mode: 0644]
src/net/InternetTextMessageBody.cpp [new file with mode: 0644]
src/net/InternetTextMessageBody.h [new file with mode: 0644]
src/net/InternetTextMessageHeader.cpp [new file with mode: 0644]
src/net/InternetTextMessageHeader.h [new file with mode: 0644]
src/ry0/iPTd/HTTPD.cpp [new file with mode: 0644]
src/ry0/iPTd/HTTPD.h [new file with mode: 0644]

diff --git a/src/net/HTTPDaemon.cpp b/src/net/HTTPDaemon.cpp
new file mode 100644 (file)
index 0000000..911e4e8
--- /dev/null
@@ -0,0 +1,752 @@
+//\r
+//  HTTPDaemon.cpp\r
+//\r
+\r
+#define DBG_LEVEL 0\r
+#include <Raym/Log.h>\r
+#include <Raym/Raym.h>\r
+#include "net/HTTPDaemon.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace NET\r
+{\r
+\r
+HTTPDaemon::HTTPDaemon()\r
+{\r
+    DebugLog2("HTTPDaemon::HTTPDaemon()");\r
+\r
+    _port = -1;\r
+    _backlog = -1;\r
+    _state = ST_IDLE;\r
+    _rootPath = NULL;\r
+    _delegate = NULL;\r
+    _sockets = NULL;\r
+\r
+}\r
+\r
+HTTPDaemon::~HTTPDaemon()\r
+{\r
+    stop();\r
+\r
+    RELEASE(_rootPath);\r
+    RELEASE(_sockets);\r
+\r
+    DebugLog2("HTTPDaemon::~HTTPDaemon()");\r
+}\r
+\r
+HTTPDaemon *HTTPDaemon::alloc()\r
+{\r
+    return new HTTPDaemon();\r
+}\r
+\r
+HTTPDaemon *HTTPDaemon::initWithPort(int port, int backlog)\r
+{\r
+    DebugLog2("HTTPDaemon::initWithPort()");\r
+\r
+    _port = port;\r
+    _backlog = backlog;\r
+    _sockets = Array::alloc()->initWithCapacity(0);\r
+    return this;\r
+}\r
+\r
+void HTTPDaemon::setDelegate(HTTPDaemonDelegate *delegate)\r
+{\r
+    DebugLog2("HTTPDaemon::setDelegate()");\r
+\r
+    EnterCriticalSection(&_cs);\r
+    _delegate = delegate;\r
+    LeaveCriticalSection(&_cs);\r
+}\r
+\r
+void HTTPDaemon::setRootPath(String *path)\r
+{\r
+    DebugLog2("HTTPDaemon::setRootPath()");\r
+\r
+    RELEASE(_rootPath);\r
+    if (path != NULL)\r
+    {\r
+        _rootPath = path;\r
+        _rootPath->retain();\r
+    }\r
+}\r
+\r
+String *HTTPDaemon::rootPath()\r
+{\r
+    return _rootPath;\r
+}\r
+\r
+HTTPResponse *HTTPDaemon::responseWithReason(String *reason, int status, String *version)\r
+{\r
+    DebugLog2("HTTPDaemon::responseWithReason()");\r
+\r
+    HTTPResponse *resp = HTTPResponse::alloc()->init();\r
+    resp->setVersion(version);\r
+    resp->setReason(reason);\r
+    resp->setStatus(status);\r
+\r
+    // header & body\r
+    InternetTextMessageHeader * header = NULL;\r
+    InternetTextMessageBody *   body   = NULL;\r
+    switch (status)\r
+    {\r
+    case HTTP_STATUS_NO_CONTENT:\r
+        {\r
+            header = InternetTextMessageHeader::alloc()->init();\r
+            header->setFieldBodyWithName(String::stringWithUTF8String("close"), String::stringWithUTF8String("Connection"));\r
+        }\r
+        break;\r
+\r
+    case HTTP_STATUS_INTERNAL_SERVER_ERROR:\r
+        {\r
+            // header\r
+            header = InternetTextMessageHeader::alloc()->init();\r
+            header->setFieldBodyWithName(String::stringWithUTF8String("close"), String::stringWithUTF8String("Connection"));\r
+\r
+            // body\r
+            char html[1024];\r
+            sprintf_s(html, 1024, "<html><head><title>%s</title></head><body>%s</body></html>", reason->cString(), reason->cString());\r
+            body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(html));\r
+        }\r
+        break;\r
+\r
+    default:\r
+        break;\r
+    }\r
+\r
+    // Contet-Length\r
+    header->setFieldBodyWithName(String::stringWithFormat("%I64u", body->body()->length()), "Content-Length");\r
+\r
+    // message\r
+    InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+    RELEASE(header);\r
+    RELEASE(body);\r
+\r
+    resp->setMessage(message);\r
+    resp->autorelease();\r
+    RELEASE(message);\r
+\r
+    return resp;\r
+}\r
+\r
+HTTPResponse *HTTPDaemon::responseWithPath(String *path, HTTPRequest *request)\r
+{\r
+    DebugLog2("HTTPDaemon::responseWithPath()");\r
+\r
+    HTTPResponse *result = NULL;\r
+    if ((path != NULL) && (request != NULL))\r
+    {\r
+        Data *bodyData = Data::alloc()->initWithContentsOfFile(path);\r
+        if (bodyData != NULL)\r
+        {\r
+            // header\r
+            InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+            // Date\r
+            // Server\r
+            // Content-Encoding\r
+            // Last-Modified\r
+            // Content-Type\r
+            String *ext = path->pathExtension()->lowercaseString();\r
+            if (ext->isEqualToString("htm") || ext->isEqualToString("html"))\r
+            {\r
+                header->setFieldBodyWithName("text/html", "Content-Type");\r
+            }\r
+            else if (ext->isEqualToString("jpg"))\r
+            {\r
+                header->setFieldBodyWithName("image/jpeg", "Content-Type");\r
+            }\r
+            else if (ext->isEqualToString("png"))\r
+            {\r
+                header->setFieldBodyWithName("image/png", "Content-Type");\r
+            }\r
+            else if (ext->isEqualToString("gif"))\r
+            {\r
+                header->setFieldBodyWithName("image/gif", "Content-Type");\r
+            }\r
+            else if (ext->isEqualToString("js"))\r
+            {\r
+                header->setFieldBodyWithName("text/javascript", "Content-Type");\r
+            }\r
+            else if (ext->isEqualToString("css"))\r
+            {\r
+                header->setFieldBodyWithName("text/css", "Content-Type");\r
+            }\r
+            else if (ext->isEqualToString("log"))\r
+            {\r
+                header->setFieldBodyWithName("text/plane", "Content-Type");\r
+            }\r
+            else if (ext->isEqualToString("m3u8"))\r
+            {\r
+                header->setFieldBodyWithName("application/x-mpegURL", "Content-Type");\r
+            }\r
+            else\r
+            {\r
+                header->setFieldBodyWithName("application/octet-stream", "Content-Type");\r
+            }\r
+            // Connection\r
+            // Transfer-Encoding\r
+            // Content-Length\r
+            header->setFieldBodyWithName(String::stringWithFormat("%I64u", bodyData->length()), "Content-Length");\r
+\r
+            // body\r
+            InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithData(bodyData);\r
+            RELEASE(bodyData);\r
+\r
+            // message\r
+            InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+            RELEASE(header);\r
+            RELEASE(body);\r
+            if (message != NULL)\r
+            {\r
+//                result = HTTPResponse::response();\r
+                result = HTTPResponse::alloc()->init();\r
+                result->setVersion(request->version());\r
+                result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_OK));\r
+                result->setStatus(HTTP_STATUS_OK);\r
+                result->setMessage(message);\r
+                result->autorelease();\r
+                RELEASE(message);\r
+            }\r
+        }\r
+        else\r
+        {\r
+            DebugLog3("HTTPDaemon::responseWithPath(): date read error.");\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+String *HTTPDaemon::reasonForStatus(int status)\r
+{\r
+    DebugLog2("HTTPDaemon::reasonForStatus()");\r
+\r
+    const char *result = NULL;\r
+    switch (status)\r
+    {\r
+    // Informational 1xx\r
+    case 100:   result = "Continue";                            break;\r
+    case 101:   result = "Swithing Protocols";                  break;\r
+    // Successful 2xx\r
+    case 200:   result = "OK";                                  break;\r
+    case 201:   result = "Created";                             break;\r
+    case 202:   result = "Accepted";                            break;\r
+    case 203:   result = "Non-Authoritative Information";       break;\r
+    case 204:   result = "No Content";                          break;\r
+    case 205:   result = "Reset Content";                       break;\r
+    case 206:   result = "Partial Content";                     break;\r
+    // Redirection 3xx\r
+    case 300:   result = "Multiple Choices";                    break;\r
+    case 301:   result = "Moved Permanently";                   break;\r
+    case 302:   result = "Found";                               break;\r
+    case 303:   result = "See Other";                           break;\r
+    case 304:   result = "Not Modified";                        break;\r
+    case 305:   result = "Use Proxy";                           break;\r
+    case 306:   result = "(Unused)";                            break;\r
+    case 307:   result = "Temporary Redirect";                  break;\r
+    // Client Error 4xx\r
+    case 400:   result = "Bad Request";                         break;\r
+    case 401:   result = "Unauthorized";                        break;\r
+    case 402:   result = "Payment Required";                    break;\r
+    case 403:   result = "Forbidden";                           break;\r
+    case 404:   result = "Not Found";                           break;\r
+    case 405:   result = "Method Not Allowed";                  break;\r
+    case 406:   result = "Not Acceptable";                      break;\r
+    case 407:   result = "Proxy Authentication Required";       break;\r
+    case 408:   result = "Request Timeout";                     break;\r
+    case 409:   result = "Conflict";                            break;\r
+    case 410:   result = "Gone";                                break;\r
+    case 411:   result = "Length Required";                     break;\r
+    case 412:   result = "Precondition Failed";                 break;\r
+    case 413:   result = "Request Entity Too Large";            break;\r
+    case 414:   result = "Request-URI Too Long";                break;\r
+    case 415:   result = "Unsupported Media Type";              break;\r
+    case 416:   result = "Requested Range Not Satisfiable";     break;\r
+    case 417:   result = "Expectation Failed";                  break;\r
+    // Server Error 5xx\r
+    case 500:   result = "Internal Server Error";               break;\r
+    case 501:   result = "Not Implemented";                     break;\r
+    case 502:   result = "Bad Gateway";                         break;\r
+    case 503:   result = "Service Unavailable";                 break;\r
+    case 504:   result = "Gateway Timeout";                     break;\r
+    case 505:   result = "HTTP Version Not Supported";          break;\r
+    default:                                                    break;\r
+    }\r
+    return String::stringWithUTF8String(result);\r
+}\r
+\r
+unsigned __stdcall HTTPDaemon_session(void *arg)\r
+{\r
+    HTTPDaemonSessionArgs *session = (HTTPDaemonSessionArgs *)arg;\r
+    session->_daemon->session(session->_sock, &session->_client);\r
+    closesocket(session->_sock);\r
+    delete session;\r
+    return 0;\r
+}\r
+\r
+void HTTPDaemon::session(SOCKET sock, struct sockaddr_in *client)\r
+{\r
+    DebugLog2("HTTPDaemon::session()");\r
+\r
+    Number *num_sock = Number::alloc()->initWithInt((int)sock);\r
+\r
+    EnterCriticalSection(&_cs);\r
+    _sockets->addObject(num_sock);\r
+    LeaveCriticalSection(&_cs);\r
+\r
+    while (true)\r
+    {\r
+        AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+        bool done;\r
+\r
+        HTTPResponse *response = NULL;\r
+        DebugLog3("before request()");\r
+        HTTPRequest *request = HTTPRequest::requestWithSocket(sock);\r
+        DebugLog3("after request()");\r
+        if (request != NULL)\r
+        {\r
+            if (_delegate != NULL)\r
+            {\r
+                response = _delegate->request(request, client);\r
+            }\r
+\r
+//            if ((response == NULL) && request->method()->isEqualToString(String::stringWithUTF8String("GET")))\r
+            if ((response == NULL) && (request->method()->isEqualToString("GET") || request->method()->isEqualToString("HEAD")))\r
+            {\r
+                if (_rootPath != NULL)\r
+                {\r
+                    String *path = _rootPath->stringByAppendingPathComponent(request->URI());\r
+                    //path = path->stringByAbbreviatingWithTildeInPath();\r
+                    //path = path->stringByStandardizingPath();\r
+                    if (path->hasPrefix(_rootPath))\r
+                    {\r
+                        FileManager *fm = FileManager::defaultManager();\r
+                        bool isDir = false;\r
+                        if (fm->fileExistsAtPath(path, &isDir))\r
+                        {\r
+                            if ((!isDir) && !(request->URI()->hasSuffix("/")))\r
+                            {\r
+                                String *ext = path->pathExtension()->lowercaseString();\r
+                                if (ext->isEqualToString("htm") ||\r
+                                    ext->isEqualToString("html") ||\r
+                                    ext->isEqualToString("jpg") ||\r
+                                    ext->isEqualToString("png") ||\r
+                                    ext->isEqualToString("gif") ||\r
+                                    ext->isEqualToString("js") ||\r
+                                    ext->isEqualToString("manifest") ||\r
+                                    ext->isEqualToString("gtpl") ||\r
+                                    ext->isEqualToString("css"))\r
+                                {\r
+                                    response = responseWithPath(path, request);\r
+                                    if (response == NULL)\r
+                                    {\r
+                                        DebugLog2("error: %s\n", path->cString());\r
+                                    }\r
+                                }\r
+                                else\r
+                                {\r
+                                    DebugLog2("unsupported type: %s\n", ext->cString());\r
+                                }\r
+                            }\r
+                        }\r
+                        else\r
+                        {\r
+                            DebugLog2("not exists: %s\n", path->cString());\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (response == NULL)\r
+            {\r
+                DebugLog3("response == NULL");\r
+                String *ver;\r
+                if (request == NULL)\r
+                {\r
+                    ver = String::stringWithUTF8String("HTTP/1.1");\r
+                }\r
+                else\r
+                {\r
+                    ver = request->version();\r
+                }\r
+\r
+                int status;\r
+                if (request == NULL)\r
+                {\r
+                DebugLog3("request == NULL");\r
+                }\r
+                else if (request->method() == NULL)\r
+                {\r
+                DebugLog3("method == NULL");\r
+                }\r
+                if (request->method()->isEqualToString("GET"))\r
+                {\r
+                    status = HTTP_STATUS_INTERNAL_SERVER_ERROR;\r
+                }\r
+                else if (request->method()->isEqualToString("POST"))\r
+                {\r
+                    status = HTTP_STATUS_NO_CONTENT;\r
+                }\r
+                response = responseWithReason(reasonForStatus(status), status, ver);\r
+            }\r
+\r
+            // status line\r
+            char statusLine[256];\r
+            sprintf_s(statusLine, sizeof(statusLine), "%s %03d %s\r\n", response->version()->cString(), response->status(), response->reason()->cString());\r
+            send(sock, statusLine, (int)strlen(statusLine), 0);\r
+\r
+            InternetTextMessage *message = response->message();\r
+            if (message != NULL)\r
+            {\r
+                // response header\r
+                InternetTextMessageHeader *header = message->header();\r
+                if (header != NULL)\r
+                {\r
+                    Array *fieldNames = header->fieldNames();\r
+                    for (uint i = 0; i < fieldNames->count(); ++i)\r
+                    {\r
+                        String *name = (String *)fieldNames->objectAtIndex(i);\r
+                        char field[16384];\r
+                        sprintf_s(field, sizeof(field), "%s: %s\r\n", name->cString(), header->fieldBodyForName(name)->cString());\r
+\r
+                        DebugLog2("send response header");\r
+                        send(sock, field, (int)strlen(field), 0);\r
+                    }\r
+                }\r
+\r
+                send(sock, "\r\n", 2, 0);\r
+\r
+                if (!request->method()->isEqualToString("HEAD"))\r
+                {\r
+                    // response entity\r
+                    InternetTextMessageBody *body = message->body();\r
+                    if (body != NULL)\r
+                    {\r
+                        const char *ptr = NULL;\r
+                        UInteger length;\r
+                        Data *data = body->data();\r
+                        if (data != NULL)\r
+                        {\r
+                            ptr = (const char *)data->bytes();\r
+                            length = data->length();\r
+                        }\r
+                        else if (body->body() != NULL)\r
+                        {\r
+                            String *str = body->body();\r
+                            ptr = str->cString();\r
+                            length = str->length();\r
+                        }\r
+                        if (ptr != NULL)\r
+                        {\r
+                            UInteger offset = 0;\r
+                            while (offset < length)\r
+                            {\r
+                                int len = ((length - offset) > 16384) ? 16384 : (int)(length - offset);\r
+                                DebugLog2("send response entity");\r
+                                send(sock, &ptr[offset], len, 0);\r
+                                offset += len;\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+            if ((response->status() / 100) != 2)\r
+            {\r
+                DebugLog2("done. response is not OK.");\r
+                done = true;\r
+            }\r
+            else if (request != NULL)\r
+            {\r
+                if (message != NULL)\r
+                {\r
+                    InternetTextMessageHeader *header = message->header();\r
+                    if (header != NULL)\r
+                    {\r
+                        String *fieldBody = header->fieldBodyForName(String::stringWithUTF8String("Connection"));\r
+                        if (fieldBody != NULL)\r
+                        {\r
+                            if (strstr(fieldBody->cString(), "close") != NULL)\r
+                            {\r
+                                DebugLog2("done. request connection is close.");\r
+                                done = true;\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        else\r
+        {\r
+            DebugLog3("reauest is null");\r
+            done = true;\r
+        }\r
+\r
+        pool->release();\r
+        if (done)\r
+        {\r
+            break;\r
+        }\r
+    }\r
+\r
+    EnterCriticalSection(&_cs);\r
+    _sockets->removeObject(num_sock);\r
+    LeaveCriticalSection(&_cs);\r
+    num_sock->release();\r
+}\r
+\r
+unsigned __stdcall HTTPDaemon_run(void *arg)\r
+{\r
+    ((HTTPDaemon *)arg)->run();\r
+    return 0;\r
+}\r
+\r
+void HTTPDaemon::run()\r
+{\r
+    DebugLog2("%s()\n", __FUNCTION__);\r
+\r
+    AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+\r
+    EnterCriticalSection(&_cs);\r
+\r
+    while (_state == ST_READY)\r
+    {\r
+/*\r
+使用側でコールしておくこと\r
+        WSADATA wsaData;\r
+        WSAStartup(MAKEWORD(2,0), &wsaData);\r
+*/\r
+        SOCKET httpd = INVALID_SOCKET;\r
+\r
+        // ready socket\r
+        httpd = socket(AF_INET, SOCK_STREAM, 0);\r
+        if (httpd == INVALID_SOCKET)\r
+        {\r
+            DebugLog3("error: socket() %d\n", WSAGetLastError());\r
+            break;\r
+        }\r
+\r
+        //\r
+        BOOL yes = 1;\r
+        setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));\r
+\r
+        // bind\r
+        struct sockaddr_in own_addr;\r
+        own_addr.sin_family = AF_INET;\r
+        own_addr.sin_port = htons(_port);\r
+        own_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r
+        if (bind(httpd, (struct sockaddr *)&own_addr, sizeof(own_addr)) != 0)\r
+        {\r
+            DebugLog3("error: bind() %d\n", WSAGetLastError());\r
+            closesocket(httpd);\r
+            break;\r
+        }\r
+\r
+        // listen\r
+        if (listen(httpd, _backlog) != 0)\r
+        {\r
+            DebugLog3("error: listen() %d\n", WSAGetLastError());\r
+            closesocket(httpd);\r
+            break;\r
+        }\r
+\r
+        // state change\r
+        _state = ST_RUN;\r
+\r
+        LeaveCriticalSection(&_cs);\r
+\r
+        bool done = false;\r
+        while (!done)\r
+        {\r
+            fd_set fdset;\r
+            FD_ZERO(&fdset);\r
+            FD_SET(httpd, &fdset);\r
+            struct timeval timeout = {1, 0};\r
+            \r
+            if (select(0, &fdset, NULL, NULL, &timeout) == SOCKET_ERROR)\r
+            {\r
+                int err = WSAGetLastError();\r
+                if (err != WSAEINTR)\r
+                {\r
+                    DebugLog3("error: select() %d\n", err);\r
+                    break;\r
+                }\r
+            }\r
+\r
+            if (FD_ISSET(httpd, &fdset))\r
+            {\r
+                // accept\r
+                struct sockaddr_in acc_addr;\r
+                int sock_len = sizeof(acc_addr);\r
+                SOCKET newsock;\r
+                if ((newsock = accept(httpd, (struct sockaddr *)&acc_addr, &sock_len)) != INVALID_SOCKET)\r
+                {\r
+                    HTTPDaemonSessionArgs *args = new HTTPDaemonSessionArgs();\r
+                    args->_daemon = this;\r
+                    args->_sock = newsock;\r
+                    memcpy(&args->_client, &acc_addr, sizeof(acc_addr));\r
+\r
+                    HANDLE h;\r
+                    unsigned int uiThreadId;\r
+                    h = (HANDLE)_beginthreadex(NULL,\r
+                                               0,\r
+                                               HTTPDaemon_session,\r
+                                               args,\r
+                                               0,\r
+                                               &uiThreadId);\r
+                    if (h == NULL)\r
+                    {\r
+                        DebugLog3("error: _beginthreades()\n");\r
+                        closesocket(newsock);\r
+                        delete args;\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    DebugLog3("error: accept() %d\n", WSAGetLastError());\r
+                }\r
+            }\r
+\r
+            // state check\r
+            EnterCriticalSection(&_cs);\r
+            done = (_state == ST_DONE);\r
+            LeaveCriticalSection(&_cs);\r
+        }\r
+\r
+        // socket close\r
+        closesocket(httpd);\r
+\r
+//        WSACleanup();\r
+\r
+        // state change\r
+        EnterCriticalSection(&_cs);\r
+        break;\r
+    }\r
+\r
+    DebugLog2("HTTPDaemon::run() loop done.");\r
+\r
+    // セッションの終了待ち\r
+    if (_sockets->count() > 0)\r
+    {\r
+        // 残存セッションを終了させる為、ソケットを閉じる\r
+        for (uint i = 0; i < _sockets->count(); ++i)\r
+        {\r
+            SOCKET s = (SOCKET)((Number *)_sockets->objectAtIndex(i))->intValue();\r
+            closesocket(s);\r
+        }\r
+        // セッションが終了して_socketからオブジェクトを削除するのを待つ\r
+        while (_sockets->count() > 0)\r
+        {\r
+            LeaveCriticalSection(&_cs);\r
+            Sleep(20);\r
+            EnterCriticalSection(&_cs);\r
+        }\r
+    }\r
+\r
+    DebugLog2("HTTPDaemon::run() session close done.");\r
+\r
+    _state = ST_IDLE;\r
+\r
+    LeaveCriticalSection(&_cs);\r
+\r
+    pool->release();\r
+\r
+    DebugLog2("%s() done.\n", __FUNCTION__);\r
+}\r
+\r
+bool HTTPDaemon::start()\r
+{\r
+    DebugLog2("%s()\n", __FUNCTION__);\r
+    \r
+    bool result = false;\r
+    \r
+    EnterCriticalSection(&_cs);\r
+    \r
+    if (_state == ST_IDLE)\r
+    {\r
+        HANDLE h;\r
+        unsigned int uiThreadId;\r
+        \r
+        h = (HANDLE)_beginthreadex(NULL,\r
+                                   0,\r
+                                   HTTPDaemon_run,\r
+                                   this,\r
+                                   0,\r
+                                   &uiThreadId);\r
+        if (h != NULL)\r
+        {\r
+            _state = ST_READY;\r
+\r
+            LeaveCriticalSection(&_cs);\r
+\r
+            bool done = false;\r
+            while (!done)\r
+            {\r
+                bool needSleep = false;\r
+\r
+                EnterCriticalSection(&_cs);\r
+\r
+                if (_state == ST_IDLE)\r
+                {\r
+                    done = true;\r
+                }\r
+                else if (_state == ST_RUN)\r
+                {\r
+                    done = true;\r
+                    result = true;\r
+                }\r
+                else if (_state == ST_READY)\r
+                {\r
+                    needSleep = true;\r
+                }\r
+                LeaveCriticalSection(&_cs);\r
+\r
+                if (needSleep)\r
+                {\r
+                    ::Sleep(100); // 100 ms\r
+                }\r
+            }\r
+\r
+            EnterCriticalSection(&_cs);\r
+        }\r
+    }\r
+\r
+    LeaveCriticalSection(&_cs);\r
+\r
+    return result;\r
+}\r
+\r
+void HTTPDaemon::stop()\r
+{\r
+    DebugLog2("HTTPDaemon::stop()", __FUNCTION__);\r
+\r
+    EnterCriticalSection(&_cs);\r
+    if (_state == ST_RUN)\r
+    {\r
+        _state = ST_DONE;\r
+    }\r
+    LeaveCriticalSection(&_cs);\r
+\r
+    wait();\r
+}\r
+\r
+void HTTPDaemon::wait()\r
+{\r
+    bool done = false;\r
+    while (!done)\r
+    {\r
+        EnterCriticalSection(&_cs);\r
+        done = (_state == ST_IDLE);\r
+        LeaveCriticalSection(&_cs);\r
+        if (!done)\r
+        {\r
+            ::Sleep(100);\r
+        }\r
+    }\r
+}\r
+\r
+const char *HTTPDaemon::className()\r
+{\r
+    return "NET::HTTPDaemon";\r
+}\r
+\r
+} // NET\r
diff --git a/src/net/HTTPDaemon.h b/src/net/HTTPDaemon.h
new file mode 100644 (file)
index 0000000..7794a88
--- /dev/null
@@ -0,0 +1,79 @@
+//\r
+//  HTTPDaemon.h\r
+//\r
+\r
+#pragma once\r
+\r
+#include <windows.h>\r
+#include <process.h>\r
+#include <stdlib.h>\r
+#include <winsock.h>\r
+\r
+#include "Raym/Raym.h"\r
+\r
+#include "net/HTTPRequest.h"\r
+#include "net/HTTPResponse.h"\r
+\r
+\r
+namespace NET\r
+{\r
+\r
+class HTTPDaemonDelegate\r
+{\r
+public:\r
+    virtual HTTPResponse *request(HTTPRequest *request, struct sockaddr_in *client) = 0;\r
+\r
+};\r
+\r
+class HTTPDaemon : public Raym::Object\r
+{\r
+private:\r
+    int                     _port;\r
+    int                     _backlog;\r
+    HTTPDaemonDelegate *    _delegate;\r
+    Raym::String *          _rootPath;\r
+    enum _state\r
+    {\r
+        ST_IDLE,\r
+        ST_READY,\r
+        ST_RUN,\r
+        ST_DONE\r
+    }                       _state;\r
+\r
+protected:\r
+    HTTPDaemon();\r
+    ~HTTPDaemon();\r
+\r
+public:\r
+    static HTTPDaemon *alloc();\r
+    HTTPDaemon *initWithPort(int port, int backlog);\r
+\r
+    void setDelegate(HTTPDaemonDelegate *delegate);\r
+    void setRootPath(Raym::String *path);\r
+    Raym::String *rootPath();\r
+    void run();\r
+    bool start();\r
+    void stop();\r
+    void wait();\r
+\r
+    HTTPResponse *responseWithReason(Raym::String *reason, int status, Raym::String *version);\r
+    HTTPResponse *responseWithPath(Raym::String *path, HTTPRequest *request);\r
+    static Raym::String *reasonForStatus(int status);\r
+\r
+    // for internal use\r
+    Raym::Array * _sockets;\r
+    void session(SOCKET sock, struct sockaddr_in *client);\r
+\r
+    virtual const char *className();\r
+};\r
+\r
+\r
+class HTTPDaemonSessionArgs\r
+{\r
+public:\r
+    HTTPDaemon *        _daemon;\r
+    SOCKET              _sock;\r
+    struct sockaddr_in  _client;\r
+};\r
+\r
+} // NET\r
diff --git a/src/net/HTTPRequest.cpp b/src/net/HTTPRequest.cpp
new file mode 100644 (file)
index 0000000..3463bc9
--- /dev/null
@@ -0,0 +1,260 @@
+//\r
+// HTTPRequest.cpp\r
+//\r
+\r
+#define DBG_LEVEL 0\r
+#include <Raym/Log.h>\r
+#include "net/HTTPRequest.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace NET\r
+{\r
+\r
+const char *HTTPRequest::KEY_CGI    = "CGI";\r
+const char *HTTPRequest::KEY_PARAMS = "PARAMS";\r
+\r
+HTTPRequest::HTTPRequest()\r
+{\r
+    _method  = NULL;\r
+    _uri     = NULL;\r
+    _version = NULL;\r
+    _message = NULL;\r
+}\r
+\r
+HTTPRequest::~HTTPRequest()\r
+{\r
+    RELEASE(_method);\r
+    RELEASE(_uri);\r
+    RELEASE(_version);\r
+    RELEASE(_message);\r
+}\r
+\r
+HTTPRequest *HTTPRequest::requestWithSocket(SOCKET sock)\r
+{\r
+    DebugLog2("%s\n", __FUNCTION__);\r
+\r
+    char buf[16384];\r
+    int offset = 0;\r
+    while (offset < sizeof(buf) - 1)\r
+    {\r
+        int len = recv(sock, &buf[offset], 1, 0);\r
+        if (len == 0)\r
+        {\r
+            break;\r
+        }\r
+        if (offset > 0)\r
+        {\r
+            if ((buf[offset - 1] == '\r') && (buf[offset] == '\n'))\r
+            {\r
+                ++offset;\r
+                break;\r
+            }\r
+        }\r
+        ++offset;\r
+    }\r
+    buf[offset] = '\0';\r
+\r
+    HTTPRequest *result = NULL;\r
+\r
+    String *method  = NULL;\r
+    String *uri     = NULL;\r
+    String *version = NULL;\r
+    if (strchr(buf, ' ') != NULL)\r
+    {\r
+        char *p = strchr(buf, ' ');\r
+        *p++ = '\0';\r
+        if ((buf[strlen(buf) - 2] == '\r') && (buf[strlen(buf) - 1] == '\n'))\r
+        {\r
+            buf[strlen(buf) - 2] = '\0';\r
+        }\r
+        method = String::alloc()->initWithUTF8String(&buf[0]);\r
+        while (*p == ' ')\r
+        {\r
+            ++p;\r
+        }\r
+        if (strchr(p, ' ') != NULL)\r
+        {\r
+            char *p2 = strchr(p, ' ');\r
+            *p2++ = '\0';\r
+            if ((p[strlen(p) - 2] == '\r') && (p[strlen(p) - 1] == '\n'))\r
+            {\r
+                p[strlen(p) - 2] = '\0';\r
+            }\r
+            uri = String::alloc()->initWithUTF8String(p);\r
+            while (*p2 == ' ')\r
+            {\r
+                ++p2;\r
+            }\r
+            if (strchr(p2, ' ') == NULL)\r
+            {\r
+                if ((p2[strlen(p2) - 2] == '\r') && (p2[strlen(p2) - 1] == '\n'))\r
+                {\r
+                    p2[strlen(p2) - 2] = '\0';\r
+                }\r
+                version = String::alloc()->initWithUTF8String(p2);\r
+            }\r
+        }\r
+    }\r
+\r
+    if (version != NULL)\r
+    {\r
+        if (version->isEqualToString(String::stringWithUTF8String("HTTP/1.0")) || version->isEqualToString(String::stringWithUTF8String("HTTP/1.1")))\r
+        {\r
+            InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->initWithSocket(sock);\r
+            if (header != NULL)\r
+            {\r
+                InternetTextMessageBody *body = NULL;\r
+                /*\r
+                if (header->fieldBodyForName("Transfer-Encoding") != NULL)\r
+                {\r
+                    DebugLog3("has Transfer-Encoding\n");\r
+                    abort();\r
+                }\r
+                else if (header->fieldBodyForName("Content-Length") != NULL)\r
+                {\r
+                    DebugLog3("has Content-Length\n");\r
+                    abort();\r
+                }\r
+                else if (header->fieldBodyForName("Content-Type") != NULL)\r
+                {\r
+                    DebugLog3("has Content-Type\n");\r
+                    abort();\r
+                }\r
+                */\r
+\r
+                InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+                RELEASE(header);\r
+                RELEASE(body);\r
+\r
+                result = new HTTPRequest();\r
+                result->setMethod(method);\r
+                result->setURI(uri);\r
+                result->setVersion(version);\r
+                result->setMessage(message);\r
+                result->autorelease();\r
+\r
+                RELEASE(message);\r
+            }\r
+            else\r
+            {\r
+                DebugLog3("error: InternetTextMessageHeader::headerWithSocket()\n");\r
+            }\r
+        }\r
+    }\r
+\r
+    RELEASE(method);\r
+    RELEASE(uri);\r
+    RELEASE(version);\r
+\r
+    return result;\r
+}\r
+\r
+void HTTPRequest::setMethod(String *method)\r
+{\r
+    SET_METHOD(_method, method);\r
+}\r
+\r
+String *HTTPRequest::method()\r
+{\r
+    return _method;\r
+}\r
+\r
+void HTTPRequest::setURI(String *uri)\r
+{\r
+    SET_METHOD(_uri, uri);\r
+}\r
+\r
+String *HTTPRequest::URI()\r
+{\r
+    return _uri;\r
+}\r
+\r
+void HTTPRequest::setVersion(String *version)\r
+{\r
+    SET_METHOD(_version, version);\r
+}\r
+\r
+String *HTTPRequest::version()\r
+{\r
+    return _version;\r
+}\r
+\r
+void HTTPRequest::setMessage(InternetTextMessage *message)\r
+{\r
+    SET_METHOD(_message, message);\r
+}\r
+\r
+InternetTextMessage *HTTPRequest::message()\r
+{\r
+    return _message;\r
+}\r
+\r
+Dictionary *HTTPRequest::parseAsCGI()\r
+{\r
+    DebugLog2("HTTPRequest::parseAsCGI()");\r
+\r
+    Dictionary *result = NULL;\r
+    if (_uri != NULL)\r
+    {\r
+        DebugLog3("_uri != NULL");\r
+        char *tmpstr = _strdup(_uri->cString());\r
+        if (tmpstr != NULL)\r
+        {\r
+            DebugLog3("tmpstr != NULL");\r
+            char *p1 = strchr(tmpstr, '?');\r
+            if (p1 != NULL)\r
+            {\r
+                DebugLog3("p1 != NULL");\r
+                *p1 = '\0';\r
+                DebugLog2("cgi: %s", tmpstr);\r
+                result = Dictionary::dictionaryWithCapacity(0);\r
+                result->setObject(String::stringWithUTF8String(tmpstr), KEY_CGI);\r
+\r
+                Array *params = Array::arrayWithCapacity(0);\r
+                result->setObject(params, KEY_PARAMS);\r
+\r
+                ++p1;\r
+                while (true)\r
+                {\r
+                    char *p2 = strchr(p1, '&');\r
+                    if (p2 != NULL)\r
+                    {\r
+                        *p2 = '\0';\r
+                    }\r
+                    char *p3 = strchr(p1, '=');\r
+                    if (p3 != NULL)\r
+                    {\r
+                        *p3 = '\0';\r
+                        String *para_name = String::stringWithUTF8String(p1);\r
+                        ++p3;\r
+                        String *para_val = String::stringWithUTF8String(p3);\r
+                        DebugLog3("para_name: %s, para_val: %s", p1, p3);\r
+                        Dictionary *para = Dictionary::dictionaryWithCapacity(0);\r
+                        para->setObject(para_val, para_name);\r
+                        params->addObject(para);\r
+                    }\r
+\r
+                    // 終端判定\r
+                    if (p2 == NULL)\r
+                    {\r
+                        break;\r
+                    }\r
+\r
+                    // 次パラメータに設定\r
+                    p1 = p2 + 1;\r
+                }\r
+            }\r
+\r
+            free(tmpstr);\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+const char *HTTPRequest::className()\r
+{\r
+    return "HTTPRequest";\r
+}\r
+\r
+} // NET\r
diff --git a/src/net/HTTPRequest.h b/src/net/HTTPRequest.h
new file mode 100644 (file)
index 0000000..fb24eb7
--- /dev/null
@@ -0,0 +1,50 @@
+//\r
+//  HTTPRequest.h\r
+//\r
+\r
+#ifndef __NET_HTTPREQUEST_H__\r
+#define __NET_HTTPREQUEST_H__\r
+\r
+#include <Raym/Raym.h>\r
+\r
+#include "net/InternetTextMessage.h"\r
+\r
+namespace NET\r
+{\r
+\r
+class HTTPRequest : public Raym::Object\r
+{\r
+private:\r
+    Raym::String *  _method;\r
+    Raym::String *  _uri;\r
+    Raym::String *  _version;\r
+    InternetTextMessage *   _message;\r
+\r
+protected:\r
+    HTTPRequest();\r
+    ~HTTPRequest();\r
+\r
+public:\r
+    static HTTPRequest *requestWithSocket(SOCKET sock);\r
+\r
+    void setMethod(Raym::String *method);\r
+    Raym::String *method();\r
+    void setURI(Raym::String *uri);\r
+    Raym::String *URI();\r
+    void setVersion(Raym::String *version);\r
+    Raym::String *version();\r
+    void setMessage(InternetTextMessage *message);\r
+    InternetTextMessage *message();\r
+\r
+\r
+    // parse\r
+    static const char *KEY_CGI;\r
+    static const char *KEY_PARAMS;\r
+    Raym::Dictionary *parseAsCGI();\r
+\r
+    virtual const char *className();\r
+};\r
+\r
+} // NET\r
+\r
+#endif // __NET_HTTPREQUEST_H__\r
diff --git a/src/net/HTTPResponse.cpp b/src/net/HTTPResponse.cpp
new file mode 100644 (file)
index 0000000..40f2413
--- /dev/null
@@ -0,0 +1,90 @@
+//\r
+// HTTPResponse.cpp\r
+//\r
+\r
+#include "net/HTTPResponse.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace NET\r
+{\r
+\r
+HTTPResponse::HTTPResponse()\r
+{\r
+    _version = NULL;\r
+    _status  = 0;\r
+    _reason  = NULL;\r
+    _message = NULL;\r
+}\r
+\r
+HTTPResponse::~HTTPResponse()\r
+{\r
+    RELEASE(_version);\r
+    RELEASE(_reason);\r
+    RELEASE(_message);\r
+}\r
+\r
+HTTPResponse *HTTPResponse::alloc()\r
+{\r
+#if 0\r
+    HTTPResponse *result;\r
+    result = new HTTPResponse();\r
+    result->init();\r
+    result->autorelease();\r
+    return result;\r
+#else\r
+    return new HTTPResponse();\r
+#endif\r
+}\r
+\r
+HTTPResponse *HTTPResponse::init()\r
+{\r
+    return this;\r
+}\r
+\r
+void HTTPResponse::setVersion(String *version)\r
+{\r
+    SET_METHOD(_version, version);\r
+}\r
+\r
+String *HTTPResponse::version()\r
+{\r
+    return _version;\r
+}\r
+\r
+void HTTPResponse::setStatus(int status)\r
+{\r
+    _status = status;\r
+}\r
+\r
+int HTTPResponse::status()\r
+{\r
+    return _status;\r
+}\r
+\r
+void HTTPResponse::setReason(String *reason)\r
+{\r
+    SET_METHOD(_reason, reason);\r
+}\r
+\r
+String *HTTPResponse::reason()\r
+{\r
+    return _reason;\r
+}\r
+\r
+void HTTPResponse::setMessage(InternetTextMessage *message)\r
+{\r
+    SET_METHOD(_message, message);\r
+}\r
+\r
+InternetTextMessage *HTTPResponse::message()\r
+{\r
+    return _message;\r
+}\r
+\r
+const char *HTTPResponse::className()\r
+{\r
+    return "HTTPResponse";\r
+}\r
+\r
+} // NET\r
diff --git a/src/net/HTTPResponse.h b/src/net/HTTPResponse.h
new file mode 100644 (file)
index 0000000..21a1fd5
--- /dev/null
@@ -0,0 +1,93 @@
+//\r
+// HTTPResponse.h\r
+//\r
+\r
+#pragma once\r
+\r
+#include <Raym/Raym.h>\r
+\r
+#include "net/InternetTextMessage.h"\r
+\r
+namespace NET\r
+{\r
+\r
+enum HTTP_STATUS\r
+{\r
+    // Informational 1xx\r
+    HTTP_STATUS_CONTINUE                        = 100,\r
+    HTTP_STATUS_SWITCHING_PROTOCOLS             = 101,\r
+    // Successful 2xx\r
+    HTTP_STATUS_OK                              = 200,\r
+    HTTP_STATUS_CREATED                         = 201,\r
+    HTTP_STATUS_ACCEPTED                        = 202,\r
+    HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION   = 203,\r
+    HTTP_STATUS_NO_CONTENT                      = 204,\r
+    HTTP_STATUS_RESET_CONTENT                   = 205,\r
+    HTTP_STATUS_PARTIAL_CONTENT                 = 206,\r
+    // Redirection 3xx\r
+    HTTP_STATUS_MULTIPLE_CHOICES                = 300,\r
+    HTTP_STATUS_MOVED_PERMANENTLY               = 301,\r
+    HTTP_STATUS_FOUND                           = 302,\r
+    HTTP_STATUS_SEE_OTHER                       = 303,\r
+    HTTP_STATUS_NOT_MODIFIED                    = 304,\r
+    HTTP_STATUS_USE_PROXY                       = 305,\r
+    HTTP_STATUS_UNUSED                          = 306,\r
+    HTTP_STATUS_TEMPORARY_REDIRECT              = 307,\r
+    // Client Error 4xx\r
+    HTTP_STATUS_BAD_REQUEST                     = 400,\r
+    HTTP_STATUS_UNAUTHORIZED                    = 401,\r
+    HTTP_STATUS_PAYMENT_REQUIRED                = 402,\r
+    HTTP_STATUS_FORBIDDEN                       = 403,\r
+    HTTP_STATUS_NOT_FOUND                       = 404,\r
+    HTTP_STATUS_METHOD_NOT_ALLOWED              = 405,\r
+    HTTP_STATUS_NOT_ACCEPTABLE                  = 406,\r
+    HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED   = 407,\r
+    HTTP_STATUS_REQUEST_TIMEOUT                 = 408,\r
+    HTTP_STATUS_CONFLICT                        = 409,\r
+    HTTP_STATUS_GONE                            = 410,\r
+    HTTP_STATUS_LENGTH_REQUIRED                 = 411,\r
+    HTTP_STATUS_PRECONDITION_FAILED             = 412,\r
+    HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE        = 413,\r
+    HTTP_STATUS_REQUEST_URI_TOO_LONG            = 414,\r
+    HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE          = 415,\r
+    HTTP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416,\r
+    HTTP_STATUS_EXPECTATION_FAILED              = 417,\r
+    // Server Error 5xx\r
+    HTTP_STATUS_INTERNAL_SERVER_ERROR           = 500,\r
+    HTTP_STATUS_NOT_IMPLEMENTED                 = 501,\r
+    HTTP_STATUS_BAD_GATEWAY                     = 502,\r
+    HTTP_STATUS_SERVICE_UNAVAILABLE             = 503,\r
+    HTTP_STATUS_GATEWAY_TIMEOUT                 = 504,\r
+    HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED      = 505\r
+};\r
+\r
+class HTTPResponse : public Raym::Object\r
+{\r
+private:\r
+    Raym::String *  _version;\r
+    int                     _status;\r
+    Raym::String *  _reason;\r
+    InternetTextMessage *   _message;\r
+\r
+protected:\r
+    HTTPResponse();\r
+    ~HTTPResponse();\r
+\r
+public:\r
+    static HTTPResponse *alloc();\r
+    HTTPResponse *init();\r
+\r
+    void setVersion(Raym::String *version);\r
+    Raym::String *version();\r
+    void setStatus(int status);\r
+    int status();\r
+    void setReason(Raym::String *reason);\r
+    Raym::String *reason();\r
+    void setMessage(InternetTextMessage *message);\r
+    InternetTextMessage *message();\r
+\r
+    virtual const char *className();\r
+};\r
+\r
+} // NET\r
+\r
diff --git a/src/net/InternetTextMessage.cpp b/src/net/InternetTextMessage.cpp
new file mode 100644 (file)
index 0000000..11bd594
--- /dev/null
@@ -0,0 +1,138 @@
+//\r
+// InternetTextMessage.cpp\r
+//\r
+\r
+#include "net/InternetTextMessage.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace NET\r
+{\r
+\r
+InternetTextMessage::InternetTextMessage()\r
+{\r
+    _header = NULL;\r
+    _body = NULL;\r
+}\r
+\r
+InternetTextMessage::~InternetTextMessage()\r
+{\r
+    if (_body != NULL)\r
+    {\r
+        _body->release();\r
+        _body = NULL;\r
+    }\r
+    if (_header != NULL)\r
+    {\r
+        _header->release();\r
+        _header = NULL;\r
+    }\r
+}\r
+\r
+InternetTextMessage *InternetTextMessage::alloc()\r
+{\r
+    return new InternetTextMessage();\r
+}\r
+\r
+InternetTextMessage *InternetTextMessage::initWithContentsOfFile(const char *path)\r
+{\r
+    /*\r
+    NSFileManager *fm = [NSFileManager defaultManager];\r
+    return [self initWithData:[fm contentsAtPath:path]];\r
+    */\r
+    release();\r
+    return NULL;\r
+}\r
+\r
+InternetTextMessage *InternetTextMessage::initWithData(Data *data)\r
+{\r
+    /*\r
+    if ((self = [super init]) != nil)\r
+    {\r
+        _header = nil;\r
+        _body = nil;\r
+\r
+        char *tmp = "\r\n\r\n";\r
+        Data *crlfcrlf = [Data dataWithBytes:tmp length:4];\r
+        NSRange r = {0, [data length]};\r
+        r = [data rangeOfData:crlfcrlf options:0 range:r];\r
+        if (r.location != NSNotFound)\r
+        {\r
+            NSRange rh = {0, r.location + 4};\r
+            Data *dh = [data subdataWithRange:rh];\r
+            _header = [[InternetTextMessageHeader alloc] initWithData:dh];\r
+            NSRange rb = {r.location + 4, ([data length] - (r.location + 4))};\r
+            Data *db = [data subdataWithRange:rb];\r
+            _body = [[InternetTextMessageBody alloc] initWithData:db];\r
+        }\r
+        else\r
+        {\r
+            [self release];\r
+            self = nil;\r
+        }\r
+    }\r
+    */\r
+    release();\r
+    return NULL;\r
+}\r
+\r
+InternetTextMessage *InternetTextMessage::initWithHeaderAndBody(InternetTextMessageHeader *header, InternetTextMessageBody *body)\r
+{\r
+    if (header != NULL)\r
+    {\r
+        _header = header->retain();\r
+        if (body != NULL)\r
+        {\r
+            _body = body->retain();\r
+        }\r
+        return this;\r
+    }\r
+\r
+    release();\r
+    return NULL;\r
+}\r
+\r
+InternetTextMessage *InternetTextMessage::retain()\r
+{\r
+    Object::retain();\r
+    return this;\r
+}\r
+\r
+InternetTextMessage *InternetTextMessage::autorelease()\r
+{\r
+    Object::autorelease();\r
+    return this;\r
+}\r
+\r
+InternetTextMessageHeader *InternetTextMessage::header()\r
+{\r
+    return _header;\r
+}\r
+\r
+InternetTextMessageBody *InternetTextMessage::body()\r
+{\r
+    return _body;\r
+}\r
+\r
+Data *InternetTextMessage::data()\r
+{\r
+    /*\r
+    NSMutableData *result = [NSMutableData dataWithData:[_header data]];\r
+    if ([_body data])\r
+    {\r
+        [result appendData:[_body data]];\r
+    }\r
+    else\r
+    {\r
+        [result appendData:[[_body body] dataUsingEncoding:[_header encoding]]];\r
+    }\r
+    */\r
+    return NULL;\r
+}\r
+\r
+const char *InternetTextMessage::className()\r
+{\r
+    return "NET::InternetTextMessage";\r
+}\r
+\r
+} // NET\r
diff --git a/src/net/InternetTextMessage.h b/src/net/InternetTextMessage.h
new file mode 100644 (file)
index 0000000..a6e4411
--- /dev/null
@@ -0,0 +1,40 @@
+//\r
+// InternetTextMessage.h\r
+//\r
+\r
+#pragma once\r
+\r
+#include "net/InternetTextMessageHeader.h"\r
+#include "net/InternetTextMessageBody.h"\r
+\r
+namespace NET\r
+{\r
+\r
+class InternetTextMessage : public Raym::Object\r
+{\r
+private:\r
+    InternetTextMessageHeader * _header;\r
+    InternetTextMessageBody *   _body;\r
+\r
+protected:\r
+    InternetTextMessage();\r
+    ~InternetTextMessage();\r
+\r
+public:\r
+    static InternetTextMessage *alloc();\r
+    InternetTextMessage *initWithContentsOfFile(const char *path);\r
+    InternetTextMessage *initWithData(Raym::Data *data);\r
+    InternetTextMessage *initWithHeaderAndBody(InternetTextMessageHeader *header, InternetTextMessageBody *body);\r
+    InternetTextMessage *retain();\r
+    InternetTextMessage *autorelease();\r
+\r
+    InternetTextMessageHeader *header();\r
+    InternetTextMessageBody *body();\r
+\r
+    Raym::Data *data();\r
+\r
+    virtual const char *className();\r
+};\r
+\r
+} // NET\r
+\r
diff --git a/src/net/InternetTextMessageBody.cpp b/src/net/InternetTextMessageBody.cpp
new file mode 100644 (file)
index 0000000..fed46ab
--- /dev/null
@@ -0,0 +1,83 @@
+//\r
+// InternetTextMessageBody.cpp\r
+//\r
+\r
+#include <stdlib.h>\r
+\r
+#include "net/InternetTextMessageBody.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace NET\r
+{\r
+\r
+InternetTextMessageBody::InternetTextMessageBody()\r
+{\r
+    _body = NULL;\r
+    _data = NULL;\r
+}\r
+\r
+InternetTextMessageBody::~InternetTextMessageBody()\r
+{\r
+    RELEASE(_body);\r
+    RELEASE(_data);\r
+}\r
+\r
+InternetTextMessageBody *InternetTextMessageBody::alloc()\r
+{\r
+    return new InternetTextMessageBody();\r
+}\r
+\r
+InternetTextMessageBody *InternetTextMessageBody::initWithString(String *string)\r
+{\r
+    if (string != NULL)\r
+    {\r
+        _body = string->retain();\r
+        return this;\r
+    }\r
+\r
+    release();\r
+    return NULL;\r
+}\r
+\r
+InternetTextMessageBody *InternetTextMessageBody::initWithData(Data *data)\r
+{\r
+\r
+    if (data != NULL)\r
+    {\r
+        _data = data->retain();\r
+        return this;\r
+    }\r
+\r
+    release();\r
+    return NULL;\r
+}\r
+\r
+InternetTextMessageBody *InternetTextMessageBody::retain()\r
+{\r
+    Object::retain();\r
+    return this;\r
+}\r
+\r
+InternetTextMessageBody *InternetTextMessageBody::autorelease()\r
+{\r
+    Object::autorelease();\r
+    return this;\r
+}\r
+\r
+String *InternetTextMessageBody::body()\r
+{\r
+    return _body;\r
+}\r
+\r
+Data *InternetTextMessageBody::data()\r
+{\r
+    return _data;\r
+}\r
+\r
+const char *InternetTextMessageBody::className()\r
+{\r
+    return "NET::InternetTextMessageBody";\r
+}\r
+\r
+} // NET\r
diff --git a/src/net/InternetTextMessageBody.h b/src/net/InternetTextMessageBody.h
new file mode 100644 (file)
index 0000000..48b845f
--- /dev/null
@@ -0,0 +1,36 @@
+//\r
+// InternetTextMessageBody.h\r
+//\r
+\r
+#pragma once\r
+\r
+#include <Raym/Raym.h>\r
+\r
+namespace NET\r
+{\r
+\r
+class InternetTextMessageBody : public Raym::Object\r
+{\r
+private:\r
+    Raym::String *  _body;\r
+    Raym::Data *    _data;\r
+\r
+protected:\r
+    InternetTextMessageBody();\r
+    ~InternetTextMessageBody();\r
+\r
+public:\r
+    static InternetTextMessageBody *alloc();\r
+    InternetTextMessageBody *initWithString(Raym::String *string);\r
+    InternetTextMessageBody *initWithData(Raym::Data *data);\r
+    InternetTextMessageBody *retain();\r
+    InternetTextMessageBody *autorelease();\r
+\r
+    Raym::String *body();\r
+    Raym::Data *data();\r
+\r
+    virtual const char *className();\r
+};\r
+\r
+} // NET\r
+\r
diff --git a/src/net/InternetTextMessageHeader.cpp b/src/net/InternetTextMessageHeader.cpp
new file mode 100644 (file)
index 0000000..43a219e
--- /dev/null
@@ -0,0 +1,286 @@
+//\r
+// InternetTextMessageHeader.h\r
+//\r
+\r
+#include <Raym/Log.h>\r
+#include "net/InternetTextMessageHeader.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace NET\r
+{\r
+\r
+InternetTextMessageHeader::InternetTextMessageHeader()\r
+{\r
+    _encoding = ASCIIStringEncoding;\r
+    _fields = NULL;\r
+    _data = NULL;\r
+}\r
+\r
+InternetTextMessageHeader::~InternetTextMessageHeader()\r
+{\r
+    RELEASE(_fields);\r
+    RELEASE(_data);\r
+}\r
+\r
+InternetTextMessageHeader *InternetTextMessageHeader::alloc()\r
+{\r
+    return new InternetTextMessageHeader();\r
+}\r
+\r
+InternetTextMessageHeader *InternetTextMessageHeader::init()\r
+{\r
+    RELEASE(_fields);\r
+    RELEASE(_data);\r
+    _fields = Dictionary::alloc()->initWithCapacity(0);\r
+    return this;\r
+}\r
+\r
+InternetTextMessageHeader *InternetTextMessageHeader::initWithData(Data *data)\r
+{\r
+    DebugLog2("%s()\n", __FUNCTION__);\r
+\r
+    init();\r
+\r
+    if (data != NULL)\r
+    {\r
+        _data = data->retain();\r
+\r
+        char field[512];\r
+        const char *buffer = (const char *)data->bytes();\r
+\r
+        int index = 0;\r
+        uint offset = 0;\r
+        while (offset < data->length())\r
+        {\r
+            field[index++] = buffer[offset++];\r
+            if ((field[index - 2] == '\r') && (field[index - 1] == '\n'))\r
+            {\r
+                field[index - 2] = '\0';\r
+                char *value = strchr(field, ':');\r
+                if (value != NULL)\r
+                {\r
+                    *value++ = '\0';\r
+                    while (*value == ' ')\r
+                    {\r
+                        ++value;\r
+                    }\r
+\r
+                    String *body = String::stringWithUTF8String(value);\r
+                    String *name = String::stringWithUTF8String(field);\r
+                    setFieldBodyWithName(body, name);\r
+                }\r
+\r
+                index = 0;\r
+            }\r
+        }\r
+    }\r
+    return this;\r
+/*\r
+        char *tmp = (char *)malloc([data length] + 1);\r
+        [data getBytes:tmp length:[data length]];\r
+        tmp[[data length]] = '\0';\r
+        String *headStr = [String stringWithCString:tmp encoding:_encoding];\r
+        if ([headStr rangeOfString:@"content-type" options:CaseInsensitiveSearch].location != NotFound)\r
+        {\r
+            if ([headStr rangeOfString:@"charset" options:CaseInsensitiveSearch].location != NotFound)\r
+            {\r
+                if ([headStr rangeOfString:@"iso-2022-jp" options:CaseInsensitiveSearch].location != NotFound)\r
+                {\r
+                    _encoding = ISO2022JPStringEncoding;\r
+                }\r
+                else if ([headStr rangeOfString:@"shift_jis" options:CaseInsensitiveSearch].location != NotFound)\r
+                {\r
+                    _encoding = ShiftJISStringEncoding;\r
+                }\r
+                else if ([headStr rangeOfString:@"euc-jp" options:CaseInsensitiveSearch].location != NotFound)\r
+                {\r
+                    _encoding = JapaneseEUCStringEncoding;\r
+                }\r
+                else if ([headStr rangeOfString:@"utf-8" options:CaseInsensitiveSearch].location != NotFound)\r
+                {\r
+                    _encoding = UTF8StringEncoding;\r
+                }\r
+            }\r
+        }\r
+*/\r
+}\r
+\r
+InternetTextMessageHeader *InternetTextMessageHeader::initWithArray(Array *array)\r
+{\r
+    /*\r
+    if ((self = [super init]) != nil)\r
+    {\r
+        _dictionary = [[MutableDictionary alloc] initWithCapacity:0];\r
+        _encoding = ASCIIStringEncoding;\r
+        for (int i = 0; i < [array count]; ++i)\r
+        {\r
+            String *line = [array objectAtIndex:i];\r
+            Range r = [line rangeOfString:@":"];\r
+            if (r.location != NotFound)\r
+            {\r
+                String *fieldName = [line substringToIndex:r.location];\r
+                String *fieldBody = [[line substringFromIndex:(r.location + 1)] stringByTrimmingCharactersInSet:[CharacterSet whitespaceAndNewlineCharacterSet]];\r
+                [_dictionary setObject:fieldBody forKey:fieldName];\r
+            }\r
+        }\r
+    }\r
+    return self;\r
+    */\r
+    return this;\r
+}\r
+\r
+InternetTextMessageHeader *InternetTextMessageHeader::initWithSocket(SOCKET sock)\r
+{\r
+    DebugLog2("%s()\n", __FUNCTION__);\r
+\r
+    char *tmp = "\r\n\r\n";\r
+\r
+    int length = 0;\r
+    char buf[16384];\r
+\r
+    while (length < sizeof(buf))\r
+    {\r
+        char c;\r
+        if (recv(sock, &c, 1, 0) == 1)\r
+        {\r
+            buf[length++] = c;\r
+            if (length >= 4)\r
+            {\r
+                if (strncmp(&buf[length - 4], tmp, 4) == 0)\r
+                {\r
+                    Data *data = Data::alloc()->initWithBytesAndLength(buf, length);\r
+                    initWithData(data);\r
+                    data->release();\r
+                    break;\r
+                }\r
+            }\r
+        }\r
+        else\r
+        {\r
+            // EOF or error\r
+            break;\r
+        }\r
+    }\r
+    return this;\r
+}\r
+\r
+/*\r
+- (StringEncoding)encoding\r
+{\r
+    return _encoding;\r
+}\r
+*/\r
+\r
+InternetTextMessageHeader *InternetTextMessageHeader::retain()\r
+{\r
+    Object::retain();\r
+    return this;\r
+}\r
+\r
+InternetTextMessageHeader *InternetTextMessageHeader::autorelease()\r
+{\r
+    Object::autorelease();\r
+    return this;\r
+}\r
+\r
+Array *InternetTextMessageHeader::fieldNames()\r
+{\r
+    return _fields->allKeys();\r
+}\r
+\r
+String *InternetTextMessageHeader::fieldBodyForName(String *name)\r
+{\r
+    return (String *)_fields->objectForKey(name);\r
+}\r
+\r
+String *InternetTextMessageHeader::fieldBodyForName(const char *name)\r
+{\r
+    return (String *)_fields->objectForKey(name);\r
+}\r
+\r
+void InternetTextMessageHeader::setFieldBodyWithName(String *body, String *name)\r
+{\r
+    _fields->setObject(body, name);\r
+    /*\r
+    if ([[name lowercaseString] isEqualToString:@"content-type"])\r
+    {\r
+        if ([body rangeOfString:@"charset" options:CaseInsensitiveSearch].location != NotFound)\r
+        {\r
+            if ([body rangeOfString:@"iso-2022-jp" options:CaseInsensitiveSearch].location != NotFound)\r
+            {\r
+                _encoding = ISO2022JPStringEncoding;\r
+            }\r
+            else if ([body rangeOfString:@"shift_jis" options:CaseInsensitiveSearch].location != NotFound)\r
+            {\r
+                _encoding = ShiftJISStringEncoding;\r
+            }\r
+            else if ([body rangeOfString:@"euc-jp" options:CaseInsensitiveSearch].location != NotFound)\r
+            {\r
+                _encoding = JapaneseEUCStringEncoding;\r
+            }\r
+            else if ([body rangeOfString:@"utf-8" options:CaseInsensitiveSearch].location != NotFound)\r
+            {\r
+                _encoding = UTF8StringEncoding;\r
+            }\r
+        }\r
+    }\r
+    */\r
+}\r
+\r
+void InternetTextMessageHeader::setFieldBodyWithName(const char *body, String *name)\r
+{\r
+    setFieldBodyWithName(String::stringWithUTF8String(body), name);\r
+}\r
+\r
+void InternetTextMessageHeader::setFieldBodyWithName(String *body, const char *name)\r
+{\r
+    setFieldBodyWithName(body, String::stringWithUTF8String(name));\r
+}\r
+\r
+void InternetTextMessageHeader::setFieldBodyWithName(const char *body, const char *name)\r
+{\r
+    setFieldBodyWithName(String::stringWithUTF8String(body), String::stringWithUTF8String(name));\r
+}\r
+\r
+Data *InternetTextMessageHeader::data()\r
+{\r
+    /*\r
+    MutableString *headerString = [MutableString stringWithCapacity:0];\r
+    Array *fieldNames = [_dictionary allKeys];\r
+\r
+    // Content-type\r
+    for (int i = 0; i < [fieldNames count]; ++i)\r
+    {\r
+        String *fieldName = [fieldNames objectAtIndex:i];\r
+        if ([[fieldName lowercaseString] isEqualToString:@"content-type"])\r
+        {\r
+            [headerString appendFormat:@"Content-type: %@\r\n", [_dictionary objectForKey:fieldName]];\r
+            break;\r
+        }\r
+    }\r
+\r
+    // other\r
+    for (int i = 0; i < [fieldNames count]; ++i)\r
+    {\r
+        String *fieldName = [fieldNames objectAtIndex:i];\r
+        String *lowercase = [fieldName lowercaseString];\r
+        if (![lowercase isEqualToString:@"content-type"])\r
+        {\r
+            [headerString appendFormat:@"%@: %@\r\n", fieldName, [_dictionary objectForKey:fieldName]];\r
+        }\r
+    }\r
+\r
+    [headerString appendString:@"\r\n\r\n"];\r
+\r
+    return [Data dataWithBytes:[headerString cStringUsingEncoding:_encoding] length:[headerString lengthOfBytesUsingEncoding:_encoding]];\r
+    */\r
+    return NULL;\r
+}\r
+\r
+const char *InternetTextMessageHeader::className()\r
+{\r
+    return "NET::InternetTextMessageHeader";\r
+}\r
+\r
+} // NET\r
diff --git a/src/net/InternetTextMessageHeader.h b/src/net/InternetTextMessageHeader.h
new file mode 100644 (file)
index 0000000..1e52017
--- /dev/null
@@ -0,0 +1,51 @@
+//\r
+// InternetTextMessageHeader.h\r
+//\r
+\r
+#pragma once\r
+\r
+#include <stdio.h>\r
+\r
+#include <Raym/Raym.h>\r
+\r
+namespace NET\r
+{\r
+\r
+class InternetTextMessageHeader : public Raym::Object\r
+{\r
+private:\r
+    Raym::StringEncoding    _encoding;\r
+    Raym::Dictionary *      _fields;\r
+    Raym::Data *            _data;\r
+\r
+protected:\r
+    InternetTextMessageHeader();\r
+    ~InternetTextMessageHeader();\r
+\r
+public:\r
+    static InternetTextMessageHeader *alloc();\r
+    InternetTextMessageHeader *init();\r
+    InternetTextMessageHeader *initWithData(Raym::Data *data);\r
+    InternetTextMessageHeader *initWithArray(Raym::Array *array);\r
+    InternetTextMessageHeader *initWithSocket(SOCKET sock);\r
+    InternetTextMessageHeader *retain();\r
+    InternetTextMessageHeader *autorelease();\r
+\r
+    Raym::StringEncoding encoding();\r
+\r
+    Raym::Array *fieldNames();\r
+    Raym::String *fieldBodyForName(Raym::String *name);\r
+    Raym::String *fieldBodyForName(const char *name);\r
+\r
+    void setFieldBodyWithName(Raym::String *body, Raym::String *name);\r
+    void setFieldBodyWithName(const char *body, Raym::String *name);\r
+    void setFieldBodyWithName(Raym::String *body, const char *name);\r
+    void setFieldBodyWithName(const char *body, const char *name);\r
+\r
+    Raym::Data *data();\r
+\r
+    virtual const char *className();\r
+};\r
+\r
+} // NET\r
+\r
diff --git a/src/ry0/iPTd/HTTPD.cpp b/src/ry0/iPTd/HTTPD.cpp
new file mode 100644 (file)
index 0000000..70ba295
--- /dev/null
@@ -0,0 +1,97 @@
+/**\r
+ * @file HTTPDaemon.cpp\r
+ *\r
+ */\r
+\r
+#include <time.h>\r
+\r
+#define DBG_LEVEL 3\r
+#include "Raym/Log.h"\r
+\r
+#include "keys.h"\r
+#include "ry0/iPTd/HTTPD.h"\r
+#include "ry0/iPTd/Controller.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace ry0\r
+{\r
+namespace iPTd\r
+{\r
+\r
+HTTPDaemon::HTTPDaemon()\r
+{\r
+    _controller = NULL;\r
+    _httpd      = NULL;\r
+    _port       = -1;\r
+    _path       = NULL;\r
+}\r
+\r
+HTTPDaemon::~HTTPDaemon()\r
+{\r
+    RELEASE(_httpd);\r
+    RELEASE(_path);\r
+\r
+    _controller = NULL;\r
+}\r
+\r
+HTTPDaemon *HTTPDaemon::alloc()\r
+{\r
+    return new HTTPDaemon();\r
+}\r
+\r
+HTTPDaemon *HTTPDaemon::initWithController(Controller *controller, int port, String *path)\r
+{\r
+    _controller = controller;\r
+    _port = port;\r
+    _path = path->retain();\r
+\r
+    return this;\r
+}\r
+\r
+bool HTTPDaemon::start()\r
+{\r
+    if (_httpd == NULL)\r
+    {\r
+        _httpd = NET::HTTPDaemon::alloc()->initWithPort(_port, 10);\r
+        _httpd->setRootPath(_path);\r
+        _httpd->setDelegate(this);\r
+    }\r
+    return _httpd->start();\r
+}\r
+\r
+void HTTPDaemon::stop()\r
+{\r
+    _httpd->stop();\r
+}\r
+\r
+NET::HTTPResponse *HTTPDaemon::request(NET::HTTPRequest *request, struct sockaddr_in *client)\r
+{\r
+    DebugLog2("%s\n", __FUNCTION__);\r
+\r
+    // 初期化チェック\r
+    bool flag = false;\r
+    EnterCriticalSection(&_cs);\r
+//    flag = _initialized;\r
+    LeaveCriticalSection(&_cs);\r
+    if (!flag)\r
+    {\r
+//        return NULL;\r
+    }\r
+\r
+    NET::HTTPResponse *response = NULL;\r
+\r
+    if (request->method()->isEqualToString("GET") ||\r
+        request->method()->isEqualToString("HEAD"))\r
+    {\r
+        // URI\r
+        String *uri = request->URI();\r
+        DebugLog0("request: %s\n", uri->cString());\r
+    }\r
+\r
+\r
+    return response;\r
+}\r
+\r
+} // iPTd\r
+} // ry0\r
diff --git a/src/ry0/iPTd/HTTPD.h b/src/ry0/iPTd/HTTPD.h
new file mode 100644 (file)
index 0000000..f0c6060
--- /dev/null
@@ -0,0 +1,43 @@
+/**\r
+ * @file HTTPDaemon.h\r
+ *\r
+ */\r
+\r
+#pragma once\r
+\r
+#include "Raym/Raym.h"\r
+#include "net/HTTPDaemon.h"\r
+\r
+namespace ry0\r
+{\r
+namespace iPTd\r
+{\r
+\r
+class Controller;\r
+\r
+class HTTPDaemon : public Raym::Object,\r
+                   public NET::HTTPDaemonDelegate\r
+{\r
+private:\r
+    Controller *        _controller;\r
+    NET::HTTPDaemon *   _httpd;\r
+    int                 _port;\r
+    Raym::String *      _path;\r
+\r
+protected:\r
+    HTTPDaemon();\r
+    ~HTTPDaemon();\r
+\r
+public:\r
+    static HTTPDaemon *alloc();\r
+    HTTPDaemon *initWithController(Controller *controller, int port, Raym::String *path);\r
+\r
+    bool start();\r
+    void stop();\r
+\r
+    NET::HTTPResponse *request(NET::HTTPRequest *request, struct sockaddr_in *client);\r
+};\r
+\r
+\r
+} // iPTd\r
+} // ry0\r