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