diff --git a/.gitignore b/.gitignore
index cafae6ca..e0419dcd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,5 @@ file_test
webserver.dSYM
webclient.dSYM
-file_test.dSYM
\ No newline at end of file
+file_test.dSYM
+*.xcodeproj
\ No newline at end of file
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/README.md b/README.md
index b1d101fd..f520b14d 100644
--- a/README.md
+++ b/README.md
@@ -9,35 +9,35 @@ Please note that node.native project is under heavy development.
## Sample code
Simplest web-server example using node.native.
-
- #include
- #include
- using namespace native::http;
-
- int main() {
- http server;
- if(!server.listen("0.0.0.0", 8080, [](request& req, response& res) {
- 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;
- return native::run();
- }
-
+```cpp
+#include
+#include
+using namespace native::http;
+
+int main() {
+ http server;
+ if(!server.listen("0.0.0.0", 8080, [](request& req, response& res) {
+ 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;
+ return native::run();
+}
+```
## Getting started
node.native consists of header files(*.h) only, but requires [libuv](https://github.com/joyent/libuv) and [http-parser](https://github.com/joyent/http-parser) lib to use.
To compile included sample application(webserver.cpp) first run the following command in the project directory:
-
- git submodule update --init
-
+```bash
+git submodule update --init
+```
then,
-
- make
-
+```bash
+make
+```
alternatively you can set custom paths to http-parser and libuv if you dont want to use the submodules.
Tested on Ubuntu 11.10 and GCC 4.6.1. and OSX 10.8.2
@@ -45,4 +45,4 @@ Tested on Ubuntu 11.10 and GCC 4.6.1. and OSX 10.8.2
## Other Resources
- [Mailing list](http://groups.google.com/group/nodenative)
-- [Public to-to lists](https://trello.com/b/1qk3tRGS)
\ No newline at end of file
+- [Public to-to lists](https://trello.com/b/1qk3tRGS)
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 62112854..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){
@@ -360,7 +360,7 @@ namespace native
public:
static bool read(const std::string& path, std::function callback)
{
- if(!fs::open(path.c_str(), fs::read_only, 0, [=](fs::file_handle fd, error e) {
+ return fs::open(path.c_str(), fs::read_only, 0, [=](fs::file_handle fd, error e) {
if(e)
{
callback(std::string(), e);
@@ -373,12 +373,12 @@ namespace native
callback(std::string(), error(uv_last_error(uv_default_loop())));
}
}
- })) return false;
+ });
}
static bool write(const std::string& path, const std::string& str, std::function callback)
{
- if(!fs::open(path.c_str(), fs::write_only|fs::create, 0664, [=](fs::file_handle fd, error e) {
+ return fs::open(path.c_str(), fs::write_only|fs::create, 0664, [=](fs::file_handle fd, error e) {
if(e)
{
callback(0, e);
@@ -391,7 +391,7 @@ namespace native
callback(0, error(uv_last_error(uv_default_loop())));
}
}
- })) return false;
+ });
}
};
}
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 cfca06b4..1c0dcaf1 100644
--- a/native/http.h
+++ b/native/http.h
@@ -11,502 +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 std::string& default_value=std::string()) 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_;
- };
-
- 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
- {
+ 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_ = 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 54743919..ff77ade6 100644
--- a/native/stream.h
+++ b/native/stream.h
@@ -6,85 +6,80 @@
#include "handle.h"
#include "callback.h"
+#include
+
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 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);
-
- return uv_read_start(get(),
- [](uv_handle_t*, size_t suggested_size){
- if(!max_alloc_size)
- {
- return uv_buf_t { new char[suggested_size], suggested_size };
- }
- else
- {
- auto size = max_alloc_size > suggested_size ? 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), 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)
+ }
+
+ bool accept(stream* client)
+ {
+ return uv_accept(get(), client->get()) == 0;
+ }
+
+ 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);
+
+ 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_buf_t bufs[] = { uv_buf_t { const_cast(buf.c_str()), buf.length()} };
callbacks::store(get()->data, native::internal::uv_cid_write, callback);
@@ -106,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/node.native.gyp b/node.native.gyp
new file mode 100644
index 00000000..86843b54
--- /dev/null
+++ b/node.native.gyp
@@ -0,0 +1,78 @@
+{
+ 'targets' : [
+ #server
+ {
+ 'target_name' : 'webserver',
+ 'type' : 'executable',
+ 'sources' : [
+ 'webserver.cpp',
+ 'native',
+ 'http-parser/http_parser.c',
+ 'http-parser/http_parser.h',
+
+ ],
+ 'include_dirs' : [
+ 'libuv/include',
+ '../node.native'
+ ],
+ 'libraries' : [
+ 'libuv/libuv.a'
+ ],
+ 'conditions' : [
+ ['OS=="mac"', {
+
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS' : ['-std=c++11','-stdlib=libc++'],
+ 'OTHER_LDFLAGS': ['-stdlib=libc++'],
+ 'ARCHS': '$(ARCHS_STANDARD_64_BIT)'
+ },
+
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreServices.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework'
+ ],
+ },
+
+ }]
+ ]
+ },
+ #client
+ {
+ 'target_name' : 'webclient',
+ 'type' : 'executable',
+ 'sources' : [
+ 'webclient.cpp',
+ 'native',
+ 'http-parser/http_parser.c',
+ 'http-parser/http_parser.h',
+
+ ],
+ 'include_dirs' : [
+ 'libuv/include',
+ '../node.native'
+ ],
+ 'libraries' : [
+ 'libuv/libuv.a'
+ ],
+ 'conditions' : [
+ ['OS=="mac"', {
+
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS' : ['-std=c++11','-stdlib=libc++'],
+ 'OTHER_LDFLAGS': ['-stdlib=libc++'],
+ 'ARCHS': '$(ARCHS_STANDARD_64_BIT)'
+ },
+
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreServices.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework'
+ ],
+ },
+
+ }]
+ ]
+ }
+ ]
+}
\ No newline at end of file
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();
}