diff --git a/Makefile b/Makefile index 5635438e..75e4c183 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ OS_NAME=$(shell uname -s) ifeq (${OS_NAME},Darwin) RTLIB= CXXFLAGS = -framework CoreFoundation -framework CoreServices -std=gnu++0x -stdlib=libc++ -g -O0 -I$(LIBUV_PATH)/include -I$(HTTP_PARSER_PATH) -I. -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 - + else RTLIB=-lrt CXXFLAGS = -std=gnu++0x -g -O0 -I$(LIBUV_PATH)/include -I$(HTTP_PARSER_PATH) -I. -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 @@ -16,10 +16,10 @@ all: webclient webserver file_test webclient: webclient.cpp $(LIBUV_PATH)/$(LIBUV_NAME) $(HTTP_PARSER_PATH)/http_parser.o $(wildcard native/*.h) $(CXX) $(CXXFLAGS) -o webclient webclient.cpp $(LIBUV_PATH)/$(LIBUV_NAME) $(HTTP_PARSER_PATH)/http_parser.o $(RTLIB) -lm -lpthread - + webserver: webserver.cpp $(LIBUV_PATH)/$(LIBUV_NAME) $(HTTP_PARSER_PATH)/http_parser.o $(wildcard native/*.h) $(CXX) $(CXXFLAGS) -o webserver webserver.cpp $(LIBUV_PATH)/$(LIBUV_NAME) $(HTTP_PARSER_PATH)/http_parser.o $(RTLIB) -lm -lpthread - + file_test: file_test.cpp $(LIBUV_PATH)/$(LIBUV_NAME) $(HTTP_PARSER_PATH)/http_parser.o $(wildcard native/*.h) $(CXX) $(CXXFLAGS) -o file_test file_test.cpp $(LIBUV_PATH)/$(LIBUV_NAME) $(HTTP_PARSER_PATH)/http_parser.o $(RTLIB) -lm -lpthread @@ -28,7 +28,7 @@ $(LIBUV_PATH)/$(LIBUV_NAME): $(HTTP_PARSER_PATH)/http_parser.o: $(MAKE) -C $(HTTP_PARSER_PATH) http_parser.o - + clean: $(MAKE) -C libuv clean $(MAKE) -C http-parser clean diff --git a/native/error.h b/native/error.h index 18dbd2c3..996b080b 100644 --- a/native/error.h +++ b/native/error.h @@ -40,7 +40,7 @@ namespace native uv_err_t uv_err_; }; - error get_last_error() { return uv_last_error(uv_default_loop()); } + inline error get_last_error() { return uv_last_error(uv_default_loop()); } } diff --git a/native/fs.h b/native/fs.h index 82e99ec8..80248fe3 100644 --- a/native/fs.h +++ b/native/fs.h @@ -73,7 +73,7 @@ namespace native delete req; } - void delete_req(uv_fs_t* req) + inline void delete_req(uv_fs_t* req) { delete reinterpret_cast(req->data); uv_fs_req_cleanup(req); @@ -122,7 +122,7 @@ namespace native } } - bool open(const std::string& path, int flags, int mode, std::function callback) + inline bool open(const std::string& path, int flags, int mode, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_open(uv_default_loop(), req, path.c_str(), flags, mode, [](uv_fs_t* req) { @@ -140,7 +140,7 @@ namespace native return true; } - bool read(file_handle fd, size_t len, off_t offset, std::function callback) + inline bool read(file_handle fd, size_t len, off_t offset, std::function callback) { auto buf = new char[len]; auto req = internal::create_req(callback, buf); @@ -172,7 +172,7 @@ namespace native return true; } - bool write(file_handle fd, const char* buf, size_t len, off_t offset, std::function callback) + inline bool write(file_handle fd, const char* buf, size_t len, off_t offset, std::function callback) { auto req = internal::create_req(callback); @@ -198,7 +198,7 @@ namespace native return true; } - bool read_to_end(file_handle fd, std::function callback) + inline bool read_to_end(file_handle fd, std::function callback) { auto ctx = new internal::rte_context; ctx->file = fd; @@ -212,7 +212,7 @@ namespace native return true; } - bool close(file_handle fd, std::function callback) + inline bool close(file_handle fd, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_close(uv_default_loop(), req, fd, [](uv_fs_t* req){ @@ -226,7 +226,7 @@ namespace native return true; } - bool unlink(const std::string& path, std::function callback) + inline bool unlink(const std::string& path, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_unlink(uv_default_loop(), req, path.c_str(), [](uv_fs_t* req){ @@ -240,7 +240,7 @@ namespace native return true; } - bool mkdir(const std::string& path, int mode, std::function callback) + inline bool mkdir(const std::string& path, int mode, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_mkdir(uv_default_loop(), req, path.c_str(), mode, [](uv_fs_t* req){ @@ -254,7 +254,7 @@ namespace native return true; } - bool rmdir(const std::string& path, std::function callback) + inline bool rmdir(const std::string& path, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_rmdir(uv_default_loop(), req, path.c_str(), [](uv_fs_t* req){ @@ -268,7 +268,7 @@ namespace native return true; } - bool rename(const std::string& path, const std::string& new_path, std::function callback) + inline bool rename(const std::string& path, const std::string& new_path, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_rename(uv_default_loop(), req, path.c_str(), new_path.c_str(), [](uv_fs_t* req){ @@ -282,7 +282,7 @@ namespace native return true; } - bool chmod(const std::string& path, int mode, std::function callback) + inline bool chmod(const std::string& path, int mode, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_chmod(uv_default_loop(), req, path.c_str(), mode, [](uv_fs_t* req){ @@ -296,7 +296,7 @@ namespace native return true; } - bool chown(const std::string& path, int uid, int gid, std::function callback) + inline bool chown(const std::string& path, int uid, int gid, std::function callback) { auto req = internal::create_req(callback); if(uv_fs_chown(uv_default_loop(), req, path.c_str(), uid, gid, [](uv_fs_t* req){ diff --git a/native/handle.h b/native/handle.h index bb383690..1becc8ac 100644 --- a/native/handle.h +++ b/native/handle.h @@ -6,85 +6,85 @@ namespace native { - namespace base - { - class handle; - - void _delete_handle(uv_handle_t* h); - - class handle - { - public: - template - handle(T* x) - : uv_handle_(reinterpret_cast(x)) - { - //printf("handle(): %x\n", this); - assert(uv_handle_); - - uv_handle_->data = new callbacks(native::internal::uv_cid_max); - assert(uv_handle_->data); - } - - virtual ~handle() - { - //printf("~handle(): %x\n", this); - uv_handle_ = nullptr; - } - - handle(const handle& h) - : uv_handle_(h.uv_handle_) - { - //printf("handle(const handle&): %x\n", this); - } - - public: - template - T* get() { return reinterpret_cast(uv_handle_); } - - template - const T* get() const { return reinterpret_cast(uv_handle_); } - - bool is_active() { return uv_is_active(get()) != 0; } - - void close(std::function callback) - { - callbacks::store(get()->data, native::internal::uv_cid_close, callback); - uv_close(get(), - [](uv_handle_t* h) { - callbacks::invoke(h->data, native::internal::uv_cid_close); - _delete_handle(h); - }); - } - - handle& operator =(const handle& h) - { - uv_handle_ = h.uv_handle_; - return *this; - } - - protected: - uv_handle_t* uv_handle_; - }; - - void _delete_handle(uv_handle_t* h) - { - assert(h); - - // clean up SCM - if(h->data) - { - delete reinterpret_cast(h->data); - h->data = nullptr; - } - - switch(h->type) - { - case UV_TCP: delete reinterpret_cast(h); break; - default: assert(0); break; - } - } - } + namespace base + { + class handle; + + inline void _delete_handle(uv_handle_t* h); + + class handle + { + public: + template + handle(T* x) + : uv_handle_(reinterpret_cast(x)) + { + //printf("handle(): %x\n", this); + assert(uv_handle_); + + uv_handle_->data = new callbacks(native::internal::uv_cid_max); + assert(uv_handle_->data); + } + + virtual ~handle() + { + //printf("~handle(): %x\n", this); + uv_handle_ = nullptr; + } + + handle(const handle& h) + : uv_handle_(h.uv_handle_) + { + //printf("handle(const handle&): %x\n", this); + } + + public: + template + T* get() { return reinterpret_cast(uv_handle_); } + + template + const T* get() const { return reinterpret_cast(uv_handle_); } + + bool is_active() { return uv_is_active(get()) != 0; } + + void close(std::function callback) + { + callbacks::store(get()->data, native::internal::uv_cid_close, callback); + uv_close(get(), + [](uv_handle_t* h) { + callbacks::invoke(h->data, native::internal::uv_cid_close); + _delete_handle(h); + }); + } + + handle& operator =(const handle& h) + { + uv_handle_ = h.uv_handle_; + return *this; + } + + protected: + uv_handle_t* uv_handle_; + }; + + inline void _delete_handle(uv_handle_t* h) + { + assert(h); + + // clean up SCM + if(h->data) + { + delete reinterpret_cast(h->data); + h->data = nullptr; + } + + switch(h->type) + { + case UV_TCP: delete reinterpret_cast(h); break; + default: assert(0); break; + } + } + } } #endif diff --git a/native/http.h b/native/http.h index 50f876e5..1c0dcaf1 100644 --- a/native/http.h +++ b/native/http.h @@ -11,503 +11,511 @@ namespace native { - namespace http - { - class url_parse_exception : public native::exception - { - public: - url_parse_exception(const std::string& message="Failed to parse URL.") - : native::exception(message) - {} - }; - - class response_exception : public native::exception - { - public: - response_exception(const std::string& message="HTTP respsonse error.") - : native::exception(message) - {} - }; - - class url_obj - { - friend class client_context; - - public: - url_obj() - : handle_(), buf_() - { - //printf("url_obj() %x\n", this); - } - - url_obj(const url_obj& c) - : handle_(c.handle_), buf_(c.buf_) - { - //printf("url_obj(const url_obj&) %x\n", this); - } - - url_obj& operator =(const url_obj& c) - { - //printf("url_obj::operator =(const url_obj&) %x\n", this); - handle_ = c.handle_; - buf_ = c.buf_; - return *this; - } - - ~url_obj() - { - //printf("~url_obj() %x\n", this); - } - - public: - std::string schema() const - { - if(has_schema()) return buf_.substr(handle_.field_data[UF_SCHEMA].off, handle_.field_data[UF_SCHEMA].len); - return "HTTP"; - } - - std::string host() const - { - // TODO: if not specified, use host name - if(has_schema()) return buf_.substr(handle_.field_data[UF_HOST].off, handle_.field_data[UF_HOST].len); - return std::string("localhost"); - } - - int port() const - { - if(has_path()) return static_cast(handle_.port); - return (schema() == "HTTP" ? 80 : 443); - } - - std::string path() const - { - if(has_path()) return buf_.substr(handle_.field_data[UF_PATH].off, handle_.field_data[UF_PATH].len); - return std::string("/"); - } - - std::string query() const - { - if(has_query()) return buf_.substr(handle_.field_data[UF_QUERY].off, handle_.field_data[UF_QUERY].len); - return std::string(); - } - - std::string fragment() const - { - if(has_query()) return buf_.substr(handle_.field_data[UF_FRAGMENT].off, handle_.field_data[UF_FRAGMENT].len); - return std::string(); - } - - private: - void from_buf(const char* buf, std::size_t len, bool is_connect=false) - { - // TODO: validate input parameters - - buf_ = std::string(buf, len); - if(http_parser_parse_url(buf, len, is_connect, &handle_) != 0) - { - // failed for some reason - // TODO: let the caller know the error code (or error message) - throw url_parse_exception(); - } - } - - bool has_schema() const { return handle_.field_set & (1< http_client_ptr; - - class response - { - friend class client_context; - - private: - response(client_context* client, native::net::tcp* socket) - : client_(client) - , socket_(socket) - , headers_() - , status_(200) - { - headers_["Content-Type"] = "text/html"; - } - - ~response() - {} - - public: - bool end(const std::string& body) - { - // Content-Length - if(headers_.find("Content-Length") == headers_.end()) - { - std::stringstream ss; - ss << body.length(); - headers_["Content-Length"] = ss.str(); - } - - std::stringstream response_text; - response_text << "HTTP/1.1 "; - response_text << status_ << " " << get_status_text(status_) << "\r\n"; - for(auto h : headers_) - { - response_text << h.first << ": " << h.second << "\r\n"; - } - response_text << "\r\n"; - response_text << body; - - auto str = response_text.str(); - return socket_->write(str.c_str(), static_cast(str.length()), [=](error e) { - if(e) - { - // TODO: handle error - } - // clean up - client_.reset(); - }); - } - - void set_status(int status_code) - { - status_ = status_code; - } - - void set_header(const std::string& key, const std::string& value) - { - headers_[key] = value; - } - - static std::string get_status_text(int status) - { - switch(status) - { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - //case 306: return "(reserved)"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - default: throw response_exception("Not supported status code."); - } - } - - private: - http_client_ptr client_; - native::net::tcp* socket_; - std::map headers_; - int status_; - }; - - class request - { - friend class client_context; - - private: - request() - : url_() - , headers_() - { - } - - ~request() - { - //printf("~request() %x\n", this); - } - - public: - const url_obj& url() const { return url_; } - - const std::string& get_header(const std::string& key) const - { - auto it = headers_.find(key); - if(it != headers_.end()) return it->second; - return default_value_; - } - - bool get_header(const std::string& key, std::string& value) const - { - auto it = headers_.find(key); - if(it != headers_.end()) - { - value = it->second; - return true; - } - return false; - } - - private: - url_obj url_; - std::map headers_; + namespace http + { + class url_parse_exception : public native::exception + { + public: + url_parse_exception(const std::string& message="Failed to parse URL.") + : native::exception(message) + {} + }; + + class response_exception : public native::exception + { + public: + response_exception(const std::string& message="HTTP respsonse error.") + : native::exception(message) + {} + }; + + class url_obj + { + friend class client_context; + + public: + url_obj() + : handle_(), buf_() + { + //printf("url_obj() %x\n", this); + } + + url_obj(const url_obj& c) + : handle_(c.handle_), buf_(c.buf_) + { + //printf("url_obj(const url_obj&) %x\n", this); + } + + url_obj& operator =(const url_obj& c) + { + //printf("url_obj::operator =(const url_obj&) %x\n", this); + handle_ = c.handle_; + buf_ = c.buf_; + return *this; + } + + ~url_obj() + { + //printf("~url_obj() %x\n", this); + } + + public: + std::string schema() const + { + if(has_schema()) return buf_.substr(handle_.field_data[UF_SCHEMA].off, handle_.field_data[UF_SCHEMA].len); + return "HTTP"; + } + + std::string host() const + { + // TODO: if not specified, use host name + if(has_schema()) return buf_.substr(handle_.field_data[UF_HOST].off, handle_.field_data[UF_HOST].len); + return std::string("localhost"); + } + + int port() const + { + if(has_path()) return static_cast(handle_.port); + return (schema() == "HTTP" ? 80 : 443); + } + + std::string path() const + { + if(has_path()) return buf_.substr(handle_.field_data[UF_PATH].off, handle_.field_data[UF_PATH].len); + return std::string("/"); + } + + std::string query() const + { + if(has_query()) return buf_.substr(handle_.field_data[UF_QUERY].off, handle_.field_data[UF_QUERY].len); + return std::string(); + } + + std::string fragment() const + { + if(has_query()) return buf_.substr(handle_.field_data[UF_FRAGMENT].off, handle_.field_data[UF_FRAGMENT].len); + return std::string(); + } + + private: + void from_buf(const char* buf, std::size_t len, bool is_connect=false) + { + // TODO: validate input parameters + + buf_ = std::string(buf, len); + if(http_parser_parse_url(buf, len, is_connect, &handle_) != 0) + { + // failed for some reason + // TODO: let the caller know the error code (or error message) + throw url_parse_exception(); + } + } + + bool has_schema() const { return handle_.field_set & (1< http_client_ptr; + + class response + { + friend class client_context; + + private: + response(client_context* client, native::net::tcp* socket) + : client_(client) + , socket_(socket) + , headers_() + , status_(200) + { + headers_["Content-Type"] = "text/html"; + } + + ~response() + {} + + public: + bool end(const std::string& body) + { + // Content-Length + if(headers_.find("Content-Length") == headers_.end()) + { + std::stringstream ss; + ss << body.length(); + headers_["Content-Length"] = ss.str(); + } + + std::stringstream response_text; + response_text << "HTTP/1.1 "; + response_text << status_ << " " << get_status_text(status_) << "\r\n"; + for(auto h : headers_) + { + response_text << h.first << ": " << h.second << "\r\n"; + } + response_text << "\r\n"; + response_text << body; + + auto str = response_text.str(); + return socket_->write(str.c_str(), static_cast(str.length()), [=](error e) { + if(e) + { + // TODO: handle error + } + // clean up + client_.reset(); + }); + } + + void set_status(int status_code) + { + status_ = status_code; + } + + void set_header(const std::string& key, const std::string& value) + { + headers_[key] = value; + } + + static std::string get_status_text(int status) + { + switch(status) + { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + //case 306: return "(reserved)"; + case 307: return "Temporary Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Requested Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + default: throw response_exception("Not supported status code."); + } + } + + private: + http_client_ptr client_; + native::net::tcp* socket_; + std::map headers_; + int status_; + }; + + class request + { + friend class client_context; + + private: + request() + : url_() + , headers_() + , body_("") + { + } + + ~request() + { + //printf("~request() %x\n", this); + } + + public: + const url_obj& url() const { return url_; } + + const std::string& get_header(const std::string& key) const + { + auto it = headers_.find(key); + if(it != headers_.end()) return it->second; + return default_value_; + } + + bool get_header(const std::string& key, std::string& value) const + { + auto it = headers_.find(key); + if(it != headers_.end()) + { + value = it->second; + return true; + } + return false; + } + + std::string get_body (void) + { + return body_; + } + + private: + url_obj url_; + std::map headers_; + std::string body_; std::string default_value_; - }; - - class client_context - { - friend class http; - - private: - client_context(native::net::tcp* server) - : socket_(nullptr) - , parser_() - , was_header_value_(true) - , last_header_field_() - , last_header_value_() - , parser_settings_() - , request_(nullptr) - , response_(nullptr) - , callback_lut_(new callbacks(1)) - { - //printf("request() %x callback_=%x\n", this, callback_); - assert(server); - - // TODO: check error - socket_ = new native::net::tcp; - server->accept(socket_); - } - - public: - ~client_context() - { - if(request_) - { - delete request_; - request_ = nullptr; - } - - if(response_) - { - delete response_; - response_ = nullptr; - } - - if(callback_lut_) - { - delete callback_lut_; - callback_lut_ = nullptr; - } - - if(socket_) - { - // TODO: maybe close() does not affect socket_ pointer itself. So, delete socket_ does not have to be inside the callback. - socket_->close([=](){ - delete socket_; - socket_ = nullptr; - }); - } - } - - private: - bool parse(std::function callback) - { - request_ = new request; - response_ = new response(this, socket_); - - http_parser_init(&parser_, HTTP_REQUEST); - parser_.data = this; - - // store callback object - callbacks::store(callback_lut_, 0, callback); - - parser_settings_.on_url = [](http_parser* parser, const char *at, size_t len) { - auto client = reinterpret_cast(parser->data); - - // TODO: from_buf() can throw an exception: check - client->request_->url_.from_buf(at, len); - - return 0; - }; - parser_settings_.on_header_field = [](http_parser* parser, const char* at, size_t len) { - auto client = reinterpret_cast(parser->data); - - if(client->was_header_value_) - { - // new field started - if(!client->last_header_field_.empty()) - { - // add new entry - client->request_->headers_[client->last_header_field_] = client->last_header_value_; - client->last_header_value_.clear(); - } - - client->last_header_field_ = std::string(at, len); - client->was_header_value_ = false; - } - else - { - // appending - client->last_header_field_ += std::string(at, len); - } - return 0; - }; - parser_settings_.on_header_value = [](http_parser* parser, const char* at, size_t len) { - auto client = reinterpret_cast(parser->data); - - if(!client->was_header_value_) - { - client->last_header_value_ = std::string(at, len); - client->was_header_value_ = true; - } - else - { - // appending - client->last_header_value_ += std::string(at, len); - } - return 0; - }; - parser_settings_.on_headers_complete = [](http_parser* parser) { - auto client = reinterpret_cast(parser->data); - - // add last entry if any - if(!client->last_header_field_.empty()) - { - // add new entry - client->request_->headers_[client->last_header_field_] = client->last_header_value_; - } - - //return 0; - return 1; // do not parse body - }; - parser_settings_.on_message_complete = [](http_parser* parser) { - auto client = reinterpret_cast(parser->data); - // invoke stored callback object - callbacks::invoke(client->callback_lut_, 0, *client->request_, *client->response_); - return 0; - }; - - socket_->read_start([=](const char* buf, int len){ - if (buf == 0x00 && len == -1) { - response_->set_status(500); - } else { - http_parser_execute(&parser_, &parser_settings_, buf, len); - } - }); - - return true; - } - - private: - http_parser parser_; - http_parser_settings parser_settings_; - bool was_header_value_; - std::string last_header_field_; - std::string last_header_value_; - - native::net::tcp* socket_; - request* request_; - response* response_; - - callbacks* callback_lut_; - }; - - class http - { - public: - http() - : socket_(new native::net::tcp) - { - } - - virtual ~http() - { - if(socket_) - { - socket_->close([](){}); - } - } - - public: - static std::shared_ptr create_server(const std::string& ip, int port, std::function callback) - { - auto server = std::shared_ptr(new http); - if(server->listen(ip, port, callback)) return server; - return nullptr; - } - - bool listen(const std::string& ip, int port, std::function callback) - { - if(!socket_->bind(ip, port)) return false; - - if(!socket_->listen([=](error e) { - if(e) - { - // TODO: handle client connection error - } - else - { + }; + + class client_context + { + friend class http; + + private: + client_context(native::net::tcp* server) + : socket_(nullptr) + , parser_() + , was_header_value_(true) + , last_header_field_() + , last_header_value_() + , parser_settings_() + , request_(nullptr) + , response_(nullptr) + , callback_lut_(new callbacks(1)) + { + //printf("request() %x callback_=%x\n", this, callback_); + assert(server); + + // TODO: check error + socket_ = std::shared_ptr (new native::net::tcp); + server->accept(socket_.get()); + } + + public: + ~client_context() + { + if(request_) + { + delete request_; + request_ = nullptr; + } + + if(response_) + { + delete response_; + response_ = nullptr; + } + + if(callback_lut_) + { + delete callback_lut_; + callback_lut_ = nullptr; + } + + if(socket_.use_count()) + { + socket_->close([=](){}); + } + } + + private: + bool parse(std::function callback) + { + request_ = new request; + response_ = new response(this, socket_.get()); + + http_parser_init(&parser_, HTTP_REQUEST); + parser_.data = this; + + // store callback object + callbacks::store(callback_lut_, 0, callback); + + parser_settings_.on_url = [](http_parser* parser, const char *at, size_t len) { + auto client = reinterpret_cast(parser->data); + + // TODO: from_buf() can throw an exception: check + client->request_->url_.from_buf(at, len); + + return 0; + }; + parser_settings_.on_header_field = [](http_parser* parser, const char* at, size_t len) { + auto client = reinterpret_cast(parser->data); + + if(client->was_header_value_) + { + // new field started + if(!client->last_header_field_.empty()) + { + // add new entry + client->request_->headers_[client->last_header_field_] = client->last_header_value_; + client->last_header_value_.clear(); + } + + client->last_header_field_ = std::string(at, len); + client->was_header_value_ = false; + } + else + { + // appending + client->last_header_field_ += std::string(at, len); + } + return 0; + }; + parser_settings_.on_header_value = [](http_parser* parser, const char* at, size_t len) { + auto client = reinterpret_cast(parser->data); + + if(!client->was_header_value_) + { + client->last_header_value_ = std::string(at, len); + client->was_header_value_ = true; + } + else + { + // appending + client->last_header_value_ += std::string(at, len); + } + return 0; + }; + parser_settings_.on_headers_complete = [](http_parser* parser) { + auto client = reinterpret_cast(parser->data); + + // add last entry if any + if(!client->last_header_field_.empty()) { + // add new entry + client->request_->headers_[client->last_header_field_] = client->last_header_value_; + } + + return 0; // 1 to prevent reading of message body. + }; + parser_settings_.on_body = [](http_parser* parser, const char* at, size_t len) { + //printf("on_body, len of 'char* at' is %d\n", len); + auto client = reinterpret_cast(parser->data); + client->request_->body_ = std::string(at, len); + return 0; + }; + parser_settings_.on_message_complete = [](http_parser* parser) { + //printf("on_message_complete, so invoke the callback.\n"); + auto client = reinterpret_cast(parser->data); + // invoke stored callback object + callbacks::invoke(client->callback_lut_, 0, *client->request_, *client->response_); + return 1; // 0 or 1? + }; + + socket_->read_start([=](const char* buf, int len){ + if (buf == 0x00 && len == -1) { + response_->set_status(500); + } else { + http_parser_execute(&parser_, &parser_settings_, buf, len); + } + }); + + return true; + } + + private: + http_parser parser_; + http_parser_settings parser_settings_; + bool was_header_value_; + std::string last_header_field_; + std::string last_header_value_; + + std::shared_ptr socket_; + request* request_; + response* response_; + + callbacks* callback_lut_; + }; + + class http + { + public: + http() + : socket_(new native::net::tcp) + { + } + + virtual ~http() + { + if(socket_) + { + socket_->close([](){}); + } + } + + public: + static std::shared_ptr create_server(const std::string& ip, int port, std::function callback) + { + auto server = std::shared_ptr(new http); + if(server->listen(ip, port, callback)) return server; + return nullptr; + } + + bool listen(const std::string& ip, int port, std::function callback) + { + if(!socket_->bind(ip, port)) return false; + + if(!socket_->listen([=](error e) { + if(e) + { + // TODO: handle client connection error + } + else + { auto client = new client_context(socket_.get()); client->parse(callback); - } - })) return false; - - return true; - } - - private: - std::shared_ptr socket_; - }; - - typedef http_method method; - typedef http_parser_url_fields url_fields; - typedef http_errno error; - - const char* get_error_name(error err) - { - return http_errno_name(err); - } - - const char* get_error_description(error err) - { - return http_errno_description(err); - } - - const char* get_method_name(method m) - { - return http_method_str(m); - } - } + } + })) return false; + + return true; + } + + private: + std::shared_ptr socket_; + }; + + typedef http_method method; + typedef http_parser_url_fields url_fields; + typedef http_errno error; + + inline const char* get_error_name(error err) + { + return http_errno_name(err); + } + + inline const char* get_error_description(error err) + { + return http_errno_description(err); + } + + inline const char* get_method_name(method m) + { + return http_method_str(m); + } + } } #endif diff --git a/native/loop.h b/native/loop.h index 5b0ffc33..5e4897c0 100644 --- a/native/loop.h +++ b/native/loop.h @@ -86,7 +86,7 @@ namespace native /*! * Starts the default loop. */ - int run() + inline int run() { /*New libuv requires a runmode enum argument*/ return uv_run(uv_default_loop(),UV_RUN_DEFAULT); @@ -95,7 +95,7 @@ namespace native /*! * Polls for new events without blocking for the default loop. */ - int run_once() + inline int run_once() { /*New libuv requires a runmode argument*/ return uv_run(uv_default_loop(),UV_RUN_ONCE); diff --git a/native/net.h b/native/net.h index ac886812..55b7b9ca 100644 --- a/native/net.h +++ b/native/net.h @@ -6,38 +6,38 @@ namespace native { - namespace net - { - typedef sockaddr_in ip4_addr; - typedef sockaddr_in6 ip6_addr; + namespace net + { + typedef sockaddr_in ip4_addr; + typedef sockaddr_in6 ip6_addr; - ip4_addr to_ip4_addr(const std::string& ip, int port) { return uv_ip4_addr(ip.c_str(), port); } - ip6_addr to_ip6_addr(const std::string& ip, int port) { return uv_ip6_addr(ip.c_str(), port); } + inline ip4_addr to_ip4_addr(const std::string& ip, int port) { return uv_ip4_addr(ip.c_str(), port); } + inline ip6_addr to_ip6_addr(const std::string& ip, int port) { return uv_ip6_addr(ip.c_str(), port); } - bool from_ip4_addr(ip4_addr* src, std::string& ip, int& port) - { - char dest[16]; - if(uv_ip4_name(src, dest, 16) == 0) - { - ip = dest; - port = static_cast(ntohs(src->sin_port)); - return true; - } - return false; - } + inline bool from_ip4_addr(ip4_addr* src, std::string& ip, int& port) + { + char dest[16]; + if(uv_ip4_name(src, dest, 16) == 0) + { + ip = dest; + port = static_cast(ntohs(src->sin_port)); + return true; + } + return false; + } - bool from_ip6_addr(ip6_addr* src, std::string& ip, int& port) - { - char dest[46]; - if(uv_ip6_name(src, dest, 46) == 0) - { - ip = dest; - port = static_cast(ntohs(src->sin6_port)); - return true; - } - return false; - } - } + inline bool from_ip6_addr(ip6_addr* src, std::string& ip, int& port) + { + char dest[46]; + if(uv_ip6_name(src, dest, 46) == 0) + { + ip = dest; + port = static_cast(ntohs(src->sin6_port)); + return true; + } + return false; + } + } } #endif diff --git a/native/stream.h b/native/stream.h index 0bf8fc9a..ff77ade6 100644 --- a/native/stream.h +++ b/native/stream.h @@ -10,76 +10,76 @@ namespace native { - namespace base - { - class stream : public handle - { - public: - template - stream(T* x) - : handle(x) - { } - - bool listen(std::function callback, int backlog=128) - { - callbacks::store(get()->data, native::internal::uv_cid_listen, callback); - return uv_listen(get(), backlog, [](uv_stream_t* s, int status) { + namespace base + { + class stream : public handle + { + public: + template + stream(T* x) + : handle(x) + { } + + bool listen(std::function callback, int backlog=128) + { + callbacks::store(get()->data, native::internal::uv_cid_listen, callback); + return uv_listen(get(), backlog, [](uv_stream_t* s, int status) { callbacks::invoke(s->data, native::internal::uv_cid_listen, status?uv_last_error(s->loop):error()); }) == 0; - } + } - bool accept(stream* client) - { - return uv_accept(get(), client->get()) == 0; - } + bool accept(stream* client) + { + return uv_accept(get(), client->get()) == 0; + } - bool read_start(std::function callback) - { - return read_start<0>(callback); - } + bool read_start(std::function callback) + { + return read_start<0>(callback); + } - template - bool read_start(std::function callback) - { - callbacks::store(get()->data, native::internal::uv_cid_read_start, callback); + template + bool read_start(std::function callback) + { + callbacks::store(get()->data, native::internal::uv_cid_read_start, callback); - return uv_read_start(get(), - [](uv_handle_t*, size_t suggested_size){ + return uv_read_start(get(), + [](uv_handle_t*, size_t suggested_size){ auto size = std::max(suggested_size, max_alloc_size); return uv_buf_t { new char[size], size }; - }, - [](uv_stream_t* s, ssize_t nread, uv_buf_t buf){ - if(nread < 0) - { - assert(uv_last_error(s->loop).code == UV_EOF); - callbacks::invoke(s->data, native::internal::uv_cid_read_start, nullptr, nread); - } - else if(nread >= 0) - { - callbacks::invoke(s->data, native::internal::uv_cid_read_start, buf.base, nread); - } - delete buf.base; - }) == 0; - } - - bool read_stop() - { - return uv_read_stop(get()) == 0; - } - - // TODO: implement read2_start() - - bool write(const char* buf, int len, std::function callback) - { - uv_buf_t bufs[] = { uv_buf_t { const_cast(buf), static_cast(len) } }; - callbacks::store(get()->data, native::internal::uv_cid_write, callback); - return uv_write(new uv_write_t, get(), bufs, 1, [](uv_write_t* req, int status) { - callbacks::invoke(req->handle->data, native::internal::uv_cid_write, status?uv_last_error(req->handle->loop):error()); - delete req; - }) == 0; - } - - bool write(const std::string& buf, std::function callback) + }, + [](uv_stream_t* s, ssize_t nread, uv_buf_t buf){ + if(nread < 0) + { + assert(uv_last_error(s->loop).code == UV_EOF); + callbacks::invoke(s->data, native::internal::uv_cid_read_start, nullptr, nread); + } + else if(nread >= 0) + { + callbacks::invoke(s->data, native::internal::uv_cid_read_start, buf.base, nread); + } + delete buf.base; + }) == 0; + } + + bool read_stop() + { + return uv_read_stop(get()) == 0; + } + + // TODO: implement read2_start() + + bool write(const char* buf, int len, std::function callback) + { + uv_buf_t bufs[] = { uv_buf_t { const_cast(buf), static_cast(len) } }; + callbacks::store(get()->data, native::internal::uv_cid_write, callback); + return uv_write(new uv_write_t, get(), bufs, 1, [](uv_write_t* req, int status) { + callbacks::invoke(req->handle->data, native::internal::uv_cid_write, status?uv_last_error(req->handle->loop):error()); + delete req; + }) == 0; + } + + bool write(const std::string& buf, std::function callback) { uv_buf_t bufs[] = { uv_buf_t { const_cast(buf.c_str()), buf.length()} }; callbacks::store(get()->data, native::internal::uv_cid_write, callback); @@ -101,16 +101,16 @@ namespace native // TODO: implement write2() - bool shutdown(std::function callback) - { - callbacks::store(get()->data, native::internal::uv_cid_shutdown, callback); - return uv_shutdown(new uv_shutdown_t, get(), [](uv_shutdown_t* req, int status) { - callbacks::invoke(req->handle->data, native::internal::uv_cid_shutdown, status?uv_last_error(req->handle->loop):error()); - delete req; - }) == 0; - } - }; - } + bool shutdown(std::function callback) + { + callbacks::store(get()->data, native::internal::uv_cid_shutdown, callback); + return uv_shutdown(new uv_shutdown_t, get(), [](uv_shutdown_t* req, int status) { + callbacks::invoke(req->handle->data, native::internal::uv_cid_shutdown, status?uv_last_error(req->handle->loop):error()); + delete req; + }) == 0; + } + }; + } } #endif diff --git a/native/text.h b/native/text.h index bd5bdac8..e2c9c4d0 100644 --- a/native/text.h +++ b/native/text.h @@ -6,27 +6,27 @@ namespace native { - namespace text - { - struct ci_less : std::binary_function - { - // case-independent (ci) compare_less binary function - struct nocase_compare : public std::binary_function - { - bool operator()(const unsigned char& c1, const unsigned char& c2) const - { - return tolower(c1) < tolower(c2); - } - }; + namespace text + { + struct ci_less : std::binary_function + { + // case-independent (ci) compare_less binary function + struct nocase_compare : public std::binary_function + { + bool operator()(const unsigned char& c1, const unsigned char& c2) const + { + return tolower(c1) < tolower(c2); + } + }; - bool operator()(const std::string & s1, const std::string & s2) const - { - return std::lexicographical_compare(s1.begin(), s1.end(), // source range - s2.begin(), s2.end(), // dest range - nocase_compare()); // comparison - } - }; - } + bool operator()(const std::string & s1, const std::string & s2) const + { + return std::lexicographical_compare(s1.begin(), s1.end(), // source range + s2.begin(), s2.end(), // dest range + nocase_compare()); // comparison + } + }; + } } #endif diff --git a/webserver.cpp b/webserver.cpp index 03effc24..7d167291 100644 --- a/webserver.cpp +++ b/webserver.cpp @@ -4,12 +4,14 @@ using namespace native::http; int main() { http server; - if(!server.listen("0.0.0.0", 8080, [](request& req, response& res) { + int port = 8080; + if(!server.listen("0.0.0.0", port, [](request& req, response& res) { + std::string body = req.get_body(); // Now you can write a custom handler for the body content. res.set_status(200); res.set_header("Content-Type", "text/plain"); res.end("C++ FTW\n"); })) return 1; // Failed to run server. - std::cout << "Server running at http://0.0.0.0:8080/" << std::endl; + std::cout << "Server running at http://0.0.0.0:" << port << "/" << std::endl; return native::run(); }