├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bin └── rest-cpp ├── docs ├── .keep └── doxygen.conf ├── example ├── .keep ├── todo_client │ └── client.rb └── todo_server │ ├── Makefile │ ├── data │ ├── foo │ │ └── .password │ ├── nowy │ │ └── .password │ └── test_user │ │ ├── .password │ │ └── to jest tesowa lista │ │ ├── 1.task │ │ └── 2.task │ ├── init.cpp │ └── resources │ ├── _base.h │ ├── list.cpp │ └── task.cpp ├── lib └── .keep ├── obj └── .keep └── src └── rest ├── dispatcher.cpp ├── dispatcher.h ├── dispatchers ├── leastconnections.cpp ├── leastconnections.h ├── roundrobin.cpp ├── roundrobin.h ├── uniform.cpp └── uniform.h ├── exceptions.cpp ├── exceptions.h ├── feature.cpp ├── feature.h ├── features ├── authorization.cpp └── authorization.h ├── json.hpp ├── lambda_service.cpp ├── lambda_service.h ├── readerwriterqueue ├── atomicops.h └── readerwriterqueue.h ├── request.cpp ├── request.h ├── resource.cpp ├── resource.h ├── response.cpp ├── response.h ├── rest.h ├── router.cpp ├── router.h ├── server.cpp ├── server.h ├── service.cpp ├── service.h ├── utils.cpp ├── utils.h ├── worker.cpp └── worker.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | example/todo_server/todo_server* 7 | 8 | tmp/* 9 | docs/* 10 | !docs/.keep 11 | !docs/doxygen.conf 12 | 13 | # Compiled Dynamic libraries 14 | *.so 15 | *.dylib 16 | *.dSYM 17 | 18 | # Compiled Static libraries 19 | *.lai 20 | *.la 21 | *.a 22 | 23 | # Vim 24 | *.swo 25 | *.swp 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Amadeusz Leonardo Juskowiak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX=/usr/bin/clang++ -Wall -Wextra -Wno-unused-parameter -std=c++11 -stdlib=libc++ -O2 -march=native 2 | INCLUDES= 3 | LIBRARY= 4 | 5 | .PHONY: clean example librestcpp install docs infolib 6 | 7 | CPP_FILES := $(shell find src -type f -name '*.cpp') 8 | OBJ_FILES := $(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o))) 9 | 10 | default: librestcpp 11 | 12 | example: librestcpp 13 | $(MAKE) -C example/todo_server build 14 | 15 | infolib: 16 | @echo "Building rest-cpp" 17 | 18 | ifeq ($(shell uname),Darwin) 19 | librestcpp: infolib | lib/librestcpp.dylib 20 | else 21 | CXX=/usr/bin/g++ -std=gnu++11 -Wall -pthread -O2 -march=native 22 | INCLUDES=-fPIC 23 | librestcpp: infolib | lib/librestcpp.so lib/librestcpp.a 24 | endif 25 | 26 | lib/librestcpp.dylib: $(OBJ_FILES) 27 | @echo " building lib/librestcpp.dylib" 28 | @$(CXX) -dynamiclib -Wl,-install_name,librestcpp.dylib -o lib/librestcpp.dylib $^ 29 | 30 | lib/librestcpp.so: $(OBJ_FILES) 31 | @echo " building lib/librestcpp.so" 32 | @$(CXX) -fPIC -shared -o lib/librestcpp.so $^ 33 | 34 | lib/librestcpp.a: $(OBJ_FILES) 35 | @echo " building lib/librestcpp.a" 36 | @ar rcs lib/librestcpp.a $^ 37 | 38 | obj/%.o: src/rest/%.cpp 39 | @echo " compiling $<" 40 | @$(CXX) $(INCLUDES) -c -o $@ $< 41 | 42 | obj/%.o: src/rest/dispatchers/%.cpp 43 | @echo " compiling $<" 44 | @$(CXX) $(INCLUDES) -c -o $@ $< 45 | 46 | obj/%.o: src/rest/features/%.cpp 47 | @echo " compiling $<" 48 | @$(CXX) $(INCLUDES) -c -o $@ $< 49 | 50 | docs: 51 | @doxygen docs/doxygen.conf 52 | 53 | clean: 54 | @echo "Cleaning build files" 55 | @rm -rf docs/html 56 | @rm -f obj/*.o 57 | 58 | install: librestcpp 59 | @echo "Installing rest-cpp" 60 | @echo " copying headers" 61 | @rsync -r --exclude="*.cpp" src/ /usr/local/include/ 62 | @echo " copying library" 63 | @cp -f lib/librestcpp.* /usr/local/lib 64 | @echo " installing rest-cpp command" 65 | @cp -f bin/rest-cpp /usr/local/bin 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rest-cpp 2 | ======== 3 | REST-like framework and server for blazing fast web applications in 4 | C++11. 5 | 6 | 7 | Quick start 8 | ----------- 9 | ``` sh 10 | $ rest-cpp new blog 11 | $ cd blog 12 | $ rest-cpp server 13 | ``` 14 | 15 | Navigate your browser to http://127.0.0.1:8080 now. 16 | 17 | 18 | Building 19 | -------- 20 | On OS X Commanand Line Tools are required; on Linux gcc-4.8 or newer is 21 | required. Not tested on other platforms. 22 | 23 | 24 | ### Library 25 | Build library using `make` on root directory. 26 | 27 | ### Installation 28 | Run `make install` on project folder - it will build the library and 29 | copy headers, shared library and generator to `/usr/local`. 30 | 31 | ### Example 32 | After bulding the [Library](#library), go to `example/todo_server` and use `make`. 33 | 34 | 35 | Usage 36 | ----- 37 | You may build your own apps from scratch, but you can just `#include ` 38 | and use simplified workflow. To make it even easier, you can use `rest-cpp` utility 39 | to do some work for you. 40 | 41 | ### rest-cpp utility 42 | `rest-cpp` allows you to create and manage app. 43 | It is installed to `/usr/local/bin` and requires Python in version at 44 | least 2.5 (which probably you have already installed). 45 | 46 | #### New application 47 | Use `rest-cpp new [directory]` to create new application. `directory` 48 | isn't required, app will be created in current directory if omitted. 49 | 50 | Created are two files: 51 | - `init.cpp` with basic "hello world" 52 | - `Makefile` with build commands 53 | 54 | Existing files are skipped, to update file to new version, just remove 55 | previous one. 56 | 57 | ##### Makefile 58 | Available tasks: 59 | - `make server` - default action, build and start server 60 | - `make build` - build server 61 | 62 | Available options: 63 | - `address=ip_or_host` - address for server to bind, default: `0.0.0.0` 64 | - `port=number` - port to listen, default: `8080` (ports lower than 1024 may require superuser privileges) 65 | - `workers=number` - number of workers, default: `4` 66 | - `dispatcher=lc/rr` - workers dispatcher algorithm - `lc` for `LeastConnections`, `rr` for `RoundRobin`, 'uf' for 'Uniform', default: `lc` 67 | 68 | To use options pass them to `make`, i.e. `make server workers=2 port=9000`. 69 | Options are complitation-time, not runtime - this means, to i.e. change 70 | port, you must pass port to make during building process. 71 | 72 | *`rest-cpp` wraps make, so you can use `rest-cpp build` and `rest-cpp server` instead of 73 | make (you can use the same options as above).* 74 | 75 | #### Generators 76 | `rest-cpp` has builtin generators for resources and services. They work 77 | assuming you made your app using `rake-cpp new`, that is you use 78 | `init.cpp` file. Generators must be run in root directory of the 79 | project. 80 | 81 | Resources classes will be stored in `resources` directory, `services` 82 | for services. Generators *do not check* if route or other symbol 83 | already exists in `init.cpp`. However, they *do not override* service or resource class file. 84 | 85 | You may use both snake_case and CamelCase as names, however, generators 86 | will always use snake_case for filenames, simple service names and URIs (unless given) and 87 | CamelCase as Service and Resource classnames. 88 | 89 | ##### Inline Service 90 | Inline Service is simple service implemented using C++11 lambda. Use 91 | `rest-cpp generate inline PATH` to generate one, where `PATH` is URI to 92 | the new service. 93 | 94 | The following code will be added to `routes()` in init.cpp: 95 | 96 | ```cpp 97 | r->match("PATH", [](REST::Service* service) { 98 | throw REST::HTTP::NotImplemented(); 99 | }); 100 | ``` 101 | 102 | ##### Simple Service 103 | Simple Service is implemented as function in `init.cpp` file. 104 | Use `rest-cpp generate simple NAME PATH` to generate one, where `NAME` 105 | is name of function implementing service and PATH is the URI. 106 | 107 | Two things are added to `init.cpp`: 108 | 109 | ```cpp 110 | create_service(NAME) { 111 | throw REST::HTTP::NotImplemented(); 112 | } 113 | 114 | // inside routes() 115 | r->match("PATH", NAME); 116 | ``` 117 | 118 | ###### Simple JSON Service 119 | This is variation of simple service with JSON response enabled by 120 | default. Instead of `create_service`, `create_json_service` is used. 121 | 122 | ##### Service 123 | Generates full featured Service class - use `rest-cpp generate service NAME [PATH]`. 124 | You may do anything you want in Service - every HTTP method is allowed. 125 | `PATH` is optional, URI is matched to snakecased `NAME` unless given. 126 | 127 | Service class is generated in `services/NAME.cpp` as follows: 128 | 129 | ```cpp 130 | #include 131 | 132 | class NAME : public REST::Service { 133 | void method(REST::Request::Method type) { 134 | throw REST::HTTP::NotImplemented(); 135 | } 136 | }; 137 | ``` 138 | 139 | Following route is added to `init.cpp`: 140 | 141 | ```cpp 142 | // inside routes() 143 | r->mount("PATH"); 144 | ``` 145 | 146 | ##### Resource 147 | Resource represents RESTable resource. HTTP methods are mapped to CRUD 148 | actions as follows: 149 | - `POST - create()` 150 | - `GET - read()` 151 | - `PATCH or PUT - update()` 152 | - `DELETE - destroy()` 153 | 154 | Resource is specialization of Service, you may override `method()` to 155 | handle other HTTP method (if you like to break REST pattern). 156 | 157 | Generator creates `resources/NAME.cpp` file: 158 | 159 | ```cpp 160 | #include 161 | 162 | class NAME : public REST::Resource { 163 | void read() { 164 | throw REST::HTTP::NotImplemented(); 165 | } 166 | }; 167 | ``` 168 | 169 | Resources may be singular or plural, depending on route generated. 170 | Singular resource match exact URI, plural resource match URI with 171 | optional splat. 172 | 173 | ###### Singular resource 174 | Use rest-cpp generate resource NAME [PATH] to generate singular 175 | resource. `PATH` is optional, URI is matched to snakecased `NAME` unless 176 | given. 177 | 178 | ```cpp 179 | // inside routes() 180 | r->resource("PATH"); 181 | 182 | // unless PATH given 183 | r->resource(); 184 | ``` 185 | 186 | ###### Plular resource 187 | Use rest-cpp generate resources NAME [PATH] to generate plular 188 | resource. `PATH` is optional, URI is matched to snakecased `NAME` if not 189 | given. 190 | 191 | ```cpp 192 | // inside rotues() 193 | r->resources("PATH"); 194 | 195 | // unless PATH given 196 | r->resources(); 197 | ``` 198 | 199 | 200 | Example 201 | ------- 202 | lorem ipsum 203 | 204 | 205 | Basis of operation 206 | ------------------ 207 | lorem ipsum 208 | 209 | 210 | Authors 211 | ------- 212 | - Amadeusz Juskowiak - amadeusz[at]me.com 213 | 214 | ### Contributors 215 | - Błażej Kotowski - kotowski.blazej[at]gmail.com 216 | 217 | Made with love, inspired by put.poznan.pl 218 | -------------------------------------------------------------------------------- /bin/rest-cpp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys, os, string, re 4 | 5 | class Template(string.Template): 6 | delimiter = '%' 7 | 8 | Makefile = """address?=0.0.0.0 9 | port?=8080 10 | workers?=4 11 | dispatcher?=lc 12 | path?=NONE 13 | size?=0 14 | name?=%name 15 | 16 | CXX=/usr/bin/clang++ -Wall -std=c++11 -stdlib=libc++ -O2 17 | INCLUDES= 18 | LIBRARY=-lrestcpp -DSERVER_BIND=$(address) -DSERVER_PORT=$(port) -DSERVER_WORKERS=$(workers) -DSERVER_DISPATCHER_$(dispatcher) 19 | 20 | ifneq ($(path),NONE) 21 | LIBRARY+= -DSERVER_PATH=$(path) 22 | pe=$(shell seq 0 $$(( $(size) - 1 )) ) 23 | else 24 | pe=$(shell seq $(port) $$(( $(port) + $(size) - 1 )) ) 25 | endif 26 | 27 | ifneq ($(shell uname),Darwin) 28 | CXX=g++ -std=gnu++11 -Wall -pthread -O2 29 | endif 30 | 31 | .PHONY: server build pool 32 | default: server 33 | 34 | server: build 35 | \t@DYLD_LIBRARY_PATH=/usr/local/lib LD_LIBRARY_PATH=/usr/local/lib ./%name 36 | 37 | 38 | pool: 39 | ifeq ($(size),0) 40 | \t@echo "You need to provide size of the pool (size=n)." 41 | else 42 | \t@echo "Building server pool: " 43 | ifeq ($(path),NONE) 44 | \t@$(foreach nport,$(pe),echo " building $(name).$(nport)"; $(MAKE) build --no-print-directory name="$(name).$(nport)" address=$(address) port=$(nport) workers=$(workers) dispatcher=$(dispatcher);) 45 | else 46 | \t@$(foreach nport,$(pe),echo " building $(name).$(nport)"; $(MAKE) build --no-print-directory name="$(name).$(nport)" workers=$(workers) dispatcher=$(dispatcher) path="$(path).$(nport)";) 47 | endif 48 | 49 | endif 50 | 51 | build: 52 | \t@$(CXX) $(INCLUDES) *.cpp -o $(name) $(LIBRARY) 53 | 54 | """ 55 | 56 | init_cpp = """#include 57 | 58 | create_json_service(hello_world) { 59 | service->response->data["result"] = "Hello world!"; 60 | } 61 | 62 | void routes(REST::Router* r) { 63 | r->match("/", hello_world); 64 | } 65 | 66 | """ 67 | 68 | _inline_cpp = """%router->match("%uri", [](REST::Service* service) { 69 | throw REST::HTTP::NotImplemented(); 70 | });""" 71 | 72 | _simple_service_cpp = """create_service(%name) { 73 | throw REST::HTTP::NotImplemented(); 74 | }""" 75 | 76 | _json_simple_service_cpp = """create_json_service(%name) { 77 | throw REST::HTTP::NotImplemented(); 78 | }""" 79 | 80 | _simple_service_route = '%router->match("%uri", %name);' 81 | 82 | resource_cpp = """#include 83 | 84 | class %name : public REST::Resource { 85 | void read() { 86 | throw REST::HTTP::NotImplemented(); 87 | } 88 | };""" 89 | 90 | service_cpp = """#include 91 | 92 | class %name : public virtual REST::Service { 93 | void method(REST::Request::Method type) { 94 | throw REST::HTTP::NotImplemented(); 95 | } 96 | };""" 97 | 98 | FILES_LIST = (("Makefile", Makefile), ("init.cpp", init_cpp)) 99 | 100 | def snake(name): 101 | s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name.replace('-', '_')) 102 | return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() 103 | 104 | def camel(name): 105 | return "".join(map(lambda _: str.capitalize(_[0])+_[1:], name.split("_"))) 106 | 107 | def project(directory='.', required=False): 108 | name = snake(os.path.basename(os.path.abspath(directory))) 109 | 110 | if required and not os.path.isfile(os.path.join(directory, "Makefile")): 111 | print("Must be in rest-cpp project!") 112 | sys.exit(1) 113 | 114 | print("rest-cpp project: %s" % name) 115 | 116 | return name 117 | 118 | def save_template(where, what, args={}, force=False, test=False): 119 | if force or not os.path.isfile(where): 120 | if not force: 121 | print(" creating file: %s" % where) 122 | else: 123 | print(" saving file: %s" % where) 124 | content = Template(what).substitute(**args) 125 | 126 | if not test: 127 | with open(where, "w") as f: 128 | f.write(content) 129 | else: 130 | print(content) 131 | else: 132 | print(" skipping file: %s" % where) 133 | 134 | def mkdir_if_not_exists(where): 135 | if not os.path.isdir(where): 136 | os.makedirs(where) 137 | print(" creating directory: %s" % where) 138 | 139 | def cmd_new(directory): 140 | full_directory = os.path.abspath(directory) 141 | name = project(full_directory) 142 | 143 | mkdir_if_not_exists(directory) 144 | 145 | for (filename, content) in FILES_LIST: 146 | save_template(os.path.join(directory, filename), content, {'name':name}) 147 | 148 | 149 | print("Done. Have fun!") 150 | 151 | init_file = '' 152 | ROUTER_VAR = 'r' 153 | 154 | def insert(what, after, where, default_indent = ''): 155 | lines = [] 156 | last_position = -1 157 | for regex in after: 158 | lines = [] 159 | for line in where.split("\n"): 160 | lines.append(line) 161 | if re.search(regex, line): 162 | last_position = len(lines)-1 163 | if last_position != -1: 164 | break 165 | 166 | indent = default_indent 167 | 168 | new_indent = re.match('^(\s*)',lines[last_position]).group(1) 169 | 170 | if len(new_indent) > 0: 171 | indent = new_indent 172 | what = "\n".join(map(lambda _: indent + _, what.split("\n"))) 173 | 174 | lines.insert(last_position+1, what) 175 | 176 | return "\n".join(lines) 177 | 178 | def new_lambda(): 179 | uri = sys.argv[3] 180 | 181 | new_file = insert(_inline_cpp, 182 | [ 183 | re.escape("});"), 184 | re.escape("%s->match"%ROUTER_VAR), 185 | re.escape("%s->resource"%ROUTER_VAR), 186 | re.escape("%s->mount"%ROUTER_VAR), 187 | "void routes"], 188 | init_file, default_indent=' ') 189 | 190 | print(" adding inline service") 191 | print(" matching to: %s" % uri) 192 | 193 | save_template("init.cpp", new_file, {'uri': uri, 'router': ROUTER_VAR},force=True) 194 | 195 | 196 | def new_simple_service(json=False): 197 | if len(sys.argv) == 4: 198 | print("You need to provide both name and URI.") 199 | sys.exit(1) 200 | 201 | name = snake(sys.argv[3]) 202 | uri = sys.argv[4] 203 | 204 | if not json: 205 | service_cpp = _simple_service_cpp 206 | print(" adding simple service: %s" % name) 207 | else: 208 | service_cpp = _json_simple_service_cpp 209 | print(" adding JSON simple service: %s" % name) 210 | 211 | print(" matching to: %s" % uri) 212 | 213 | new_file = insert("\n"+service_cpp, 214 | [ 215 | re.escape("#include")], 216 | init_file) 217 | 218 | new_file = insert(_simple_service_route, 219 | [ 220 | "%s->match\(.*?,[ a-zA-Z_0-9]+\);"%ROUTER_VAR, 221 | "void routes"], 222 | new_file, default_indent=' ') 223 | 224 | save_template("init.cpp", new_file, {'uri': uri, 'router': ROUTER_VAR, 225 | 'name': name},force=True) 226 | 227 | def new_json_simple_service(): 228 | new_simple_service(json=True) 229 | 230 | def new_resource(recursive=False): 231 | mkdir_if_not_exists("resources") 232 | 233 | classname = camel(sys.argv[3]) 234 | filename = os.path.join("resources", "%s.cpp" % snake(classname)) 235 | 236 | print(" adding resource: %s" % classname) 237 | 238 | sys.stdout.write(" ") 239 | save_template(filename, resource_cpp, {'name': classname}) 240 | 241 | if len(sys.argv) == 5: 242 | uri = sys.argv[4].rstrip("/") 243 | if len(uri) == 0: 244 | uri = "/" 245 | if not recursive: 246 | route = '%router->resource<%name>("%uri");' 247 | print(" matching to: %s" % uri) 248 | else: 249 | route = '%router->resources<%name>("%uri");' 250 | print(" matching to: %s" % uri) 251 | if uri == "/": 252 | print(" matching to: /*") 253 | else: 254 | print(" matching to: %s/*" % uri) 255 | else: 256 | uri = "/" + classname.lower() 257 | if not recursive: 258 | route = "%router->resource<%name>();" 259 | print(" matching to: %s" % uri) 260 | else: 261 | route = "%router->resources<%name>();" 262 | print(" matching to: %s" % uri) 263 | print(" matching to: %s/*" % uri) 264 | 265 | new_file = insert("#include \"%s\"" % filename, 266 | [ 267 | re.escape('#include "resources/'), 268 | re.escape('#include ') 269 | ], 270 | init_file) 271 | 272 | new_file = insert(route, 273 | [ 274 | "%s->resource"%ROUTER_VAR, 275 | "void routes"], 276 | new_file, default_indent=' ') 277 | 278 | sys.stdout.write(" ") 279 | save_template("init.cpp", new_file, 280 | {'uri':uri,'name':classname,'router':ROUTER_VAR},force=True) 281 | 282 | 283 | def new_resources(): 284 | new_resource(recursive=True) 285 | 286 | def new_service(): 287 | if len(sys.argv) == 4: 288 | print("You need to provide both name and URI.") 289 | sys.exit(1) 290 | 291 | mkdir_if_not_exists("services") 292 | 293 | uri = sys.argv[4] 294 | classname = camel(sys.argv[3]) 295 | filename = os.path.join("services", "%s.cpp" % snake(classname)) 296 | 297 | print(" adding service: %s" % classname) 298 | 299 | sys.stdout.write(" ") 300 | save_template(filename, service_cpp, {'name': classname}) 301 | 302 | print(" matching to: %s" % uri) 303 | if uri == "/": 304 | print(" matching to: /*") 305 | else: 306 | print(" matching to: %s/*" % uri) 307 | 308 | new_file = insert("#include \"%s\"" % filename, 309 | [ 310 | re.escape('#include "services/'), 311 | re.escape('#include ') 312 | ], 313 | init_file) 314 | 315 | new_file = insert('%router->mount<%name>("%uri");', 316 | [ 317 | "%s->mount"%ROUTER_VAR, 318 | "void routes"], 319 | new_file, default_indent=' ') 320 | 321 | sys.stdout.write(" ") 322 | save_template("init.cpp", new_file, 323 | {'uri':uri,'name':classname,'router':ROUTER_VAR},force=True) 324 | 325 | 326 | GENERATE_ACTIONS = { 327 | 'inline' : new_lambda, 328 | 'i': new_lambda, 329 | 'service' : new_service, 330 | 's': new_service, 331 | 'resource': new_resource, 332 | 'resources': new_resources, 333 | 'r': new_resources, 334 | 'simple': new_simple_service, 335 | 'json': new_json_simple_service, 336 | 'j': new_json_simple_service 337 | } 338 | 339 | def cmd_generate(): 340 | global init_file 341 | 342 | if len(sys.argv) == 2: 343 | print("You need to provide what to generate.") 344 | sys.exit(1) 345 | try: 346 | action = GENERATE_ACTIONS[sys.argv[2]] 347 | except: 348 | print("You need to provide right action (inline/service/resource/resources/simple/json)") 349 | sys.exit(1) 350 | if len(sys.argv) == 3: 351 | print("You need to provide a name.") 352 | sys.exit(1) 353 | 354 | project(required=True) 355 | with open("init.cpp", "r") as f: 356 | init_file = f.read() 357 | 358 | ROUTER_VAR = re.search("void\s+routes\(REST::Router\s*\*\s+(.+?)\)", init_file, re.MULTILINE).group(1) 359 | 360 | action() 361 | 362 | def cmd_help(): 363 | print("rest-cpp") 364 | print("REST-like framework and server for blazing fast web applications in C++11.\n") 365 | print("Commands") 366 | print(" - new [directory] - create new project or update files") 367 | print(" - (b)uild - build binary") 368 | print(" - (s)erver - build and start server") 369 | print(" - help - show this message") 370 | 371 | SHORTCUTS = {'g' : 'generate', 'b' : 'build', 's' : 'server'} 372 | 373 | if __name__ == "__main__": 374 | directory = "." 375 | 376 | if len(sys.argv) == 1: 377 | cmd_help() 378 | sys.exit(1) 379 | elif len(sys.argv) > 1: 380 | for k,v in SHORTCUTS.iteritems(): 381 | if k == sys.argv[1]: 382 | sys.argv[1] = v 383 | break 384 | 385 | if sys.argv[1] == "new": 386 | if len(sys.argv) == 3: 387 | directory = sys.argv[2] 388 | 389 | cmd_new(directory) 390 | elif sys.argv[1] == "build" or sys.argv[1] == "server" or sys.argv[1] == "pool": 391 | project(required=True) 392 | os.execvp("make", sys.argv) 393 | elif sys.argv[1] == "generate": 394 | cmd_generate() 395 | else: 396 | cmd_help() 397 | sys.exit(1) 398 | -------------------------------------------------------------------------------- /docs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfanick/rest-cpp/575a498bc4ad727903b07d135bd316df1a85e274/docs/.keep -------------------------------------------------------------------------------- /docs/doxygen.conf: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.8.4 2 | 3 | # This file describes the settings to be used by the documentation system 4 | # doxygen (www.doxygen.org) for a project. 5 | # 6 | # All text after a double hash (##) is considered a comment and is placed 7 | # in front of the TAG it is preceding . 8 | # All text after a hash (#) is considered a comment and will be ignored. 9 | # The format is: 10 | # TAG = value [value, ...] 11 | # For lists items can also be appended using: 12 | # TAG += value [value, ...] 13 | # Values that contain spaces should be placed between quotes (" "). 14 | 15 | #--------------------------------------------------------------------------- 16 | # Project related configuration options 17 | #--------------------------------------------------------------------------- 18 | 19 | # This tag specifies the encoding used for all characters in the config file 20 | # that follow. The default is UTF-8 which is also the encoding used for all 21 | # text before the first occurrence of this tag. Doxygen uses libiconv (or the 22 | # iconv built into libc) for the transcoding. See 23 | # http://www.gnu.org/software/libiconv for the list of possible encodings. 24 | 25 | DOXYFILE_ENCODING = UTF-8 26 | 27 | # The PROJECT_NAME tag is a single word (or sequence of words) that should 28 | # identify the project. Note that if you do not use Doxywizard you need 29 | # to put quotes around the project name if it contains spaces. 30 | 31 | PROJECT_NAME = "rest-cpp" 32 | 33 | # The PROJECT_NUMBER tag can be used to enter a project or revision number. 34 | # This could be handy for archiving the generated documentation or 35 | # if some version control system is used. 36 | 37 | PROJECT_NUMBER = 38 | 39 | # Using the PROJECT_BRIEF tag one can provide an optional one line description 40 | # for a project that appears at the top of each page and should give viewer 41 | # a quick idea about the purpose of the project. Keep the description short. 42 | 43 | PROJECT_BRIEF = "REST-like framework and server for blazing fast web applications in C++11" 44 | 45 | # With the PROJECT_LOGO tag one can specify an logo or icon that is 46 | # included in the documentation. The maximum height of the logo should not 47 | # exceed 55 pixels and the maximum width should not exceed 200 pixels. 48 | # Doxygen will copy the logo to the output directory. 49 | 50 | PROJECT_LOGO = 51 | 52 | # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 53 | # base path where the generated documentation will be put. 54 | # If a relative path is entered, it will be relative to the location 55 | # where doxygen was started. If left blank the current directory will be used. 56 | 57 | OUTPUT_DIRECTORY = ./docs 58 | 59 | # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 60 | # 4096 sub-directories (in 2 levels) under the output directory of each output 61 | # format and will distribute the generated files over these directories. 62 | # Enabling this option can be useful when feeding doxygen a huge amount of 63 | # source files, where putting all generated files in the same directory would 64 | # otherwise cause performance problems for the file system. 65 | 66 | CREATE_SUBDIRS = NO 67 | 68 | # The OUTPUT_LANGUAGE tag is used to specify the language in which all 69 | # documentation generated by doxygen is written. Doxygen will use this 70 | # information to generate all constant output in the proper language. 71 | # The default language is English, other supported languages are: 72 | # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 73 | # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 74 | # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 75 | # messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, 76 | # Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, 77 | # Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. 78 | 79 | OUTPUT_LANGUAGE = English 80 | 81 | # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 82 | # include brief member descriptions after the members that are listed in 83 | # the file and class documentation (similar to JavaDoc). 84 | # Set to NO to disable this. 85 | 86 | BRIEF_MEMBER_DESC = YES 87 | 88 | # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 89 | # the brief description of a member or function before the detailed description. 90 | # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 91 | # brief descriptions will be completely suppressed. 92 | 93 | REPEAT_BRIEF = YES 94 | 95 | # This tag implements a quasi-intelligent brief description abbreviator 96 | # that is used to form the text in various listings. Each string 97 | # in this list, if found as the leading text of the brief description, will be 98 | # stripped from the text and the result after processing the whole list, is 99 | # used as the annotated text. Otherwise, the brief description is used as-is. 100 | # If left blank, the following values are used ("$name" is automatically 101 | # replaced with the name of the entity): "The $name class" "The $name widget" 102 | # "The $name file" "is" "provides" "specifies" "contains" 103 | # "represents" "a" "an" "the" 104 | 105 | ABBREVIATE_BRIEF = 106 | 107 | # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 108 | # Doxygen will generate a detailed section even if there is only a brief 109 | # description. 110 | 111 | ALWAYS_DETAILED_SEC = NO 112 | 113 | # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 114 | # inherited members of a class in the documentation of that class as if those 115 | # members were ordinary class members. Constructors, destructors and assignment 116 | # operators of the base classes will not be shown. 117 | 118 | INLINE_INHERITED_MEMB = NO 119 | 120 | # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 121 | # path before files name in the file list and in the header files. If set 122 | # to NO the shortest path that makes the file name unique will be used. 123 | 124 | FULL_PATH_NAMES = YES 125 | 126 | # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 127 | # can be used to strip a user-defined part of the path. Stripping is 128 | # only done if one of the specified strings matches the left-hand part of 129 | # the path. The tag can be used to show relative paths in the file list. 130 | # If left blank the directory from which doxygen is run is used as the 131 | # path to strip. Note that you specify absolute paths here, but also 132 | # relative paths, which will be relative from the directory where doxygen is 133 | # started. 134 | 135 | STRIP_FROM_PATH = src/ 136 | 137 | # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 138 | # the path mentioned in the documentation of a class, which tells 139 | # the reader which header file to include in order to use a class. 140 | # If left blank only the name of the header file containing the class 141 | # definition is used. Otherwise one should specify the include paths that 142 | # are normally passed to the compiler using the -I flag. 143 | 144 | STRIP_FROM_INC_PATH = 145 | 146 | # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 147 | # (but less readable) file names. This can be useful if your file system 148 | # doesn't support long names like on DOS, Mac, or CD-ROM. 149 | 150 | SHORT_NAMES = NO 151 | 152 | # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 153 | # will interpret the first line (until the first dot) of a JavaDoc-style 154 | # comment as the brief description. If set to NO, the JavaDoc 155 | # comments will behave just like regular Qt-style comments 156 | # (thus requiring an explicit @brief command for a brief description.) 157 | 158 | JAVADOC_AUTOBRIEF = NO 159 | 160 | # If the QT_AUTOBRIEF tag is set to YES then Doxygen will 161 | # interpret the first line (until the first dot) of a Qt-style 162 | # comment as the brief description. If set to NO, the comments 163 | # will behave just like regular Qt-style comments (thus requiring 164 | # an explicit \brief command for a brief description.) 165 | 166 | QT_AUTOBRIEF = NO 167 | 168 | # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 169 | # treat a multi-line C++ special comment block (i.e. a block of //! or /// 170 | # comments) as a brief description. This used to be the default behaviour. 171 | # The new default is to treat a multi-line C++ comment block as a detailed 172 | # description. Set this tag to YES if you prefer the old behaviour instead. 173 | 174 | MULTILINE_CPP_IS_BRIEF = YES 175 | 176 | # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 177 | # member inherits the documentation from any documented member that it 178 | # re-implements. 179 | 180 | INHERIT_DOCS = YES 181 | 182 | # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 183 | # a new page for each member. If set to NO, the documentation of a member will 184 | # be part of the file/class/namespace that contains it. 185 | 186 | SEPARATE_MEMBER_PAGES = NO 187 | 188 | # The TAB_SIZE tag can be used to set the number of spaces in a tab. 189 | # Doxygen uses this value to replace tabs by spaces in code fragments. 190 | 191 | TAB_SIZE = 2 192 | 193 | # This tag can be used to specify a number of aliases that acts 194 | # as commands in the documentation. An alias has the form "name=value". 195 | # For example adding "sideeffect=\par Side Effects:\n" will allow you to 196 | # put the command \sideeffect (or @sideeffect) in the documentation, which 197 | # will result in a user-defined paragraph with heading "Side Effects:". 198 | # You can put \n's in the value part of an alias to insert newlines. 199 | 200 | ALIASES = 201 | 202 | # This tag can be used to specify a number of word-keyword mappings (TCL only). 203 | # A mapping has the form "name=value". For example adding 204 | # "class=itcl::class" will allow you to use the command class in the 205 | # itcl::class meaning. 206 | 207 | TCL_SUBST = 208 | 209 | # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 210 | # sources only. Doxygen will then generate output that is more tailored for C. 211 | # For instance, some of the names that are used will be different. The list 212 | # of all members will be omitted, etc. 213 | 214 | OPTIMIZE_OUTPUT_FOR_C = NO 215 | 216 | # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 217 | # sources only. Doxygen will then generate output that is more tailored for 218 | # Java. For instance, namespaces will be presented as packages, qualified 219 | # scopes will look different, etc. 220 | 221 | OPTIMIZE_OUTPUT_JAVA = NO 222 | 223 | # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 224 | # sources only. Doxygen will then generate output that is more tailored for 225 | # Fortran. 226 | 227 | OPTIMIZE_FOR_FORTRAN = NO 228 | 229 | # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 230 | # sources. Doxygen will then generate output that is tailored for 231 | # VHDL. 232 | 233 | OPTIMIZE_OUTPUT_VHDL = NO 234 | 235 | # Doxygen selects the parser to use depending on the extension of the files it 236 | # parses. With this tag you can assign which parser to use for a given 237 | # extension. Doxygen has a built-in mapping, but you can override or extend it 238 | # using this tag. The format is ext=language, where ext is a file extension, 239 | # and language is one of the parsers supported by doxygen: IDL, Java, 240 | # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, 241 | # C++. For instance to make doxygen treat .inc files as Fortran files (default 242 | # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note 243 | # that for custom extensions you also need to set FILE_PATTERNS otherwise the 244 | # files are not read by doxygen. 245 | 246 | EXTENSION_MAPPING = 247 | 248 | # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all 249 | # comments according to the Markdown format, which allows for more readable 250 | # documentation. See http://daringfireball.net/projects/markdown/ for details. 251 | # The output of markdown processing is further processed by doxygen, so you 252 | # can mix doxygen, HTML, and XML commands with Markdown formatting. 253 | # Disable only in case of backward compatibilities issues. 254 | 255 | MARKDOWN_SUPPORT = YES 256 | 257 | # When enabled doxygen tries to link words that correspond to documented 258 | # classes, or namespaces to their corresponding documentation. Such a link can 259 | # be prevented in individual cases by by putting a % sign in front of the word 260 | # or globally by setting AUTOLINK_SUPPORT to NO. 261 | 262 | AUTOLINK_SUPPORT = YES 263 | 264 | # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 265 | # to include (a tag file for) the STL sources as input, then you should 266 | # set this tag to YES in order to let doxygen match functions declarations and 267 | # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 268 | # func(std::string) {}). This also makes the inheritance and collaboration 269 | # diagrams that involve STL classes more complete and accurate. 270 | 271 | BUILTIN_STL_SUPPORT = YES 272 | 273 | # If you use Microsoft's C++/CLI language, you should set this option to YES to 274 | # enable parsing support. 275 | 276 | CPP_CLI_SUPPORT = NO 277 | 278 | # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 279 | # Doxygen will parse them like normal C++ but will assume all classes use public 280 | # instead of private inheritance when no explicit protection keyword is present. 281 | 282 | SIP_SUPPORT = NO 283 | 284 | # For Microsoft's IDL there are propget and propput attributes to indicate 285 | # getter and setter methods for a property. Setting this option to YES (the 286 | # default) will make doxygen replace the get and set methods by a property in 287 | # the documentation. This will only work if the methods are indeed getting or 288 | # setting a simple type. If this is not the case, or you want to show the 289 | # methods anyway, you should set this option to NO. 290 | 291 | IDL_PROPERTY_SUPPORT = YES 292 | 293 | # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 294 | # tag is set to YES, then doxygen will reuse the documentation of the first 295 | # member in the group (if any) for the other members of the group. By default 296 | # all members of a group must be documented explicitly. 297 | 298 | DISTRIBUTE_GROUP_DOC = NO 299 | 300 | # Set the SUBGROUPING tag to YES (the default) to allow class member groups of 301 | # the same type (for instance a group of public functions) to be put as a 302 | # subgroup of that type (e.g. under the Public Functions section). Set it to 303 | # NO to prevent subgrouping. Alternatively, this can be done per class using 304 | # the \nosubgrouping command. 305 | 306 | SUBGROUPING = YES 307 | 308 | # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and 309 | # unions are shown inside the group in which they are included (e.g. using 310 | # @ingroup) instead of on a separate page (for HTML and Man pages) or 311 | # section (for LaTeX and RTF). 312 | 313 | INLINE_GROUPED_CLASSES = NO 314 | 315 | # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and 316 | # unions with only public data fields or simple typedef fields will be shown 317 | # inline in the documentation of the scope in which they are defined (i.e. file, 318 | # namespace, or group documentation), provided this scope is documented. If set 319 | # to NO (the default), structs, classes, and unions are shown on a separate 320 | # page (for HTML and Man pages) or section (for LaTeX and RTF). 321 | 322 | INLINE_SIMPLE_STRUCTS = NO 323 | 324 | # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 325 | # is documented as struct, union, or enum with the name of the typedef. So 326 | # typedef struct TypeS {} TypeT, will appear in the documentation as a struct 327 | # with name TypeT. When disabled the typedef will appear as a member of a file, 328 | # namespace, or class. And the struct will be named TypeS. This can typically 329 | # be useful for C code in case the coding convention dictates that all compound 330 | # types are typedef'ed and only the typedef is referenced, never the tag name. 331 | 332 | TYPEDEF_HIDES_STRUCT = NO 333 | 334 | # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This 335 | # cache is used to resolve symbols given their name and scope. Since this can 336 | # be an expensive process and often the same symbol appear multiple times in 337 | # the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too 338 | # small doxygen will become slower. If the cache is too large, memory is wasted. 339 | # The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid 340 | # range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 341 | # symbols. 342 | 343 | LOOKUP_CACHE_SIZE = 0 344 | 345 | #--------------------------------------------------------------------------- 346 | # Build related configuration options 347 | #--------------------------------------------------------------------------- 348 | 349 | # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 350 | # documentation are documented, even if no documentation was available. 351 | # Private class members and static file members will be hidden unless 352 | # the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES 353 | 354 | EXTRACT_ALL = YES 355 | 356 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class 357 | # will be included in the documentation. 358 | 359 | EXTRACT_PRIVATE = NO 360 | 361 | # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal 362 | # scope will be included in the documentation. 363 | 364 | EXTRACT_PACKAGE = NO 365 | 366 | # If the EXTRACT_STATIC tag is set to YES all static members of a file 367 | # will be included in the documentation. 368 | 369 | EXTRACT_STATIC = YES 370 | 371 | # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 372 | # defined locally in source files will be included in the documentation. 373 | # If set to NO only classes defined in header files are included. 374 | 375 | EXTRACT_LOCAL_CLASSES = NO 376 | 377 | # This flag is only useful for Objective-C code. When set to YES local 378 | # methods, which are defined in the implementation section but not in 379 | # the interface are included in the documentation. 380 | # If set to NO (the default) only methods in the interface are included. 381 | 382 | EXTRACT_LOCAL_METHODS = NO 383 | 384 | # If this flag is set to YES, the members of anonymous namespaces will be 385 | # extracted and appear in the documentation as a namespace called 386 | # 'anonymous_namespace{file}', where file will be replaced with the base 387 | # name of the file that contains the anonymous namespace. By default 388 | # anonymous namespaces are hidden. 389 | 390 | EXTRACT_ANON_NSPACES = NO 391 | 392 | # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 393 | # undocumented members of documented classes, files or namespaces. 394 | # If set to NO (the default) these members will be included in the 395 | # various overviews, but no documentation section is generated. 396 | # This option has no effect if EXTRACT_ALL is enabled. 397 | 398 | HIDE_UNDOC_MEMBERS = NO 399 | 400 | # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 401 | # undocumented classes that are normally visible in the class hierarchy. 402 | # If set to NO (the default) these classes will be included in the various 403 | # overviews. This option has no effect if EXTRACT_ALL is enabled. 404 | 405 | HIDE_UNDOC_CLASSES = NO 406 | 407 | # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 408 | # friend (class|struct|union) declarations. 409 | # If set to NO (the default) these declarations will be included in the 410 | # documentation. 411 | 412 | HIDE_FRIEND_COMPOUNDS = YES 413 | 414 | # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 415 | # documentation blocks found inside the body of a function. 416 | # If set to NO (the default) these blocks will be appended to the 417 | # function's detailed documentation block. 418 | 419 | HIDE_IN_BODY_DOCS = NO 420 | 421 | # The INTERNAL_DOCS tag determines if documentation 422 | # that is typed after a \internal command is included. If the tag is set 423 | # to NO (the default) then the documentation will be excluded. 424 | # Set it to YES to include the internal documentation. 425 | 426 | INTERNAL_DOCS = NO 427 | 428 | # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 429 | # file names in lower-case letters. If set to YES upper-case letters are also 430 | # allowed. This is useful if you have classes or files whose names only differ 431 | # in case and if your file system supports case sensitive file names. Windows 432 | # and Mac users are advised to set this option to NO. 433 | 434 | CASE_SENSE_NAMES = NO 435 | 436 | # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 437 | # will show members with their full class and namespace scopes in the 438 | # documentation. If set to YES the scope will be hidden. 439 | 440 | HIDE_SCOPE_NAMES = NO 441 | 442 | # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 443 | # will put a list of the files that are included by a file in the documentation 444 | # of that file. 445 | 446 | SHOW_INCLUDE_FILES = NO 447 | 448 | # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen 449 | # will list include files with double quotes in the documentation 450 | # rather than with sharp brackets. 451 | 452 | FORCE_LOCAL_INCLUDES = NO 453 | 454 | # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 455 | # is inserted in the documentation for inline members. 456 | 457 | INLINE_INFO = YES 458 | 459 | # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 460 | # will sort the (detailed) documentation of file and class members 461 | # alphabetically by member name. If set to NO the members will appear in 462 | # declaration order. 463 | 464 | SORT_MEMBER_DOCS = YES 465 | 466 | # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 467 | # brief documentation of file, namespace and class members alphabetically 468 | # by member name. If set to NO (the default) the members will appear in 469 | # declaration order. 470 | 471 | SORT_BRIEF_DOCS = YES 472 | 473 | # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen 474 | # will sort the (brief and detailed) documentation of class members so that 475 | # constructors and destructors are listed first. If set to NO (the default) 476 | # the constructors will appear in the respective orders defined by 477 | # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. 478 | # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO 479 | # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. 480 | 481 | SORT_MEMBERS_CTORS_1ST = NO 482 | 483 | # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 484 | # hierarchy of group names into alphabetical order. If set to NO (the default) 485 | # the group names will appear in their defined order. 486 | 487 | SORT_GROUP_NAMES = NO 488 | 489 | # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 490 | # sorted by fully-qualified names, including namespaces. If set to 491 | # NO (the default), the class list will be sorted only by class name, 492 | # not including the namespace part. 493 | # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 494 | # Note: This option applies only to the class list, not to the 495 | # alphabetical list. 496 | 497 | SORT_BY_SCOPE_NAME = NO 498 | 499 | # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to 500 | # do proper type resolution of all parameters of a function it will reject a 501 | # match between the prototype and the implementation of a member function even 502 | # if there is only one candidate or it is obvious which candidate to choose 503 | # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen 504 | # will still accept a match between prototype and implementation in such cases. 505 | 506 | STRICT_PROTO_MATCHING = NO 507 | 508 | # The GENERATE_TODOLIST tag can be used to enable (YES) or 509 | # disable (NO) the todo list. This list is created by putting \todo 510 | # commands in the documentation. 511 | 512 | GENERATE_TODOLIST = YES 513 | 514 | # The GENERATE_TESTLIST tag can be used to enable (YES) or 515 | # disable (NO) the test list. This list is created by putting \test 516 | # commands in the documentation. 517 | 518 | GENERATE_TESTLIST = YES 519 | 520 | # The GENERATE_BUGLIST tag can be used to enable (YES) or 521 | # disable (NO) the bug list. This list is created by putting \bug 522 | # commands in the documentation. 523 | 524 | GENERATE_BUGLIST = YES 525 | 526 | # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 527 | # disable (NO) the deprecated list. This list is created by putting 528 | # \deprecated commands in the documentation. 529 | 530 | GENERATE_DEPRECATEDLIST= YES 531 | 532 | # The ENABLED_SECTIONS tag can be used to enable conditional 533 | # documentation sections, marked by \if section-label ... \endif 534 | # and \cond section-label ... \endcond blocks. 535 | 536 | ENABLED_SECTIONS = 537 | 538 | # The MAX_INITIALIZER_LINES tag determines the maximum number of lines 539 | # the initial value of a variable or macro consists of for it to appear in 540 | # the documentation. If the initializer consists of more lines than specified 541 | # here it will be hidden. Use a value of 0 to hide initializers completely. 542 | # The appearance of the initializer of individual variables and macros in the 543 | # documentation can be controlled using \showinitializer or \hideinitializer 544 | # command in the documentation regardless of this setting. 545 | 546 | MAX_INITIALIZER_LINES = 30 547 | 548 | # Set the SHOW_USED_FILES tag to NO to disable the list of files generated 549 | # at the bottom of the documentation of classes and structs. If set to YES the 550 | # list will mention the files that were used to generate the documentation. 551 | 552 | SHOW_USED_FILES = NO 553 | 554 | # Set the SHOW_FILES tag to NO to disable the generation of the Files page. 555 | # This will remove the Files entry from the Quick Index and from the 556 | # Folder Tree View (if specified). The default is YES. 557 | 558 | SHOW_FILES = YES 559 | 560 | # Set the SHOW_NAMESPACES tag to NO to disable the generation of the 561 | # Namespaces page. 562 | # This will remove the Namespaces entry from the Quick Index 563 | # and from the Folder Tree View (if specified). The default is YES. 564 | 565 | SHOW_NAMESPACES = YES 566 | 567 | # The FILE_VERSION_FILTER tag can be used to specify a program or script that 568 | # doxygen should invoke to get the current version for each file (typically from 569 | # the version control system). Doxygen will invoke the program by executing (via 570 | # popen()) the command , where is the value of 571 | # the FILE_VERSION_FILTER tag, and is the name of an input file 572 | # provided by doxygen. Whatever the program writes to standard output 573 | # is used as the file version. See the manual for examples. 574 | 575 | FILE_VERSION_FILTER = 576 | 577 | # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed 578 | # by doxygen. The layout file controls the global structure of the generated 579 | # output files in an output format independent way. To create the layout file 580 | # that represents doxygen's defaults, run doxygen with the -l option. 581 | # You can optionally specify a file name after the option, if omitted 582 | # DoxygenLayout.xml will be used as the name of the layout file. 583 | 584 | LAYOUT_FILE = 585 | 586 | # The CITE_BIB_FILES tag can be used to specify one or more bib files 587 | # containing the references data. This must be a list of .bib files. The 588 | # .bib extension is automatically appended if omitted. Using this command 589 | # requires the bibtex tool to be installed. See also 590 | # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 591 | # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this 592 | # feature you need bibtex and perl available in the search path. Do not use 593 | # file names with spaces, bibtex cannot handle them. 594 | 595 | CITE_BIB_FILES = 596 | 597 | #--------------------------------------------------------------------------- 598 | # configuration options related to warning and progress messages 599 | #--------------------------------------------------------------------------- 600 | 601 | # The QUIET tag can be used to turn on/off the messages that are generated 602 | # by doxygen. Possible values are YES and NO. If left blank NO is used. 603 | 604 | QUIET = YES 605 | 606 | # The WARNINGS tag can be used to turn on/off the warning messages that are 607 | # generated by doxygen. Possible values are YES and NO. If left blank 608 | # NO is used. 609 | 610 | WARNINGS = NO 611 | 612 | # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 613 | # for undocumented members. If EXTRACT_ALL is set to YES then this flag will 614 | # automatically be disabled. 615 | 616 | WARN_IF_UNDOCUMENTED = NO 617 | 618 | # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 619 | # potential errors in the documentation, such as not documenting some 620 | # parameters in a documented function, or documenting parameters that 621 | # don't exist or using markup commands wrongly. 622 | 623 | WARN_IF_DOC_ERROR = YES 624 | 625 | # The WARN_NO_PARAMDOC option can be enabled to get warnings for 626 | # functions that are documented, but have no documentation for their parameters 627 | # or return value. If set to NO (the default) doxygen will only warn about 628 | # wrong or incomplete parameter documentation, but not about the absence of 629 | # documentation. 630 | 631 | WARN_NO_PARAMDOC = NO 632 | 633 | # The WARN_FORMAT tag determines the format of the warning messages that 634 | # doxygen can produce. The string should contain the $file, $line, and $text 635 | # tags, which will be replaced by the file and line number from which the 636 | # warning originated and the warning text. Optionally the format may contain 637 | # $version, which will be replaced by the version of the file (if it could 638 | # be obtained via FILE_VERSION_FILTER) 639 | 640 | WARN_FORMAT = "$file:$line: $text" 641 | 642 | # The WARN_LOGFILE tag can be used to specify a file to which warning 643 | # and error messages should be written. If left blank the output is written 644 | # to stderr. 645 | 646 | WARN_LOGFILE = 647 | 648 | #--------------------------------------------------------------------------- 649 | # configuration options related to the input files 650 | #--------------------------------------------------------------------------- 651 | 652 | # The INPUT tag can be used to specify the files and/or directories that contain 653 | # documented source files. You may enter file names like "myfile.cpp" or 654 | # directories like "/usr/src/myproject". Separate the files or directories 655 | # with spaces. 656 | 657 | INPUT = README.md ./src 658 | 659 | # This tag can be used to specify the character encoding of the source files 660 | # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 661 | # also the default input encoding. Doxygen uses libiconv (or the iconv built 662 | # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 663 | # the list of possible encodings. 664 | 665 | INPUT_ENCODING = UTF-8 666 | 667 | # If the value of the INPUT tag contains directories, you can use the 668 | # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 669 | # and *.h) to filter out the source-files in the directories. If left 670 | # blank the following patterns are tested: 671 | # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh 672 | # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py 673 | # *.f90 *.f *.for *.vhd *.vhdl 674 | 675 | FILE_PATTERNS = 676 | 677 | # The RECURSIVE tag can be used to turn specify whether or not subdirectories 678 | # should be searched for input files as well. Possible values are YES and NO. 679 | # If left blank NO is used. 680 | 681 | RECURSIVE = YES 682 | 683 | # The EXCLUDE tag can be used to specify files and/or directories that should be 684 | # excluded from the INPUT source files. This way you can easily exclude a 685 | # subdirectory from a directory tree whose root is specified with the INPUT tag. 686 | # Note that relative paths are relative to the directory from which doxygen is 687 | # run. 688 | 689 | EXCLUDE = 690 | 691 | # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or 692 | # directories that are symbolic links (a Unix file system feature) are excluded 693 | # from the input. 694 | 695 | EXCLUDE_SYMLINKS = NO 696 | 697 | # If the value of the INPUT tag contains directories, you can use the 698 | # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 699 | # certain files from those directories. Note that the wildcards are matched 700 | # against the file with absolute path, so to exclude all test directories 701 | # for example use the pattern */test/* 702 | 703 | EXCLUDE_PATTERNS = *json* 704 | 705 | # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 706 | # (namespaces, classes, functions, etc.) that should be excluded from the 707 | # output. The symbol name can be a fully qualified name, a word, or if the 708 | # wildcard * is used, a substring. Examples: ANamespace, AClass, 709 | # AClass::ANamespace, ANamespace::*Test 710 | 711 | EXCLUDE_SYMBOLS = Json::*, *Error 712 | 713 | # The EXAMPLE_PATH tag can be used to specify one or more files or 714 | # directories that contain example code fragments that are included (see 715 | # the \include command). 716 | 717 | EXAMPLE_PATH = ./example 718 | 719 | # If the value of the EXAMPLE_PATH tag contains directories, you can use the 720 | # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 721 | # and *.h) to filter out the source-files in the directories. If left 722 | # blank all files are included. 723 | 724 | EXAMPLE_PATTERNS = 725 | 726 | # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 727 | # searched for input files to be used with the \include or \dontinclude 728 | # commands irrespective of the value of the RECURSIVE tag. 729 | # Possible values are YES and NO. If left blank NO is used. 730 | 731 | EXAMPLE_RECURSIVE = YES 732 | 733 | # The IMAGE_PATH tag can be used to specify one or more files or 734 | # directories that contain image that are included in the documentation (see 735 | # the \image command). 736 | 737 | IMAGE_PATH = 738 | 739 | # The INPUT_FILTER tag can be used to specify a program that doxygen should 740 | # invoke to filter for each input file. Doxygen will invoke the filter program 741 | # by executing (via popen()) the command , where 742 | # is the value of the INPUT_FILTER tag, and is the name of an 743 | # input file. Doxygen will then use the output that the filter program writes 744 | # to standard output. 745 | # If FILTER_PATTERNS is specified, this tag will be ignored. 746 | # Note that the filter must not add or remove lines; it is applied before the 747 | # code is scanned, but not when the output code is generated. If lines are added 748 | # or removed, the anchors will not be placed correctly. 749 | 750 | INPUT_FILTER = 751 | 752 | # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 753 | # basis. 754 | # Doxygen will compare the file name with each pattern and apply the 755 | # filter if there is a match. 756 | # The filters are a list of the form: 757 | # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 758 | # info on how filters are used. If FILTER_PATTERNS is empty or if 759 | # non of the patterns match the file name, INPUT_FILTER is applied. 760 | 761 | FILTER_PATTERNS = 762 | 763 | # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 764 | # INPUT_FILTER) will be used to filter the input files when producing source 765 | # files to browse (i.e. when SOURCE_BROWSER is set to YES). 766 | 767 | FILTER_SOURCE_FILES = NO 768 | 769 | # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file 770 | # pattern. A pattern will override the setting for FILTER_PATTERN (if any) 771 | # and it is also possible to disable source filtering for a specific pattern 772 | # using *.ext= (so without naming a filter). This option only has effect when 773 | # FILTER_SOURCE_FILES is enabled. 774 | 775 | FILTER_SOURCE_PATTERNS = 776 | 777 | # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that 778 | # is part of the input, its contents will be placed on the main page 779 | # (index.html). This can be useful if you have a project on for instance GitHub 780 | # and want reuse the introduction page also for the doxygen output. 781 | 782 | USE_MDFILE_AS_MAINPAGE = README.md 783 | 784 | #--------------------------------------------------------------------------- 785 | # configuration options related to source browsing 786 | #--------------------------------------------------------------------------- 787 | 788 | # If the SOURCE_BROWSER tag is set to YES then a list of source files will 789 | # be generated. Documented entities will be cross-referenced with these sources. 790 | # Note: To get rid of all source code in the generated output, make sure also 791 | # VERBATIM_HEADERS is set to NO. 792 | 793 | SOURCE_BROWSER = NO 794 | 795 | # Setting the INLINE_SOURCES tag to YES will include the body 796 | # of functions and classes directly in the documentation. 797 | 798 | INLINE_SOURCES = NO 799 | 800 | # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 801 | # doxygen to hide any special comment blocks from generated source code 802 | # fragments. Normal C, C++ and Fortran comments will always remain visible. 803 | 804 | STRIP_CODE_COMMENTS = YES 805 | 806 | # If the REFERENCED_BY_RELATION tag is set to YES 807 | # then for each documented function all documented 808 | # functions referencing it will be listed. 809 | 810 | REFERENCED_BY_RELATION = NO 811 | 812 | # If the REFERENCES_RELATION tag is set to YES 813 | # then for each documented function all documented entities 814 | # called/used by that function will be listed. 815 | 816 | REFERENCES_RELATION = NO 817 | 818 | # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 819 | # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 820 | # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 821 | # link to the source code. 822 | # Otherwise they will link to the documentation. 823 | 824 | REFERENCES_LINK_SOURCE = YES 825 | 826 | # If the USE_HTAGS tag is set to YES then the references to source code 827 | # will point to the HTML generated by the htags(1) tool instead of doxygen 828 | # built-in source browser. The htags tool is part of GNU's global source 829 | # tagging system (see http://www.gnu.org/software/global/global.html). You 830 | # will need version 4.8.6 or higher. 831 | 832 | USE_HTAGS = NO 833 | 834 | # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 835 | # will generate a verbatim copy of the header file for each class for 836 | # which an include is specified. Set to NO to disable this. 837 | 838 | VERBATIM_HEADERS = NO 839 | 840 | #--------------------------------------------------------------------------- 841 | # configuration options related to the alphabetical class index 842 | #--------------------------------------------------------------------------- 843 | 844 | # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 845 | # of all compounds will be generated. Enable this if the project 846 | # contains a lot of classes, structs, unions or interfaces. 847 | 848 | ALPHABETICAL_INDEX = YES 849 | 850 | # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 851 | # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 852 | # in which this list will be split (can be a number in the range [1..20]) 853 | 854 | COLS_IN_ALPHA_INDEX = 5 855 | 856 | # In case all classes in a project start with a common prefix, all 857 | # classes will be put under the same header in the alphabetical index. 858 | # The IGNORE_PREFIX tag can be used to specify one or more prefixes that 859 | # should be ignored while generating the index headers. 860 | 861 | IGNORE_PREFIX = 862 | 863 | #--------------------------------------------------------------------------- 864 | # configuration options related to the HTML output 865 | #--------------------------------------------------------------------------- 866 | 867 | # If the GENERATE_HTML tag is set to YES (the default) Doxygen will 868 | # generate HTML output. 869 | 870 | GENERATE_HTML = YES 871 | 872 | # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 873 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 874 | # put in front of it. If left blank `html' will be used as the default path. 875 | 876 | HTML_OUTPUT = 877 | 878 | # The HTML_FILE_EXTENSION tag can be used to specify the file extension for 879 | # each generated HTML page (for example: .htm,.php,.asp). If it is left blank 880 | # doxygen will generate files with .html extension. 881 | 882 | HTML_FILE_EXTENSION = .html 883 | 884 | # The HTML_HEADER tag can be used to specify a personal HTML header for 885 | # each generated HTML page. If it is left blank doxygen will generate a 886 | # standard header. Note that when using a custom header you are responsible 887 | # for the proper inclusion of any scripts and style sheets that doxygen 888 | # needs, which is dependent on the configuration options used. 889 | # It is advised to generate a default header using "doxygen -w html 890 | # header.html footer.html stylesheet.css YourConfigFile" and then modify 891 | # that header. Note that the header is subject to change so you typically 892 | # have to redo this when upgrading to a newer version of doxygen or when 893 | # changing the value of configuration settings such as GENERATE_TREEVIEW! 894 | 895 | HTML_HEADER = 896 | 897 | # The HTML_FOOTER tag can be used to specify a personal HTML footer for 898 | # each generated HTML page. If it is left blank doxygen will generate a 899 | # standard footer. 900 | 901 | HTML_FOOTER = 902 | 903 | # The HTML_STYLESHEET tag can be used to specify a user-defined cascading 904 | # style sheet that is used by each HTML page. It can be used to 905 | # fine-tune the look of the HTML output. If left blank doxygen will 906 | # generate a default style sheet. Note that it is recommended to use 907 | # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this 908 | # tag will in the future become obsolete. 909 | 910 | HTML_STYLESHEET = 911 | 912 | # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional 913 | # user-defined cascading style sheet that is included after the standard 914 | # style sheets created by doxygen. Using this option one can overrule 915 | # certain style aspects. This is preferred over using HTML_STYLESHEET 916 | # since it does not replace the standard style sheet and is therefor more 917 | # robust against future updates. Doxygen will copy the style sheet file to 918 | # the output directory. 919 | 920 | HTML_EXTRA_STYLESHEET = 921 | 922 | # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or 923 | # other source files which should be copied to the HTML output directory. Note 924 | # that these files will be copied to the base HTML output directory. Use the 925 | # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these 926 | # files. In the HTML_STYLESHEET file, use the file name only. Also note that 927 | # the files will be copied as-is; there are no commands or markers available. 928 | 929 | HTML_EXTRA_FILES = 930 | 931 | # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. 932 | # Doxygen will adjust the colors in the style sheet and background images 933 | # according to this color. Hue is specified as an angle on a colorwheel, 934 | # see http://en.wikipedia.org/wiki/Hue for more information. 935 | # For instance the value 0 represents red, 60 is yellow, 120 is green, 936 | # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. 937 | # The allowed range is 0 to 359. 938 | 939 | HTML_COLORSTYLE_HUE = 210 940 | 941 | # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of 942 | # the colors in the HTML output. For a value of 0 the output will use 943 | # grayscales only. A value of 255 will produce the most vivid colors. 944 | 945 | HTML_COLORSTYLE_SAT = 60 946 | 947 | # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to 948 | # the luminance component of the colors in the HTML output. Values below 949 | # 100 gradually make the output lighter, whereas values above 100 make 950 | # the output darker. The value divided by 100 is the actual gamma applied, 951 | # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, 952 | # and 100 does not change the gamma. 953 | 954 | HTML_COLORSTYLE_GAMMA = 80 955 | 956 | # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 957 | # page will contain the date and time when the page was generated. Setting 958 | # this to NO can help when comparing the output of multiple runs. 959 | 960 | HTML_TIMESTAMP = NO 961 | 962 | # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 963 | # documentation will contain sections that can be hidden and shown after the 964 | # page has loaded. 965 | 966 | HTML_DYNAMIC_SECTIONS = YES 967 | 968 | # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of 969 | # entries shown in the various tree structured indices initially; the user 970 | # can expand and collapse entries dynamically later on. Doxygen will expand 971 | # the tree to such a level that at most the specified number of entries are 972 | # visible (unless a fully collapsed tree already exceeds this amount). 973 | # So setting the number of entries 1 will produce a full collapsed tree by 974 | # default. 0 is a special value representing an infinite number of entries 975 | # and will result in a full expanded tree by default. 976 | 977 | HTML_INDEX_NUM_ENTRIES = 100 978 | 979 | # If the GENERATE_DOCSET tag is set to YES, additional index files 980 | # will be generated that can be used as input for Apple's Xcode 3 981 | # integrated development environment, introduced with OSX 10.5 (Leopard). 982 | # To create a documentation set, doxygen will generate a Makefile in the 983 | # HTML output directory. Running make will produce the docset in that 984 | # directory and running "make install" will install the docset in 985 | # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 986 | # it at startup. 987 | # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html 988 | # for more information. 989 | 990 | GENERATE_DOCSET = NO 991 | 992 | # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 993 | # feed. A documentation feed provides an umbrella under which multiple 994 | # documentation sets from a single provider (such as a company or product suite) 995 | # can be grouped. 996 | 997 | DOCSET_FEEDNAME = "Doxygen generated docs" 998 | 999 | # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 1000 | # should uniquely identify the documentation set bundle. This should be a 1001 | # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 1002 | # will append .docset to the name. 1003 | 1004 | DOCSET_BUNDLE_ID = org.doxygen.Project 1005 | 1006 | # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely 1007 | # identify the documentation publisher. This should be a reverse domain-name 1008 | # style string, e.g. com.mycompany.MyDocSet.documentation. 1009 | 1010 | DOCSET_PUBLISHER_ID = org.doxygen.Publisher 1011 | 1012 | # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. 1013 | 1014 | DOCSET_PUBLISHER_NAME = Publisher 1015 | 1016 | # If the GENERATE_HTMLHELP tag is set to YES, additional index files 1017 | # will be generated that can be used as input for tools like the 1018 | # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 1019 | # of the generated HTML documentation. 1020 | 1021 | GENERATE_HTMLHELP = NO 1022 | 1023 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 1024 | # be used to specify the file name of the resulting .chm file. You 1025 | # can add a path in front of the file if the result should not be 1026 | # written to the html output directory. 1027 | 1028 | CHM_FILE = 1029 | 1030 | # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 1031 | # be used to specify the location (absolute path including file name) of 1032 | # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 1033 | # the HTML help compiler on the generated index.hhp. 1034 | 1035 | HHC_LOCATION = 1036 | 1037 | # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 1038 | # controls if a separate .chi index file is generated (YES) or that 1039 | # it should be included in the master .chm file (NO). 1040 | 1041 | GENERATE_CHI = NO 1042 | 1043 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 1044 | # is used to encode HtmlHelp index (hhk), content (hhc) and project file 1045 | # content. 1046 | 1047 | CHM_INDEX_ENCODING = 1048 | 1049 | # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 1050 | # controls whether a binary table of contents is generated (YES) or a 1051 | # normal table of contents (NO) in the .chm file. 1052 | 1053 | BINARY_TOC = NO 1054 | 1055 | # The TOC_EXPAND flag can be set to YES to add extra items for group members 1056 | # to the contents of the HTML help documentation and to the tree view. 1057 | 1058 | TOC_EXPAND = NO 1059 | 1060 | # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and 1061 | # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated 1062 | # that can be used as input for Qt's qhelpgenerator to generate a 1063 | # Qt Compressed Help (.qch) of the generated HTML documentation. 1064 | 1065 | GENERATE_QHP = NO 1066 | 1067 | # If the QHG_LOCATION tag is specified, the QCH_FILE tag can 1068 | # be used to specify the file name of the resulting .qch file. 1069 | # The path specified is relative to the HTML output folder. 1070 | 1071 | QCH_FILE = 1072 | 1073 | # The QHP_NAMESPACE tag specifies the namespace to use when generating 1074 | # Qt Help Project output. For more information please see 1075 | # http://doc.trolltech.com/qthelpproject.html#namespace 1076 | 1077 | QHP_NAMESPACE = org.doxygen.Project 1078 | 1079 | # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 1080 | # Qt Help Project output. For more information please see 1081 | # http://doc.trolltech.com/qthelpproject.html#virtual-folders 1082 | 1083 | QHP_VIRTUAL_FOLDER = doc 1084 | 1085 | # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to 1086 | # add. For more information please see 1087 | # http://doc.trolltech.com/qthelpproject.html#custom-filters 1088 | 1089 | QHP_CUST_FILTER_NAME = 1090 | 1091 | # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the 1092 | # custom filter to add. For more information please see 1093 | # 1094 | # Qt Help Project / Custom Filters. 1095 | 1096 | QHP_CUST_FILTER_ATTRS = 1097 | 1098 | # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this 1099 | # project's 1100 | # filter section matches. 1101 | # 1102 | # Qt Help Project / Filter Attributes. 1103 | 1104 | QHP_SECT_FILTER_ATTRS = 1105 | 1106 | # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 1107 | # be used to specify the location of Qt's qhelpgenerator. 1108 | # If non-empty doxygen will try to run qhelpgenerator on the generated 1109 | # .qhp file. 1110 | 1111 | QHG_LOCATION = 1112 | 1113 | # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files 1114 | # will be generated, which together with the HTML files, form an Eclipse help 1115 | # plugin. To install this plugin and make it available under the help contents 1116 | # menu in Eclipse, the contents of the directory containing the HTML and XML 1117 | # files needs to be copied into the plugins directory of eclipse. The name of 1118 | # the directory within the plugins directory should be the same as 1119 | # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before 1120 | # the help appears. 1121 | 1122 | GENERATE_ECLIPSEHELP = NO 1123 | 1124 | # A unique identifier for the eclipse help plugin. When installing the plugin 1125 | # the directory name containing the HTML and XML files should also have 1126 | # this name. 1127 | 1128 | ECLIPSE_DOC_ID = org.doxygen.Project 1129 | 1130 | # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) 1131 | # at top of each HTML page. The value NO (the default) enables the index and 1132 | # the value YES disables it. Since the tabs have the same information as the 1133 | # navigation tree you can set this option to NO if you already set 1134 | # GENERATE_TREEVIEW to YES. 1135 | 1136 | DISABLE_INDEX = NO 1137 | 1138 | # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 1139 | # structure should be generated to display hierarchical information. 1140 | # If the tag value is set to YES, a side panel will be generated 1141 | # containing a tree-like index structure (just like the one that 1142 | # is generated for HTML Help). For this to work a browser that supports 1143 | # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 1144 | # Windows users are probably better off using the HTML help feature. 1145 | # Since the tree basically has the same information as the tab index you 1146 | # could consider to set DISABLE_INDEX to NO when enabling this option. 1147 | 1148 | GENERATE_TREEVIEW = YES 1149 | 1150 | # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 1151 | # (range [0,1..20]) that doxygen will group on one line in the generated HTML 1152 | # documentation. Note that a value of 0 will completely suppress the enum 1153 | # values from appearing in the overview section. 1154 | 1155 | ENUM_VALUES_PER_LINE = 0 1156 | 1157 | # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 1158 | # used to set the initial width (in pixels) of the frame in which the tree 1159 | # is shown. 1160 | 1161 | TREEVIEW_WIDTH = 250 1162 | 1163 | # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open 1164 | # links to external symbols imported via tag files in a separate window. 1165 | 1166 | EXT_LINKS_IN_WINDOW = NO 1167 | 1168 | # Use this tag to change the font size of Latex formulas included 1169 | # as images in the HTML documentation. The default is 10. Note that 1170 | # when you change the font size after a successful doxygen run you need 1171 | # to manually remove any form_*.png images from the HTML output directory 1172 | # to force them to be regenerated. 1173 | 1174 | FORMULA_FONTSIZE = 10 1175 | 1176 | # Use the FORMULA_TRANPARENT tag to determine whether or not the images 1177 | # generated for formulas are transparent PNGs. Transparent PNGs are 1178 | # not supported properly for IE 6.0, but are supported on all modern browsers. 1179 | # Note that when changing this option you need to delete any form_*.png files 1180 | # in the HTML output before the changes have effect. 1181 | 1182 | FORMULA_TRANSPARENT = YES 1183 | 1184 | # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax 1185 | # (see http://www.mathjax.org) which uses client side Javascript for the 1186 | # rendering instead of using prerendered bitmaps. Use this if you do not 1187 | # have LaTeX installed or if you want to formulas look prettier in the HTML 1188 | # output. When enabled you may also need to install MathJax separately and 1189 | # configure the path to it using the MATHJAX_RELPATH option. 1190 | 1191 | USE_MATHJAX = NO 1192 | 1193 | # When MathJax is enabled you can set the default output format to be used for 1194 | # the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and 1195 | # SVG. The default value is HTML-CSS, which is slower, but has the best 1196 | # compatibility. 1197 | 1198 | MATHJAX_FORMAT = HTML-CSS 1199 | 1200 | # When MathJax is enabled you need to specify the location relative to the 1201 | # HTML output directory using the MATHJAX_RELPATH option. The destination 1202 | # directory should contain the MathJax.js script. For instance, if the mathjax 1203 | # directory is located at the same level as the HTML output directory, then 1204 | # MATHJAX_RELPATH should be ../mathjax. The default value points to 1205 | # the MathJax Content Delivery Network so you can quickly see the result without 1206 | # installing MathJax. 1207 | # However, it is strongly recommended to install a local 1208 | # copy of MathJax from http://www.mathjax.org before deployment. 1209 | 1210 | MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest 1211 | 1212 | # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension 1213 | # names that should be enabled during MathJax rendering. 1214 | 1215 | MATHJAX_EXTENSIONS = 1216 | 1217 | # The MATHJAX_CODEFILE tag can be used to specify a file with javascript 1218 | # pieces of code that will be used on startup of the MathJax code. 1219 | 1220 | MATHJAX_CODEFILE = 1221 | 1222 | # When the SEARCHENGINE tag is enabled doxygen will generate a search box 1223 | # for the HTML output. The underlying search engine uses javascript 1224 | # and DHTML and should work on any modern browser. Note that when using 1225 | # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets 1226 | # (GENERATE_DOCSET) there is already a search function so this one should 1227 | # typically be disabled. For large projects the javascript based search engine 1228 | # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. 1229 | 1230 | SEARCHENGINE = NO 1231 | 1232 | # When the SERVER_BASED_SEARCH tag is enabled the search engine will be 1233 | # implemented using a web server instead of a web client using Javascript. 1234 | # There are two flavours of web server based search depending on the 1235 | # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for 1236 | # searching and an index file used by the script. When EXTERNAL_SEARCH is 1237 | # enabled the indexing and searching needs to be provided by external tools. 1238 | # See the manual for details. 1239 | 1240 | SERVER_BASED_SEARCH = NO 1241 | 1242 | # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP 1243 | # script for searching. Instead the search results are written to an XML file 1244 | # which needs to be processed by an external indexer. Doxygen will invoke an 1245 | # external search engine pointed to by the SEARCHENGINE_URL option to obtain 1246 | # the search results. Doxygen ships with an example indexer (doxyindexer) and 1247 | # search engine (doxysearch.cgi) which are based on the open source search 1248 | # engine library Xapian. See the manual for configuration details. 1249 | 1250 | EXTERNAL_SEARCH = NO 1251 | 1252 | # The SEARCHENGINE_URL should point to a search engine hosted by a web server 1253 | # which will returned the search results when EXTERNAL_SEARCH is enabled. 1254 | # Doxygen ships with an example search engine (doxysearch) which is based on 1255 | # the open source search engine library Xapian. See the manual for configuration 1256 | # details. 1257 | 1258 | SEARCHENGINE_URL = 1259 | 1260 | # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed 1261 | # search data is written to a file for indexing by an external tool. With the 1262 | # SEARCHDATA_FILE tag the name of this file can be specified. 1263 | 1264 | SEARCHDATA_FILE = searchdata.xml 1265 | 1266 | # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the 1267 | # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is 1268 | # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple 1269 | # projects and redirect the results back to the right project. 1270 | 1271 | EXTERNAL_SEARCH_ID = 1272 | 1273 | # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen 1274 | # projects other than the one defined by this configuration file, but that are 1275 | # all added to the same external search index. Each project needs to have a 1276 | # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id 1277 | # of to a relative location where the documentation can be found. 1278 | # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... 1279 | 1280 | EXTRA_SEARCH_MAPPINGS = 1281 | 1282 | #--------------------------------------------------------------------------- 1283 | # configuration options related to the LaTeX output 1284 | #--------------------------------------------------------------------------- 1285 | 1286 | # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 1287 | # generate Latex output. 1288 | 1289 | GENERATE_LATEX = NO 1290 | 1291 | # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 1292 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1293 | # put in front of it. If left blank `latex' will be used as the default path. 1294 | 1295 | LATEX_OUTPUT = latex 1296 | 1297 | # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 1298 | # invoked. If left blank `latex' will be used as the default command name. 1299 | # Note that when enabling USE_PDFLATEX this option is only used for 1300 | # generating bitmaps for formulas in the HTML output, but not in the 1301 | # Makefile that is written to the output directory. 1302 | 1303 | LATEX_CMD_NAME = latex 1304 | 1305 | # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 1306 | # generate index for LaTeX. If left blank `makeindex' will be used as the 1307 | # default command name. 1308 | 1309 | MAKEINDEX_CMD_NAME = makeindex 1310 | 1311 | # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 1312 | # LaTeX documents. This may be useful for small projects and may help to 1313 | # save some trees in general. 1314 | 1315 | COMPACT_LATEX = NO 1316 | 1317 | # The PAPER_TYPE tag can be used to set the paper type that is used 1318 | # by the printer. Possible values are: a4, letter, legal and 1319 | # executive. If left blank a4 will be used. 1320 | 1321 | PAPER_TYPE = a4 1322 | 1323 | # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 1324 | # packages that should be included in the LaTeX output. 1325 | 1326 | EXTRA_PACKAGES = 1327 | 1328 | # The LATEX_HEADER tag can be used to specify a personal LaTeX header for 1329 | # the generated latex document. The header should contain everything until 1330 | # the first chapter. If it is left blank doxygen will generate a 1331 | # standard header. Notice: only use this tag if you know what you are doing! 1332 | 1333 | LATEX_HEADER = 1334 | 1335 | # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for 1336 | # the generated latex document. The footer should contain everything after 1337 | # the last chapter. If it is left blank doxygen will generate a 1338 | # standard footer. Notice: only use this tag if you know what you are doing! 1339 | 1340 | LATEX_FOOTER = 1341 | 1342 | # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images 1343 | # or other source files which should be copied to the LaTeX output directory. 1344 | # Note that the files will be copied as-is; there are no commands or markers 1345 | # available. 1346 | 1347 | LATEX_EXTRA_FILES = 1348 | 1349 | # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 1350 | # is prepared for conversion to pdf (using ps2pdf). The pdf file will 1351 | # contain links (just like the HTML output) instead of page references 1352 | # This makes the output suitable for online browsing using a pdf viewer. 1353 | 1354 | PDF_HYPERLINKS = YES 1355 | 1356 | # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 1357 | # plain latex in the generated Makefile. Set this option to YES to get a 1358 | # higher quality PDF documentation. 1359 | 1360 | USE_PDFLATEX = YES 1361 | 1362 | # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 1363 | # command to the generated LaTeX files. This will instruct LaTeX to keep 1364 | # running if errors occur, instead of asking the user for help. 1365 | # This option is also used when generating formulas in HTML. 1366 | 1367 | LATEX_BATCHMODE = NO 1368 | 1369 | # If LATEX_HIDE_INDICES is set to YES then doxygen will not 1370 | # include the index chapters (such as File Index, Compound Index, etc.) 1371 | # in the output. 1372 | 1373 | LATEX_HIDE_INDICES = NO 1374 | 1375 | # If LATEX_SOURCE_CODE is set to YES then doxygen will include 1376 | # source code with syntax highlighting in the LaTeX output. 1377 | # Note that which sources are shown also depends on other settings 1378 | # such as SOURCE_BROWSER. 1379 | 1380 | LATEX_SOURCE_CODE = NO 1381 | 1382 | # The LATEX_BIB_STYLE tag can be used to specify the style to use for the 1383 | # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See 1384 | # http://en.wikipedia.org/wiki/BibTeX for more info. 1385 | 1386 | LATEX_BIB_STYLE = plain 1387 | 1388 | #--------------------------------------------------------------------------- 1389 | # configuration options related to the RTF output 1390 | #--------------------------------------------------------------------------- 1391 | 1392 | # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 1393 | # The RTF output is optimized for Word 97 and may not look very pretty with 1394 | # other RTF readers or editors. 1395 | 1396 | GENERATE_RTF = NO 1397 | 1398 | # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 1399 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1400 | # put in front of it. If left blank `rtf' will be used as the default path. 1401 | 1402 | RTF_OUTPUT = rtf 1403 | 1404 | # If the COMPACT_RTF tag is set to YES Doxygen generates more compact 1405 | # RTF documents. This may be useful for small projects and may help to 1406 | # save some trees in general. 1407 | 1408 | COMPACT_RTF = NO 1409 | 1410 | # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 1411 | # will contain hyperlink fields. The RTF file will 1412 | # contain links (just like the HTML output) instead of page references. 1413 | # This makes the output suitable for online browsing using WORD or other 1414 | # programs which support those fields. 1415 | # Note: wordpad (write) and others do not support links. 1416 | 1417 | RTF_HYPERLINKS = NO 1418 | 1419 | # Load style sheet definitions from file. Syntax is similar to doxygen's 1420 | # config file, i.e. a series of assignments. You only have to provide 1421 | # replacements, missing definitions are set to their default value. 1422 | 1423 | RTF_STYLESHEET_FILE = 1424 | 1425 | # Set optional variables used in the generation of an rtf document. 1426 | # Syntax is similar to doxygen's config file. 1427 | 1428 | RTF_EXTENSIONS_FILE = 1429 | 1430 | #--------------------------------------------------------------------------- 1431 | # configuration options related to the man page output 1432 | #--------------------------------------------------------------------------- 1433 | 1434 | # If the GENERATE_MAN tag is set to YES (the default) Doxygen will 1435 | # generate man pages 1436 | 1437 | GENERATE_MAN = NO 1438 | 1439 | # The MAN_OUTPUT tag is used to specify where the man pages will be put. 1440 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1441 | # put in front of it. If left blank `man' will be used as the default path. 1442 | 1443 | MAN_OUTPUT = man 1444 | 1445 | # The MAN_EXTENSION tag determines the extension that is added to 1446 | # the generated man pages (default is the subroutine's section .3) 1447 | 1448 | MAN_EXTENSION = .3 1449 | 1450 | # If the MAN_LINKS tag is set to YES and Doxygen generates man output, 1451 | # then it will generate one additional man file for each entity 1452 | # documented in the real man page(s). These additional files 1453 | # only source the real man page, but without them the man command 1454 | # would be unable to find the correct page. The default is NO. 1455 | 1456 | MAN_LINKS = NO 1457 | 1458 | #--------------------------------------------------------------------------- 1459 | # configuration options related to the XML output 1460 | #--------------------------------------------------------------------------- 1461 | 1462 | # If the GENERATE_XML tag is set to YES Doxygen will 1463 | # generate an XML file that captures the structure of 1464 | # the code including all documentation. 1465 | 1466 | GENERATE_XML = NO 1467 | 1468 | # The XML_OUTPUT tag is used to specify where the XML pages will be put. 1469 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1470 | # put in front of it. If left blank `xml' will be used as the default path. 1471 | 1472 | XML_OUTPUT = xml 1473 | 1474 | # The XML_SCHEMA tag can be used to specify an XML schema, 1475 | # which can be used by a validating XML parser to check the 1476 | # syntax of the XML files. 1477 | 1478 | XML_SCHEMA = 1479 | 1480 | # The XML_DTD tag can be used to specify an XML DTD, 1481 | # which can be used by a validating XML parser to check the 1482 | # syntax of the XML files. 1483 | 1484 | XML_DTD = 1485 | 1486 | # If the XML_PROGRAMLISTING tag is set to YES Doxygen will 1487 | # dump the program listings (including syntax highlighting 1488 | # and cross-referencing information) to the XML output. Note that 1489 | # enabling this will significantly increase the size of the XML output. 1490 | 1491 | XML_PROGRAMLISTING = YES 1492 | 1493 | #--------------------------------------------------------------------------- 1494 | # configuration options related to the DOCBOOK output 1495 | #--------------------------------------------------------------------------- 1496 | 1497 | # If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files 1498 | # that can be used to generate PDF. 1499 | 1500 | GENERATE_DOCBOOK = NO 1501 | 1502 | # The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. 1503 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in 1504 | # front of it. If left blank docbook will be used as the default path. 1505 | 1506 | DOCBOOK_OUTPUT = docbook 1507 | 1508 | #--------------------------------------------------------------------------- 1509 | # configuration options for the AutoGen Definitions output 1510 | #--------------------------------------------------------------------------- 1511 | 1512 | # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 1513 | # generate an AutoGen Definitions (see autogen.sf.net) file 1514 | # that captures the structure of the code including all 1515 | # documentation. Note that this feature is still experimental 1516 | # and incomplete at the moment. 1517 | 1518 | GENERATE_AUTOGEN_DEF = NO 1519 | 1520 | #--------------------------------------------------------------------------- 1521 | # configuration options related to the Perl module output 1522 | #--------------------------------------------------------------------------- 1523 | 1524 | # If the GENERATE_PERLMOD tag is set to YES Doxygen will 1525 | # generate a Perl module file that captures the structure of 1526 | # the code including all documentation. Note that this 1527 | # feature is still experimental and incomplete at the 1528 | # moment. 1529 | 1530 | GENERATE_PERLMOD = NO 1531 | 1532 | # If the PERLMOD_LATEX tag is set to YES Doxygen will generate 1533 | # the necessary Makefile rules, Perl scripts and LaTeX code to be able 1534 | # to generate PDF and DVI output from the Perl module output. 1535 | 1536 | PERLMOD_LATEX = NO 1537 | 1538 | # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 1539 | # nicely formatted so it can be parsed by a human reader. 1540 | # This is useful 1541 | # if you want to understand what is going on. 1542 | # On the other hand, if this 1543 | # tag is set to NO the size of the Perl module output will be much smaller 1544 | # and Perl will parse it just the same. 1545 | 1546 | PERLMOD_PRETTY = YES 1547 | 1548 | # The names of the make variables in the generated doxyrules.make file 1549 | # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 1550 | # This is useful so different doxyrules.make files included by the same 1551 | # Makefile don't overwrite each other's variables. 1552 | 1553 | PERLMOD_MAKEVAR_PREFIX = 1554 | 1555 | #--------------------------------------------------------------------------- 1556 | # Configuration options related to the preprocessor 1557 | #--------------------------------------------------------------------------- 1558 | 1559 | # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 1560 | # evaluate all C-preprocessor directives found in the sources and include 1561 | # files. 1562 | 1563 | ENABLE_PREPROCESSING = YES 1564 | 1565 | # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 1566 | # names in the source code. If set to NO (the default) only conditional 1567 | # compilation will be performed. Macro expansion can be done in a controlled 1568 | # way by setting EXPAND_ONLY_PREDEF to YES. 1569 | 1570 | MACRO_EXPANSION = YES 1571 | 1572 | # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 1573 | # then the macro expansion is limited to the macros specified with the 1574 | # PREDEFINED and EXPAND_AS_DEFINED tags. 1575 | 1576 | EXPAND_ONLY_PREDEF = NO 1577 | 1578 | # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 1579 | # pointed to by INCLUDE_PATH will be searched when a #include is found. 1580 | 1581 | SEARCH_INCLUDES = YES 1582 | 1583 | # The INCLUDE_PATH tag can be used to specify one or more directories that 1584 | # contain include files that are not input files but should be processed by 1585 | # the preprocessor. 1586 | 1587 | INCLUDE_PATH = 1588 | 1589 | # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 1590 | # patterns (like *.h and *.hpp) to filter out the header-files in the 1591 | # directories. If left blank, the patterns specified with FILE_PATTERNS will 1592 | # be used. 1593 | 1594 | INCLUDE_FILE_PATTERNS = 1595 | 1596 | # The PREDEFINED tag can be used to specify one or more macro names that 1597 | # are defined before the preprocessor is started (similar to the -D option of 1598 | # gcc). The argument of the tag is a list of macros of the form: name 1599 | # or name=definition (no spaces). If the definition and the = are 1600 | # omitted =1 is assumed. To prevent a macro definition from being 1601 | # undefined via #undef or recursively expanded use the := operator 1602 | # instead of the = operator. 1603 | 1604 | PREDEFINED = 1605 | 1606 | # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 1607 | # this tag can be used to specify a list of macro names that should be expanded. 1608 | # The macro definition that is found in the sources will be used. 1609 | # Use the PREDEFINED tag if you want to use a different macro definition that 1610 | # overrules the definition found in the source code. 1611 | 1612 | EXPAND_AS_DEFINED = 1613 | 1614 | # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 1615 | # doxygen's preprocessor will remove all references to function-like macros 1616 | # that are alone on a line, have an all uppercase name, and do not end with a 1617 | # semicolon, because these will confuse the parser if not removed. 1618 | 1619 | SKIP_FUNCTION_MACROS = YES 1620 | 1621 | #--------------------------------------------------------------------------- 1622 | # Configuration::additions related to external references 1623 | #--------------------------------------------------------------------------- 1624 | 1625 | # The TAGFILES option can be used to specify one or more tagfiles. For each 1626 | # tag file the location of the external documentation should be added. The 1627 | # format of a tag file without this location is as follows: 1628 | # 1629 | # TAGFILES = file1 file2 ... 1630 | # Adding location for the tag files is done as follows: 1631 | # 1632 | # TAGFILES = file1=loc1 "file2 = loc2" ... 1633 | # where "loc1" and "loc2" can be relative or absolute paths 1634 | # or URLs. Note that each tag file must have a unique name (where the name does 1635 | # NOT include the path). If a tag file is not located in the directory in which 1636 | # doxygen is run, you must also specify the path to the tagfile here. 1637 | 1638 | TAGFILES = 1639 | 1640 | # When a file name is specified after GENERATE_TAGFILE, doxygen will create 1641 | # a tag file that is based on the input files it reads. 1642 | 1643 | GENERATE_TAGFILE = 1644 | 1645 | # If the ALLEXTERNALS tag is set to YES all external classes will be listed 1646 | # in the class index. If set to NO only the inherited external classes 1647 | # will be listed. 1648 | 1649 | ALLEXTERNALS = NO 1650 | 1651 | # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 1652 | # in the modules index. If set to NO, only the current project's groups will 1653 | # be listed. 1654 | 1655 | EXTERNAL_GROUPS = YES 1656 | 1657 | # If the EXTERNAL_PAGES tag is set to YES all external pages will be listed 1658 | # in the related pages index. If set to NO, only the current project's 1659 | # pages will be listed. 1660 | 1661 | EXTERNAL_PAGES = YES 1662 | 1663 | # The PERL_PATH should be the absolute path and name of the perl script 1664 | # interpreter (i.e. the result of `which perl'). 1665 | 1666 | PERL_PATH = /usr/bin/perl 1667 | 1668 | #--------------------------------------------------------------------------- 1669 | # Configuration options related to the dot tool 1670 | #--------------------------------------------------------------------------- 1671 | 1672 | # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 1673 | # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 1674 | # or super classes. Setting the tag to NO turns the diagrams off. Note that 1675 | # this option also works with HAVE_DOT disabled, but it is recommended to 1676 | # install and use dot, since it yields more powerful graphs. 1677 | 1678 | CLASS_DIAGRAMS = YES 1679 | 1680 | # You can define message sequence charts within doxygen comments using the \msc 1681 | # command. Doxygen will then run the mscgen tool (see 1682 | # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 1683 | # documentation. The MSCGEN_PATH tag allows you to specify the directory where 1684 | # the mscgen tool resides. If left empty the tool is assumed to be found in the 1685 | # default search path. 1686 | 1687 | MSCGEN_PATH = 1688 | 1689 | # If set to YES, the inheritance and collaboration graphs will hide 1690 | # inheritance and usage relations if the target is undocumented 1691 | # or is not a class. 1692 | 1693 | HIDE_UNDOC_RELATIONS = NO 1694 | 1695 | # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 1696 | # available from the path. This tool is part of Graphviz, a graph visualization 1697 | # toolkit from AT&T and Lucent Bell Labs. The other options in this section 1698 | # have no effect if this option is set to NO (the default) 1699 | 1700 | HAVE_DOT = YES 1701 | 1702 | # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is 1703 | # allowed to run in parallel. When set to 0 (the default) doxygen will 1704 | # base this on the number of processors available in the system. You can set it 1705 | # explicitly to a value larger than 0 to get control over the balance 1706 | # between CPU load and processing speed. 1707 | 1708 | DOT_NUM_THREADS = 0 1709 | 1710 | # By default doxygen will use the Helvetica font for all dot files that 1711 | # doxygen generates. When you want a differently looking font you can specify 1712 | # the font name using DOT_FONTNAME. You need to make sure dot is able to find 1713 | # the font, which can be done by putting it in a standard location or by setting 1714 | # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the 1715 | # directory containing the font. 1716 | 1717 | DOT_FONTNAME = 1718 | 1719 | # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 1720 | # The default size is 10pt. 1721 | 1722 | DOT_FONTSIZE = 9 1723 | 1724 | # By default doxygen will tell dot to use the Helvetica font. 1725 | # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to 1726 | # set the path where dot can find it. 1727 | 1728 | DOT_FONTPATH = 1729 | 1730 | # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 1731 | # will generate a graph for each documented class showing the direct and 1732 | # indirect inheritance relations. Setting this tag to YES will force the 1733 | # CLASS_DIAGRAMS tag to NO. 1734 | 1735 | CLASS_GRAPH = YES 1736 | 1737 | # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 1738 | # will generate a graph for each documented class showing the direct and 1739 | # indirect implementation dependencies (inheritance, containment, and 1740 | # class references variables) of the class with other documented classes. 1741 | 1742 | COLLABORATION_GRAPH = NO 1743 | 1744 | # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 1745 | # will generate a graph for groups, showing the direct groups dependencies 1746 | 1747 | GROUP_GRAPHS = YES 1748 | 1749 | # If the UML_LOOK tag is set to YES doxygen will generate inheritance and 1750 | # collaboration diagrams in a style similar to the OMG's Unified Modeling 1751 | # Language. 1752 | 1753 | UML_LOOK = YES 1754 | 1755 | # If the UML_LOOK tag is enabled, the fields and methods are shown inside 1756 | # the class node. If there are many fields or methods and many nodes the 1757 | # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS 1758 | # threshold limits the number of items for each type to make the size more 1759 | # manageable. Set this to 0 for no limit. Note that the threshold may be 1760 | # exceeded by 50% before the limit is enforced. 1761 | 1762 | UML_LIMIT_NUM_FIELDS = 0 1763 | 1764 | # If set to YES, the inheritance and collaboration graphs will show the 1765 | # relations between templates and their instances. 1766 | 1767 | TEMPLATE_RELATIONS = YES 1768 | 1769 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 1770 | # tags are set to YES then doxygen will generate a graph for each documented 1771 | # file showing the direct and indirect include dependencies of the file with 1772 | # other documented files. 1773 | 1774 | INCLUDE_GRAPH = NO 1775 | 1776 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 1777 | # HAVE_DOT tags are set to YES then doxygen will generate a graph for each 1778 | # documented header file showing the documented files that directly or 1779 | # indirectly include this file. 1780 | 1781 | INCLUDED_BY_GRAPH = NO 1782 | 1783 | # If the CALL_GRAPH and HAVE_DOT options are set to YES then 1784 | # doxygen will generate a call dependency graph for every global function 1785 | # or class method. Note that enabling this option will significantly increase 1786 | # the time of a run. So in most cases it will be better to enable call graphs 1787 | # for selected functions only using the \callgraph command. 1788 | 1789 | CALL_GRAPH = YES 1790 | 1791 | # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 1792 | # doxygen will generate a caller dependency graph for every global function 1793 | # or class method. Note that enabling this option will significantly increase 1794 | # the time of a run. So in most cases it will be better to enable caller 1795 | # graphs for selected functions only using the \callergraph command. 1796 | 1797 | CALLER_GRAPH = YES 1798 | 1799 | # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 1800 | # will generate a graphical hierarchy of all classes instead of a textual one. 1801 | 1802 | GRAPHICAL_HIERARCHY = YES 1803 | 1804 | # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES 1805 | # then doxygen will show the dependencies a directory has on other directories 1806 | # in a graphical way. The dependency relations are determined by the #include 1807 | # relations between the files in the directories. 1808 | 1809 | DIRECTORY_GRAPH = YES 1810 | 1811 | # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 1812 | # generated by dot. Possible values are svg, png, jpg, or gif. 1813 | # If left blank png will be used. If you choose svg you need to set 1814 | # HTML_FILE_EXTENSION to xhtml in order to make the SVG files 1815 | # visible in IE 9+ (other browsers do not have this requirement). 1816 | 1817 | DOT_IMAGE_FORMAT = svg 1818 | 1819 | # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to 1820 | # enable generation of interactive SVG images that allow zooming and panning. 1821 | # Note that this requires a modern browser other than Internet Explorer. 1822 | # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you 1823 | # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files 1824 | # visible. Older versions of IE do not have SVG support. 1825 | 1826 | INTERACTIVE_SVG = YES 1827 | 1828 | # The tag DOT_PATH can be used to specify the path where the dot tool can be 1829 | # found. If left blank, it is assumed the dot tool can be found in the path. 1830 | 1831 | DOT_PATH = 1832 | 1833 | # The DOTFILE_DIRS tag can be used to specify one or more directories that 1834 | # contain dot files that are included in the documentation (see the 1835 | # \dotfile command). 1836 | 1837 | DOTFILE_DIRS = 1838 | 1839 | # The MSCFILE_DIRS tag can be used to specify one or more directories that 1840 | # contain msc files that are included in the documentation (see the 1841 | # \mscfile command). 1842 | 1843 | MSCFILE_DIRS = 1844 | 1845 | # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 1846 | # nodes that will be shown in the graph. If the number of nodes in a graph 1847 | # becomes larger than this value, doxygen will truncate the graph, which is 1848 | # visualized by representing a node as a red box. Note that doxygen if the 1849 | # number of direct children of the root node in a graph is already larger than 1850 | # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 1851 | # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. 1852 | 1853 | DOT_GRAPH_MAX_NODES = 50 1854 | 1855 | # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 1856 | # graphs generated by dot. A depth value of 3 means that only nodes reachable 1857 | # from the root by following a path via at most 3 edges will be shown. Nodes 1858 | # that lay further from the root node will be omitted. Note that setting this 1859 | # option to 1 or 2 may greatly reduce the computation time needed for large 1860 | # code bases. Also note that the size of a graph can be further restricted by 1861 | # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. 1862 | 1863 | MAX_DOT_GRAPH_DEPTH = 0 1864 | 1865 | # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 1866 | # background. This is disabled by default, because dot on Windows does not 1867 | # seem to support this out of the box. Warning: Depending on the platform used, 1868 | # enabling this option may lead to badly anti-aliased labels on the edges of 1869 | # a graph (i.e. they become hard to read). 1870 | 1871 | DOT_TRANSPARENT = NO 1872 | 1873 | # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 1874 | # files in one run (i.e. multiple -o and -T options on the command line). This 1875 | # makes dot run faster, but since only newer versions of dot (>1.8.10) 1876 | # support this, this feature is disabled by default. 1877 | 1878 | DOT_MULTI_TARGETS = YES 1879 | 1880 | # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 1881 | # generate a legend page explaining the meaning of the various boxes and 1882 | # arrows in the dot generated graphs. 1883 | 1884 | GENERATE_LEGEND = YES 1885 | 1886 | # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 1887 | # remove the intermediate dot files that are used to generate 1888 | # the various graphs. 1889 | 1890 | DOT_CLEANUP = YES 1891 | -------------------------------------------------------------------------------- /example/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfanick/rest-cpp/575a498bc4ad727903b07d135bd316df1a85e274/example/.keep -------------------------------------------------------------------------------- /example/todo_client/client.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfanick/rest-cpp/575a498bc4ad727903b07d135bd316df1a85e274/example/todo_client/client.rb -------------------------------------------------------------------------------- /example/todo_server/Makefile: -------------------------------------------------------------------------------- 1 | address?=0.0.0.0 2 | port?=8080 3 | workers?=4 4 | dispatcher?=lc 5 | path?=NONE 6 | size?=0 7 | name?=todo_server 8 | 9 | CXX=/usr/bin/clang++ -Wall -std=c++11 -stdlib=libc++ -O2 -march=native 10 | INCLUDES= 11 | LIBRARY=-DSERVER_BIND=$(address) -DSERVER_PORT=$(port) -DSERVER_WORKERS=$(workers) -DSERVER_DISPATCHER_$(dispatcher) 12 | 13 | ifneq ($(path),NONE) 14 | LIBRARY+= -DSERVER_PATH=$(path) 15 | pe=$(shell seq 0 $$(( $(size) - 1 )) ) 16 | else 17 | pe=$(shell seq $(port) $$(( $(port) + $(size) - 1 )) ) 18 | endif 19 | 20 | ifneq ($(shell uname),Darwin) 21 | CXX=g++-4.9 -std=gnu++11 -Wall -pthread -O2 -march=native -I../../src 22 | endif 23 | 24 | .PHONY: server build pool 25 | default: server 26 | 27 | server: build 28 | @./todo_server 29 | 30 | 31 | pool: 32 | ifeq ($(size),0) 33 | @echo "You need to provide size of the pool (size=n)." 34 | else 35 | @echo "Building server pool: " 36 | ifeq ($(path),NONE) 37 | @$(foreach nport,$(pe),echo " building $(name).$(nport)"; $(MAKE) build --no-print-directory name="$(name).$(nport)" address=$(address) port=$(nport) workers=$(workers) dispatcher=$(dispatcher);) 38 | else 39 | @$(foreach nport,$(pe),echo " building $(name).$(nport)"; $(MAKE) build --no-print-directory name="$(name).$(nport)" workers=$(workers) dispatcher=$(dispatcher) path="$(path).$(nport)";) 40 | endif 41 | 42 | endif 43 | 44 | build: 45 | @$(CXX) $(INCLUDES) *.cpp -o $(name) $(LIBRARY) ../../lib/librestcpp.a 46 | 47 | -------------------------------------------------------------------------------- /example/todo_server/data/foo/.password: -------------------------------------------------------------------------------- 1 | bar 2 | -------------------------------------------------------------------------------- /example/todo_server/data/nowy/.password: -------------------------------------------------------------------------------- 1 | lolo -------------------------------------------------------------------------------- /example/todo_server/data/test_user/.password: -------------------------------------------------------------------------------- 1 | foobar 2 | -------------------------------------------------------------------------------- /example/todo_server/data/test_user/to jest tesowa lista/1.task: -------------------------------------------------------------------------------- 1 | lorem ipsum 2 | -------------------------------------------------------------------------------- /example/todo_server/data/test_user/to jest tesowa lista/2.task: -------------------------------------------------------------------------------- 1 | erjgiorejio 2 | -------------------------------------------------------------------------------- /example/todo_server/init.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources/list.cpp" 3 | #include "resources/task.cpp" 4 | 5 | void routes(REST::Router* r) { 6 | r->resource("/"); 7 | 8 | std::atomic_int a(0); 9 | r->match("/foo", [](REST::LambdaService* s) { 10 | s->response->raw = "foo"; 11 | // s->response->data["counter"] = a++; 12 | }); 13 | r->resource("/:list_id"); 14 | r->resources("/:list_id/task"); 15 | r->match("/:type/*album/:name.jpg", [](REST::LambdaService* s) { 16 | s->response->data = s->request->parameters; 17 | }); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /example/todo_server/resources/_base.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE_H 2 | #define _BASE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | class BaseResource : public REST::Resource, 12 | public REST::Features::Authorization { 13 | void before() { 14 | ensure_authorization("Need to authorize", [this](std::string username, std::string password) { 15 | std::ifstream password_file("./data/" + username + "/.password"); 16 | bool authorized = false; 17 | 18 | if (password_file.is_open()) { 19 | std::string saved_password; 20 | 21 | password_file >> saved_password; 22 | password_file.close(); 23 | 24 | authorized = (password == saved_password); 25 | } else { 26 | mkdir(("./data/" + username).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 27 | std::ofstream new_password_file("./data/" + username + "/.password"); 28 | 29 | new_password_file << password; 30 | new_password_file.close(); 31 | 32 | authorized = true; 33 | } 34 | 35 | if (authorized) 36 | data_path = "./data/" + username; 37 | 38 | return authorized; 39 | }); 40 | } 41 | 42 | protected: 43 | std::string data_path; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /example/todo_server/resources/list.cpp: -------------------------------------------------------------------------------- 1 | #include "_base.h" 2 | #include 3 | 4 | 5 | class List : public BaseResource { 6 | /** 7 | * Send names and ids of todo lists 8 | */ 9 | void read() { 10 | auto& object = response->data["folder"]; 11 | object["owner"] = authorization.first; 12 | 13 | auto dir = opendir(data_path.c_str()); 14 | 15 | struct dirent* entry; 16 | while ((entry = readdir(dir)) != NULL) 17 | if (entry->d_name[0] != '.') 18 | object["lists"].push_back(entry->d_name); 19 | 20 | closedir(dir); 21 | } 22 | /** 23 | * Remove todo list 24 | */ 25 | void destroy() { 26 | 27 | } 28 | 29 | /** 30 | * Rename todolist 31 | */ 32 | void update() { 33 | 34 | } 35 | 36 | /** 37 | * create new todolist 38 | */ 39 | void create() { 40 | 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /example/todo_server/resources/task.cpp: -------------------------------------------------------------------------------- 1 | #include "_base.h" 2 | 3 | class Task : public BaseResource { 4 | void read() { 5 | //throw REST::HTTP::NotImplemented(); 6 | response->raw = "foboar"; 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /lib/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfanick/rest-cpp/575a498bc4ad727903b07d135bd316df1a85e274/lib/.keep -------------------------------------------------------------------------------- /obj/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfanick/rest-cpp/575a498bc4ad727903b07d135bd316df1a85e274/obj/.keep -------------------------------------------------------------------------------- /src/rest/dispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "dispatcher.h" 2 | 3 | namespace REST { 4 | 5 | Dispatcher::Dispatcher(int wc, int sc) : workers_count(wc), streamers_count(sc) { 6 | Worker::POOL_SIZE = workers_count; 7 | workers.resize(workers_count); 8 | 9 | for (int i = 0; i < workers_count; i++) { 10 | workers[i] = new Worker(i, sc); 11 | } 12 | } 13 | 14 | 15 | Dispatcher::~Dispatcher() { 16 | for (int i = 0; i < workers_count; i++) { 17 | workers[i]->stop(); 18 | delete workers[i]; 19 | } 20 | } 21 | 22 | void Dispatcher::next(Request::client const &client) { 23 | workers[next_worker_id()]->enqueue(client); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/rest/dispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_DISPATCHER_H 2 | #define REST_CPP_DISPATCHER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "worker.h" 13 | #include "request.h" 14 | #include "router.h" 15 | 16 | namespace REST { 17 | 18 | /** 19 | * Dispatcher maintains Worker pool. It distributes 20 | * requests to specific Workers. 21 | * 22 | * @private 23 | * @see Worker 24 | * @see Router 25 | */ 26 | class Dispatcher { 27 | 28 | public: 29 | Dispatcher(int workers_count, int streamers_count); 30 | virtual ~Dispatcher(); 31 | 32 | void next(Request::client const &client); 33 | 34 | protected: 35 | virtual int next_worker_id() = 0; 36 | 37 | int workers_count = 0; 38 | int streamers_count = 0; 39 | std::vector workers; 40 | }; 41 | 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/rest/dispatchers/leastconnections.cpp: -------------------------------------------------------------------------------- 1 | #include "leastconnections.h" 2 | #include 3 | #include 4 | 5 | namespace REST { 6 | 7 | namespace Dispatchers { 8 | 9 | int LeastConnections::next_worker_id() { 10 | return std::distance(workers.begin(), std::min_element(workers.begin(), workers.end(), 11 | [](Worker* const& a, Worker* const& b) { 12 | return a->clients_count < b->clients_count; 13 | } 14 | )); 15 | } 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/rest/dispatchers/leastconnections.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_DISPATCHER_LEASTCONNECTIONS_H 2 | #define REST_CPP_DISPATCHER_LEASTCONNECTIONS_H 3 | 4 | #include "../dispatcher.h" 5 | 6 | namespace REST { 7 | 8 | namespace Dispatchers { 9 | 10 | /** 11 | * Selects next Worker with least connections in its queue. 12 | */ 13 | class LeastConnections final : public Dispatcher { 14 | public: 15 | LeastConnections(int workers_count, int sc) : Dispatcher(workers_count, sc) { }; 16 | 17 | private: 18 | int next_worker_id(); 19 | }; 20 | 21 | } 22 | 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/rest/dispatchers/roundrobin.cpp: -------------------------------------------------------------------------------- 1 | #include "roundrobin.h" 2 | 3 | namespace REST { 4 | 5 | namespace Dispatchers { 6 | 7 | int RoundRobin::next_worker_id() { 8 | return last_worker_id++ % workers_count; 9 | } 10 | 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/rest/dispatchers/roundrobin.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_DISPATCHER_ROUNDROBIN_H 2 | #define REST_CPP_DISPATCHER_ROUNDROBIN_H 3 | 4 | #include "../dispatcher.h" 5 | 6 | namespace REST { 7 | 8 | namespace Dispatchers { 9 | 10 | /** 11 | * Selects next not yet used Worker (Round Robin algorithm). 12 | */ 13 | class RoundRobin final : public Dispatcher { 14 | public: 15 | RoundRobin(int workers_count, int sc) : Dispatcher(workers_count, sc), last_worker_id(0) {}; 16 | 17 | private: 18 | int next_worker_id(); 19 | 20 | unsigned int last_worker_id; 21 | }; 22 | 23 | } 24 | 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/rest/dispatchers/uniform.cpp: -------------------------------------------------------------------------------- 1 | #include "uniform.h" 2 | 3 | namespace REST { 4 | 5 | namespace Dispatchers { 6 | 7 | int Uniform::next_worker_id() { 8 | return distribution(engine); 9 | } 10 | 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/rest/dispatchers/uniform.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_DISPATCHER_UNIFORM_H 2 | #define REST_CPP_DISPATCHER_UNIFORM_H 3 | 4 | #include 5 | #include "../dispatcher.h" 6 | 7 | namespace REST { 8 | 9 | namespace Dispatchers { 10 | 11 | /** 12 | * Selects next random Worker, using uniform distribution. 13 | */ 14 | class Uniform final : public Dispatcher { 15 | public: 16 | Uniform(int workers_count, int sc) : Dispatcher(workers_count, sc), distribution(0, workers_count-1) {}; 17 | 18 | private: 19 | std::uniform_int_distribution distribution; 20 | std::linear_congruential_engine engine; 21 | 22 | int next_worker_id(); 23 | }; 24 | 25 | } 26 | 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/rest/exceptions.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfanick/rest-cpp/575a498bc4ad727903b07d135bd316df1a85e274/src/rest/exceptions.cpp -------------------------------------------------------------------------------- /src/rest/exceptions.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_EXCEPTIONS_H 2 | #define REST_CPP_EXCEPTIONS_H 3 | #define CREATE(NAME, PARENT, MESSAGE) class NAME : public PARENT { public: virtual const char* what() const throw() { return (MESSAGE); } virtual int code() {return 401; }}; 4 | #define ERROR(NAME, CODE, MESSAGE) class NAME : public Error { public: virtual const char* what() const throw() { return (MESSAGE); } virtual int code() { return (CODE); } }; 5 | 6 | #include 7 | 8 | namespace REST { 9 | 10 | /** 11 | * @private 12 | */ 13 | CREATE(Exception, std::exception, "Undefined exception"); 14 | 15 | CREATE(ServerError, Exception, "Server exception"); 16 | CREATE(AddressResolvingError, ServerError, "Cannot resolve address"); 17 | CREATE(SocketCreationError, ServerError, "Cannot create socket"); 18 | CREATE(PortInUseError, ServerError, "Port is already in use"); 19 | 20 | namespace HTTP { 21 | CREATE(Error, Exception, "Unknown HTTP protocol error"); 22 | 23 | ERROR(BadRequest, 400, "Bad Request"); 24 | ERROR(Unauthorized, 401, "Unauthorized"); 25 | ERROR(NotFound, 404, "Not Found"); 26 | ERROR(MethodNotAllowed, 405, "Method Not Allowed"); 27 | ERROR(RequestTimeout, 408, "Request Timeout"); 28 | ERROR(PayloadTooLarge, 413, "Payload Too Large"); 29 | ERROR(URITooLong, 414, "URI Too Long"); 30 | ERROR(InternalServerError, 500, "Internal Server Error"); 31 | ERROR(NotImplemented, 501, "Not Implemented"); 32 | } 33 | } 34 | 35 | #undef CREATE 36 | #undef ERROR 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/rest/feature.cpp: -------------------------------------------------------------------------------- 1 | #include "feature.h" 2 | 3 | namespace REST { 4 | 5 | Feature::Feature() { 6 | features.push_back(this); 7 | } 8 | 9 | void Feature::feature_push() { 10 | } 11 | 12 | void Feature::feature_pop() { 13 | 14 | } 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /src/rest/feature.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_FEATURE_H 2 | #define REST_CPP_FEATURE_H 3 | 4 | #include "service.h" 5 | 6 | namespace REST { 7 | 8 | class Service; 9 | 10 | class Feature : public virtual Service { 11 | public: 12 | Feature(); 13 | 14 | virtual std::string feature_name() const = 0; 15 | virtual void feature_push(); 16 | virtual void feature_pop(); 17 | }; 18 | 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/rest/features/authorization.cpp: -------------------------------------------------------------------------------- 1 | #include "authorization.h" 2 | 3 | namespace REST { 4 | namespace Features { 5 | 6 | void Authorization::feature_push() { 7 | std::string auth_header = request->header("Authorization", ""); 8 | if (!auth_header.empty()) { 9 | if (auth_header.find("Basic") == 0) { 10 | std::string decoded = Utils::base64_decode(auth_header.substr(auth_header.find(" ")+1)); 11 | size_t colon = decoded.find(":"); 12 | authorization = std::make_pair(decoded.substr(0, colon), decoded.substr(colon+1)); 13 | } 14 | } 15 | } 16 | 17 | void Authorization::ensure_authorization(std::string const& realm, std::function handler) { 18 | if (authorization.first.empty() || !handler(authorization.first, authorization.second)) { 19 | response->headers["WWW-Authenticate"] = "Basic realm=\"" + realm + "\""; 20 | throw HTTP::Unauthorized(); 21 | } 22 | } 23 | 24 | void Authorization::feature_pop() { 25 | authorization.first.clear(); 26 | authorization.second.clear(); 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/rest/features/authorization.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_FEATURES_AUTHORIZATION_H 2 | #define REST_CPP_FEATURES_AUTHORIZATION_H 3 | 4 | #include "../feature.h" 5 | #include 6 | 7 | namespace REST { 8 | namespace Features { 9 | 10 | class Authorization : public Feature { 11 | public: 12 | virtual std::string feature_name() const { return "authorization"; } 13 | 14 | std::pair< std::string, std::string > authorization; 15 | void ensure_authorization(std::string const& realm, std::function handler); 16 | 17 | void feature_push(); 18 | void feature_pop(); 19 | }; 20 | 21 | } 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/rest/lambda_service.cpp: -------------------------------------------------------------------------------- 1 | #include "lambda_service.h" 2 | 3 | namespace REST { 4 | 5 | LambdaService::LambdaService() {} 6 | LambdaService::LambdaService(LambdaService::function lambda) { 7 | fun = lambda; 8 | } 9 | 10 | void LambdaService::method(Request::Method method) { 11 | fun(this); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/rest/lambda_service.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_LAMBDA_SERVICE_H 2 | #define REST_CPP_LAMBDA_SERVICE_H 3 | 4 | #include 5 | #include 6 | #include "service.h" 7 | #include "features/authorization.h" 8 | 9 | namespace REST { 10 | 11 | /** 12 | * @private 13 | */ 14 | class LambdaService : public virtual Service, 15 | public Features::Authorization { 16 | public: 17 | typedef std::function function; 18 | 19 | LambdaService(); 20 | LambdaService(function); 21 | protected: 22 | void method(Request::Method m) final; 23 | private: 24 | function fun; 25 | }; 26 | 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/rest/readerwriterqueue/atomicops.h: -------------------------------------------------------------------------------- 1 | // ©2013-2015 Cameron Desrochers. 2 | // Distributed under the simplified BSD license (see the license file that 3 | // should have come with this header). 4 | // Uses Jeff Preshing's semaphore implementation (under the terms of its 5 | // separate zlib license, embedded below). 6 | 7 | #pragma once 8 | 9 | // Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation 10 | // of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment). 11 | // Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees). 12 | // Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols. 13 | 14 | #include 15 | #include 16 | 17 | 18 | // Platform detection 19 | #if defined(__INTEL_COMPILER) 20 | #define AE_ICC 21 | #elif defined(_MSC_VER) 22 | #define AE_VCPP 23 | #elif defined(__GNUC__) 24 | #define AE_GCC 25 | #endif 26 | 27 | #if defined(_M_IA64) || defined(__ia64__) 28 | #define AE_ARCH_IA64 29 | #elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__) 30 | #define AE_ARCH_X64 31 | #elif defined(_M_IX86) || defined(__i386__) 32 | #define AE_ARCH_X86 33 | #elif defined(_M_PPC) || defined(__powerpc__) 34 | #define AE_ARCH_PPC 35 | #else 36 | #define AE_ARCH_UNKNOWN 37 | #endif 38 | 39 | 40 | // AE_UNUSED 41 | #define AE_UNUSED(x) ((void)x) 42 | 43 | 44 | // AE_FORCEINLINE 45 | #if defined(AE_VCPP) || defined(AE_ICC) 46 | #define AE_FORCEINLINE __forceinline 47 | #elif defined(AE_GCC) 48 | //#define AE_FORCEINLINE __attribute__((always_inline)) 49 | #define AE_FORCEINLINE inline 50 | #else 51 | #define AE_FORCEINLINE inline 52 | #endif 53 | 54 | 55 | // AE_ALIGN 56 | #if defined(AE_VCPP) || defined(AE_ICC) 57 | #define AE_ALIGN(x) __declspec(align(x)) 58 | #elif defined(AE_GCC) 59 | #define AE_ALIGN(x) __attribute__((aligned(x))) 60 | #else 61 | // Assume GCC compliant syntax... 62 | #define AE_ALIGN(x) __attribute__((aligned(x))) 63 | #endif 64 | 65 | 66 | // Portable atomic fences implemented below: 67 | 68 | namespace moodycamel { 69 | 70 | enum memory_order { 71 | memory_order_relaxed, 72 | memory_order_acquire, 73 | memory_order_release, 74 | memory_order_acq_rel, 75 | memory_order_seq_cst, 76 | 77 | // memory_order_sync: Forces a full sync: 78 | // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad 79 | memory_order_sync = memory_order_seq_cst 80 | }; 81 | 82 | } // end namespace moodycamel 83 | 84 | #if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || defined(AE_ICC) 85 | // VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences 86 | 87 | #include 88 | 89 | #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) 90 | #define AeFullSync _mm_mfence 91 | #define AeLiteSync _mm_mfence 92 | #elif defined(AE_ARCH_IA64) 93 | #define AeFullSync __mf 94 | #define AeLiteSync __mf 95 | #elif defined(AE_ARCH_PPC) 96 | #include 97 | #define AeFullSync __sync 98 | #define AeLiteSync __lwsync 99 | #endif 100 | 101 | 102 | #ifdef AE_VCPP 103 | #pragma warning(push) 104 | #pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert` 105 | #ifdef __cplusplus_cli 106 | #pragma managed(push, off) 107 | #endif 108 | #endif 109 | 110 | namespace moodycamel { 111 | 112 | AE_FORCEINLINE void compiler_fence(memory_order order) 113 | { 114 | switch (order) { 115 | case memory_order_relaxed: break; 116 | case memory_order_acquire: _ReadBarrier(); break; 117 | case memory_order_release: _WriteBarrier(); break; 118 | case memory_order_acq_rel: _ReadWriteBarrier(); break; 119 | case memory_order_seq_cst: _ReadWriteBarrier(); break; 120 | default: assert(false); 121 | } 122 | } 123 | 124 | // x86/x64 have a strong memory model -- all loads and stores have 125 | // acquire and release semantics automatically (so only need compiler 126 | // barriers for those). 127 | #if defined(AE_ARCH_X86) || defined(AE_ARCH_X64) 128 | AE_FORCEINLINE void fence(memory_order order) 129 | { 130 | switch (order) { 131 | case memory_order_relaxed: break; 132 | case memory_order_acquire: _ReadBarrier(); break; 133 | case memory_order_release: _WriteBarrier(); break; 134 | case memory_order_acq_rel: _ReadWriteBarrier(); break; 135 | case memory_order_seq_cst: 136 | _ReadWriteBarrier(); 137 | AeFullSync(); 138 | _ReadWriteBarrier(); 139 | break; 140 | default: assert(false); 141 | } 142 | } 143 | #else 144 | AE_FORCEINLINE void fence(memory_order order) 145 | { 146 | // Non-specialized arch, use heavier memory barriers everywhere just in case :-( 147 | switch (order) { 148 | case memory_order_relaxed: 149 | break; 150 | case memory_order_acquire: 151 | _ReadBarrier(); 152 | AeLiteSync(); 153 | _ReadBarrier(); 154 | break; 155 | case memory_order_release: 156 | _WriteBarrier(); 157 | AeLiteSync(); 158 | _WriteBarrier(); 159 | break; 160 | case memory_order_acq_rel: 161 | _ReadWriteBarrier(); 162 | AeLiteSync(); 163 | _ReadWriteBarrier(); 164 | break; 165 | case memory_order_seq_cst: 166 | _ReadWriteBarrier(); 167 | AeFullSync(); 168 | _ReadWriteBarrier(); 169 | break; 170 | default: assert(false); 171 | } 172 | } 173 | #endif 174 | } // end namespace moodycamel 175 | #else 176 | // Use standard library of atomics 177 | #include 178 | 179 | namespace moodycamel { 180 | 181 | AE_FORCEINLINE void compiler_fence(memory_order order) 182 | { 183 | switch (order) { 184 | case memory_order_relaxed: break; 185 | case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break; 186 | case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break; 187 | case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break; 188 | case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break; 189 | default: assert(false); 190 | } 191 | } 192 | 193 | AE_FORCEINLINE void fence(memory_order order) 194 | { 195 | switch (order) { 196 | case memory_order_relaxed: break; 197 | case memory_order_acquire: std::atomic_thread_fence(std::memory_order_acquire); break; 198 | case memory_order_release: std::atomic_thread_fence(std::memory_order_release); break; 199 | case memory_order_acq_rel: std::atomic_thread_fence(std::memory_order_acq_rel); break; 200 | case memory_order_seq_cst: std::atomic_thread_fence(std::memory_order_seq_cst); break; 201 | default: assert(false); 202 | } 203 | } 204 | 205 | } // end namespace moodycamel 206 | 207 | #endif 208 | 209 | 210 | #if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli)) 211 | #define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC 212 | #endif 213 | 214 | #ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC 215 | #include 216 | #endif 217 | #include 218 | 219 | // WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY: 220 | // Provides basic support for atomic variables -- no memory ordering guarantees are provided. 221 | // The guarantee of atomicity is only made for types that already have atomic load and store guarantees 222 | // at the hardware level -- on most platforms this generally means aligned pointers and integers (only). 223 | namespace moodycamel { 224 | template 225 | class weak_atomic 226 | { 227 | public: 228 | weak_atomic() { } 229 | #ifdef AE_VCPP 230 | #pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning 231 | #endif 232 | template weak_atomic(U&& x) : value(std::forward(x)) { } 233 | #ifdef __cplusplus_cli 234 | // Work around bug with universal reference/nullptr combination that only appears when /clr is on 235 | weak_atomic(nullptr_t) : value(nullptr) { } 236 | #endif 237 | weak_atomic(weak_atomic const& other) : value(other.value) { } 238 | weak_atomic(weak_atomic&& other) : value(std::move(other.value)) { } 239 | #ifdef AE_VCPP 240 | #pragma warning(default: 4100) 241 | #endif 242 | 243 | AE_FORCEINLINE operator T() const { return load(); } 244 | 245 | 246 | #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC 247 | template AE_FORCEINLINE weak_atomic const& operator=(U&& x) { value = std::forward(x); return *this; } 248 | AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) { value = other.value; return *this; } 249 | 250 | AE_FORCEINLINE T load() const { return value; } 251 | 252 | AE_FORCEINLINE T fetch_add_acquire(T increment) 253 | { 254 | #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) 255 | if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); 256 | #if defined(_M_AMD64) 257 | else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); 258 | #endif 259 | #else 260 | #error Unsupported platform 261 | #endif 262 | assert(false && "T must be either a 32 or 64 bit type"); 263 | return value; 264 | } 265 | 266 | AE_FORCEINLINE T fetch_add_release(T increment) 267 | { 268 | #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) 269 | if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); 270 | #if defined(_M_AMD64) 271 | else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); 272 | #endif 273 | #else 274 | #error Unsupported platform 275 | #endif 276 | assert(false && "T must be either a 32 or 64 bit type"); 277 | return value; 278 | } 279 | #else 280 | template 281 | AE_FORCEINLINE weak_atomic const& operator=(U&& x) 282 | { 283 | value.store(std::forward(x), std::memory_order_relaxed); 284 | return *this; 285 | } 286 | 287 | AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) 288 | { 289 | value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); 290 | return *this; 291 | } 292 | 293 | AE_FORCEINLINE T load() const { return value.load(std::memory_order_relaxed); } 294 | 295 | AE_FORCEINLINE T fetch_add_acquire(T increment) 296 | { 297 | return value.fetch_add(increment, std::memory_order_acquire); 298 | } 299 | 300 | AE_FORCEINLINE T fetch_add_release(T increment) 301 | { 302 | return value.fetch_add(increment, std::memory_order_release); 303 | } 304 | #endif 305 | 306 | 307 | private: 308 | #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC 309 | // No std::atomic support, but still need to circumvent compiler optimizations. 310 | // `volatile` will make memory access slow, but is guaranteed to be reliable. 311 | volatile T value; 312 | #else 313 | std::atomic value; 314 | #endif 315 | }; 316 | 317 | } // end namespace moodycamel 318 | 319 | 320 | 321 | // Portable single-producer, single-consumer semaphore below: 322 | 323 | #if defined(_WIN32) 324 | // Avoid including windows.h in a header; we only need a handful of 325 | // items, so we'll redeclare them here (this is relatively safe since 326 | // the API generally has to remain stable between Windows versions). 327 | // I know this is an ugly hack but it still beats polluting the global 328 | // namespace with thousands of generic names or adding a .cpp for nothing. 329 | extern "C" { 330 | struct _SECURITY_ATTRIBUTES; 331 | __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName); 332 | __declspec(dllimport) int __stdcall CloseHandle(void* hObject); 333 | __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds); 334 | __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount); 335 | } 336 | #elif defined(__MACH__) 337 | #include 338 | #elif defined(__unix__) 339 | #include 340 | #endif 341 | 342 | namespace moodycamel 343 | { 344 | // Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's 345 | // portable + lightweight semaphore implementations, originally from 346 | // https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h 347 | // LICENSE: 348 | // Copyright (c) 2015 Jeff Preshing 349 | // 350 | // This software is provided 'as-is', without any express or implied 351 | // warranty. In no event will the authors be held liable for any damages 352 | // arising from the use of this software. 353 | // 354 | // Permission is granted to anyone to use this software for any purpose, 355 | // including commercial applications, and to alter it and redistribute it 356 | // freely, subject to the following restrictions: 357 | // 358 | // 1. The origin of this software must not be misrepresented; you must not 359 | // claim that you wrote the original software. If you use this software 360 | // in a product, an acknowledgement in the product documentation would be 361 | // appreciated but is not required. 362 | // 2. Altered source versions must be plainly marked as such, and must not be 363 | // misrepresented as being the original software. 364 | // 3. This notice may not be removed or altered from any source distribution. 365 | namespace spsc_sema 366 | { 367 | #if defined(_WIN32) 368 | class Semaphore 369 | { 370 | private: 371 | void* m_hSema; 372 | 373 | Semaphore(const Semaphore& other); 374 | Semaphore& operator=(const Semaphore& other); 375 | 376 | public: 377 | Semaphore(int initialCount = 0) 378 | { 379 | assert(initialCount >= 0); 380 | const long maxLong = 0x7fffffff; 381 | m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); 382 | } 383 | 384 | ~Semaphore() 385 | { 386 | CloseHandle(m_hSema); 387 | } 388 | 389 | void wait() 390 | { 391 | const unsigned long infinite = 0xffffffff; 392 | WaitForSingleObject(m_hSema, infinite); 393 | } 394 | 395 | void signal(int count = 1) 396 | { 397 | ReleaseSemaphore(m_hSema, count, nullptr); 398 | } 399 | }; 400 | #elif defined(__MACH__) 401 | //--------------------------------------------------------- 402 | // Semaphore (Apple iOS and OSX) 403 | // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html 404 | //--------------------------------------------------------- 405 | class Semaphore 406 | { 407 | private: 408 | semaphore_t m_sema; 409 | 410 | Semaphore(const Semaphore& other); 411 | Semaphore& operator=(const Semaphore& other); 412 | 413 | public: 414 | Semaphore(int initialCount = 0) 415 | { 416 | assert(initialCount >= 0); 417 | semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount); 418 | } 419 | 420 | ~Semaphore() 421 | { 422 | semaphore_destroy(mach_task_self(), m_sema); 423 | } 424 | 425 | void wait() 426 | { 427 | semaphore_wait(m_sema); 428 | } 429 | 430 | void signal() 431 | { 432 | semaphore_signal(m_sema); 433 | } 434 | 435 | void signal(int count) 436 | { 437 | while (count-- > 0) 438 | { 439 | semaphore_signal(m_sema); 440 | } 441 | } 442 | }; 443 | #elif defined(__unix__) 444 | //--------------------------------------------------------- 445 | // Semaphore (POSIX, Linux) 446 | //--------------------------------------------------------- 447 | class Semaphore 448 | { 449 | private: 450 | sem_t m_sema; 451 | 452 | Semaphore(const Semaphore& other); 453 | Semaphore& operator=(const Semaphore& other); 454 | 455 | public: 456 | Semaphore(int initialCount = 0) 457 | { 458 | assert(initialCount >= 0); 459 | sem_init(&m_sema, 0, initialCount); 460 | } 461 | 462 | ~Semaphore() 463 | { 464 | sem_destroy(&m_sema); 465 | } 466 | 467 | void wait() 468 | { 469 | // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error 470 | int rc; 471 | do 472 | { 473 | rc = sem_wait(&m_sema); 474 | } 475 | while (rc == -1 && errno == EINTR); 476 | } 477 | 478 | void signal() 479 | { 480 | sem_post(&m_sema); 481 | } 482 | 483 | void signal(int count) 484 | { 485 | while (count-- > 0) 486 | { 487 | sem_post(&m_sema); 488 | } 489 | } 490 | }; 491 | #else 492 | #error Unsupported platform! (No semaphore wrapper available) 493 | #endif 494 | 495 | //--------------------------------------------------------- 496 | // LightweightSemaphore 497 | //--------------------------------------------------------- 498 | class LightweightSemaphore 499 | { 500 | public: 501 | typedef std::make_signed::type ssize_t; 502 | 503 | private: 504 | weak_atomic m_count; 505 | Semaphore m_sema; 506 | 507 | void waitWithPartialSpinning() 508 | { 509 | ssize_t oldCount; 510 | // Is there a better way to set the initial spin count? 511 | // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC, 512 | // as threads start hitting the kernel semaphore. 513 | int spin = 10000; 514 | while (--spin >= 0) 515 | { 516 | if (m_count.load() > 0) 517 | { 518 | m_count.fetch_add_acquire(-1); 519 | return; 520 | } 521 | compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop. 522 | } 523 | oldCount = m_count.fetch_add_acquire(-1); 524 | if (oldCount <= 0) 525 | { 526 | m_sema.wait(); 527 | } 528 | } 529 | 530 | public: 531 | LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount) 532 | { 533 | assert(initialCount >= 0); 534 | } 535 | 536 | bool tryWait() 537 | { 538 | if (m_count.load() > 0) 539 | { 540 | m_count.fetch_add_acquire(-1); 541 | return true; 542 | } 543 | return false; 544 | } 545 | 546 | void wait() 547 | { 548 | if (!tryWait()) 549 | waitWithPartialSpinning(); 550 | } 551 | 552 | void signal(ssize_t count = 1) 553 | { 554 | assert(count >= 0); 555 | ssize_t oldCount = m_count.fetch_add_release(count); 556 | assert(oldCount >= -1); 557 | if (oldCount < 0) 558 | { 559 | m_sema.signal(1); 560 | } 561 | } 562 | 563 | ssize_t availableApprox() const 564 | { 565 | ssize_t count = m_count.load(); 566 | return count > 0 ? count : 0; 567 | } 568 | }; 569 | } // end namespace spsc_sema 570 | } // end namespace moodycamel 571 | 572 | #if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli)) 573 | #pragma warning(pop) 574 | #ifdef __cplusplus_cli 575 | #pragma managed(pop) 576 | #endif 577 | #endif 578 | -------------------------------------------------------------------------------- /src/rest/readerwriterqueue/readerwriterqueue.h: -------------------------------------------------------------------------------- 1 | // ©2013-2015 Cameron Desrochers. 2 | // Distributed under the simplified BSD license (see the license file that 3 | // should have come with this header). 4 | 5 | #pragma once 6 | 7 | #include "atomicops.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include // For malloc/free & size_t 14 | 15 | 16 | // A lock-free queue for a single-consumer, single-producer architecture. 17 | // The queue is also wait-free in the common path (except if more memory 18 | // needs to be allocated, in which case malloc is called). 19 | // Allocates memory sparingly (O(lg(n) times, amortized), and only once if 20 | // the original maximum size estimate is never exceeded. 21 | // Tested on x86/x64 processors, but semantics should be correct for all 22 | // architectures (given the right implementations in atomicops.h), provided 23 | // that aligned integer and pointer accesses are naturally atomic. 24 | // Note that there should only be one consumer thread and producer thread; 25 | // Switching roles of the threads, or using multiple consecutive threads for 26 | // one role, is not safe unless properly synchronized. 27 | // Using the queue exclusively from one thread is fine, though a bit silly. 28 | 29 | #define CACHE_LINE_SIZE 64 30 | 31 | #ifdef AE_VCPP 32 | #pragma warning(push) 33 | #pragma warning(disable: 4324) // structure was padded due to __declspec(align()) 34 | #pragma warning(disable: 4820) // padding was added 35 | #pragma warning(disable: 4127) // conditional expression is constant 36 | #endif 37 | 38 | namespace moodycamel { 39 | 40 | template 41 | class ReaderWriterQueue 42 | { 43 | // Design: Based on a queue-of-queues. The low-level queues are just 44 | // circular buffers with front and tail indices indicating where the 45 | // next element to dequeue is and where the next element can be enqueued, 46 | // respectively. Each low-level queue is called a "block". Each block 47 | // wastes exactly one element's worth of space to keep the design simple 48 | // (if front == tail then the queue is empty, and can't be full). 49 | // The high-level queue is a circular linked list of blocks; again there 50 | // is a front and tail, but this time they are pointers to the blocks. 51 | // The front block is where the next element to be dequeued is, provided 52 | // the block is not empty. The back block is where elements are to be 53 | // enqueued, provided the block is not full. 54 | // The producer thread owns all the tail indices/pointers. The consumer 55 | // thread owns all the front indices/pointers. Both threads read each 56 | // other's variables, but only the owning thread updates them. E.g. After 57 | // the consumer reads the producer's tail, the tail may change before the 58 | // consumer is done dequeuing an object, but the consumer knows the tail 59 | // will never go backwards, only forwards. 60 | // If there is no room to enqueue an object, an additional block (of 61 | // equal size to the last block) is added. Blocks are never removed. 62 | 63 | public: 64 | // Constructs a queue that can hold maxSize elements without further 65 | // allocations. If more than MAX_BLOCK_SIZE elements are requested, 66 | // then several blocks of MAX_BLOCK_SIZE each are reserved (including 67 | // at least one extra buffer block). 68 | explicit ReaderWriterQueue(size_t maxSize = 15) 69 | #ifndef NDEBUG 70 | : enqueuing(false) 71 | ,dequeuing(false) 72 | #endif 73 | { 74 | assert(maxSize > 0); 75 | assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2"); 76 | assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); 77 | 78 | Block* firstBlock = nullptr; 79 | 80 | largestBlockSize = ceilToPow2(maxSize + 1); // We need a spare slot to fit maxSize elements in the block 81 | if (largestBlockSize > MAX_BLOCK_SIZE * 2) { 82 | // We need a spare block in case the producer is writing to a different block the consumer is reading from, and 83 | // wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity 84 | // between front == tail meaning "empty" and "full". 85 | // So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the 86 | // number of blocks - 1. Solving for maxSize and applying a ceiling to the division gives us (after simplifying): 87 | size_t initialBlockCount = (maxSize + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); 88 | largestBlockSize = MAX_BLOCK_SIZE; 89 | Block* lastBlock = nullptr; 90 | for (size_t i = 0; i != initialBlockCount; ++i) { 91 | auto block = make_block(largestBlockSize); 92 | if (block == nullptr) { 93 | throw std::bad_alloc(); 94 | } 95 | if (firstBlock == nullptr) { 96 | firstBlock = block; 97 | } 98 | else { 99 | lastBlock->next = block; 100 | } 101 | lastBlock = block; 102 | block->next = firstBlock; 103 | } 104 | } 105 | else { 106 | firstBlock = make_block(largestBlockSize); 107 | if (firstBlock == nullptr) { 108 | throw std::bad_alloc(); 109 | } 110 | firstBlock->next = firstBlock; 111 | } 112 | frontBlock = firstBlock; 113 | tailBlock = firstBlock; 114 | 115 | // Make sure the reader/writer threads will have the initialized memory setup above: 116 | fence(memory_order_sync); 117 | } 118 | 119 | // Note: The queue should not be accessed concurrently while it's 120 | // being deleted. It's up to the user to synchronize this. 121 | ~ReaderWriterQueue() 122 | { 123 | // Make sure we get the latest version of all variables from other CPUs: 124 | fence(memory_order_sync); 125 | 126 | // Destroy any remaining objects in queue and free memory 127 | Block* frontBlock_ = frontBlock; 128 | Block* block = frontBlock_; 129 | do { 130 | Block* nextBlock = block->next; 131 | size_t blockFront = block->front; 132 | size_t blockTail = block->tail; 133 | 134 | for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { 135 | auto element = reinterpret_cast(block->data + i * sizeof(T)); 136 | element->~T(); 137 | (void)element; 138 | } 139 | 140 | auto rawBlock = block->rawThis; 141 | block->~Block(); 142 | std::free(rawBlock); 143 | block = nextBlock; 144 | } while (block != frontBlock_); 145 | } 146 | 147 | 148 | // Enqueues a copy of element if there is room in the queue. 149 | // Returns true if the element was enqueued, false otherwise. 150 | // Does not allocate memory. 151 | AE_FORCEINLINE bool try_enqueue(T const& element) 152 | { 153 | return inner_enqueue(element); 154 | } 155 | 156 | // Enqueues a moved copy of element if there is room in the queue. 157 | // Returns true if the element was enqueued, false otherwise. 158 | // Does not allocate memory. 159 | AE_FORCEINLINE bool try_enqueue(T&& element) 160 | { 161 | return inner_enqueue(std::forward(element)); 162 | } 163 | 164 | 165 | // Enqueues a copy of element on the queue. 166 | // Allocates an additional block of memory if needed. 167 | // Only fails (returns false) if memory allocation fails. 168 | AE_FORCEINLINE bool enqueue(T const& element) 169 | { 170 | return inner_enqueue(element); 171 | } 172 | 173 | // Enqueues a moved copy of element on the queue. 174 | // Allocates an additional block of memory if needed. 175 | // Only fails (returns false) if memory allocation fails. 176 | AE_FORCEINLINE bool enqueue(T&& element) 177 | { 178 | return inner_enqueue(std::forward(element)); 179 | } 180 | 181 | 182 | // Attempts to dequeue an element; if the queue is empty, 183 | // returns false instead. If the queue has at least one element, 184 | // moves front to result using operator=, then returns true. 185 | template 186 | bool try_dequeue(U& result) 187 | { 188 | #ifndef NDEBUG 189 | ReentrantGuard guard(this->dequeuing); 190 | #endif 191 | 192 | // High-level pseudocode: 193 | // Remember where the tail block is 194 | // If the front block has an element in it, dequeue it 195 | // Else 196 | // If front block was the tail block when we entered the function, return false 197 | // Else advance to next block and dequeue the item there 198 | 199 | // Note that we have to use the value of the tail block from before we check if the front 200 | // block is full or not, in case the front block is empty and then, before we check if the 201 | // tail block is at the front block or not, the producer fills up the front block *and 202 | // moves on*, which would make us skip a filled block. Seems unlikely, but was consistently 203 | // reproducible in practice. 204 | // In order to avoid overhead in the common case, though, we do a double-checked pattern 205 | // where we have the fast path if the front block is not empty, then read the tail block, 206 | // then re-read the front block and check if it's not empty again, then check if the tail 207 | // block has advanced. 208 | 209 | Block* frontBlock_ = frontBlock.load(); 210 | size_t blockTail = frontBlock_->localTail; 211 | size_t blockFront = frontBlock_->front.load(); 212 | 213 | if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { 214 | fence(memory_order_acquire); 215 | 216 | non_empty_front_block: 217 | // Front block not empty, dequeue from here 218 | auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); 219 | result = std::move(*element); 220 | element->~T(); 221 | 222 | blockFront = (blockFront + 1) & frontBlock_->sizeMask; 223 | 224 | fence(memory_order_release); 225 | frontBlock_->front = blockFront; 226 | } 227 | else if (frontBlock_ != tailBlock.load()) { 228 | fence(memory_order_acquire); 229 | 230 | frontBlock_ = frontBlock.load(); 231 | blockTail = frontBlock_->localTail = frontBlock_->tail.load(); 232 | blockFront = frontBlock_->front.load(); 233 | fence(memory_order_acquire); 234 | 235 | if (blockFront != blockTail) { 236 | // Oh look, the front block isn't empty after all 237 | goto non_empty_front_block; 238 | } 239 | 240 | // Front block is empty but there's another block ahead, advance to it 241 | Block* nextBlock = frontBlock_->next; 242 | // Don't need an acquire fence here since next can only ever be set on the tailBlock, 243 | // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which 244 | // ensures next is up-to-date on this CPU in case we recently were at tailBlock. 245 | 246 | size_t nextBlockFront = nextBlock->front.load(); 247 | size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); 248 | fence(memory_order_acquire); 249 | 250 | // Since the tailBlock is only ever advanced after being written to, 251 | // we know there's for sure an element to dequeue on it 252 | assert(nextBlockFront != nextBlockTail); 253 | AE_UNUSED(nextBlockTail); 254 | 255 | // We're done with this block, let the producer use it if it needs 256 | fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue 257 | frontBlock = frontBlock_ = nextBlock; 258 | 259 | compiler_fence(memory_order_release); // Not strictly needed 260 | 261 | auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); 262 | 263 | result = std::move(*element); 264 | element->~T(); 265 | 266 | nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; 267 | 268 | fence(memory_order_release); 269 | frontBlock_->front = nextBlockFront; 270 | } 271 | else { 272 | // No elements in current block and no other block to advance to 273 | return false; 274 | } 275 | 276 | return true; 277 | } 278 | 279 | 280 | // Returns a pointer to the front element in the queue (the one that 281 | // would be removed next by a call to `try_dequeue` or `pop`). If the 282 | // queue appears empty at the time the method is called, nullptr is 283 | // returned instead. 284 | // Must be called only from the consumer thread. 285 | T* peek() 286 | { 287 | #ifndef NDEBUG 288 | ReentrantGuard guard(this->dequeuing); 289 | #endif 290 | // See try_dequeue() for reasoning 291 | 292 | Block* frontBlock_ = frontBlock.load(); 293 | size_t blockTail = frontBlock_->localTail; 294 | size_t blockFront = frontBlock_->front.load(); 295 | 296 | if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { 297 | fence(memory_order_acquire); 298 | non_empty_front_block: 299 | return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); 300 | } 301 | else if (frontBlock_ != tailBlock.load()) { 302 | fence(memory_order_acquire); 303 | frontBlock_ = frontBlock.load(); 304 | blockTail = frontBlock_->localTail = frontBlock_->tail.load(); 305 | blockFront = frontBlock_->front.load(); 306 | fence(memory_order_acquire); 307 | 308 | if (blockFront != blockTail) { 309 | goto non_empty_front_block; 310 | } 311 | 312 | Block* nextBlock = frontBlock_->next; 313 | 314 | size_t nextBlockFront = nextBlock->front.load(); 315 | fence(memory_order_acquire); 316 | 317 | assert(nextBlockFront != nextBlock->tail.load()); 318 | return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); 319 | } 320 | 321 | return nullptr; 322 | } 323 | 324 | // Removes the front element from the queue, if any, without returning it. 325 | // Returns true on success, or false if the queue appeared empty at the time 326 | // `pop` was called. 327 | bool pop() 328 | { 329 | #ifndef NDEBUG 330 | ReentrantGuard guard(this->dequeuing); 331 | #endif 332 | // See try_dequeue() for reasoning 333 | 334 | Block* frontBlock_ = frontBlock.load(); 335 | size_t blockTail = frontBlock_->localTail; 336 | size_t blockFront = frontBlock_->front.load(); 337 | 338 | if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { 339 | fence(memory_order_acquire); 340 | 341 | non_empty_front_block: 342 | auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); 343 | element->~T(); 344 | 345 | blockFront = (blockFront + 1) & frontBlock_->sizeMask; 346 | 347 | fence(memory_order_release); 348 | frontBlock_->front = blockFront; 349 | } 350 | else if (frontBlock_ != tailBlock.load()) { 351 | fence(memory_order_acquire); 352 | frontBlock_ = frontBlock.load(); 353 | blockTail = frontBlock_->localTail = frontBlock_->tail.load(); 354 | blockFront = frontBlock_->front.load(); 355 | fence(memory_order_acquire); 356 | 357 | if (blockFront != blockTail) { 358 | goto non_empty_front_block; 359 | } 360 | 361 | // Front block is empty but there's another block ahead, advance to it 362 | Block* nextBlock = frontBlock_->next; 363 | 364 | size_t nextBlockFront = nextBlock->front.load(); 365 | size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); 366 | fence(memory_order_acquire); 367 | 368 | assert(nextBlockFront != nextBlockTail); 369 | AE_UNUSED(nextBlockTail); 370 | 371 | fence(memory_order_release); 372 | frontBlock = frontBlock_ = nextBlock; 373 | 374 | compiler_fence(memory_order_release); 375 | 376 | auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); 377 | element->~T(); 378 | 379 | nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; 380 | 381 | fence(memory_order_release); 382 | frontBlock_->front = nextBlockFront; 383 | } 384 | else { 385 | // No elements in current block and no other block to advance to 386 | return false; 387 | } 388 | 389 | return true; 390 | } 391 | 392 | // Returns the approximate number of items currently in the queue. 393 | // Safe to call from both the producer and consumer threads. 394 | inline size_t size_approx() const 395 | { 396 | size_t result = 0; 397 | Block* frontBlock_ = frontBlock.load(); 398 | Block* block = frontBlock_; 399 | do { 400 | fence(memory_order_acquire); 401 | size_t blockFront = block->front.load(); 402 | size_t blockTail = block->tail.load(); 403 | result += (blockTail - blockFront) & block->sizeMask; 404 | block = block->next.load(); 405 | } while (block != frontBlock_); 406 | return result; 407 | } 408 | 409 | 410 | private: 411 | enum AllocationMode { CanAlloc, CannotAlloc }; 412 | 413 | template 414 | bool inner_enqueue(U&& element) 415 | { 416 | #ifndef NDEBUG 417 | ReentrantGuard guard(this->enqueuing); 418 | #endif 419 | 420 | // High-level pseudocode (assuming we're allowed to alloc a new block): 421 | // If room in tail block, add to tail 422 | // Else check next block 423 | // If next block is not the head block, enqueue on next block 424 | // Else create a new block and enqueue there 425 | // Advance tail to the block we just enqueued to 426 | 427 | Block* tailBlock_ = tailBlock.load(); 428 | size_t blockFront = tailBlock_->localFront; 429 | size_t blockTail = tailBlock_->tail.load(); 430 | 431 | size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; 432 | if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { 433 | fence(memory_order_acquire); 434 | // This block has room for at least one more element 435 | char* location = tailBlock_->data + blockTail * sizeof(T); 436 | new (location) T(std::forward(element)); 437 | 438 | fence(memory_order_release); 439 | tailBlock_->tail = nextBlockTail; 440 | } 441 | else { 442 | fence(memory_order_acquire); 443 | if (tailBlock_->next.load() != frontBlock) { 444 | // Note that the reason we can't advance to the frontBlock and start adding new entries there 445 | // is because if we did, then dequeue would stay in that block, eventually reading the new values, 446 | // instead of advancing to the next full block (whose values were enqueued first and so should be 447 | // consumed first). 448 | 449 | fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock 450 | 451 | // tailBlock is full, but there's a free block ahead, use it 452 | Block* tailBlockNext = tailBlock_->next.load(); 453 | size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); 454 | nextBlockTail = tailBlockNext->tail.load(); 455 | fence(memory_order_acquire); 456 | 457 | // This block must be empty since it's not the head block and we 458 | // go through the blocks in a circle 459 | assert(nextBlockFront == nextBlockTail); 460 | tailBlockNext->localFront = nextBlockFront; 461 | 462 | char* location = tailBlockNext->data + nextBlockTail * sizeof(T); 463 | new (location) T(std::forward(element)); 464 | 465 | tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; 466 | 467 | fence(memory_order_release); 468 | tailBlock = tailBlockNext; 469 | } 470 | else if (canAlloc == CanAlloc) { 471 | // tailBlock is full and there's no free block ahead; create a new block 472 | auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; 473 | auto newBlock = make_block(newBlockSize); 474 | if (newBlock == nullptr) { 475 | // Could not allocate a block! 476 | return false; 477 | } 478 | largestBlockSize = newBlockSize; 479 | 480 | new (newBlock->data) T(std::forward(element)); 481 | 482 | assert(newBlock->front == 0); 483 | newBlock->tail = newBlock->localTail = 1; 484 | 485 | newBlock->next = tailBlock_->next.load(); 486 | tailBlock_->next = newBlock; 487 | 488 | // Might be possible for the dequeue thread to see the new tailBlock->next 489 | // *without* seeing the new tailBlock value, but this is OK since it can't 490 | // advance to the next block until tailBlock is set anyway (because the only 491 | // case where it could try to read the next is if it's already at the tailBlock, 492 | // and it won't advance past tailBlock in any circumstance). 493 | 494 | fence(memory_order_release); 495 | tailBlock = newBlock; 496 | } 497 | else if (canAlloc == CannotAlloc) { 498 | // Would have had to allocate a new block to enqueue, but not allowed 499 | return false; 500 | } 501 | else { 502 | assert(false && "Should be unreachable code"); 503 | return false; 504 | } 505 | } 506 | 507 | return true; 508 | } 509 | 510 | 511 | // Disable copying 512 | ReaderWriterQueue(ReaderWriterQueue const&) { } 513 | 514 | // Disable assignment 515 | ReaderWriterQueue& operator=(ReaderWriterQueue const&) { } 516 | 517 | 518 | 519 | AE_FORCEINLINE static size_t ceilToPow2(size_t x) 520 | { 521 | // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 522 | --x; 523 | x |= x >> 1; 524 | x |= x >> 2; 525 | x |= x >> 4; 526 | for (size_t i = 1; i < sizeof(size_t); i <<= 1) { 527 | x |= x >> (i << 3); 528 | } 529 | ++x; 530 | return x; 531 | } 532 | 533 | template 534 | static AE_FORCEINLINE char* align_for(char* ptr) 535 | { 536 | const std::size_t alignment = std::alignment_of::value; 537 | return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; 538 | } 539 | private: 540 | #ifndef NDEBUG 541 | struct ReentrantGuard 542 | { 543 | ReentrantGuard(bool& _inSection) 544 | : inSection(_inSection) 545 | { 546 | assert(!inSection); 547 | if (inSection) { 548 | throw std::runtime_error("ReaderWriterQueue does not support enqueuing or dequeuing elements from other elements' ctors and dtors"); 549 | } 550 | 551 | inSection = true; 552 | } 553 | 554 | ~ReentrantGuard() { inSection = false; } 555 | 556 | private: 557 | ReentrantGuard& operator=(ReentrantGuard const&); 558 | 559 | private: 560 | bool& inSection; 561 | }; 562 | #endif 563 | 564 | struct Block 565 | { 566 | // Avoid false-sharing by putting highly contended variables on their own cache lines 567 | weak_atomic front; // (Atomic) Elements are read from here 568 | size_t localTail; // An uncontended shadow copy of tail, owned by the consumer 569 | 570 | char cachelineFiller0[CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; 571 | weak_atomic tail; // (Atomic) Elements are enqueued here 572 | size_t localFront; 573 | 574 | char cachelineFiller1[CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is) 575 | weak_atomic next; // (Atomic) 576 | 577 | char* data; // Contents (on heap) are aligned to T's alignment 578 | 579 | const size_t sizeMask; 580 | 581 | 582 | // size must be a power of two (and greater than 0) 583 | Block(size_t const& _size, char* _rawThis, char* _data) 584 | : front(0), localTail(0), tail(0), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis) 585 | { 586 | } 587 | 588 | private: 589 | // C4512 - Assignment operator could not be generated 590 | Block& operator=(Block const&); 591 | 592 | public: 593 | char* rawThis; 594 | }; 595 | 596 | 597 | static Block* make_block(size_t capacity) 598 | { 599 | // Allocate enough memory for the block itself, as well as all the elements it will contain 600 | auto size = sizeof(Block) + std::alignment_of::value - 1; 601 | size += sizeof(T) * capacity + std::alignment_of::value - 1; 602 | auto newBlockRaw = static_cast(std::malloc(size)); 603 | if (newBlockRaw == nullptr) { 604 | return nullptr; 605 | } 606 | 607 | auto newBlockAligned = align_for(newBlockRaw); 608 | auto newBlockData = align_for(newBlockAligned + sizeof(Block)); 609 | return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData); 610 | } 611 | 612 | private: 613 | weak_atomic frontBlock; // (Atomic) Elements are enqueued to this block 614 | 615 | char cachelineFiller[CACHE_LINE_SIZE - sizeof(weak_atomic)]; 616 | weak_atomic tailBlock; // (Atomic) Elements are dequeued from this block 617 | 618 | size_t largestBlockSize; 619 | 620 | #ifndef NDEBUG 621 | bool enqueuing; 622 | bool dequeuing; 623 | #endif 624 | }; 625 | 626 | // Like ReaderWriterQueue, but also providees blocking operations 627 | template 628 | class BlockingReaderWriterQueue 629 | { 630 | private: 631 | typedef ::moodycamel::ReaderWriterQueue ReaderWriterQueue; 632 | 633 | public: 634 | explicit BlockingReaderWriterQueue(size_t maxSize = 15) 635 | : inner(maxSize) 636 | { } 637 | 638 | 639 | // Enqueues a copy of element if there is room in the queue. 640 | // Returns true if the element was enqueued, false otherwise. 641 | // Does not allocate memory. 642 | AE_FORCEINLINE bool try_enqueue(T const& element) 643 | { 644 | if (inner.try_enqueue(element)) { 645 | sema.signal(); 646 | return true; 647 | } 648 | return false; 649 | } 650 | 651 | // Enqueues a moved copy of element if there is room in the queue. 652 | // Returns true if the element was enqueued, false otherwise. 653 | // Does not allocate memory. 654 | AE_FORCEINLINE bool try_enqueue(T&& element) 655 | { 656 | if (inner.try_enqueue(std::forward(element))) { 657 | sema.signal(); 658 | return true; 659 | } 660 | return false; 661 | } 662 | 663 | 664 | // Enqueues a copy of element on the queue. 665 | // Allocates an additional block of memory if needed. 666 | // Only fails (returns false) if memory allocation fails. 667 | AE_FORCEINLINE bool enqueue(T const& element) 668 | { 669 | if (inner.enqueue(element)) { 670 | sema.signal(); 671 | return true; 672 | } 673 | return false; 674 | } 675 | 676 | // Enqueues a moved copy of element on the queue. 677 | // Allocates an additional block of memory if needed. 678 | // Only fails (returns false) if memory allocation fails. 679 | AE_FORCEINLINE bool enqueue(T&& element) 680 | { 681 | if (inner.enqueue(std::forward(element))) { 682 | sema.signal(); 683 | return true; 684 | } 685 | return false; 686 | } 687 | 688 | 689 | // Attempts to dequeue an element; if the queue is empty, 690 | // returns false instead. If the queue has at least one element, 691 | // moves front to result using operator=, then returns true. 692 | template 693 | bool try_dequeue(U& result) 694 | { 695 | if (sema.tryWait()) { 696 | bool success = inner.try_dequeue(result); 697 | assert(success); 698 | AE_UNUSED(success); 699 | return true; 700 | } 701 | return false; 702 | } 703 | 704 | 705 | // Attempts to dequeue an element; if the queue is empty, 706 | // waits until an element is available, then dequeues it. 707 | template 708 | void wait_dequeue(U& result) 709 | { 710 | sema.wait(); 711 | bool success = inner.try_dequeue(result); 712 | AE_UNUSED(result); 713 | assert(success); 714 | AE_UNUSED(success); 715 | } 716 | 717 | 718 | // Returns a pointer to the front element in the queue (the one that 719 | // would be removed next by a call to `try_dequeue` or `pop`). If the 720 | // queue appears empty at the time the method is called, nullptr is 721 | // returned instead. 722 | // Must be called only from the consumer thread. 723 | AE_FORCEINLINE T* peek() 724 | { 725 | return inner.peek(); 726 | } 727 | 728 | // Removes the front element from the queue, if any, without returning it. 729 | // Returns true on success, or false if the queue appeared empty at the time 730 | // `pop` was called. 731 | AE_FORCEINLINE bool pop() 732 | { 733 | if (sema.tryWait()) { 734 | bool result = inner.pop(); 735 | assert(result); 736 | AE_UNUSED(result); 737 | return true; 738 | } 739 | return false; 740 | } 741 | 742 | // Returns the approximate number of items currently in the queue. 743 | // Safe to call from both the producer and consumer threads. 744 | AE_FORCEINLINE size_t size_approx() const 745 | { 746 | return sema.availableApprox(); 747 | } 748 | 749 | 750 | private: 751 | // Disable copying & assignment 752 | BlockingReaderWriterQueue(ReaderWriterQueue const&) { } 753 | BlockingReaderWriterQueue& operator=(ReaderWriterQueue const&) { } 754 | 755 | private: 756 | ReaderWriterQueue inner; 757 | spsc_sema::LightweightSemaphore sema; 758 | }; 759 | 760 | } // end namespace moodycamel 761 | 762 | #ifdef AE_VCPP 763 | #pragma warning(pop) 764 | #endif 765 | -------------------------------------------------------------------------------- /src/rest/request.cpp: -------------------------------------------------------------------------------- 1 | #include "request.h" 2 | #include "exceptions.h" 3 | #include 4 | 5 | namespace REST { 6 | 7 | const size_t Request::BUFFER_SIZE = 4096; 8 | 9 | Request::Request(int client, struct sockaddr_storage client_addr) : handle(client), addr(client_addr) { 10 | time = std::chrono::high_resolution_clock::now(); 11 | 12 | // TODO config 13 | struct timeval tv { 14 | .tv_sec = 30, 15 | .tv_usec = 0 16 | }; 17 | 18 | // set timeout for reading 19 | setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); 20 | } 21 | 22 | void Request::process() { 23 | std::string line; 24 | bool is_header = true; 25 | char buffer[BUFFER_SIZE]; 26 | ssize_t request_length; 27 | 28 | // receive data from client 29 | request_length = recv(handle, buffer, BUFFER_SIZE, 0); 30 | 31 | // handle timeout 32 | if (request_length <= 0) 33 | throw HTTP::RequestTimeout(); 34 | 35 | // parse each line 36 | std::istringstream request_stream(buffer); 37 | 38 | // first line is HTTP header 39 | std::getline(request_stream, line); 40 | parse_header(line); 41 | 42 | // parse headers 43 | while (std::getline(request_stream, line)) { 44 | // next lines are headers, strip them 45 | line.erase(line.find_last_not_of(" \n\r\t")+1); 46 | 47 | // if line is empty, then content should follow 48 | if (line.size() == 0) { 49 | is_header = false; 50 | break; 51 | } 52 | 53 | // extract name and value from header 54 | size_t colon; 55 | std::string name = line.substr(0, colon = line.find(":")); 56 | std::string value = line.substr(colon+1); 57 | value.erase(0, value.find_first_not_of(" \n\r\t")); 58 | 59 | headers.emplace(name, value); 60 | } 61 | 62 | // TODO: handle long headers (longer than buffer) 63 | // if has content 64 | if (!is_header) { 65 | // assume proper content length 66 | size_t content_length = header("Content-Length", 0); 67 | 68 | // TODO: config 69 | if (content_length > 1024 * 1024 * 128) 70 | throw HTTP::PayloadTooLarge(); 71 | 72 | if (content_length > 0) { 73 | raw.reserve(content_length); 74 | // read whats left in header 75 | length = std::min(content_length, (size_t)(request_length - request_stream.tellg())); 76 | raw.append(buffer + request_stream.tellg(), length); 77 | 78 | if (length < content_length) { 79 | char* large_buffer = new char[content_length]; 80 | 81 | // receive some more 82 | while (length < content_length) { 83 | memset(large_buffer, 0, content_length); 84 | ssize_t buffer_length = recv(handle, large_buffer, content_length, 0); 85 | 86 | if (buffer_length <= 0) 87 | throw HTTP::RequestTimeout(); 88 | 89 | raw.append(large_buffer, buffer_length); 90 | length += buffer_length; 91 | } 92 | 93 | delete[] large_buffer; 94 | } 95 | } 96 | } 97 | 98 | // if has some content 99 | if (!raw.empty()) { 100 | // try to parse it 101 | std::string ct = header("Content-Type", ""); 102 | 103 | if (!ct.empty()) { 104 | if (ct == "application/x-www-form-urlencoded") { 105 | parse_query_string(raw); 106 | } else 107 | if (ct == "application/json" || ct == "text/json") { 108 | try { 109 | data = nlohmann::json::parse(raw); 110 | } catch (...) { 111 | throw HTTP::BadRequest(); 112 | } 113 | } 114 | } 115 | } 116 | } 117 | 118 | void Request::parse_header(std::string const &line) { 119 | if (line.find("GET") == 0) { 120 | method = Method::GET; 121 | } else 122 | if (line.find("POST") == 0) { 123 | method = Method::POST; 124 | } else 125 | if (line.find("PUT") == 0) { 126 | method = Method::PUT; 127 | } else 128 | if (line.find("DELETE") == 0) { 129 | method = Method::DELETE; 130 | } else 131 | if (line.find("HEAD") == 0) { 132 | method = Method::HEAD; 133 | } else 134 | if (line.find("PATCH") == 0) { 135 | method = Method::PATCH; 136 | } else 137 | if (line.find("TRACE") == 0) { 138 | method = Method::TRACE; 139 | } else 140 | if (line.find("CONNECT") == 0) { 141 | method = Method::CONNECT; 142 | } else 143 | if (line.find("OPTIONS") == 0) { 144 | method = Method::OPTIONS; 145 | } 146 | 147 | size_t path_start = line.find_first_of(" ")+1; 148 | size_t path_end = line.rfind("HTTP/1.")-1; 149 | 150 | path = line.substr(path_start, path_end - path_start); 151 | 152 | // TODO config 153 | if (path.size() >= 2048) 154 | throw HTTP::URITooLong(); 155 | 156 | size_t path_query = path.find("?"); 157 | 158 | if (path_query != std::string::npos) { 159 | std::string query = path.substr(path_query+1, path.size() - path_query); 160 | 161 | parse_query_string(query); 162 | 163 | path.erase(path_query, query.size()+1); 164 | } 165 | } 166 | 167 | void Request::parse_query_string(std::string &query) { 168 | query.erase(query.find_last_not_of(" \n\r\t")+1); 169 | std::istringstream query_stream(query); 170 | std::string pair; 171 | while (std::getline(query_stream, pair, '&')) { 172 | std::string name, value; 173 | size_t value_position = pair.find("="); 174 | 175 | name = pair.substr(0, value_position); 176 | if (value_position != std::string::npos) 177 | value = pair.substr(value_position+1, pair.size() - value_position-1); 178 | 179 | parameters[Utils::uri_decode(name)] = Utils::uri_decode(value); 180 | } 181 | } 182 | 183 | Request::~Request() { 184 | } 185 | 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/rest/request.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_REQUEST_H 2 | #define REST_CPP_REQUEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "utils.h" 12 | #include "json.hpp" 13 | 14 | namespace REST { 15 | 16 | class Worker; 17 | class Response; 18 | /** 19 | * Basic REST request. 20 | * 21 | * Request defines request method, parameters 22 | * and headers. 23 | */ 24 | class Request { 25 | friend class Dispatcher; 26 | friend class Worker; 27 | friend class Response; 28 | 29 | struct lowercase_comparator { 30 | bool operator() (const std::string& lhs, const std::string& rhs) const { 31 | return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; 32 | } 33 | }; 34 | 35 | public: 36 | typedef struct { 37 | struct sockaddr_storage address; 38 | int handle; 39 | } client; 40 | enum class Method { GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, OPTIONS, PATCH, UNDEFINED }; 41 | 42 | ~Request(); 43 | 44 | Method method = Method::UNDEFINED; 45 | std::string path; 46 | 47 | std::map< std::string, std::string, lowercase_comparator > headers; 48 | std::map< std::string, std::string > parameters; 49 | 50 | std::string raw; 51 | size_t length = 0; 52 | 53 | std::string header(std::string const& key, const char default_value[]) const { 54 | auto h = headers.find(key); 55 | if (h == headers.end()) 56 | return default_value; 57 | else 58 | return h->second; 59 | } 60 | 61 | std::string header(std::string const& key, const std::string& default_value) const { 62 | auto h = headers.find(key); 63 | if (h == headers.end()) 64 | return default_value; 65 | else 66 | return h->second; 67 | } 68 | 69 | template 70 | T header(std::string const& key, const T& default_value) const { 71 | auto h = headers.find(key); 72 | if (h == headers.end()) 73 | return default_value; 74 | else 75 | return Utils::parse_string(h->second); 76 | } 77 | 78 | std::string parameter(std::string const& key, const char default_value[]) const { 79 | auto p = parameters.find(key); 80 | if (p == parameters.end()) 81 | return default_value; 82 | else 83 | return p->second; 84 | } 85 | 86 | std::string parameter(std::string const& key, const std::string& default_value) const { 87 | auto p = parameters.find(key); 88 | if (p == parameters.end()) 89 | return default_value; 90 | else 91 | return p->second; 92 | } 93 | 94 | template 95 | T parameter(std::string const& key, const T& default_value) const { 96 | auto p = parameters.find(key); 97 | if (p == parameters.end()) 98 | return default_value; 99 | else 100 | return Utils::parse_string(p->second); 101 | } 102 | 103 | nlohmann::json data; 104 | 105 | private: 106 | Request(int client, struct sockaddr_storage client_addr); 107 | void process(); 108 | 109 | const static size_t BUFFER_SIZE; 110 | 111 | void parse_header(std::string const &line); 112 | void parse_query_string(std::string &query); 113 | std::chrono::high_resolution_clock::time_point time; 114 | 115 | int handle; 116 | struct sockaddr_storage addr; 117 | }; 118 | 119 | } 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /src/rest/resource.cpp: -------------------------------------------------------------------------------- 1 | #include "resource.h" 2 | #include "feature.h" 3 | 4 | namespace REST { 5 | 6 | Resource::~Resource() { 7 | 8 | } 9 | 10 | void Resource::create() { 11 | throw HTTP::MethodNotAllowed(); 12 | } 13 | 14 | void Resource::read() { 15 | throw HTTP::MethodNotAllowed(); 16 | } 17 | 18 | void Resource::update() { 19 | throw HTTP::MethodNotAllowed(); 20 | } 21 | 22 | void Resource::destroy() { 23 | throw HTTP::MethodNotAllowed(); 24 | } 25 | 26 | void Resource::method(Request::Method method) { 27 | switch (method) { 28 | case Request::Method::POST: 29 | create(); 30 | break; 31 | case Request::Method::GET: 32 | read(); 33 | break; 34 | case Request::Method::PATCH: 35 | case Request::Method::PUT: 36 | update(); 37 | break; 38 | case Request::Method::DELETE: 39 | destroy(); 40 | break; 41 | default: 42 | throw HTTP::MethodNotAllowed(); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/rest/resource.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_RESOURCE_H 2 | #define REST_CPP_RESOURCE_H 3 | 4 | #include "service.h" 5 | 6 | namespace REST { 7 | 8 | class Resource : public virtual Service { 9 | public: 10 | virtual void create(); 11 | virtual void read(); 12 | virtual void update(); 13 | virtual void destroy(); 14 | 15 | //! \private 16 | virtual ~Resource() = 0; 17 | 18 | private: 19 | void method(Request::Method method) final; 20 | }; 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/rest/response.cpp: -------------------------------------------------------------------------------- 1 | #include "response.h" 2 | #include 3 | #include 4 | #include 5 | 6 | namespace REST { 7 | 8 | Response::Response(Request const &request, std::vector &s) : 9 | start_time(request.time), 10 | streamers(s), 11 | handle(request.handle) { 12 | headers["Content-Type"] = "text/plain; charset=utf-8"; 13 | headers["Connection"] = "close"; 14 | } 15 | 16 | void Response::error(HTTP::Error &error) { 17 | status = error.code(); 18 | status_message = error.what(); 19 | raw.clear(); 20 | data = { 21 | { "status", "error" }, 22 | { "error", { 23 | { "code", status }, 24 | { "message", status_message } 25 | }} 26 | }; 27 | } 28 | 29 | void Response::stream_async(std::function streamer) { 30 | stream(streamer, true); 31 | } 32 | 33 | void Response::stream(std::function streamer, bool async) { 34 | is_streamed = true; 35 | 36 | std::string content = "HTTP/1.1 " + std::to_string(status) + " " + status_message + "\r\n"; 37 | headers.emplace("Date", Utils::rfc1123_datetime(time(0))); 38 | 39 | // add headers 40 | for (auto header : headers) 41 | content += header.first + ": " + header.second + "\r\n"; 42 | 43 | content += "\r\n"; 44 | 45 | // send every byte 46 | ::send(handle, content.c_str(), content.size(), MSG_DONTWAIT | MSG_NOSIGNAL); 47 | 48 | if (async && streamers.capacity() > 0) { 49 | int h = handle; 50 | streamers.emplace_back([streamer, h]() { 51 | signal(SIGPIPE, SIG_IGN); 52 | try { 53 | // std::this_thread::yield(); 54 | streamer(h); 55 | close(h); 56 | } catch (...) { 57 | } 58 | }); 59 | } else { 60 | std::this_thread::yield(); 61 | streamer(handle); 62 | close(handle); 63 | } 64 | } 65 | 66 | size_t Response::send() { 67 | if (is_streamed) 68 | return 0; 69 | 70 | size_t bytes_sent = 0; 71 | 72 | std::string payload; 73 | 74 | if (!data.empty()) { 75 | headers["Content-Type"] = "application/json; charset=utf-8"; 76 | payload = data.dump(); 77 | } else { 78 | payload = raw; 79 | } 80 | 81 | // start http 82 | std::string content = "HTTP/1.1 " + std::to_string(status) + " " + status_message + "\r\n"; 83 | 84 | // content size 85 | headers["Server"] += ", took " + std::to_string(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count() / 1000.0f) + "ms"; 86 | headers.emplace("Content-Length", std::to_string(payload.size())); 87 | headers.emplace("Date", Utils::rfc1123_datetime(time(0))); 88 | 89 | // add headers 90 | for (auto header : headers) 91 | content += header.first + ": " + header.second + "\r\n"; 92 | 93 | content += "\r\n"; 94 | 95 | content += payload; 96 | 97 | // send every byte 98 | bytes_sent = ::send(handle, content.c_str(), content.size(), MSG_DONTWAIT | MSG_NOSIGNAL); 99 | 100 | // close connection with client 101 | close(handle); 102 | 103 | return bytes_sent; 104 | } 105 | 106 | void Response::cache(unsigned long t) { 107 | if (t > 0) { 108 | headers["Cache-Control"] = "max-age=" + std::to_string(t) + ", public"; 109 | headers["Expires"] = REST::Utils::rfc1123_datetime(time(0) + t); 110 | } else { 111 | headers["Cache-Control"] = "no-cache, no-store, must-revalidate"; 112 | } 113 | } 114 | 115 | Response::~Response() { 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/rest/response.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_RESPONSE_H 2 | #define REST_CPP_RESPONSE_H 3 | 4 | #ifdef __APPLE__ 5 | #define MSG_NOSIGNAL 0 6 | #endif 7 | 8 | #include "exceptions.h" 9 | #include "request.h" 10 | #include "json.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace REST { 20 | 21 | /** 22 | * Response holds response data such as JSON data 23 | * and status code. 24 | */ 25 | class Response { 26 | friend class Worker; 27 | 28 | public: 29 | ~Response(); 30 | 31 | 32 | int status = 200; 33 | std::string status_message = "OK"; 34 | std::string raw; 35 | std::map< std::string, std::string > headers; 36 | 37 | void cache(unsigned long t); 38 | void stream(std::function streamer, bool async=false); 39 | void stream_async(std::function streamer); 40 | 41 | nlohmann::json data; 42 | 43 | 44 | private: 45 | Response(Request const &request, std::vector &streamers); 46 | 47 | void error(HTTP::Error &e); 48 | size_t send(); 49 | 50 | std::chrono::high_resolution_clock::time_point start_time; 51 | 52 | std::vector &streamers; 53 | int handle; 54 | bool is_streamed = false; 55 | }; 56 | 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/rest/rest.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_REST_H 2 | #define REST_REST_H 3 | 4 | #define STR_QUOTE(name) #name 5 | #define STR(macro) STR_QUOTE(macro) 6 | 7 | #ifndef SERVER_BIND 8 | #define SERVER_BIND 0.0.0.0 9 | #endif 10 | 11 | #ifndef SERVER_PORT 12 | #define SERVER_PORT 8080 13 | #endif 14 | 15 | #ifndef SERVER_WORKERS 16 | #define SERVER_WORKERS 4 17 | #endif 18 | 19 | #ifndef WORKER_STREAMERS 20 | #define WORKER_STREAMERS 4 21 | #endif 22 | 23 | #ifdef SERVER_DISPATCHER_lc 24 | #define SERVER_DISPATCHER Dispatchers::LeastConnections 25 | #endif 26 | 27 | #ifdef SERVER_DISPATCHER_rr 28 | #define SERVER_DISPATCHER Dispatchers::RoundRobin 29 | #endif 30 | 31 | #ifdef SERVER_DISPATCHER_uf 32 | #define SERVER_DISPATCHER Dispatchers::Uniform 33 | #endif 34 | 35 | #ifndef SERVER_DISPATCHER 36 | #define SERVER_DISPATCHER Dispatchers::LeastConnections 37 | #endif 38 | 39 | #define create_service(NAME) void NAME(REST::Service* service) 40 | 41 | #include 42 | #include 43 | 44 | #include "exceptions.h" 45 | #include "server.h" 46 | 47 | /// \file 48 | 49 | using namespace REST::HTTP; 50 | 51 | void routes(REST::Router*); 52 | 53 | namespace REST { 54 | 55 | /** 56 | * @private 57 | */ 58 | REST::Server* server_instance; 59 | 60 | //! \private 61 | void main_stop_server(int a = 0) { 62 | delete server_instance; 63 | } 64 | 65 | int main(int argc, char **argv) { 66 | signal(SIGINT, main_stop_server); 67 | 68 | #ifndef SERVER_PATH 69 | std::cout << "Listening on " << STR(SERVER_BIND) << ":" << SERVER_PORT << ", " << SERVER_WORKERS << " workers (" << SERVER_WORKERS * WORKER_STREAMERS << " streamers), " << STR(SERVER_DISPATCHER) << "\n"; 70 | server_instance = new REST::Server(STR(SERVER_BIND), SERVER_PORT, new REST::SERVER_DISPATCHER(SERVER_WORKERS, WORKER_STREAMERS)); 71 | #else 72 | std::cout << "Listening on Unix socket " << STR(SERVER_PATH) << ", " << SERVER_WORKERS << " workers (" << SERVER_WORKERS * WORKER_STREAMERS <<" streamers), " << STR(SERVER_DISPATCHER) << "\n"; 73 | server_instance = new REST::Server(STR(SERVER_PATH), new REST::SERVER_DISPATCHER(SERVER_WORKERS, WORKER_STREAMERS)); 74 | #endif 75 | 76 | ::routes(server_instance->router()); 77 | 78 | server_instance->run(); 79 | 80 | return 0; 81 | } 82 | 83 | } 84 | 85 | #ifndef WITHOUT_MAIN 86 | int main(int argc, char **argv) { 87 | return REST::main(argc, argv); 88 | } 89 | #endif 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/rest/router.cpp: -------------------------------------------------------------------------------- 1 | #include "router.h" 2 | #include "service.h" 3 | #include "resource.h" 4 | #include "lambda_service.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace REST { 10 | Router::Router() { 11 | } 12 | 13 | Router::~Router() { 14 | // delete root; 15 | } 16 | 17 | void Router::print() { 18 | std::cout << "Available routes:\n"; 19 | 20 | for (auto& r : routes) { 21 | std::cout << " " << std::get<1>(r); 22 | 23 | auto service = std::get<2>(r)[0]; 24 | 25 | if (dynamic_cast< LambdaService* >(service) != nullptr) { 26 | std::cout << " - lambda"; 27 | } else 28 | if (dynamic_cast< Resource* >(service) != nullptr) { 29 | std::cout << " - resource"; 30 | } else { 31 | std::cout << " - service"; 32 | } 33 | 34 | if (service->features.size() > 0) { 35 | std::cout << " ("; 36 | int s = service->features.size(); 37 | 38 | for (auto feature : service->features) { 39 | std::cout << feature->feature_name(); 40 | if (--s) 41 | std::cout << ", "; 42 | } 43 | std::cout << ")"; 44 | } 45 | 46 | std::cout << std::endl; 47 | } 48 | } 49 | 50 | std::pair> Router::to_regex(std::string path, bool exact) { 51 | std::vector params; 52 | path = ((!path.empty()) && (path[0] != '/')) ? "/" + path : path; 53 | 54 | std::string regex = "^"; 55 | regex.reserve(path.size()); 56 | 57 | size_t start = 1; 58 | 59 | while (true) { 60 | regex += "/"; 61 | size_t next = path.find("/", start); 62 | 63 | std::string section = path.substr(start, next - start); 64 | std::string format; 65 | 66 | if (next >= path.size()+1) { 67 | size_t dot = section.find("."); 68 | 69 | if (dot != std::string::npos) { 70 | format = section.substr(dot); 71 | section = section.substr(0, dot); 72 | } 73 | } 74 | 75 | if (section[0] == ':') { 76 | regex += "([^/]+)"; 77 | params.emplace_back(section.substr(1)); 78 | } else 79 | if (section[0] == '*') { 80 | regex += "(.+)"; 81 | params.emplace_back(section.size() == 1 ? "*" : section.substr(1)); 82 | } else { 83 | regex += section; 84 | } 85 | 86 | if (next >= path.size()) { 87 | if (!format.empty()) 88 | regex += "\\" + format; 89 | break; 90 | } 91 | 92 | start = next + 1; 93 | } 94 | 95 | if (exact) { 96 | regex += "$"; 97 | } else { 98 | regex += "/?(.*)$"; 99 | params.emplace_back("*"); 100 | } 101 | 102 | return std::make_pair(std::regex(regex), params); 103 | } 104 | 105 | Service* Router::find(Request &request, int worker_id) { 106 | const std::string &path = request.path; 107 | 108 | for (const auto &route : instance()->routes) { 109 | std::smatch matches; 110 | std::regex_match(path, matches, std::get<0>(route).first); 111 | 112 | if (!matches.empty()) { 113 | const auto ¶ms = std::get<0>(route).second; 114 | 115 | for (size_t i = 1; i < matches.size(); i++) { 116 | request.parameters[params[i-1]] = matches[i]; 117 | request.parameters[std::to_string(i)] = matches[i]; 118 | } 119 | 120 | return std::get<2>(route)[worker_id]; 121 | } 122 | } 123 | 124 | return nullptr; 125 | } 126 | 127 | void Router::match(std::string path, LambdaService::function lambda) { 128 | auto r = to_regex(path, false); 129 | 130 | std::vector services; 131 | services.resize(Worker::POOL_SIZE); 132 | for (int i = 0; i < Worker::POOL_SIZE; i++) 133 | services[i] = new LambdaService(lambda); 134 | 135 | path = nested_path(path); 136 | 137 | routes.emplace_back(r, path, services); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/rest/router.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_ROUTER_H 2 | #define REST_CPP_ROUTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "service.h" 12 | #include "lambda_service.h" 13 | #include "worker.h" 14 | 15 | namespace REST { 16 | 17 | /** 18 | * Router resolves URL to Request::parameters and 19 | * finds Service corresponding to the URL. 20 | * 21 | * @see Request 22 | * @see Service 23 | */ 24 | class Router { 25 | public: 26 | inline static Router* instance() { 27 | static Router router; 28 | return &router; 29 | } 30 | 31 | static Service* find(Request&, int); 32 | 33 | void match(std::string, LambdaService::function); 34 | 35 | template 36 | void mount(std::string path, bool exact) { 37 | path = nested_path(path); 38 | 39 | auto r = to_regex(path, exact); 40 | 41 | std::vector services; 42 | services.resize(Worker::POOL_SIZE); 43 | for (int i = 0; i < Worker::POOL_SIZE; i++) 44 | services[i] = new R(); 45 | 46 | routes.emplace_back(r, path, services); 47 | } 48 | 49 | template 50 | void mount(std::string const& path) { 51 | mount(path, false); 52 | } 53 | 54 | template 55 | void resource(std::string const& path) { 56 | mount(path, true); 57 | } 58 | 59 | template 60 | void resource(std::string const& path, std::function subroutes) { 61 | nested_paths.push_back(path); 62 | subroutes(this); 63 | nested_paths.pop_back(); 64 | 65 | mount(path, true); 66 | } 67 | 68 | template 69 | void resources(std::string const& path) { 70 | mount(path); 71 | } 72 | 73 | template 74 | void resources() { 75 | resources(to_path()); 76 | } 77 | 78 | template 79 | void resources() { 80 | resources(); 81 | } 82 | 83 | template 84 | void resource() { 85 | resource(to_path()); 86 | } 87 | 88 | template 89 | void resource() { 90 | resource(); 91 | } 92 | 93 | void print(); 94 | 95 | ~Router(); 96 | 97 | private: 98 | std::pair> to_regex(std::string path, bool exact); 99 | 100 | template 101 | static std::string to_path() { 102 | std::string name = typeid(R).name(); 103 | std::string path = ""; 104 | path.reserve(name.size()); 105 | std::transform(name.begin(), name.end(), name.begin(), ::tolower); 106 | size_t number_position = 0; 107 | int strip = N; 108 | while ((number_position = name.find_first_of("0123456789")) != std::string::npos) { 109 | if (number_position != 0) 110 | name = name.substr(number_position); 111 | std::string number; 112 | 113 | for (size_t next_digit = 0; ::isdigit(name[next_digit]); next_digit++) 114 | number += name[next_digit]; 115 | 116 | size_t name_length = std::stol(number); 117 | 118 | name = name.substr(number.size()); 119 | if (strip <= 0) 120 | path += "/"+name.substr(0, name_length); 121 | else 122 | strip--; 123 | name = name.substr(name_length); 124 | } 125 | 126 | auto s = path.rfind("service"); 127 | if (s == (path.size() - 7)) 128 | path.erase(path.rfind("service")); 129 | 130 | s = path.rfind("resource"); 131 | if (s == (path.size() - 8)) 132 | path.erase(path.rfind("resource")); 133 | 134 | while (strip < 0) { 135 | strip++; 136 | 137 | //std::cout << path << std::endl; 138 | path.erase(path.rfind("/")); 139 | } 140 | 141 | return path; 142 | } 143 | 144 | Router(); 145 | 146 | std::vector>, std::string, std::vector>> routes; 147 | std::vector nested_paths; 148 | 149 | std::string nested_path(std::string const& path) { 150 | std::string np; 151 | for (auto& p : nested_paths) 152 | np += p; 153 | 154 | return np + path; 155 | } 156 | 157 | }; 158 | 159 | } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /src/rest/server.cpp: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | #include 3 | 4 | namespace REST { 5 | 6 | Router* Server::router() { 7 | return Router::instance(); 8 | } 9 | 10 | Server::Server(std::string path, Dispatcher* d) : dispatcher(d) { 11 | srand(time(0)); 12 | signal(SIGPIPE, SIG_IGN); 13 | Router::instance(); 14 | 15 | unlink(path.c_str()); 16 | struct sockaddr_un local; 17 | handle = socket(AF_UNIX, SOCK_STREAM, 0); 18 | local.sun_family = AF_UNIX; 19 | strcpy(local.sun_path, path.c_str()); 20 | 21 | if (bind(handle, (struct sockaddr *)&local, strlen(local.sun_path) + sizeof(local.sun_family) + 1) == -1) 22 | throw PortInUseError(); 23 | 24 | } 25 | 26 | Server::Server(std::string address, int port, Dispatcher* d) : dispatcher(d) { 27 | srand(time(0)); 28 | signal(SIGPIPE, SIG_IGN); 29 | Router::instance(); 30 | int status; 31 | 32 | memset(&host_info, 0, sizeof(host_info)); 33 | 34 | host_info.ai_family = AF_UNSPEC; 35 | host_info.ai_socktype = SOCK_STREAM; 36 | 37 | status = getaddrinfo(address.c_str(), std::to_string(port).c_str(), &host_info, &host_info_list); 38 | if (status != 0) 39 | throw AddressResolvingError(); 40 | 41 | handle = socket(host_info_list->ai_family, host_info_list->ai_socktype, host_info_list->ai_protocol); 42 | if (handle == -1) 43 | throw SocketCreationError(); 44 | 45 | int yes = 1; 46 | status = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); 47 | status = bind(handle, host_info_list->ai_addr, host_info_list->ai_addrlen); 48 | if (status == -1) 49 | throw PortInUseError(); 50 | } 51 | 52 | Server::~Server() { 53 | is_running = false; 54 | 55 | delete dispatcher; 56 | 57 | freeaddrinfo(host_info_list); 58 | close(handle); 59 | 60 | #ifdef SERVER_PATH 61 | unlink(STR(SERVER_PATH)); 62 | #endif 63 | } 64 | 65 | void Server::run() { 66 | int status; 67 | 68 | router()->print(); 69 | 70 | status = listen(handle, SOMAXCONN); 71 | if (status == -1) 72 | throw ServerError(); 73 | 74 | while (is_running) { 75 | Request::client client; 76 | socklen_t addr_size = sizeof(client.address); 77 | client.handle = accept(handle, (struct sockaddr *)&(client.address), &addr_size); 78 | 79 | try { 80 | if (client.handle == -1) 81 | throw ServerError(); 82 | 83 | dispatcher->next(client); 84 | } catch (Exception &e) { 85 | if (is_running) 86 | std::cerr << "!!! " << e.what() << std::endl; 87 | close(client.handle); 88 | } 89 | } 90 | } 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/rest/server.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_SERVER_H 2 | #define REST_CPP_SERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "exceptions.h" 16 | #include "dispatchers/roundrobin.h" 17 | #include "dispatchers/leastconnections.h" 18 | #include "dispatchers/uniform.h" 19 | #include "router.h" 20 | 21 | namespace REST { 22 | 23 | /** 24 | * Server creates socket and listens for incoming 25 | * connections. Connections are maintained by Dispatcher. 26 | * 27 | * @see Dispatcher 28 | */ 29 | class Server { 30 | 31 | public: 32 | Server(std::string path, Dispatcher* d); 33 | Server(std::string address, int port, Dispatcher* d); 34 | ~Server(); 35 | 36 | void run(); 37 | Router* router(); 38 | 39 | private: 40 | Dispatcher* dispatcher; 41 | 42 | bool is_running = true; 43 | 44 | struct addrinfo host_info; 45 | struct addrinfo* host_info_list; 46 | int handle; 47 | }; 48 | 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/rest/service.cpp: -------------------------------------------------------------------------------- 1 | #include "service.h" 2 | #include "feature.h" 3 | 4 | namespace REST { 5 | 6 | Service::Service() {} 7 | Service::~Service() {} 8 | 9 | void Service::before() {} 10 | void Service::after() {} 11 | 12 | void Service::method(Request::Method method) { 13 | throw HTTP::NotImplemented(); 14 | } 15 | 16 | void Service::make_action() { 17 | for (auto feature = features.cbegin(); feature != features.cend(); ++feature) { 18 | (*feature)->feature_push(); 19 | } 20 | 21 | before(); 22 | 23 | method(request->method); 24 | 25 | after(); 26 | 27 | for (auto feature = features.crbegin(); feature != features.crend(); ++feature) { 28 | (*feature)->feature_pop(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/rest/service.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_SERVICE_H 2 | #define REST_CPP_SERVICE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "request.h" 10 | #include "response.h" 11 | #include "exceptions.h" 12 | 13 | namespace REST { 14 | 15 | class Worker; 16 | class Feature; 17 | 18 | /** 19 | * Service provice RESTful stuff to other classes. 20 | * 21 | * Classes must inherit REST::Service to be open to public. 22 | */ 23 | class Service { 24 | friend class Worker; 25 | friend class Router; 26 | 27 | public: 28 | Service(); 29 | //! \private 30 | virtual ~Service() = 0; 31 | 32 | Response* response; 33 | Request* request; 34 | 35 | protected: 36 | std::vector< Feature* > features; 37 | 38 | virtual void make_action() final; 39 | 40 | virtual void before(); 41 | virtual void after(); 42 | 43 | virtual void method(Request::Method method); 44 | }; 45 | 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/rest/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | 5 | namespace REST { 6 | namespace Utils { 7 | 8 | const static char DEC2HEX[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 9 | 10 | std::string random_uuid() { 11 | std::string uuid(36, ' '); 12 | 13 | for (int i = 0; i < 36; i++) { 14 | switch (i) { 15 | case 8: 16 | case 13: 17 | case 18: 18 | case 23: 19 | uuid[i] = '-'; 20 | break; 21 | case 14: 22 | uuid[i] = '4'; 23 | break; 24 | case 19: 25 | uuid[i] = DEC2HEX[rand()%4 + 8]; 26 | break; 27 | default: 28 | uuid[i] = DEC2HEX[rand()%16]; 29 | } 30 | } 31 | 32 | return uuid; 33 | } 34 | 35 | // Source: http://www.codeguru.com/cpp/cpp/algorithms/strings/article.php/c12759/URI-Encoding-and-Decoding.htm 36 | 37 | const static char HEX2DEC[256] = 38 | { 39 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 40 | /* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 41 | /* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 42 | /* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 43 | /* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, 44 | 45 | /* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 46 | /* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 47 | /* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 48 | /* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 49 | 50 | /* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 51 | /* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 52 | /* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 53 | /* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 54 | 55 | /* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 56 | /* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 57 | /* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 58 | /* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 59 | }; 60 | 61 | std::string uri_decode(const std::string & sSrc) { 62 | // Note from RFC1630: "Sequences which start with a percent 63 | // sign but are not followed by two hexadecimal characters 64 | // (0-9, A-F) are reserved for future extension" 65 | 66 | const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); 67 | const int SRC_LEN = sSrc.length(); 68 | const unsigned char * const SRC_END = pSrc + SRC_LEN; 69 | // last decodable '%' 70 | const unsigned char * const SRC_LAST_DEC = SRC_END - 2; 71 | 72 | char * const pStart = new char[SRC_LEN]; 73 | char * pEnd = pStart; 74 | 75 | while (pSrc < SRC_LAST_DEC) 76 | { 77 | if (*pSrc == '%') 78 | { 79 | char dec1, dec2; 80 | if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)]) 81 | && -1 != (dec2 = HEX2DEC[*(pSrc + 2)])) 82 | { 83 | *pEnd++ = (dec1 << 4) + dec2; 84 | pSrc += 3; 85 | continue; 86 | } 87 | } 88 | 89 | *pEnd++ = *pSrc++; 90 | } 91 | 92 | // the last 2- chars 93 | while (pSrc < SRC_END) 94 | *pEnd++ = *pSrc++; 95 | 96 | std::string sResult(pStart, pEnd); 97 | delete [] pStart; 98 | return sResult; 99 | } 100 | 101 | std::string rfc1123_datetime( time_t time ) { 102 | struct tm * timeinfo; 103 | char buffer [80]; 104 | 105 | timeinfo = gmtime ( &time ); 106 | strftime (buffer,80,"%a, %d %b %Y %H:%M:%S GMT",timeinfo); 107 | 108 | return buffer; 109 | } 110 | 111 | // from http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c 112 | 113 | static const std::string base64_chars = 114 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 115 | "abcdefghijklmnopqrstuvwxyz" 116 | "0123456789+/"; 117 | 118 | 119 | static inline bool is_base64(unsigned char c) { 120 | return (isalnum(c) || (c == '+') || (c == '/')); 121 | } 122 | 123 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { 124 | std::string ret; 125 | int i = 0; 126 | int j = 0; 127 | unsigned char char_array_3[3]; 128 | unsigned char char_array_4[4]; 129 | 130 | while (in_len--) { 131 | char_array_3[i++] = *(bytes_to_encode++); 132 | if (i == 3) { 133 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 134 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 135 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 136 | char_array_4[3] = char_array_3[2] & 0x3f; 137 | 138 | for(i = 0; (i <4) ; i++) 139 | ret += base64_chars[char_array_4[i]]; 140 | i = 0; 141 | } 142 | } 143 | 144 | if (i) 145 | { 146 | for(j = i; j < 3; j++) 147 | char_array_3[j] = '\0'; 148 | 149 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 150 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 151 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 152 | char_array_4[3] = char_array_3[2] & 0x3f; 153 | 154 | for (j = 0; (j < i + 1); j++) 155 | ret += base64_chars[char_array_4[j]]; 156 | 157 | while((i++ < 3)) 158 | ret += '='; 159 | 160 | } 161 | 162 | return ret; 163 | 164 | } 165 | std::string base64_decode(std::string const& encoded_string) { 166 | int in_len = encoded_string.size(); 167 | int i = 0; 168 | int j = 0; 169 | int in_ = 0; 170 | unsigned char char_array_4[4], char_array_3[3]; 171 | std::string ret; 172 | 173 | while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 174 | char_array_4[i++] = encoded_string[in_]; in_++; 175 | if (i ==4) { 176 | for (i = 0; i <4; i++) 177 | char_array_4[i] = base64_chars.find(char_array_4[i]); 178 | 179 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 180 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 181 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 182 | 183 | for (i = 0; (i < 3); i++) 184 | ret += char_array_3[i]; 185 | i = 0; 186 | } 187 | } 188 | 189 | if (i) { 190 | for (j = i; j <4; j++) 191 | char_array_4[j] = 0; 192 | 193 | for (j = 0; j <4; j++) 194 | char_array_4[j] = base64_chars.find(char_array_4[j]); 195 | 196 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 197 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 198 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 199 | 200 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 201 | } 202 | 203 | return ret; 204 | } 205 | 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/rest/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_UTILS_H 2 | #define REST_CPP_UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace REST { 9 | namespace Utils { 10 | 11 | template 12 | R parse_string(const S& str) { 13 | std::stringstream buf; 14 | buf << str; 15 | R val; 16 | buf >> val; 17 | return val; 18 | } 19 | 20 | std::string random_uuid(); 21 | std::string uri_decode(const std::string& src); 22 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); 23 | std::string base64_decode(std::string const& encoded_string); 24 | std::string rfc1123_datetime(time_t time); 25 | 26 | } 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/rest/worker.cpp: -------------------------------------------------------------------------------- 1 | #include "worker.h" 2 | #include "service.h" 3 | #include "router.h" 4 | 5 | #include 6 | 7 | namespace REST { 8 | 9 | int Worker::POOL_SIZE = 256; 10 | 11 | Worker::Worker(int i, int sc) : 12 | id(i), streamers_count(sc), clients_queue(1024) { 13 | THREAD_NAME("rest-cpp - main thread"); 14 | clients_count.store(0); 15 | server_header = "rest-cpp, worker " + std::to_string(id); 16 | streamers.reserve(sc); 17 | run(); 18 | } 19 | 20 | void Worker::enqueue(Request::client const& client) { 21 | clients_queue.enqueue(std::move(client)); 22 | clients_count++; 23 | } 24 | 25 | void Worker::run() { 26 | should_run = true; 27 | 28 | thread = std::thread([this] () { 29 | THREAD_NAME(server_header.c_str()); 30 | 31 | // while worker is alive 32 | while (should_run) { 33 | Request::client client; 34 | 35 | clients_queue.wait_dequeue(client); 36 | 37 | // make request 38 | Request request(client.handle, client.address); 39 | 40 | if (!should_run) 41 | break; 42 | 43 | Response response(request, streamers); 44 | response.headers["Server"] = server_header + ", waiting " + std::to_string(clients_count); 45 | 46 | try { 47 | request.process(); 48 | 49 | make_action(request, response); 50 | } catch (HTTP::Error &e) { 51 | response.error(e); 52 | } 53 | 54 | response.send(); 55 | 56 | clear_streamers(); 57 | 58 | clients_count--; 59 | total_clients_count++; 60 | } 61 | 62 | std::cout << "Stopped worker #" << id << " with " << clients_count << " clients in queue (processed " << total_clients_count << ")" << std::endl; 63 | }); 64 | } 65 | 66 | void Worker::clear_streamers(bool force) { 67 | if (streamers.size() >= streamers_count || force) { 68 | for (auto& s : streamers) 69 | s.join(); 70 | streamers.clear(); 71 | } 72 | } 73 | 74 | 75 | void Worker::make_action(Request &request, Response &response) { 76 | Service* service = Router::find(request, id); 77 | 78 | if (service == nullptr) 79 | throw HTTP::NotFound(); 80 | 81 | service->request = &request; 82 | service->response = &response; 83 | 84 | service->make_action(); 85 | } 86 | 87 | void Worker::stop() { 88 | should_run = false; 89 | clear_streamers(); 90 | Request::client c; 91 | clients_queue.enqueue(c); 92 | thread.join(); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/rest/worker.h: -------------------------------------------------------------------------------- 1 | #ifndef REST_CPP_WORKER_H 2 | #define REST_CPP_WORKER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "readerwriterqueue/readerwriterqueue.h" 9 | 10 | #include "exceptions.h" 11 | #include "response.h" 12 | #include "request.h" 13 | 14 | #include 15 | #ifdef __APPLE__ 16 | #define THREAD_NAME(x) pthread_setname_np((x)) 17 | #else 18 | #define THREAD_NAME(x) pthread_setname_np(pthread_self(), (x)) 19 | #endif 20 | 21 | namespace REST { 22 | 23 | /** 24 | * Worker is single operating thread. Worker can process only 25 | * one request at a time. 26 | * 27 | * @private 28 | * @see Dispatcher 29 | */ 30 | class Worker final { 31 | friend class Dispatcher; 32 | public: 33 | Worker(int id, int sc); 34 | 35 | void make_action(Request &request, Response &response); 36 | void stop(); 37 | 38 | std::atomic_uint clients_count; 39 | unsigned long long total_clients_count = 0; 40 | 41 | void enqueue(Request::client const &client); 42 | 43 | static int POOL_SIZE; 44 | private: 45 | void run(); 46 | inline void clear_streamers(bool force = false); 47 | std::string server_header; 48 | 49 | int id; 50 | bool should_run; 51 | 52 | unsigned int streamers_count; 53 | 54 | std::thread thread; 55 | std::vector streamers; 56 | 57 | moodycamel::BlockingReaderWriterQueue clients_queue; 58 | }; 59 | 60 | } 61 | 62 | #endif 63 | --------------------------------------------------------------------------------